1
1
mirror of https://github.com/kanaka/mal.git synced 2024-11-11 00:52:44 +03:00
mal/d/step3_env.d
2016-03-31 14:46:52 -04:00

158 lines
3.9 KiB
D

module main;
import std.algorithm;
import std.array;
import std.range;
import std.stdio;
import std.string;
import env;
import readline;
import reader;
import printer;
import types;
MalType READ(string str)
{
return read_str(str);
}
MalType eval_ast(MalType ast, Env env)
{
if (typeid(ast) == typeid(MalSymbol))
{
auto sym = verify_cast!MalSymbol(ast);
return env.get(sym);
}
else if (typeid(ast) == typeid(MalList))
{
auto lst = verify_cast!MalList(ast);
auto el = array(lst.elements.map!(e => EVAL(e, env)));
return new MalList(el);
}
else if (typeid(ast) == typeid(MalVector))
{
auto lst = verify_cast!MalVector(ast);
auto el = array(lst.elements.map!(e => EVAL(e, env)));
return new MalVector(el);
}
else if (typeid(ast) == typeid(MalHashmap))
{
auto hm = verify_cast!MalHashmap(ast);
typeof(hm.data) new_data;
foreach (string k, MalType v; hm.data)
{
new_data[k] = EVAL(v, env);
}
return new MalHashmap(new_data);
}
else
{
return ast;
}
}
MalType EVAL(MalType ast, Env env)
{
MalList ast_list = cast(MalList) ast;
if (ast_list is null)
{
return eval_ast(ast, env);
}
if (ast_list.elements.length == 0)
{
return ast;
}
auto a0_sym = verify_cast!MalSymbol(ast_list.elements[0]);
switch (a0_sym.name)
{
case "def!":
auto a1 = verify_cast!MalSymbol(ast_list.elements[1]);
return env.set(a1, EVAL(ast_list.elements[2], env));
case "let*":
auto a1 = verify_cast!MalSequential(ast_list.elements[1]);
auto let_env = new Env(env);
foreach (kv; chunks(a1.elements, 2))
{
if (kv.length < 2) throw new Exception("let* requires even number of elements");
auto var_name = verify_cast!MalSymbol(kv[0]);
let_env.set(var_name, EVAL(kv[1], let_env));
}
return EVAL(ast_list.elements[2], let_env);
default:
auto el = verify_cast!MalList(eval_ast(ast_list, env));
auto fobj = verify_cast!MalBuiltinFunc(el.elements[0]);
auto args = el.elements[1..$];
return fobj.fn(args);
}
}
string PRINT(MalType ast)
{
return pr_str(ast);
}
string rep(string str, Env env)
{
return PRINT(EVAL(READ(str), env));
}
static MalType mal_add(MalType[] a ...)
{
verify_args_count(a, 2);
MalInteger i0 = verify_cast!MalInteger(a[0]);
MalInteger i1 = verify_cast!MalInteger(a[1]);
return new MalInteger(i0.val + i1.val);
}
static MalType mal_sub(MalType[] a ...)
{
verify_args_count(a, 2);
MalInteger i0 = verify_cast!MalInteger(a[0]);
MalInteger i1 = verify_cast!MalInteger(a[1]);
return new MalInteger(i0.val - i1.val);
}
static MalType mal_mul(MalType[] a ...)
{
verify_args_count(a, 2);
MalInteger i0 = verify_cast!MalInteger(a[0]);
MalInteger i1 = verify_cast!MalInteger(a[1]);
return new MalInteger(i0.val * i1.val);
}
static MalType mal_div(MalType[] a ...)
{
verify_args_count(a, 2);
MalInteger i0 = verify_cast!MalInteger(a[0]);
MalInteger i1 = verify_cast!MalInteger(a[1]);
return new MalInteger(i0.val / i1.val);
}
void main()
{
auto repl_env = new Env(null);
repl_env.set(new MalSymbol("+"), new MalBuiltinFunc(&mal_add, "+"));
repl_env.set(new MalSymbol("-"), new MalBuiltinFunc(&mal_sub, "-"));
repl_env.set(new MalSymbol("*"), new MalBuiltinFunc(&mal_mul, "*"));
repl_env.set(new MalSymbol("/"), new MalBuiltinFunc(&mal_div, "/"));
for (;;)
{
string line = _readline("user> ");
if (line is null) break;
if (line.length == 0) continue;
try
{
writeln(rep(line, repl_env));
}
catch (Exception e)
{
writeln("Error: ", e.msg);
}
}
writeln("");
}