|
|
@ -0,0 +1,446 @@ |
|
|
|
#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; |
|
|
|
|
|
|
|
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::unique_ptr<cons> other{}; |
|
|
|
|
|
|
|
cons() |
|
|
|
{ |
|
|
|
self = empty{}; |
|
|
|
other = std::unique_ptr<cons>{}; |
|
|
|
} |
|
|
|
|
|
|
|
cons(lvalue first) |
|
|
|
: self(first) |
|
|
|
, other() |
|
|
|
{ } |
|
|
|
|
|
|
|
cons(std::vector<lvalue> data) |
|
|
|
{ |
|
|
|
if(data.size() == 0) { |
|
|
|
self = empty{}; |
|
|
|
other = std::unique_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_unique<cons>(*(oth.other)) : nullptr) |
|
|
|
{} |
|
|
|
void operator=(const cons& oth) |
|
|
|
{ |
|
|
|
self = oth.self; |
|
|
|
if(oth.other) { |
|
|
|
other = std::make_unique<cons>(*oth.other); |
|
|
|
} else { |
|
|
|
other = std::unique_ptr<cons>{}; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
cons(cons&& oth) |
|
|
|
: self(oth.self) |
|
|
|
, other(oth.other ? std::move(std::make_unique<cons>(*(oth.other))) : std::unique_ptr<cons>{}) |
|
|
|
{} |
|
|
|
|
|
|
|
void append(lvalue value) |
|
|
|
{ |
|
|
|
if(!other) { |
|
|
|
other = std::make_unique<cons>(value); |
|
|
|
} else { |
|
|
|
other->append(value); |
|
|
|
} |
|
|
|
} |
|
|
|
}; |
|
|
|
|
|
|
|
class function { |
|
|
|
public: |
|
|
|
virtual lvalue operator() (cons arguments) = 0; |
|
|
|
}; |
|
|
|
|
|
|
|
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, 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>){ |
|
|
|
assert(false); |
|
|
|
} else if constexpr (std::is_same_v<T, sp_range>){ |
|
|
|
assert(false); |
|
|
|
} 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, 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<lvalue, std::string_view> parse_selector(std::string_view data) |
|
|
|
{ |
|
|
|
return std::make_pair(lvalue{}, std::string_view{data.begin(), 0}); |
|
|
|
} |
|
|
|
|
|
|
|
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()+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()+1; |
|
|
|
} 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 == '$') { |
|
|
|
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(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); |
|
|
|
return p.second; |
|
|
|
} |
|
|
|
} |