|
|
@ -42,6 +42,8 @@ namespace lispy { |
|
|
|
std::unordered_map<std::string, int> atoms; |
|
|
|
int last_atom = 0; |
|
|
|
bool error_crash = 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) |
|
|
|
{ |
|
|
@ -56,12 +58,12 @@ namespace lispy { |
|
|
|
class cons { |
|
|
|
public: |
|
|
|
lvalue self = empty{}; |
|
|
|
std::unique_ptr<cons> other{}; |
|
|
|
std::shared_ptr<cons> other{}; |
|
|
|
|
|
|
|
cons() |
|
|
|
{ |
|
|
|
self = empty{}; |
|
|
|
other = std::unique_ptr<cons>{}; |
|
|
|
other = std::shared_ptr<cons>{}; |
|
|
|
} |
|
|
|
|
|
|
|
cons(lvalue first) |
|
|
@ -73,7 +75,7 @@ namespace lispy { |
|
|
|
{ |
|
|
|
if(data.size() == 0) { |
|
|
|
self = empty{}; |
|
|
|
other = std::unique_ptr<cons>{}; |
|
|
|
other = std::shared_ptr<cons>{}; |
|
|
|
} else if(data.size() >= 1) { |
|
|
|
self = data[0]; |
|
|
|
for(auto it = data.begin()+1; it != data.end(); ++it) |
|
|
@ -83,27 +85,27 @@ namespace lispy { |
|
|
|
|
|
|
|
cons(const cons& oth) |
|
|
|
: self(oth.self) |
|
|
|
, other(oth.other ? std::make_unique<cons>(*(oth.other)) : nullptr) |
|
|
|
, other(oth.other ? std::make_shared<cons>(*(oth.other)) : nullptr) |
|
|
|
{} |
|
|
|
void operator=(const cons& oth) |
|
|
|
{ |
|
|
|
self = oth.self; |
|
|
|
if(oth.other) { |
|
|
|
other = std::make_unique<cons>(*oth.other); |
|
|
|
other = std::make_shared<cons>(*oth.other); |
|
|
|
} else { |
|
|
|
other = std::unique_ptr<cons>{}; |
|
|
|
other = std::shared_ptr<cons>{}; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
cons(cons&& oth) |
|
|
|
: self(oth.self) |
|
|
|
, other(oth.other ? std::move(std::make_unique<cons>(*(oth.other))) : std::unique_ptr<cons>{}) |
|
|
|
, other(oth.other ? std::move(std::make_shared<cons>(*(oth.other))) : std::shared_ptr<cons>{}) |
|
|
|
{} |
|
|
|
|
|
|
|
void append(lvalue value) |
|
|
|
{ |
|
|
|
if(!other) { |
|
|
|
other = std::make_unique<cons>(value); |
|
|
|
other = std::make_shared<cons>(value); |
|
|
|
} else { |
|
|
|
other->append(value); |
|
|
|
} |
|
|
@ -112,7 +114,8 @@ namespace lispy { |
|
|
|
|
|
|
|
class function { |
|
|
|
public: |
|
|
|
virtual lvalue operator() (cons arguments) = 0; |
|
|
|
virtual lvalue operator() (const std::shared_ptr<cons> arguments, lispy::context& ctx) = 0; |
|
|
|
virtual ~function(){}; |
|
|
|
}; |
|
|
|
|
|
|
|
inline char hexdigit(char v) |
|
|
@ -179,7 +182,7 @@ namespace lispy { |
|
|
|
stream << arg; |
|
|
|
} else if constexpr (std::is_same_v<T, empty>) { |
|
|
|
stream << "()"; |
|
|
|
} else if constexpr (std::is_same_v<T, function>) { |
|
|
|
} 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); |
|
|
@ -203,9 +206,9 @@ namespace lispy { |
|
|
|
stream << ")"; |
|
|
|
} |
|
|
|
else if constexpr (std::is_same_v<T, sp_coords>){ |
|
|
|
assert(false); |
|
|
|
stream << (arg.is_relative ? "$[" : "@[") << arg.x << "," << arg.y << "]"; |
|
|
|
} else if constexpr (std::is_same_v<T, sp_range>){ |
|
|
|
assert(false); |
|
|
|
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) |
|
|
|
{ |
|
|
@ -235,7 +238,7 @@ namespace lispy { |
|
|
|
stream << "double"; |
|
|
|
} else if constexpr (std::is_same_v<T, empty>) { |
|
|
|
stream << "nil"; |
|
|
|
} else if constexpr (std::is_same_v<T, function>) { |
|
|
|
} 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"; |
|
|
@ -319,9 +322,52 @@ namespace lispy { |
|
|
|
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) |
|
|
|
{ |
|
|
|
return std::make_pair(lvalue{}, std::string_view{data.begin(), 0}); |
|
|
|
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) |
|
|
@ -375,9 +421,9 @@ namespace lispy { |
|
|
|
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); |
|
|
|
auto res = parse(std::basic_string_view<token>{data.begin()+n">idx+1, matching-idx-1}, ctx); |
|
|
|
ret->append(res.second); |
|
|
|
skip = matching - idx; |
|
|
|
skip = matching - idx + 1; |
|
|
|
++sz; |
|
|
|
} else if constexpr (std::is_same_v<T, cons_end>) { |
|
|
|
std::cerr << typeid(T).name() << " mismatched parenthesis" << std::endl; |
|
|
@ -409,7 +455,7 @@ namespace lispy { |
|
|
|
} 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()o">+1; |
|
|
|
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); |
|
|
@ -423,7 +469,7 @@ namespace lispy { |
|
|
|
++it; |
|
|
|
} else if (iswspace(*it)) { |
|
|
|
++it; |
|
|
|
} else if (*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; |
|
|
@ -437,10 +483,178 @@ namespace lispy { |
|
|
|
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); |
|
|
|
return p.second; |
|
|
|
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, integer 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, integer 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; |
|
|
|
}}; |
|
|
|
}; |
|
|
|
|
|
|
|
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()}; |
|
|
|
} |
|
|
|
} |