|
|
- #pragma once
- #include <variant>
- #include <memory>
- #include <vector>
- #include <sstream>
- #include <functional>
- #include <cassert>
- #include <iostream>
- #include <iomanip>
- #include <unordered_map>
- #include <cstdlib>
- #include <cmath>
-
- namespace lispy {
- class cons;
- class function;
-
- struct empty {};
- struct cons_start{};
- struct cons_end{};
-
- struct atom {
- int value;
- };
-
- struct sp_coords {
- bool is_relative = false;
- size_t x, y;
- };
-
- struct sp_range {
- bool is_relative = false;
- size_t x, y;
- size_t width, height;
- };
-
- using lvalue = std::variant<int64_t, double, std::string, atom, empty, std::shared_ptr<cons>, sp_coords, sp_range, std::shared_ptr<function>>;
-
- using token = std::variant<cons_start, cons_end, lvalue>;
-
- struct context {
- std::unordered_map<std::string, int> atoms;
- int last_atom = 0;
- bool error_crash = false;
- bool print_parse_results = false;
- std::unordered_map<int, std::shared_ptr<function>> function_table;
- std::unordered_map<int, lvalue> variable_table;
-
- int get_atom(const std::string& key)
- {
- if(atoms.count(key)) {
- return atoms[key];
- } else {
- return atoms[key] = ++last_atom;
- }
- }
- };
-
- class cons {
- public:
- lvalue self = empty{};
- std::shared_ptr<cons> other{};
-
- cons()
- {
- self = empty{};
- other = std::shared_ptr<cons>{};
- }
-
- cons(lvalue first)
- : self(first)
- , other()
- { }
-
- cons(std::vector<lvalue> data)
- {
- if(data.size() == 0) {
- self = empty{};
- other = std::shared_ptr<cons>{};
- } else if(data.size() >= 1) {
- self = data[0];
- for(auto it = data.begin()+1; it != data.end(); ++it)
- this->append(*it);
- }
- }
-
- cons(const cons& oth)
- : self(oth.self)
- , other(oth.other ? std::make_shared<cons>(*(oth.other)) : nullptr)
- {}
- void operator=(const cons& oth)
- {
- self = oth.self;
- if(oth.other) {
- other = std::make_shared<cons>(*oth.other);
- } else {
- other = std::shared_ptr<cons>{};
- }
- }
-
- cons(cons&& oth)
- : self(oth.self)
- , other(oth.other ? std::move(std::make_shared<cons>(*(oth.other))) : std::shared_ptr<cons>{})
- {}
-
- void append(lvalue value)
- {
- if(!other) {
- other = std::make_shared<cons>(value);
- } else {
- other->append(value);
- }
- }
- };
-
- class function {
- public:
- virtual lvalue operator() (const std::shared_ptr<cons> arguments, lispy::context& ctx) = 0;
- virtual ~function(){};
- };
-
- inline char hexdigit(char v)
- {
- if(v >= '0' && v<='9') {
- return v - '0';
- } else if(v >= 'a' && v <= 'f') {
- return 10 + v - 'a';
- } else if(v >= 'A' && v <= 'F') {
- return 10 + v - 'A';
- }
- return -1;
- }
-
- inline std::string escape(std::string_view v)
- {
- auto it = v.begin();
- std::stringstream stream;
- stream<<'"';
- while(it != v.end())
- {
- switch(*it)
- {
- case '"':
- {
- stream << "\\\"";
- }
- break;
- case '\n':
- {
- stream << "\\n";
- }
- break;
- case '\t':
- {
- stream << "\\t";
- }
- break;
- case '\v':
- {
- stream << "\\v";
- }
- break;
- case '\a':
- {
- stream << "\\a";
- }
- break;
- default:
- stream << *it;
- }
- ++it;
- }
- stream<<'"';
- return stream.str();
- }
-
- inline void print_visitor(std::ostream& stream, const lvalue& value, const context& ctx) {
- std::visit([&](auto arg) {
- using T = std::decay_t<decltype(arg)>;
- if constexpr (std::is_same_v<T, int64_t>) {
- stream << arg;
- } else if constexpr (std::is_same_v<T, double>) {
- stream << arg;
- } else if constexpr (std::is_same_v<T, empty>) {
- stream << "()";
- } else if constexpr (std::is_same_v<T, std::shared_ptr<function>>) {
- stream << "#func"<<&*arg;
- } else if constexpr (std::is_same_v<T, std::string>) {
- stream << escape(arg);
- } else if constexpr (std::is_same_v<T, std::shared_ptr<cons>>) {
- stream << "(";
- bool is_first = true;
- cons p = *arg;
- do{
-
- if(!is_first)
- {
- p = *p.other;
- }
- print_visitor(stream, p.self, ctx);
- if(p.other)
- {
- stream << " ";
- }
- is_first = false;
- } while(p.other);
- stream << ")";
- }
- else if constexpr (std::is_same_v<T, sp_coords>){
- stream << (arg.is_relative ? "$[" : "@[") << arg.x << "," << arg.y << "]";
- } else if constexpr (std::is_same_v<T, sp_range>){
- stream << (arg.is_relative ? "$[" : "@[") << arg.x << "," << arg.y << "," << arg.width << "," << arg.height << "]";
- } else if constexpr (std::is_same_v<T, atom>) {
- for(auto& v : ctx.atoms)
- {
- if(v.second == arg.value)
- {
- stream << v.first;
- return;
- }
- }
- assert(false);
- } else {
- std::cerr << typeid(T).name() << " detected in print_visitor ?" << std::endl;
- if(ctx.error_crash)
- {
- std::exit(-1);
- }
- }
- }, value);
- }
-
- inline void print_types_visitor(std::ostream& stream, const lvalue& value, const context& ctx) {
- std::visit([&](auto arg) {
- using T = std::decay_t<decltype(arg)>;
- if constexpr (std::is_same_v<T, int64_t>) {
- stream << "integer";
- } else if constexpr (std::is_same_v<T, double>) {
- stream << "double";
- } else if constexpr (std::is_same_v<T, empty>) {
- stream << "nil";
- } else if constexpr (std::is_same_v<T, std::shared_ptr<function>>) {
- stream << "function";
- } else if constexpr (std::is_same_v<T, std::string>) {
- stream << "string";
- } else if constexpr (std::is_same_v<T, std::shared_ptr<cons>>){
- stream << "(";
- bool is_first = true;
- cons p = *arg;
- do{
-
- if(!is_first)
- {
- p = *p.other;
- }
- print_types_visitor(stream, p.self, ctx);
- if(p.other)
- {
- stream << " ";
- }
- is_first = false;
- } while(p.other);
- stream << ")";
- } else if constexpr (std::is_same_v<T, sp_coords>){
- stream << "coords";
- } else if constexpr (std::is_same_v<T, sp_range>){
- stream << "range";
- } else if constexpr (std::is_same_v<T, atom>) {
- stream << "atom";
- } else {
- std::cerr << typeid(T).name() << " detected in print_types_visitor ?" << std::endl;
- if(ctx.error_crash)
- {
- std::exit(-1);
- }
- }
- }, value);
- }
-
- inline std::pair<lvalue, std::string_view> parse_string(std::string_view data)
- {
- auto it = data.begin();
- assert(*it == '\"');
- ++it;
- std::stringstream value(std::string(data.begin(), data.end()));
- std::string ret;
- value >> std::quoted(ret);
- return std::make_pair<lvalue, std::string_view>(ret, std::string_view{data.begin(), (size_t)value.rdbuf()->in_avail()});
- }
-
- inline std::pair<lvalue, std::string_view> parse_atom(std::string_view data, context& ctx)
- {
- assert(!iswspace(data[0]));
- size_t idx = 1;
- while(!iswspace(data[idx]))
- {
- idx++;
- }
- atom v;
- v.value = ctx.get_atom(std::string(data.begin(), data.begin()+idx));
- return std::make_pair(lvalue{v}, std::string_view{data.begin(), idx});
- }
-
- inline std::pair<lvalue, std::string_view> parse_number(std::string_view data)
- {
- char* end_f;
- char* end_d;
- double try_f = strtod (data.data(), &end_f);
- int err_f = errno;
- int64_t try_d = strtoll(data.data(), &end_d, 10);
- int err_d = errno;
-
- if(err_d == ERANGE)
- {
- return std::make_pair(lvalue{(double)try_f}, std::string_view{data.begin(), (size_t)(end_f-data.data())});
- }
-
- if(try_f != std::trunc(try_f))
- {
- return std::make_pair(lvalue{(double)try_f}, std::string_view{data.begin(), (size_t)(end_f-data.data())});
- }
-
- return std::make_pair(lvalue{int64_t(try_d)}, std::string_view{data.begin(), (size_t)(end_f-data.data())});
- }
-
- inline std::pair<size_t,sp_coords> get_coords_from_id(const std::string_view id) {
- size_t x = 0;
- size_t y = 0;
- auto c = id.begin();
- while(*c >= 'A' && *c <= 'Z')
- {
- x += *c-'A'+1;
- x *= 26;
- c++;
- }
- x /= 26;
- while(*c >= '0' && *c <= '9')
- {
- y += *c-'0';
- y *= 10;
- c++;
- }
- y /= 10;
- sp_coords ret;
- ret.x = x;
- ret.y = y;
- return std::make_pair((size_t)(c-id.begin()) ,ret);
- }
-
- inline std::pair<lvalue, std::string_view> parse_selector(std::string_view data)
- {
- auto it = data.begin();
- auto is_rel = *it == '$';
- ++it;
- auto a = get_coords_from_id(std::string_view{it, data.size()-1});
- a.second.is_relative = is_rel;
- it += a.first;
- if(*it == ':')
- {
- auto b = get_coords_from_id(std::string_view{++it, data.size()-1});
- it += b.first;
- sp_range ret;
- a.second.is_relative = is_rel;
- ret.x = a.second.x;
- ret.y = a.second.y;
- ret.width = b.second.x;
- ret.height = b.second.y;
- return std::make_pair(lvalue{ret}, std::string_view{data.begin(), (size_t)(it - data.begin())});
- } else {
- return std::make_pair(lvalue{a.second}, std::string_view{data.begin(), (size_t)(it - data.begin())});
- }
- }
-
- inline size_t find_matching(const std::basic_string_view<token>& data, const size_t idx)
- {
- size_t try_idx = idx;
- int mass = 1;
-
- do{
- try_idx++;
- std::visit([&](auto arg) {
- using T = std::decay_t<decltype(arg)>;
- if constexpr (std::is_same_v<T, cons_start>) {
- ++mass;
- } else if constexpr (std::is_same_v<T, cons_end>) {
- --mass;
- } else {}
- }, data[try_idx]);
- } while(mass != 0 && try_idx < data.size());
-
- if(try_idx<data.size())
- {
- return try_idx;
- }
- return idx;
- }
-
- inline std::pair<size_t, lvalue> parse(const std::basic_string_view<token>& data, context& ctx)
- {
- auto ret = std::make_shared<cons>();
- size_t sz = 0;
-
- if(data.size() == 0)
- {
- return std::make_pair(0,(lvalue)empty{});
- }
-
- size_t skip = 0;
-
- size_t idx = 0;
-
- while(idx < data.size())
- {
- if(skip)
- {
- skip--;
- ++idx;
- continue;
- }
-
- std::visit([&](const auto& arg) {
- using T = std::decay_t<decltype(arg)>;
- if constexpr (std::is_same_v<T, cons_start>) {
- auto matching = find_matching(data, idx);
- auto res = parse(std::basic_string_view<token>{data.begin()+idx+1, matching-idx-1}, ctx);
- ret->append(res.second);
- skip = matching - idx;
- ++sz;
- } else if constexpr (std::is_same_v<T, cons_end>) {
- std::cerr << typeid(T).name() << " mismatched parenthesis" << std::endl;
- } else if constexpr (std::is_same_v<T, lvalue>) {
- ret->append(arg);
- ++sz;
- } else {
- std::cerr << typeid(T).name() << " cat in the parser ?" << std::endl;
- if(ctx.error_crash)
- {
- std::exit(-1);
- }
- }
- }, data[idx]);
- ++idx;
- }
- return std::make_pair(sz, (lvalue)(std::make_shared<cons>(*ret->other)));
- }
-
- inline std::vector<token> lex(const std::string_view data, context& ctx)
- {
- std::vector<token> ret;
-
- auto it = data.begin();
- bool is_done = false;
- do{
- if (it == data.end()) {
- is_done = true;
- } else if(isdigit(*it) || (*it == '-' && isdigit(*(it+1)))) {
- auto value = parse_number(std::string_view{it, (size_t)(data.end() - it)});
- ret.push_back(value.first);
- it += value.second.size();
- } else if(*it == '\"') {
- auto value = parse_string(std::string_view{it, (size_t)(data.end() - it)});
- ret.push_back(value.first);
- size_t forward_jump = std::string_view{it, (size_t)(data.end() - it)}.size()-value.second.size();
- it += forward_jump;
- } else if (*it == '(') {
- ret.push_back(cons_start{});
- ++it;
- } else if (*it == ')') {
- ret.push_back(cons_end{});
- ++it;
- } else if (iswspace(*it)) {
- ++it;
- } else if (*it == '$' || *it == '@') {
- auto value = parse_selector(std::string_view{it, (size_t)(data.end() - it)});
- ret.push_back(value.first);
- it += value.second.size()+1;
- } else {
- auto value = parse_atom(std::string_view{it, (size_t)(data.end() - it)}, ctx);
- ret.push_back(value.first);
- it += value.second.size();
- }
- }while(!is_done);
-
- return ret;
- }
-
- inline lvalue eval(lvalue& data, context& ctx)
- {
- lvalue ret = empty{};
- std::visit([&](auto& arg) {
- using T = std::decay_t<decltype(arg)>;
- if constexpr (std::is_same_v<T, std::shared_ptr<cons>>) {
- auto it = arg;
- auto evaluated = std::make_shared<cons>();
- auto iterated = evaluated;
-
- do{
- iterated->self = eval(it->self, ctx);
- if(it->other)
- {
- iterated->other = std::make_shared<cons>();
- iterated = iterated->other;
- }
- it = it->other;
- } while(it);
- std::visit([&](auto arg) {
- using T = std::decay_t<decltype(arg)>;
- if constexpr (std::is_same_v<T, std::shared_ptr<function>>) {
- ret = (*arg)(evaluated->other, ctx);
- } else {
- ret = evaluated;
- }
- }, evaluated->self);
- } else if constexpr (std::is_same_v<T, atom>) {
- if(ctx.function_table.count(arg.value))
- {
- ret = ctx.function_table[arg.value];
- } else if (ctx.variable_table.count(arg.value)) {
- ret = ctx.variable_table[arg.value];
- } else {
- ret = arg;
- }
- } else {
- ret = arg;
- }
- }, data);
- return ret;
- }
-
- inline lvalue eval(const std::string_view& data, context& ctx)
- {
- auto n = lex(data, ctx);
- auto p = parse(std::basic_string_view<token>(n.data(), n.size()), ctx);
- if(ctx.print_parse_results)
- {
- print_types_visitor(std::cerr, p.second, ctx);
- std::cerr << std::endl;
- print_visitor(std::cerr, p.second, ctx);
- std::cerr << std::endl;
- }
- return eval(p.second, ctx);
- }
- }
-
- namespace lispy_math {
-
- int64_t next_as_int(std::shared_ptr<lispy::cons>& arguments, lispy::context& ctx) {
- int64_t ret = 0;
- if(!arguments)
- {
- std::cerr << "no argument provided, number expected" << std::endl;
- if(ctx.error_crash)
- {
- std::exit(-1);
- }
- return ret;
- }
- std::visit([&](auto arg) {
- using T = std::decay_t<decltype(arg)>;
- if constexpr (std::is_same_v<T, int64_t>) {
- ret = arg;
- } else if constexpr (std::is_same_v<T, double>) {
- ret = arg;
- } else {
- std::cerr << "bad argument provided, number expected" << std::endl;
- if(ctx.error_crash)
- {
- std::exit(-1);
- }
- }
- }, arguments->self);
- arguments = arguments->other;
- return ret;
- }
-
- double next_as_floating(std::shared_ptr<lispy::cons>& arguments, lispy::context& ctx) {
- double ret = 0;
- if(!arguments)
- {
- std::cerr << "no argument provided, number expected" << std::endl;
- if(ctx.error_crash)
- {
- std::exit(-1);
- }
- return ret;
- }
- std::visit([&](auto arg) {
- using T = std::decay_t<decltype(arg)>;
- if constexpr (std::is_same_v<T, int64_t>) {
- ret = arg;
- } else if constexpr (std::is_same_v<T, double>) {
- ret = arg;
- } else {
- std::cerr << "bad argument provided, number expected" << std::endl;
- if(ctx.error_crash)
- {
- std::exit(-1);
- }
- }
- }, arguments->self);
- arguments = arguments->other;
- return ret;
- }
-
- namespace integer_functions {
- class plus : lispy::function {virtual lispy::lvalue operator() (const std::shared_ptr<lispy::cons> arguments, lispy::context& ctx) {
- auto p = arguments;
- int64_t a = next_as_int(p, ctx);
- int64_t b = next_as_int(p, ctx);
-
- if(p)
- {
- std::cerr << "expected arity of 2 but more arguments provided" << std::endl;
- if(ctx.error_crash)
- {
- std::exit(-1);
- }
- }
- return a+b;
- }};
- class minus : lispy::function {virtual lispy::lvalue operator() (const std::shared_ptr<lispy::cons> arguments, lispy::context& ctx) {
- auto p = arguments;
- int64_t a = next_as_int(p, ctx);
-
- if(!p) {
- return -a;
- }
-
- int64_t b = next_as_int(p, ctx);
-
- if(p)
- {
- std::cerr << "expected arity of 1 or 2 but more arguments provided" << std::endl;
- if(ctx.error_crash)
- {
- std::exit(-1);
- }
- }
- return a-b;
- }};
- class product : lispy::function {virtual lispy::lvalue operator() (const std::shared_ptr<lispy::cons> arguments, lispy::context& ctx) {
- auto p = arguments;
- int64_t a = next_as_int(p, ctx);
- int64_t b = next_as_int(p, ctx);
-
- if(p)
- {
- std::cerr << "expected arity of 2 but more arguments provided" << std::endl;
- if(ctx.error_crash)
- {
- std::exit(-1);
- }
- }
- return a*b;
- }};
- class divide : lispy::function {virtual lispy::lvalue operator() (const std::shared_ptr<lispy::cons> arguments, lispy::context& ctx) {
- auto p = arguments;
- int64_t a = next_as_int(p, ctx);
- int64_t b = next_as_int(p, ctx);
-
- if(p)
- {
- std::cerr << "expected arity of 2 but more arguments provided" << std::endl;
- if(ctx.error_crash)
- {
- std::exit(-1);
- }
- }
- return a/b;
- }};
- class remainder : lispy::function {virtual lispy::lvalue operator() (const std::shared_ptr<lispy::cons> arguments, lispy::context& ctx) {
- auto p = arguments;
- int64_t a = next_as_int(p, ctx);
- int64_t b = next_as_int(p, ctx);
-
- if(p)
- {
- std::cerr << "expected arity of 2 but more arguments provided" << std::endl;
- if(ctx.error_crash)
- {
- std::exit(-1);
- }
- }
- return a%b;
- }};
- };
-
- namespace float_functions {
- class plus : lispy::function {virtual lispy::lvalue operator() (const std::shared_ptr<lispy::cons> arguments, lispy::context& ctx) {
- auto p = arguments;
- double a = next_as_floating(p, ctx);
- double b = next_as_floating(p, ctx);
-
- if(p)
- {
- std::cerr << "expected arity of 2 but more arguments provided" << std::endl;
- if(ctx.error_crash)
- {
- std::exit(-1);
- }
- }
- return a+b;
- }};
- class minus : lispy::function {virtual lispy::lvalue operator() (const std::shared_ptr<lispy::cons> arguments, lispy::context& ctx) {
- auto p = arguments;
- double a = next_as_floating(p, ctx);
-
- if(!p) {
- return -a;
- }
-
- double b = next_as_floating(p, ctx);
-
- if(p)
- {
- std::cerr << "expected arity of 1 or 2 but more arguments provided" << std::endl;
- if(ctx.error_crash)
- {
- std::exit(-1);
- }
- }
- return a-b;
- }};
- class product : lispy::function {virtual lispy::lvalue operator() (const std::shared_ptr<lispy::cons> arguments, lispy::context& ctx) {
- auto p = arguments;
- double a = next_as_floating(p, ctx);
- double b = next_as_floating(p, ctx);
-
- if(p)
- {
- std::cerr << "expected arity of 2 but more arguments provided" << std::endl;
- if(ctx.error_crash)
- {
- std::exit(-1);
- }
- }
- return a*b;
- }};
- class divide : lispy::function {virtual lispy::lvalue operator() (const std::shared_ptr<lispy::cons> arguments, lispy::context& ctx) {
- auto p = arguments;
- double a = next_as_floating(p, ctx);
- double b = next_as_floating(p, ctx);
-
- if(p)
- {
- std::cerr << "expected arity of 2 but more arguments provided" << std::endl;
- if(ctx.error_crash)
- {
- std::exit(-1);
- }
- }
- return a/b;
- }};
- };
-
- inline void add_integer_functions(lispy::context& ctx) {
- using func_ptr = std::shared_ptr<lispy::function>;
-
- ctx.function_table[ctx.get_atom("+")] = func_ptr{(lispy::function*)new integer_functions::plus()};
- ctx.function_table[ctx.get_atom("-")] = func_ptr{(lispy::function*)new integer_functions::minus()};
- ctx.function_table[ctx.get_atom("*")] = func_ptr{(lispy::function*)new integer_functions::product()};
- ctx.function_table[ctx.get_atom("/")] = func_ptr{(lispy::function*)new integer_functions::divide()};
- ctx.function_table[ctx.get_atom("%")] = func_ptr{(lispy::function*)new integer_functions::remainder()};
- }
-
- inline void add_floatingp_functions(lispy::context& ctx) {
- using func_ptr = std::shared_ptr<lispy::function>;
-
- ctx.function_table[ctx.get_atom("+.")] = func_ptr{(lispy::function*)new float_functions::plus()};
- ctx.function_table[ctx.get_atom("-.")] = func_ptr{(lispy::function*)new float_functions::minus()};
- ctx.function_table[ctx.get_atom("*.")] = func_ptr{(lispy::function*)new float_functions::product()};
- ctx.function_table[ctx.get_atom("/.")] = func_ptr{(lispy::function*)new float_functions::divide()};
- }
- }
|