commit 92f433cb3835b8b0b70ecc682b69f1b3d231ac8d
parent 3ed539e164b4d88fb9599411cd085e6fcac61081
Author: Mohammad-Reza Nabipoor <mnabipoor@gnu.org>
Date: Sat, 28 May 2022 00:49:13 +0430
Add ast and cgen
Diffstat:
A | .gitignore | | | 1 | + |
A | Makefile | | | 15 | +++++++++++++++ |
A | _clang-format | | | 2 | ++ |
A | ast.hpp | | | 166 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
A | ast.test.cpp | | | 106 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
A | cgen.hpp | | | 103 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
A | cgen.test.cpp | | | 54 | ++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
7 files changed, 447 insertions(+), 0 deletions(-)
diff --git a/.gitignore b/.gitignore
@@ -0,0 +1 @@
+*.test
diff --git a/Makefile b/Makefile
@@ -0,0 +1,15 @@
+
+CXXFLAGS += -Wall -Wextra -std=c++17 -g3 -fsanitize=address
+CPPFLAGS += -I.
+
+BINS = ast.test cgen.test
+
+all: $(BINS)
+
+ast.test: ast.test.cpp
+cgen.test: cgen.test.cpp
+
+
+.PHONY: clean
+clean:
+ rm -f $(BINS)
diff --git a/_clang-format b/_clang-format
@@ -0,0 +1,2 @@
+BasedOnStyle: Mozilla
+Language: Cpp
diff --git a/ast.hpp b/ast.hpp
@@ -0,0 +1,166 @@
+
+#pragma once
+
+#include <ostream>
+#include <string>
+#include <utility>
+#include <variant>
+
+#include <stlab/forest.hpp>
+
+namespace ast {
+
+struct Num
+{
+ double num{};
+};
+
+struct Op
+{
+ char op;
+};
+
+struct Id
+{
+ std::string id;
+};
+
+struct Str
+{
+ std::string str;
+};
+
+struct As
+{
+ Id id;
+};
+
+struct Call
+{
+ Id id;
+};
+
+using Node = std::variant<Num, Op, Id, Str, As, Call>;
+
+std::ostream&
+operator<<(std::ostream& os, const Num& n)
+{
+ os << "Num{ " << n.num << " }";
+ return os;
+}
+
+std::ostream&
+operator<<(std::ostream& os, const Op& o)
+{
+ os << "Op{ " << o.op << " }";
+ return os;
+}
+
+std::ostream&
+operator<<(std::ostream& os, const Id& i)
+{
+ os << "Id{ " << i.id << " }";
+ return os;
+}
+
+std::ostream&
+operator<<(std::ostream& os, const Str& s)
+{
+ os << "Str{ \"" << s.str << "\" }";
+ return os;
+}
+
+std::ostream&
+operator<<(std::ostream& os, const As& a)
+{
+ os << "As{ " << a.id << " }";
+ return os;
+}
+
+std::ostream&
+operator<<(std::ostream& os, const Call& c)
+{
+ os << "Call{ " << c.id << " }";
+ return os;
+}
+
+std::ostream&
+operator<<(std::ostream& os, const Node& n)
+{
+ if (std::holds_alternative<Num>(n))
+ os << std::get<Num>(n);
+ else if (std::holds_alternative<Op>(n))
+ os << std::get<Op>(n);
+ else if (std::holds_alternative<Id>(n))
+ os << std::get<Id>(n);
+ else if (std::holds_alternative<Str>(n))
+ os << std::get<Str>(n);
+ else if (std::holds_alternative<As>(n))
+ os << std::get<As>(n);
+ else if (std::holds_alternative<Call>(n))
+ os << std::get<Call>(n);
+ return os;
+}
+
+using Nodes = stlab::forest<Node>;
+using NodesIter = Nodes::iterator;
+
+Nodes
+mknum(double n)
+{
+ Nodes f;
+
+ f.insert(f.end(), Num{ n });
+ return f;
+}
+
+Nodes
+mkop(Op op, Nodes lhs, Nodes rhs)
+{
+ Nodes f;
+ auto it{ stlab::trailing_of(f.insert(f.end(), std::move(op))) };
+
+ f.splice(it, lhs);
+ f.splice(it, rhs);
+ return f;
+}
+
+Nodes
+mkid(Id id)
+{
+ Nodes f;
+
+ f.insert(f.end(), std::move(id));
+ return f;
+}
+
+Nodes
+mkstr(Str str)
+{
+ Nodes f;
+
+ f.insert(f.end(), std::move(str));
+ return f;
+}
+
+Nodes
+mkass(Id id, Nodes expr)
+{
+ Nodes f;
+ auto it{ stlab::trailing_of(f.insert(f.end(), ast::As{ std::move(id) })) };
+
+ f.splice(it, expr);
+ return f;
+}
+
+Nodes
+mkcall(Id id, Nodes args)
+{
+ Nodes f;
+ auto it{ stlab::trailing_of(f.insert(f.end(), ast::Call{ std::move(id) })) };
+
+ f.splice(it, args);
+ return f;
+}
+
+} // namespace ast
diff --git a/ast.test.cpp b/ast.test.cpp
@@ -0,0 +1,106 @@
+
+#include "ast.hpp"
+
+#include <cassert>
+#include <iostream>
+#include <sstream>
+
+namespace {
+
+void
+ast_1()
+{
+ auto i{ 0 };
+ auto post = [](const auto& f) {
+ std::ostringstream oss;
+
+ for (const auto& n : stlab::postorder_range(f))
+ oss << n << ' ';
+ return oss.str();
+ };
+ auto p = [&](const auto& s) {
+ std::cout << "# " << i << " {\n" << s << "\n# " << i << " }\n";
+ ++i;
+ };
+
+ ast::Nodes binop1{ ast::mkop(ast::Op{ '+' }, ast::mknum(1), ast::mknum(2)) };
+
+ {
+ auto s{ post(binop1) };
+
+ p(s);
+ assert(s == "Num{ 1 } Num{ 2 } Op{ + } ");
+ }
+
+ ast::Nodes binop2{ ast::mkop(
+ ast::Op{ '+' },
+ ast::mkop(ast::Op{ '/' }, ast::mknum(1), ast::mknum(2)),
+ ast::mkop(ast::Op{ '*' }, ast::mknum(3), ast::mknum(4))) };
+
+ {
+ auto s{ post(binop2) };
+
+ p(s);
+ assert(s == "Num{ 1 } Num{ 2 } Op{ / } Num{ 3 } Num{ 4 } Op{ * } Op{ + } ");
+ }
+
+ ast::Nodes binop3{ ast::mkop(
+ ast::Op{ '+' },
+ binop2,
+ ast::mkop(ast::Op{ '-' }, ast::mknum(5), ast::mknum(6))) };
+
+ {
+ auto s{ post(binop3) };
+
+ p(s);
+ assert(s ==
+ "Num{ 1 } Num{ 2 } Op{ / } Num{ 3 } Num{ 4 } Op{ * } Op{ + } Num{ 5 "
+ "} Num{ 6 } Op{ - } Op{ + } ");
+ }
+
+ ast::Nodes ass1{ ast::mkass(
+ ast::Id{ "x" },
+ ast::mkop(ast::Op{ '+' },
+ ast::mkop(ast::Op{ '/' }, ast::mknum(1), ast::mknum(2)),
+ ast::mkop(ast::Op{ '*' }, ast::mknum(3), ast::mknum(4)))) };
+
+ {
+ auto s{ post(ass1) };
+
+ p(s);
+ assert(s ==
+ "Num{ 1 } Num{ 2 } Op{ / } Num{ 3 } Num{ 4 } Op{ * } Op{ + } As{ "
+ "Id{ x } } ");
+ }
+
+ ast::Nodes ass2{ ast::mkass(ast::Id{ "y" },
+ ast::mkop(ast::Op{ '+' }, binop1, binop2)) };
+
+ {
+ auto s{ post(ass2) };
+
+ p(s);
+ assert(s ==
+ "Num{ 1 } Num{ 2 } Op{ + } Num{ 1 } Num{ 2 } Op{ / } Num{ 3 } Num{ "
+ "4 } Op{ * } Op{ + } Op{ + } As{ Id{ y } } ");
+ }
+
+ ast::Nodes eval1{ ast::mkcall(ast::Id{ "eval" },
+ ast::mkstr(ast::Str{ "y = 1 + x" })) };
+
+ {
+ auto s{ post(eval1) };
+
+ p(s);
+ assert(s == "Str{ \"y = 1 + x\" } Call{ Id{ eval } } ");
+ }
+}
+
+} // anonymous namespace
+
+int
+main()
+{
+ ast_1();
+ return 0;
+}
diff --git a/cgen.hpp b/cgen.hpp
@@ -0,0 +1,103 @@
+
+#pragma once
+
+#include <sstream>
+
+#include "ast.hpp"
+
+namespace cgen {
+
+template<typename... Ts>
+struct overloaded : Ts...
+{
+ using Ts::operator()...;
+};
+template<typename... Ts>
+overloaded(Ts...) -> overloaded<Ts...>;
+
+struct jroutine
+{};
+
+jroutine
+jitter(const ast::Nodes& ns)
+{
+ jroutine r;
+ auto first{ ns.begin() };
+ auto last{ ns.end() };
+
+ while (first != last) {
+ for (const auto& stmt : stlab::child_range(stlab::leading_of(first))) {
+ std::visit(overloaded{
+ [&](const ast::Num&) {},
+ [&](const ast::Op&) {},
+ [&](const ast::Id&) {},
+ [&](const ast::Str&) {},
+ [&](const ast::As&) {},
+ [&](const ast::Call&) {},
+ },
+ stmt);
+ }
+ first = ++stlab::trailing_of(first);
+ }
+
+ return r;
+}
+
+std::string
+sexpr(ast::NodesIter first, ast::NodesIter last)
+{
+ std::ostringstream oss;
+
+ while (first != last) {
+ std::visit(overloaded{
+ [&](const ast::Num& n) { oss << n.num; },
+ [&](const ast::Op& op) {
+ auto f{ ++stlab::leading_of(first) };
+ auto l{ stlab::trailing_of(first) };
+ auto count{ 0u };
+
+ oss << "(" << op.op;
+ while (f != l) {
+ auto n{ ++stlab::trailing_of(f) };
+ oss << " " << sexpr(f, n);
+ f = n;
+ ++count;
+ }
+ assert(count == 2);
+ oss << ")";
+ },
+ [&](const ast::Id& id) { oss << id.id; },
+ [&](const ast::Str& str) { oss << '"' << str.str << '"'; },
+ [&](const ast::As& as) {
+ auto f{ ++stlab::leading_of(first) };
+ auto l{ stlab::trailing_of(first) };
+
+ assert(stlab::has_children(first));
+
+ oss << "(setq " << as.id.id << " ";
+ oss << sexpr(f, ++stlab::trailing_of(f)) << ")";
+ },
+ [&](const ast::Call& call) {
+ auto f{ ++stlab::leading_of(first) };
+ auto l{ stlab::trailing_of(first) };
+
+ assert(stlab::has_children(first));
+
+ oss << "(" << call.id.id;
+ while (f != l) {
+ auto n{ ++stlab::trailing_of(f) };
+ oss << " " << sexpr(f, n);
+ f = n;
+ }
+ oss << ")";
+ },
+ },
+ *first);
+
+ first = ++stlab::trailing_of(first);
+ }
+
+ return oss.str();
+}
+
+} // namespace cgen
diff --git a/cgen.test.cpp b/cgen.test.cpp
@@ -0,0 +1,54 @@
+
+#include "cgen.hpp"
+
+#include <cassert>
+#include <iostream>
+
+namespace {
+
+void
+cgen_1()
+{
+ auto i{ 0 };
+ auto p = [&](const auto& s) {
+ std::cout << "# " << i << " {\n" << s << "\n# " << i << " }\n";
+ ++i;
+ };
+
+ {
+ ast::Nodes binop1{ ast::mkop(
+ ast::Op{ '+' }, ast::mknum(1), ast::mknum(2)) };
+ auto s{ cgen::sexpr(binop1.begin(), binop1.end()) };
+
+ p(s);
+ assert(s == "(+ 1 2)");
+ }
+ {
+ ast::Nodes ass1{ ast::mkass(
+ ast::Id{ "x" },
+ ast::mkop(ast::Op{ '+' },
+ ast::mkop(ast::Op{ '/' }, ast::mknum(1), ast::mknum(2)),
+ ast::mkop(ast::Op{ '*' }, ast::mknum(3), ast::mknum(4)))) };
+ auto s{ cgen::sexpr(ass1.begin(), ass1.end()) };
+
+ p(s);
+ assert(s == "(setq x (+ (/ 1 2) (* 3 4)))");
+ }
+ {
+ ast::Nodes eval1{ ast::mkcall(ast::Id{ "eval" },
+ ast::mkstr(ast::Str{ "y = 1 + x" })) };
+ auto s{ cgen::sexpr(eval1.begin(), eval1.end()) };
+
+ p(s);
+ assert(s == "(eval \"y = 1 + x\")");
+ }
+}
+
+} // anonymous namespace
+
+int
+main()
+{
+ cgen_1();
+ return 0;
+}