283 lines
9.0 KiB
C++
283 lines
9.0 KiB
C++
#include <a8/a8.h>
|
|
|
|
#include <ctype.h>
|
|
|
|
#include <a8/lisp.h>
|
|
#include <a8/mutable_xobject.h>
|
|
|
|
namespace a8
|
|
{
|
|
|
|
namespace lisp
|
|
{
|
|
|
|
Value::Value(Atom atom)
|
|
{
|
|
type = ValueType::kAtom;
|
|
value = atom;
|
|
}
|
|
|
|
Value::Value(List list)
|
|
{
|
|
type = ValueType::kList;
|
|
value = list;
|
|
}
|
|
|
|
Value::Value(Symbol symbol)
|
|
{
|
|
type = ValueType::kSymbol;
|
|
value = symbol;
|
|
}
|
|
|
|
Value::Value(const std::string& proc_name, CProc cproc)
|
|
{
|
|
type = ValueType::kCProc;
|
|
name = proc_name;
|
|
value = cproc;
|
|
}
|
|
|
|
a8::XObject Value::ToXObject()
|
|
{
|
|
switch (type) {
|
|
case ValueType::kAtom:
|
|
{
|
|
Atom&& atom = std::any_cast<Atom>(value);
|
|
a8::XValue v(atom.val);
|
|
return a8::XObject(v);
|
|
}
|
|
break;
|
|
case ValueType::kList:
|
|
{
|
|
auto arr = a8::MutableXObject::CreateArray();
|
|
List list = std::any_cast<List>(value);
|
|
for (auto ele : *list) {
|
|
a8::XObject xobj = ele->ToXObject();
|
|
arr->Push(xobj);
|
|
}
|
|
return *arr;
|
|
}
|
|
break;
|
|
case ValueType::kSymbol:
|
|
{
|
|
Symbol&& symbol = std::any_cast<Symbol>(value);
|
|
auto obj = a8::MutableXObject::CreateObject();
|
|
obj->SetVal("name", symbol.name);
|
|
return *obj;
|
|
}
|
|
break;
|
|
case ValueType::kCProc:
|
|
{
|
|
auto obj = a8::MutableXObject::CreateObject();
|
|
obj->SetVal("type", "lambda");
|
|
obj->SetVal("name", name);
|
|
return *obj;
|
|
}
|
|
break;
|
|
default:
|
|
{
|
|
abort();
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
std::shared_ptr<Value> Scope::Find(const std::string& name)
|
|
{
|
|
auto itr = vars_.find(name);
|
|
return itr != vars_.end() ? itr->second : nullptr;
|
|
}
|
|
|
|
void Scope::RegisterCProc(const std::string& name, CProc proc)
|
|
{
|
|
vars_[name] = std::make_shared<Value>(name, proc);
|
|
}
|
|
|
|
GlobalScope::GlobalScope()
|
|
{
|
|
RegisterCProc
|
|
(
|
|
"+",
|
|
[] (const List& params) -> std::shared_ptr<Value>
|
|
{
|
|
Atom result;
|
|
for (auto param : *params) {
|
|
Atom&& atom = std::any_cast<Atom>(param->value);
|
|
result.val += atom.val;
|
|
}
|
|
return std::make_shared<Value>(result);
|
|
});
|
|
RegisterCProc
|
|
(
|
|
"-",
|
|
[] (const List& params) -> std::shared_ptr<Value>
|
|
{
|
|
Atom result;
|
|
for (auto param : *params) {
|
|
Atom&& atom = std::any_cast<Atom>(param->value);
|
|
result.val -= atom.val;
|
|
}
|
|
return std::make_shared<Value>(result);
|
|
});
|
|
RegisterCProc
|
|
(
|
|
"*",
|
|
[] (const List& params) -> std::shared_ptr<Value>
|
|
{
|
|
Atom result;
|
|
for (auto param : *params) {
|
|
Atom&& atom = std::any_cast<Atom>(param->value);
|
|
result.val *= atom.val;
|
|
}
|
|
return std::make_shared<Value>(result);
|
|
});
|
|
RegisterCProc
|
|
(
|
|
"/",
|
|
[] (const List& params) -> std::shared_ptr<Value>
|
|
{
|
|
Atom result;
|
|
for (auto param : *params) {
|
|
Atom&& atom = std::any_cast<Atom>(param->value);
|
|
result.val /= atom.val;
|
|
}
|
|
return std::make_shared<Value>(result);
|
|
});
|
|
}
|
|
|
|
std::shared_ptr<Value> Expr::Compile(const std::string& script, std::shared_ptr<Scope> env)
|
|
{
|
|
std::vector<List> stack;
|
|
int pos = 0;
|
|
int depth = -1;
|
|
while (true) {
|
|
std::string token;
|
|
int n = GetToken(script, pos, token);
|
|
if (n != 1) {
|
|
break;
|
|
}
|
|
if (token == "(") {
|
|
List new_list = std::make_shared<std::vector<std::shared_ptr<Value>>>();
|
|
stack.push_back(new_list);
|
|
++depth;
|
|
} else if (token == ")") {
|
|
--depth;
|
|
} else {
|
|
if (depth < 0) {
|
|
abort();
|
|
}
|
|
double val = 0.0f;
|
|
if (sscanf(token.c_str(), "%lf", &val) == 1) {
|
|
Atom atom;
|
|
atom.val = val;
|
|
stack.at(depth)->push_back(std::make_shared<Value>(atom));
|
|
} else {
|
|
auto symbol = env->Find(token);
|
|
if (symbol) {
|
|
if (!symbol->IsType(ValueType::kCProc)) {
|
|
abort();
|
|
}
|
|
stack.at(depth)->push_back(symbol);
|
|
} else {
|
|
abort();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
if (depth != -1) {
|
|
abort();
|
|
}
|
|
if (stack.empty()) {
|
|
abort();
|
|
}
|
|
return std::make_shared<Value>(stack[0]);
|
|
}
|
|
|
|
/*
|
|
1: token
|
|
-1: end
|
|
*/
|
|
int Expr::GetToken(const std::string& script, int& pos, std::string& token)
|
|
{
|
|
if (pos >= script.size()) {
|
|
return -1;
|
|
}
|
|
while (pos < script.size() && isspace(script[pos])) {
|
|
++pos;
|
|
}
|
|
if (pos >= script.size()) {
|
|
return -1;
|
|
}
|
|
if (script[pos] == '(') {
|
|
++pos;
|
|
token = "(";
|
|
return 1;
|
|
}
|
|
if (script[pos] == ')') {
|
|
++pos;
|
|
token = ")";
|
|
return 1;
|
|
}
|
|
while (pos < script.size() &&
|
|
!isspace(script[pos]) &&
|
|
script[pos] != ')' &&
|
|
script[pos] != '(') {
|
|
token += script[pos];
|
|
++pos;
|
|
}
|
|
if (token.empty()) {
|
|
return -1;
|
|
} else {
|
|
return 1;
|
|
}
|
|
}
|
|
|
|
std::shared_ptr<Value> Expr::Eval(std::shared_ptr<Value> x, std::shared_ptr<Scope> env)
|
|
{
|
|
while (true) {
|
|
switch (x->type) {
|
|
case ValueType::kAtom:
|
|
{
|
|
return x;
|
|
}
|
|
break;
|
|
case ValueType::kSymbol:
|
|
{
|
|
Symbol&& symbol = std::any_cast<Symbol>(x->value);
|
|
return env->Find(symbol.name);
|
|
}
|
|
break;
|
|
case ValueType::kList:
|
|
{
|
|
List list = std::any_cast<List>(x->value);
|
|
auto leader = list->at(0);
|
|
if (leader->type == ValueType::kCProc) {
|
|
CProc&& cproc = std::any_cast<CProc>(leader->value);
|
|
List exps = std::make_shared<std::vector<std::shared_ptr<Value>>>();
|
|
for (auto itr = list->begin() + 1; itr != list->end(); ++itr) {
|
|
exps->push_back(Eval(*itr, env));
|
|
}
|
|
return cproc(exps);
|
|
} else {
|
|
CProc&& cproc = std::any_cast<CProc>(Eval(leader, env)->value);
|
|
List exps = std::make_shared<std::vector<std::shared_ptr<Value>>>();
|
|
for (auto itr = list->begin() + 1; itr != list->end(); ++itr) {
|
|
exps->push_back(Eval(*itr, env));
|
|
}
|
|
return cproc(exps);
|
|
}
|
|
}
|
|
break;
|
|
default:
|
|
{
|
|
abort();
|
|
return nullptr;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
}
|