aboutsummaryrefslogtreecommitdiff
path: root/src/builtin.cc
diff options
context:
space:
mode:
Diffstat (limited to 'src/builtin.cc')
-rw-r--r--src/builtin.cc180
1 files changed, 180 insertions, 0 deletions
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 <math.h>
+
+#include <functional>
+#include <memory>
+#include <string>
+
+#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<const GroundTerm> PopGroundTerm(absl::string_view op,
+ State& s) {
+ if (s.stack.empty()) {
+ throw StackUnderflow(op);
+ }
+ auto ground = std::dynamic_pointer_cast<const GroundTerm>(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<double(double)> 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<double(double, double)> f,
+ State& s) {
+ if (s.stack.size() < 2) {
+ throw StackUnderflow(op);
+ }
+ std::shared_ptr<const GroundTerm> 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<std::string, std::shared_ptr<const Term>>
+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<const Term> 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<double>(), 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<double>(), s); }
+void BuiltinSub(State& s) { Binop("sub", std::minus<double>(), s); }
+void BuiltinMul(State& s) { Binop("mul", std::multiplies<double>(), s); }
+void BuiltinDiv(State& s) { Binop("div", std::divides<double>(), 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