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 { | |
74 | if (ast is Mal.Sym) | |
75 | return env.get(ast as Mal.Sym); | |
76 | if (ast is Mal.List) { | |
77 | var results = new GLib.List<Mal.Val>(); | |
78 | foreach (var elt in (ast as Mal.List).vs) | |
79 | results.append(EVAL(elt, env)); | |
80 | return new Mal.List(results); | |
81 | } | |
82 | if (ast is Mal.Vector) { | |
83 | var results = new GLib.List<Mal.Val>(); | |
84 | foreach (var elt in (ast as Mal.Vector).vs) | |
85 | results.append(EVAL(elt, env)); | |
86 | return new Mal.Vector.from_list(results); | |
87 | } | |
88 | if (ast is Mal.Hashmap) { | |
89 | var result = new Mal.Hashmap(); | |
90 | var map = (ast as Mal.Hashmap).vs; | |
91 | foreach (var key in map.get_keys()) | |
92 | result.insert(key, EVAL(map[key], env)); | |
93 | return result; | |
94 | } | |
95 | return ast; | |
96 | } | |
97 | ||
98 | private static Mal.Val define_eval(Mal.Val key, Mal.Val value, | |
99 | Mal.Env eval_env, Mal.Env def_env) | |
100 | throws Mal.Error { | |
101 | var symkey = key as Mal.Sym; | |
102 | if (symkey == null) | |
103 | throw new Mal.Error.BAD_PARAMS( | |
104 | "let*: expected a symbol to define"); | |
105 | var val = EVAL(value, eval_env); | |
106 | def_env.set(symkey, val); | |
107 | return val; | |
108 | } | |
109 | ||
110 | public static Mal.Val EVAL(Mal.Val ast, Mal.Env env) | |
111 | throws Mal.Error { | |
112 | if (ast is Mal.List) { | |
113 | unowned GLib.List<Mal.Val> list = (ast as Mal.List).vs; | |
114 | if (list.first() == null) | |
115 | return ast; | |
116 | ||
117 | var first = list.first().data; | |
118 | if (first is Mal.Sym) { | |
119 | var sym = first as Mal.Sym; | |
120 | switch (sym.v) { | |
121 | case "def!": | |
122 | if (list.length() != 3) | |
123 | throw new Mal.Error.BAD_PARAMS( | |
124 | "def!: expected two values"); | |
125 | return define_eval(list.next.data, list.next.next.data, | |
126 | env, env); | |
127 | case "let*": | |
128 | if (list.length() != 3) | |
129 | throw new Mal.Error.BAD_PARAMS( | |
130 | "let*: expected two values"); | |
131 | var defns = list.nth(1).data; | |
132 | var newenv = new Mal.Env.within(env); | |
133 | ||
134 | if (defns is Mal.List) { | |
135 | for (unowned GLib.List<Mal.Val> iter = | |
136 | (defns as Mal.List).vs; | |
137 | iter != null; iter = iter.next.next) { | |
138 | if (iter.next == null) | |
139 | throw new Mal.Error.BAD_PARAMS( | |
140 | "let*: expected an even-length list" + | |
141 | " of definitions"); | |
142 | define_eval(iter.data, iter.next.data, | |
143 | newenv, newenv); | |
144 | } | |
145 | } else if (defns is Mal.Vector) { | |
146 | var vec = (defns as Mal.Vector).vs; | |
147 | if (vec.length % 2 != 0) | |
148 | throw new Mal.Error.BAD_PARAMS( | |
149 | "let*: expected an even-length vector" + | |
150 | " of definitions"); | |
151 | for (var i = 0; i < vec.length; i += 2) | |
152 | define_eval(vec[i], vec[i+1], newenv, newenv); | |
153 | } else { | |
154 | throw new Mal.Error.BAD_PARAMS( | |
155 | "let*: expected a list or vector of definitions"); | |
156 | } | |
157 | return EVAL(list.nth(2).data, newenv); | |
158 | } | |
159 | } | |
160 | ||
161 | var newlist = eval_ast(ast, env) as Mal.List; | |
162 | unowned GLib.List<Mal.Val> firstlink = newlist.vs.first(); | |
163 | Mal.Val firstdata = firstlink.data; | |
164 | newlist.vs.remove_link(firstlink); | |
165 | ||
166 | if (firstdata is Mal.BuiltinFunction) { | |
167 | return (firstdata as Mal.BuiltinFunction).call(newlist); | |
168 | } else { | |
169 | throw new Mal.Error.CANNOT_APPLY( | |
170 | "bad value at start of list"); | |
171 | } | |
172 | } else { | |
173 | return eval_ast(ast, env); | |
174 | } | |
175 | } | |
176 | ||
177 | public static void PRINT(Mal.Val value) { | |
178 | stdout.printf("%s\n", pr_str(value)); | |
179 | } | |
180 | ||
181 | public static void rep(Mal.Env env) throws Mal.Error { | |
182 | Mal.Val? val = READ(); | |
183 | if (val != null) { | |
184 | val = EVAL(val, env); | |
185 | PRINT(val); | |
186 | } | |
187 | } | |
188 | ||
189 | public static int main(string[] args) { | |
190 | var env = new Mal.Env(); | |
191 | ||
192 | env.set(new Mal.Sym("+"), new BuiltinFunctionAdd()); | |
193 | env.set(new Mal.Sym("-"), new BuiltinFunctionSub()); | |
194 | env.set(new Mal.Sym("*"), new BuiltinFunctionMul()); | |
195 | env.set(new Mal.Sym("/"), new BuiltinFunctionDiv()); | |
196 | ||
197 | while (!eof) { | |
198 | try { | |
199 | rep(env); | |
200 | } catch (Mal.Error err) { | |
201 | GLib.stderr.printf("%s\n", err.message); | |
202 | } | |
203 | } | |
204 | return 0; | |
205 | } | |
206 | } |