From a4635fb95235ba4bf077bd59957da0626fc5ba72 Mon Sep 17 00:00:00 2001 From: Benjamin Barenblat Date: Tue, 14 Dec 2021 12:34:06 -0500 Subject: EC, a terminal-based RPN calculator --- src/builtin.cc | 180 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 180 insertions(+) create mode 100644 src/builtin.cc (limited to 'src/builtin.cc') diff --git a/src/builtin.cc b/src/builtin.cc new file mode 100644 index 0000000..e182edd --- /dev/null +++ b/src/builtin.cc @@ -0,0 +1,180 @@ +// Copyright 2021 Benjamin Barenblat +// +// Licensed under the Apache License, Version 2.0 (the "License"); you may not +// use this file except in compliance with the License. You may obtain a copy of +// the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +// License for the specific language governing permissions and limitations under +// the License. + +#include "src/builtin.h" + +#include + +#include +#include +#include + +#include "src/language.h" +#include "third_party/abseil/absl/container/flat_hash_map.h" +#include "third_party/abseil/absl/memory/memory.h" +#include "third_party/abseil/absl/strings/string_view.h" + +namespace ec { + +namespace { + +// Requires the compiler to select the double-valued overload of its argument. +double (*DoubleOverload(double (*f)(double)) noexcept)(double) { return f; } +double (*DoubleOverload(double (*f)(double, double)) noexcept)(double, double) { + return f; +} + +// Pops the top of the stack and returns it as a GroundTerm; throws TypeError if +// it's not. +std::shared_ptr PopGroundTerm(absl::string_view op, + State& s) { + if (s.stack.empty()) { + throw StackUnderflow(op); + } + auto ground = std::dynamic_pointer_cast(s.stack.back()); + if (ground == nullptr) { + throw TypeError( + absl::StrCat("expected number; got ", s.stack.back()->Show())); + } + s.stack.pop_back(); + return ground; +} + +// Executes a unary operation on the top of the stack. +void Unop(absl::string_view op, std::function f, State& s) { + if (s.stack.empty()) { + throw StackUnderflow(op); + } + s.stack.push_back(GroundTerm::Make(f(PopGroundTerm(op, s)->value()))); +} + +// Executes a binary operation on the top of the stack. +void Binop(absl::string_view op, std::function f, + State& s) { + if (s.stack.size() < 2) { + throw StackUnderflow(op); + } + std::shared_ptr right, left; + right = PopGroundTerm(op, s); + left = PopGroundTerm(op, s); + s.stack.push_back(GroundTerm::Make(f(left->value(), right->value()))); +} + +} // namespace + +absl::flat_hash_map> +BuiltinEnvironment() noexcept { + return { + {"dup", ForeignProgramTerm::Make(BuiltinDup)}, // + {"drop", ForeignProgramTerm::Make(BuiltinDrop)}, // + {"swap", ForeignProgramTerm::Make(BuiltinSwap)}, + + {"pi", GroundTerm::Make(M_PI)}, // + {"e", GroundTerm::Make(M_E)}, + + {"neg", ForeignProgramTerm::Make(BuiltinNeg)}, // + {"inv", ForeignProgramTerm::Make(BuiltinInv)}, // + {"sq", ForeignProgramTerm::Make(BuiltinSq)}, // + {"sqrt", ForeignProgramTerm::Make(BuiltinSqrt)}, // + {"alog", ForeignProgramTerm::Make(BuiltinAlog)}, // + {"log", ForeignProgramTerm::Make(BuiltinLog)}, // + {"exp", ForeignProgramTerm::Make(BuiltinExp)}, // + {"ln", ForeignProgramTerm::Make(BuiltinLn)}, // + {"sin", ForeignProgramTerm::Make(BuiltinSin)}, // + {"cos", ForeignProgramTerm::Make(BuiltinCos)}, // + {"tan", ForeignProgramTerm::Make(BuiltinTan)}, // + {"asin", ForeignProgramTerm::Make(BuiltinAsin)}, // + {"acos", ForeignProgramTerm::Make(BuiltinAcos)}, // + {"atan", ForeignProgramTerm::Make(BuiltinAtan)}, // + {"abs", ForeignProgramTerm::Make(BuiltinAbs)}, + + {"add", ForeignProgramTerm::Make(BuiltinAdd)}, // + {"sub", ForeignProgramTerm::Make(BuiltinSub)}, // + {"mul", ForeignProgramTerm::Make(BuiltinMul)}, // + {"div", ForeignProgramTerm::Make(BuiltinDiv)}, // + {"pow", ForeignProgramTerm::Make(BuiltinPow)}, // + {"xroot", ForeignProgramTerm::Make(BuiltinXroot)}, + }; +} + +void BuiltinDup(State& s) { + if (s.stack.empty()) { + throw StackUnderflow("dup"); + } + s.stack.push_back(s.stack.back()->Clone()); +} + +void BuiltinDrop(State& s) { + if (s.stack.empty()) { + throw StackUnderflow("drop"); + } + s.stack.pop_back(); +} + +void BuiltinSwap(State& s) { + if (s.stack.size() < 2) { + throw StackUnderflow("swap"); + } + std::shared_ptr x, y; + x = s.stack.back(); + s.stack.pop_back(); + y = s.stack.back(); + s.stack.pop_back(); + s.stack.insert(s.stack.end(), {x, y}); +} + +void BuiltinNeg(State& s) { Unop("neg", std::negate(), s); } + +void BuiltinInv(State& s) { + Unop( + "inv", [](double d) { return 1 / d; }, s); +} + +void BuiltinSq(State& s) { + Unop( + "sq", [](double d) { return d * d; }, s); +} +void BuiltinSqrt(State& s) { Unop("sqrt", DoubleOverload(sqrt), s); } + +void BuiltinAlog(State& s) { + Unop( + "alog", [](double d) { return pow(10, d); }, s); +} +void BuiltinLog(State& s) { Unop("log", DoubleOverload(log10), s); } + +void BuiltinExp(State& s) { Unop("exp", DoubleOverload(exp), s); } +void BuiltinLn(State& s) { Unop("ln", DoubleOverload(log), s); } + +void BuiltinSin(State& s) { Unop("sin", DoubleOverload(sin), s); } +void BuiltinCos(State& s) { Unop("cos", DoubleOverload(cos), s); } +void BuiltinTan(State& s) { Unop("tan", DoubleOverload(tan), s); } + +void BuiltinAsin(State& s) { Unop("asin", DoubleOverload(asin), s); } +void BuiltinAcos(State& s) { Unop("acos", DoubleOverload(acos), s); } +void BuiltinAtan(State& s) { Unop("atan", DoubleOverload(atan), s); } + +void BuiltinAbs(State& s) { Unop("abs", DoubleOverload(fabs), s); } + +void BuiltinAdd(State& s) { Binop("add", std::plus(), s); } +void BuiltinSub(State& s) { Binop("sub", std::minus(), s); } +void BuiltinMul(State& s) { Binop("mul", std::multiplies(), s); } +void BuiltinDiv(State& s) { Binop("div", std::divides(), s); } + +void BuiltinPow(State& s) { Binop("div", DoubleOverload(pow), s); } +void BuiltinXroot(State& s) { + Binop( + "xroot", [](double y, double x) { return pow(y, 1 / x); }, s); +} + +} // namespace ec -- cgit v1.2.3