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