Você não pode selecionar mais de 25 tópicos Os tópicos devem começar com uma letra ou um número, podem incluir traços ('-') e podem ter até 35 caracteres.
 
 

774 linhas
19 KiB

#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()};
}
}