Commit | Line | Data |
---|---|---|
213a3288 ST |
1 | class Mal.Main: GLib.Object { |
2 | static bool eof; | |
3 | ||
4 | static construct { | |
5 | eof = false; | |
6 | } | |
7 | ||
8 | public static Mal.Val? READ() { | |
9 | string? line = Readline.readline("user> "); | |
10 | if (line != null) { | |
11 | if (line.length > 0) | |
12 | Readline.History.add(line); | |
13 | ||
14 | try { | |
15 | return Reader.read_str(line); | |
16 | } catch (Mal.Error err) { | |
17 | GLib.stderr.printf("%s\n", err.message); | |
18 | return null; | |
19 | } | |
20 | } else { | |
21 | stdout.printf("\n"); | |
22 | eof = true; | |
23 | return null; | |
24 | } | |
25 | } | |
26 | ||
27 | public static Mal.Val eval_ast(Mal.Val ast, Mal.Env env) | |
28 | throws Mal.Error { | |
5ff3e2e8 ST |
29 | var roota = new GC.Root(ast); (void)roota; |
30 | var roote = new GC.Root(env); (void)roote; | |
213a3288 ST |
31 | if (ast is Mal.Sym) |
32 | return env.get(ast as Mal.Sym); | |
33 | if (ast is Mal.List) { | |
5ff3e2e8 ST |
34 | var result = new Mal.List.empty(); |
35 | var root = new GC.Root(result); (void)root; | |
213a3288 | 36 | foreach (var elt in (ast as Mal.List).vs) |
5ff3e2e8 ST |
37 | result.vs.append(EVAL(elt, env)); |
38 | return result; | |
213a3288 ST |
39 | } |
40 | if (ast is Mal.Vector) { | |
9cb52cf1 ST |
41 | var vec = ast as Mal.Vector; |
42 | var result = new Mal.Vector.with_size(vec.length); | |
43 | var root = new GC.Root(result); (void)root; | |
44 | for (var i = 0; i < vec.length; i++) | |
45 | result[i] = EVAL(vec[i], env); | |
46 | return result; | |
213a3288 ST |
47 | } |
48 | if (ast is Mal.Hashmap) { | |
49 | var result = new Mal.Hashmap(); | |
5ff3e2e8 | 50 | var root = new GC.Root(result); (void)root; |
213a3288 ST |
51 | var map = (ast as Mal.Hashmap).vs; |
52 | foreach (var key in map.get_keys()) | |
53 | result.insert(key, EVAL(map[key], env)); | |
54 | return result; | |
55 | } | |
56 | return ast; | |
57 | } | |
58 | ||
59 | private static Mal.Val define_eval(Mal.Val key, Mal.Val value, | |
73030faf | 60 | Mal.Env env) |
213a3288 | 61 | throws Mal.Error { |
5ff3e2e8 | 62 | var rootk = new GC.Root(key); (void)rootk; |
73030faf | 63 | var roote = new GC.Root(env); (void)roote; |
213a3288 ST |
64 | var symkey = key as Mal.Sym; |
65 | if (symkey == null) | |
66 | throw new Mal.Error.BAD_PARAMS( | |
67 | "let*: expected a symbol to define"); | |
73030faf NB |
68 | var val = EVAL(value, env); |
69 | env.set(symkey, val); | |
213a3288 ST |
70 | return val; |
71 | } | |
72 | ||
73 | public static Mal.Val EVAL(Mal.Val ast, Mal.Env env) | |
74 | throws Mal.Error { | |
5ff3e2e8 ST |
75 | var ast_root = new GC.Root(ast); (void)ast_root; |
76 | var env_root = new GC.Root(env); (void)env_root; | |
77 | GC.Core.maybe_collect(); | |
78 | ||
213a3288 ST |
79 | if (ast is Mal.List) { |
80 | unowned GLib.List<Mal.Val> list = (ast as Mal.List).vs; | |
81 | if (list.first() == null) | |
82 | return ast; | |
83 | ||
84 | var first = list.first().data; | |
85 | if (first is Mal.Sym) { | |
86 | var sym = first as Mal.Sym; | |
87 | switch (sym.v) { | |
88 | case "def!": | |
89 | if (list.length() != 3) | |
90 | throw new Mal.Error.BAD_PARAMS( | |
91 | "def!: expected two values"); | |
92 | return define_eval(list.next.data, list.next.next.data, | |
73030faf | 93 | env); |
213a3288 ST |
94 | case "let*": |
95 | if (list.length() != 3) | |
96 | throw new Mal.Error.BAD_PARAMS( | |
97 | "let*: expected two values"); | |
98 | var defns = list.nth(1).data; | |
99 | var newenv = new Mal.Env.within(env); | |
100 | ||
101 | if (defns is Mal.List) { | |
102 | for (unowned GLib.List<Mal.Val> iter = | |
103 | (defns as Mal.List).vs; | |
104 | iter != null; iter = iter.next.next) { | |
105 | if (iter.next == null) | |
106 | throw new Mal.Error.BAD_PARAMS( | |
107 | "let*: expected an even-length list" + | |
108 | " of definitions"); | |
73030faf | 109 | define_eval(iter.data, iter.next.data, newenv); |
213a3288 ST |
110 | } |
111 | } else if (defns is Mal.Vector) { | |
54c4ae61 | 112 | var vec = defns as Mal.Vector; |
213a3288 ST |
113 | if (vec.length % 2 != 0) |
114 | throw new Mal.Error.BAD_PARAMS( | |
115 | "let*: expected an even-length vector" + | |
116 | " of definitions"); | |
117 | for (var i = 0; i < vec.length; i += 2) | |
73030faf | 118 | define_eval(vec[i], vec[i+1], newenv); |
213a3288 ST |
119 | } else { |
120 | throw new Mal.Error.BAD_PARAMS( | |
121 | "let*: expected a list or vector of definitions"); | |
122 | } | |
123 | return EVAL(list.nth(2).data, newenv); | |
124 | case "do": | |
125 | Mal.Val result = null; | |
126 | for (list = list.next; list != null; list = list.next) | |
127 | result = EVAL(list.data, env); | |
128 | if (result == null) | |
129 | throw new Mal.Error.BAD_PARAMS( | |
130 | "do: expected at least one argument"); | |
131 | return result; | |
132 | case "if": | |
133 | if (list.length() != 3 && list.length() != 4) | |
134 | throw new Mal.Error.BAD_PARAMS( | |
135 | "if: expected two or three arguments"); | |
136 | list = list.next; | |
137 | var cond = EVAL(list.data, env); | |
138 | list = list.next; | |
139 | if (!cond.truth_value()) { | |
140 | // Skip to the else clause, which defaults to nil. | |
141 | list = list.next; | |
142 | if (list == null) | |
143 | return new Mal.Nil(); | |
144 | } | |
145 | return EVAL(list.data, env); | |
146 | case "fn*": | |
147 | if (list.length() != 3) | |
148 | throw new Mal.Error.BAD_PARAMS( | |
149 | "fn*: expected two arguments"); | |
150 | var binds = list.next.data as Mal.Listlike; | |
151 | var body = list.next.next.data; | |
152 | if (binds == null) | |
153 | throw new Mal.Error.BAD_PARAMS( | |
154 | "fn*: expected a list of parameter names"); | |
155 | for (var iter = binds.iter(); iter.nonempty(); iter.step()) | |
156 | if (!(iter.deref() is Mal.Sym)) | |
157 | throw new Mal.Error.BAD_PARAMS( | |
158 | "fn*: expected parameter name to be "+ | |
159 | "symbol"); | |
160 | return new Mal.Function(binds, body, env); | |
161 | } | |
162 | } | |
163 | ||
164 | var newlist = eval_ast(ast, env) as Mal.List; | |
165 | unowned GLib.List<Mal.Val> firstlink = newlist.vs.first(); | |
166 | Mal.Val firstdata = firstlink.data; | |
167 | newlist.vs.remove_link(firstlink); | |
168 | ||
169 | if (firstdata is Mal.BuiltinFunction) { | |
170 | return (firstdata as Mal.BuiltinFunction).call(newlist); | |
171 | } else if (firstdata is Mal.Function) { | |
172 | var fn = firstdata as Mal.Function; | |
173 | var newenv = new Mal.Env.funcall( | |
174 | fn.env, fn.parameters, newlist); | |
175 | return EVAL(fn.body, newenv); | |
176 | } else { | |
177 | throw new Mal.Error.CANNOT_APPLY( | |
178 | "bad value at start of list"); | |
179 | } | |
180 | } else { | |
181 | return eval_ast(ast, env); | |
182 | } | |
183 | } | |
184 | ||
185 | public static void PRINT(Mal.Val value) { | |
186 | stdout.printf("%s\n", pr_str(value)); | |
187 | } | |
188 | ||
189 | public static void rep(Mal.Env env) throws Mal.Error { | |
190 | Mal.Val? val = READ(); | |
191 | if (val != null) { | |
192 | val = EVAL(val, env); | |
193 | PRINT(val); | |
194 | } | |
195 | } | |
196 | ||
197 | public static int main(string[] args) { | |
198 | var env = new Mal.Env(); | |
5ff3e2e8 | 199 | var root = new GC.Root(env); (void)root; |
213a3288 ST |
200 | |
201 | Mal.Core.make_ns(); | |
202 | foreach (var key in Mal.Core.ns.get_keys()) | |
203 | env.set(new Mal.Sym(key), Mal.Core.ns[key]); | |
204 | ||
205 | try { | |
206 | EVAL(Mal.Reader.read_str("(def! not (fn* (a) (if a false true)))"), | |
207 | env); | |
208 | } catch (Mal.Error err) { | |
209 | assert(false); // shouldn't happen | |
210 | } | |
211 | ||
212 | while (!eof) { | |
213 | try { | |
214 | rep(env); | |
215 | } catch (Mal.Error err) { | |
216 | GLib.stderr.printf("%s\n", err.message); | |
217 | } | |
218 | } | |
219 | return 0; | |
220 | } | |
221 | } |