toctave

t(iny)octave
git clone https://0xff.ir/g/toctave.git
Log | Files | Refs | README

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+
AMakefile | 15+++++++++++++++
A_clang-format | 2++
Aast.hpp | 166+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Aast.test.cpp | 106+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Acgen.hpp | 103+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Acgen.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; +}