Merge pull request #383 from asarhaddon/ada2tco-do
[jackhill/mal.git] / vala / step3_env.vala
CommitLineData
213a3288
ST
1abstract 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
14class 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
22class 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
30class 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
38class 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
46class 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}