#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;
|
|
|
|
std::function<lvalue(sp_coords)> resolve_coords = [&](sp_coords) -> lvalue {
|
|
std::cerr << "no coord system provided" << std::endl;
|
|
if(error_crash)
|
|
{
|
|
std::exit(-1);
|
|
}
|
|
return (int64_t)0;
|
|
};
|
|
|
|
std::function<lvalue(sp_range)> resolve_range = [&](sp_range) -> lvalue {
|
|
std::cerr << "no coord system provided" << std::endl;
|
|
if(error_crash)
|
|
{
|
|
std::exit(-1);
|
|
}
|
|
return empty{};
|
|
};
|
|
|
|
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::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 <<
|
|
#ifdef __cpp_rtti
|
|
typeid(T).name()
|
|
#else
|
|
"unknown type"
|
|
#endif
|
|
<< " 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 <<
|
|
#ifdef __cpp_rtti
|
|
typeid(T).name()
|
|
#else
|
|
"unknown type"
|
|
#endif
|
|
<< " 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);
|
|
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 <<
|
|
#ifdef __cpp_rtti
|
|
typeid(T).name()
|
|
#else
|
|
"unknown type"
|
|
#endif
|
|
<< " mismatched parenthesis" << std::endl;
|
|
} else if constexpr (std::is_same_v<T, lvalue>) {
|
|
ret->append(arg);
|
|
++sz;
|
|
} else {
|
|
std::cerr <<
|
|
#ifdef __cpp_rtti
|
|
typeid(T).name()
|
|
#else
|
|
"unknown type"
|
|
#endif
|
|
<< " 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();
|
|
} 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;
|
|
}
|
|
|
|
auto extractor = [&](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);
|
|
}
|
|
}
|
|
};
|
|
|
|
std::visit([&](auto arg) {
|
|
using T = std::decay_t<decltype(arg)>;
|
|
if constexpr (std::is_same_v<T, lispy::sp_coords>) {
|
|
std::visit(extractor, ctx.resolve_coords(arg));
|
|
} else {
|
|
std::visit(extractor, arguments->self);
|
|
}
|
|
}, 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;
|
|
}
|
|
|
|
auto extractor = [&](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);
|
|
}
|
|
}
|
|
};
|
|
|
|
std::visit([&](auto arg) {
|
|
using T = std::decay_t<decltype(arg)>;
|
|
if constexpr (std::is_same_v<T, lispy::sp_coords>) {
|
|
std::visit(extractor, ctx.resolve_coords(arg));
|
|
} else {
|
|
std::visit(extractor, arguments->self);
|
|
}
|
|
}, arguments->self);
|
|
|
|
arguments = arguments->other;
|
|
return ret;
|
|
}
|
|
|
|
lispy::lvalue next_as_floating_aggregate(std::shared_ptr<lispy::cons>& arguments, lispy::context& ctx) {
|
|
lispy::lvalue src = lispy::empty{};
|
|
if(!arguments)
|
|
{
|
|
std::cerr << "no argument provided, s-exp or range expected" << std::endl;
|
|
if(ctx.error_crash)
|
|
{
|
|
std::exit(-1);
|
|
}
|
|
return src;
|
|
}
|
|
|
|
auto extractor = [&](auto arg) {
|
|
using T = std::decay_t<decltype(arg)>;
|
|
if constexpr (std::is_same_v<T, lispy::empty>) {
|
|
src = arg;
|
|
} else if constexpr (std::is_same_v<T, std::shared_ptr<lispy::cons>>) {
|
|
src = arg;
|
|
} else {
|
|
std::cerr << "bad argument provided, s-exp or range expected" << std::endl;
|
|
if(ctx.error_crash)
|
|
{
|
|
std::exit(-1);
|
|
}
|
|
}
|
|
};
|
|
|
|
std::visit([&](auto arg) {
|
|
using T = std::decay_t<decltype(arg)>;
|
|
if constexpr (std::is_same_v<T, lispy::sp_range>) {
|
|
std::visit(extractor, ctx.resolve_range(arg));
|
|
} else if constexpr (std::is_same_v<T, std::shared_ptr<lispy::cons>>) {
|
|
std::visit(extractor, arguments->self);
|
|
} else {
|
|
std::cerr << "bad argument provided, s-exp or range expected" << std::endl;
|
|
if(ctx.error_crash)
|
|
{
|
|
std::exit(-1);
|
|
}
|
|
}
|
|
}, arguments->self);
|
|
|
|
std::shared_ptr<lispy::cons> dest;
|
|
|
|
std::visit([&](auto arg) {
|
|
using T = std::decay_t<decltype(arg)>;
|
|
if constexpr (std::is_same_v<T, lispy::empty>) {}
|
|
else if constexpr (std::is_same_v<T, std::shared_ptr<lispy::cons>>) {
|
|
dest = std::make_shared<lispy::cons>(lispy_math::next_as_floating(arg, ctx));
|
|
while(arg){
|
|
dest->append(lispy_math::next_as_floating(arg, ctx));
|
|
}
|
|
} else {
|
|
std::cerr << "bad argument provided, s-exp or range expected" << std::endl;
|
|
if(ctx.error_crash)
|
|
{
|
|
std::exit(-1);
|
|
}
|
|
}
|
|
}, src);
|
|
arguments = arguments->other;
|
|
if(dest)
|
|
{
|
|
return dest;
|
|
}
|
|
return lispy::empty{};
|
|
}
|
|
|
|
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;
|
|
}};
|
|
|
|
class iota : 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);
|
|
}
|
|
}
|
|
|
|
if(b<=a)
|
|
{
|
|
std::cerr << "expected first argument lower than second" << std::endl;
|
|
if(ctx.error_crash)
|
|
{
|
|
std::exit(-1);
|
|
}
|
|
return lispy::empty{};
|
|
}
|
|
|
|
std::shared_ptr<lispy::cons> ret = std::make_shared<lispy::cons>(a);
|
|
a+=1;
|
|
for(;a<b;a++)
|
|
{
|
|
ret->append(a);
|
|
}
|
|
|
|
|
|
return ret;
|
|
}};
|
|
};
|
|
|
|
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;
|
|
}};
|
|
};
|
|
|
|
namespace aggregate_functions {
|
|
|
|
|
|
class sum : lispy::function {virtual lispy::lvalue operator() (const std::shared_ptr<lispy::cons> arguments, lispy::context& ctx) {
|
|
auto p = arguments;
|
|
lispy::lvalue cells = next_as_floating_aggregate(p, ctx);
|
|
|
|
if(p)
|
|
{
|
|
std::cerr << "expected arity of 1 but more arguments provided" << std::endl;
|
|
if(ctx.error_crash)
|
|
{
|
|
std::exit(-1);
|
|
}
|
|
}
|
|
|
|
double ret = 0;
|
|
|
|
while(std::get<std::shared_ptr<lispy::cons>>(cells)) {
|
|
ret += lispy_math::next_as_floating(std::get<std::shared_ptr<lispy::cons>>(cells), ctx);
|
|
}
|
|
|
|
return ret;
|
|
}};
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
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()};
|
|
|
|
ctx.function_table[ctx.get_atom("iota")] = func_ptr{(lispy::function*)new integer_functions::iota()};
|
|
}
|
|
|
|
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()};
|
|
}
|
|
|
|
inline void add_aggregate_functions(lispy::context& ctx) {
|
|
using func_ptr = std::shared_ptr<lispy::function>;
|
|
|
|
ctx.function_table[ctx.get_atom("sum")] = func_ptr{(lispy::function*)new aggregate_functions::sum()};
|
|
}
|
|
}
|