d: Fix crash on literal empty list
[jackhill/mal.git] / d / step3_env.d
1 module main;
2
3 import std.algorithm;
4 import std.array;
5 import std.range;
6 import std.stdio;
7 import std.string;
8 import env;
9 import readline;
10 import reader;
11 import printer;
12 import types;
13
14 MalType READ(string str)
15 {
16 return read_str(str);
17 }
18
19 MalType eval_ast(MalType ast, Env env)
20 {
21 if (typeid(ast) == typeid(MalSymbol))
22 {
23 auto sym = verify_cast!MalSymbol(ast);
24 return env.get(sym);
25 }
26 else if (typeid(ast) == typeid(MalList))
27 {
28 auto lst = verify_cast!MalList(ast);
29 auto el = array(lst.elements.map!(e => EVAL(e, env)));
30 return new MalList(el);
31 }
32 else if (typeid(ast) == typeid(MalVector))
33 {
34 auto lst = verify_cast!MalVector(ast);
35 auto el = array(lst.elements.map!(e => EVAL(e, env)));
36 return new MalVector(el);
37 }
38 else if (typeid(ast) == typeid(MalHashmap))
39 {
40 auto hm = verify_cast!MalHashmap(ast);
41 typeof(hm.data) new_data;
42 foreach (string k, MalType v; hm.data)
43 {
44 new_data[k] = EVAL(v, env);
45 }
46 return new MalHashmap(new_data);
47 }
48 else
49 {
50 return ast;
51 }
52 }
53
54 MalType EVAL(MalType ast, Env env)
55 {
56 MalList ast_list = cast(MalList) ast;
57 if (ast_list is null)
58 {
59 return eval_ast(ast, env);
60 }
61 if (ast_list.elements.length == 0)
62 {
63 return ast;
64 }
65
66 auto a0_sym = verify_cast!MalSymbol(ast_list.elements[0]);
67 switch (a0_sym.name)
68 {
69 case "def!":
70 auto a1 = verify_cast!MalSymbol(ast_list.elements[1]);
71 return env.set(a1, EVAL(ast_list.elements[2], env));
72
73 case "let*":
74 auto a1 = verify_cast!MalSequential(ast_list.elements[1]);
75 auto let_env = new Env(env);
76 foreach (kv; chunks(a1.elements, 2))
77 {
78 if (kv.length < 2) throw new Exception("let* requires even number of elements");
79 auto var_name = verify_cast!MalSymbol(kv[0]);
80 let_env.set(var_name, EVAL(kv[1], let_env));
81 }
82 return EVAL(ast_list.elements[2], let_env);
83
84 default:
85 auto el = verify_cast!MalList(eval_ast(ast_list, env));
86 auto fobj = verify_cast!MalBuiltinFunc(el.elements[0]);
87 auto args = el.elements[1..$];
88 return fobj.fn(args);
89 }
90 }
91
92 string PRINT(MalType ast)
93 {
94 return pr_str(ast);
95 }
96
97 string rep(string str, Env env)
98 {
99 return PRINT(EVAL(READ(str), env));
100 }
101
102 static MalType mal_add(MalType[] a ...)
103 {
104 verify_args_count(a, 2);
105 MalInteger i0 = verify_cast!MalInteger(a[0]);
106 MalInteger i1 = verify_cast!MalInteger(a[1]);
107 return new MalInteger(i0.val + i1.val);
108 }
109
110 static MalType mal_sub(MalType[] a ...)
111 {
112 verify_args_count(a, 2);
113 MalInteger i0 = verify_cast!MalInteger(a[0]);
114 MalInteger i1 = verify_cast!MalInteger(a[1]);
115 return new MalInteger(i0.val - i1.val);
116 }
117
118 static MalType mal_mul(MalType[] a ...)
119 {
120 verify_args_count(a, 2);
121 MalInteger i0 = verify_cast!MalInteger(a[0]);
122 MalInteger i1 = verify_cast!MalInteger(a[1]);
123 return new MalInteger(i0.val * i1.val);
124 }
125
126 static MalType mal_div(MalType[] a ...)
127 {
128 verify_args_count(a, 2);
129 MalInteger i0 = verify_cast!MalInteger(a[0]);
130 MalInteger i1 = verify_cast!MalInteger(a[1]);
131 return new MalInteger(i0.val / i1.val);
132 }
133
134 void main()
135 {
136 auto repl_env = new Env(null);
137 repl_env.set(new MalSymbol("+"), new MalBuiltinFunc(&mal_add, "+"));
138 repl_env.set(new MalSymbol("-"), new MalBuiltinFunc(&mal_sub, "-"));
139 repl_env.set(new MalSymbol("*"), new MalBuiltinFunc(&mal_mul, "*"));
140 repl_env.set(new MalSymbol("/"), new MalBuiltinFunc(&mal_div, "/"));
141
142 for (;;)
143 {
144 string line = _readline("user> ");
145 if (line is null) break;
146 if (line.length == 0) continue;
147 try
148 {
149 writeln(rep(line, repl_env));
150 }
151 catch (Exception e)
152 {
153 writeln("Error: ", e.msg);
154 }
155 }
156 writeln("");
157 }