Merge pull request #137 from ivern/kotlin
[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 (typeid(ast) == typeid(MalSymbol))
23 {
24 auto sym = verify_cast!MalSymbol(ast);
25 return env.get(sym);
26 }
27 else if (typeid(ast) == typeid(MalList))
28 {
29 auto lst = verify_cast!MalList(ast);
30 auto el = array(lst.elements.map!(e => EVAL(e, env)));
31 return new MalList(el);
32 }
33 else if (typeid(ast) == typeid(MalVector))
34 {
35 auto lst = verify_cast!MalVector(ast);
36 auto el = array(lst.elements.map!(e => EVAL(e, env)));
37 return new MalVector(el);
38 }
39 else if (typeid(ast) == typeid(MalHashmap))
40 {
41 auto hm = verify_cast!MalHashmap(ast);
42 typeof(hm.data) new_data;
43 foreach (string k, MalType v; hm.data)
44 {
45 new_data[k] = EVAL(v, env);
46 }
47 return new MalHashmap(new_data);
48 }
49 else
50 {
51 return ast;
52 }
53 }
54
55 MalType EVAL(MalType ast, Env env)
56 {
57 MalList ast_list = cast(MalList) ast;
58 if (ast_list is null)
59 {
60 return eval_ast(ast, env);
61 }
62
63 auto aste = ast_list.elements;
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 (typeid(first) == typeid(MalFunc))
111 {
112 auto funcobj = verify_cast!MalFunc(first);
113 auto callenv = new Env(funcobj.def_env, funcobj.arg_names, rest);
114 return EVAL(funcobj.func_body, callenv);
115 }
116 else if (typeid(first) == typeid(MalBuiltinFunc))
117 {
118 auto builtinfuncobj = verify_cast!MalBuiltinFunc(first);
119 return builtinfuncobj.fn(rest);
120 }
121 else
122 {
123 throw new Exception("Expected a function");
124 }
125 }
126 }
127
128 string PRINT(MalType ast)
129 {
130 return pr_str(ast);
131 }
132
133 MalType re(string str, Env env)
134 {
135 return EVAL(READ(str), env);
136 }
137
138 string rep(string str, Env env)
139 {
140 return PRINT(re(str, env));
141 }
142
143 void main()
144 {
145 auto repl_env = new Env(null);
146 foreach (string sym_name, BuiltinStaticFuncType f; core_ns)
147 {
148 repl_env.set(new MalSymbol(sym_name), new MalBuiltinFunc(f, sym_name));
149 }
150
151 // core.mal: defined using the language itself
152 re("(def! not (fn* (a) (if a false true)))", repl_env);
153
154 for (;;)
155 {
156 string line = _readline("user> ");
157 if (line is null) break;
158 if (line.length == 0) continue;
159 try
160 {
161 writeln(rep(line, repl_env));
162 }
163 catch (Exception e)
164 {
165 writeln("Error: ", e.msg);
166 }
167 }
168 writeln("");
169 }