Merge pull request #383 from asarhaddon/ada2tco-do
[jackhill/mal.git] / d / step4_if_fn_do.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 mal_core;
10 import readline;
11 import reader;
12 import printer;
13 import types;
14
15 MalType READ(string str)
16 {
17 return read_str(str);
18 }
19
20 MalType eval_ast(MalType ast, Env env)
21 {
22 if (auto sym = cast(MalSymbol)ast)
23 {
24 return env.get(sym);
25 }
26 else if (auto lst = cast(MalList)ast)
27 {
28 auto el = array(lst.elements.map!(e => EVAL(e, env)));
29 return new MalList(el);
30 }
31 else if (auto lst = cast(MalVector)ast)
32 {
33 auto el = array(lst.elements.map!(e => EVAL(e, env)));
34 return new MalVector(el);
35 }
36 else if (auto hm = cast(MalHashmap)ast)
37 {
38 typeof(hm.data) new_data;
39 foreach (string k, MalType v; hm.data)
40 {
41 new_data[k] = EVAL(v, env);
42 }
43 return new MalHashmap(new_data);
44 }
45 else
46 {
47 return ast;
48 }
49 }
50
51 MalType EVAL(MalType ast, Env env)
52 {
53 MalList ast_list = cast(MalList) ast;
54 if (ast_list is null)
55 {
56 return eval_ast(ast, env);
57 }
58
59 auto aste = ast_list.elements;
60 if (aste.length == 0)
61 {
62 return ast;
63 }
64 auto a0_sym = cast(MalSymbol) aste[0];
65 auto sym_name = a0_sym is null ? "" : a0_sym.name;
66 switch (sym_name)
67 {
68 case "def!":
69 auto a1 = verify_cast!MalSymbol(aste[1]);
70 return env.set(a1, EVAL(aste[2], env));
71
72 case "let*":
73 auto a1 = verify_cast!MalSequential(aste[1]);
74 auto let_env = new Env(env);
75 foreach (kv; chunks(a1.elements, 2))
76 {
77 if (kv.length < 2) throw new Exception("let* requires even number of elements");
78 auto var_name = verify_cast!MalSymbol(kv[0]);
79 let_env.set(var_name, EVAL(kv[1], let_env));
80 }
81 return EVAL(aste[2], let_env);
82
83 case "do":
84 auto rest = new MalList(aste[1..$]);
85 auto el = verify_cast!MalList(eval_ast(rest, env));
86 return el.elements[$-1];
87
88 case "if":
89 auto cond = EVAL(aste[1], env);
90 if (cond.is_truthy())
91 return EVAL(aste[2], env);
92 else
93 if (aste.length > 3)
94 return EVAL(aste[3], env);
95 else
96 return mal_nil;
97
98 case "fn*":
99 auto args_list = verify_cast!MalSequential(aste[1]);
100 return new MalFunc(args_list.elements, aste[2], env);
101
102 default:
103 auto el = verify_cast!MalList(eval_ast(ast, env));
104 if (el.elements.length == 0)
105 {
106 throw new Exception("Expected a non-empty list");
107 }
108 auto first = el.elements[0];
109 auto rest = el.elements[1..$];
110 if (auto funcobj = cast(MalFunc)first)
111 {
112 auto callenv = new Env(funcobj.def_env, funcobj.arg_names, rest);
113 return EVAL(funcobj.func_body, callenv);
114 }
115 else if (auto builtinfuncobj = cast(MalBuiltinFunc)first)
116 {
117 return builtinfuncobj.fn(rest);
118 }
119 else
120 {
121 throw new Exception("Expected a function");
122 }
123 }
124 }
125
126 string PRINT(MalType ast)
127 {
128 return pr_str(ast);
129 }
130
131 MalType re(string str, Env env)
132 {
133 return EVAL(READ(str), env);
134 }
135
136 string rep(string str, Env env)
137 {
138 return PRINT(re(str, env));
139 }
140
141 void main()
142 {
143 auto repl_env = new Env(null);
144 foreach (string sym_name, BuiltinStaticFuncType f; core_ns)
145 {
146 repl_env.set(new MalSymbol(sym_name), new MalBuiltinFunc(f, sym_name));
147 }
148
149 // core.mal: defined using the language itself
150 re("(def! not (fn* (a) (if a false true)))", repl_env);
151
152 for (;;)
153 {
154 string line = _readline("user> ");
155 if (line is null) break;
156 if (line.length == 0) continue;
157 try
158 {
159 writeln(rep(line, repl_env));
160 }
161 catch (Exception e)
162 {
163 writeln("Error: ", e.msg);
164 }
165 }
166 writeln("");
167 }