Merge pull request #374 from sgtatham/vala-fixes
[jackhill/mal.git] / vala / step4_if_fn_do.vala
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 {
29 var roota = new GC.Root(ast); (void)roota;
30 var roote = new GC.Root(env); (void)roote;
31 if (ast is Mal.Sym)
32 return env.get(ast as Mal.Sym);
33 if (ast is Mal.List) {
34 var result = new Mal.List.empty();
35 var root = new GC.Root(result); (void)root;
36 foreach (var elt in (ast as Mal.List).vs)
37 result.vs.append(EVAL(elt, env));
38 return result;
39 }
40 if (ast is Mal.Vector) {
41 var results = new GLib.List<Mal.Val>();
42 for (var iter = (ast as Mal.Vector).iter();
43 iter.nonempty(); iter.step())
44 results.append(EVAL(iter.deref(), env));
45 return new Mal.Vector.from_list(results);
46 }
47 if (ast is Mal.Hashmap) {
48 var result = new Mal.Hashmap();
49 var root = new GC.Root(result); (void)root;
50 var map = (ast as Mal.Hashmap).vs;
51 foreach (var key in map.get_keys())
52 result.insert(key, EVAL(map[key], env));
53 return result;
54 }
55 return ast;
56 }
57
58 private static Mal.Val define_eval(Mal.Val key, Mal.Val value,
59 Mal.Env eval_env, Mal.Env def_env)
60 throws Mal.Error {
61 var rootk = new GC.Root(key); (void)rootk;
62 var roote = new GC.Root(def_env); (void)roote;
63 var symkey = key as Mal.Sym;
64 if (symkey == null)
65 throw new Mal.Error.BAD_PARAMS(
66 "let*: expected a symbol to define");
67 var val = EVAL(value, eval_env);
68 def_env.set(symkey, val);
69 return val;
70 }
71
72 public static Mal.Val EVAL(Mal.Val ast, Mal.Env env)
73 throws Mal.Error {
74 var ast_root = new GC.Root(ast); (void)ast_root;
75 var env_root = new GC.Root(env); (void)env_root;
76 GC.Core.maybe_collect();
77
78 if (ast is Mal.List) {
79 unowned GLib.List<Mal.Val> list = (ast as Mal.List).vs;
80 if (list.first() == null)
81 return ast;
82
83 var first = list.first().data;
84 if (first is Mal.Sym) {
85 var sym = first as Mal.Sym;
86 switch (sym.v) {
87 case "def!":
88 if (list.length() != 3)
89 throw new Mal.Error.BAD_PARAMS(
90 "def!: expected two values");
91 return define_eval(list.next.data, list.next.next.data,
92 env, env);
93 case "let*":
94 if (list.length() != 3)
95 throw new Mal.Error.BAD_PARAMS(
96 "let*: expected two values");
97 var defns = list.nth(1).data;
98 var newenv = new Mal.Env.within(env);
99
100 if (defns is Mal.List) {
101 for (unowned GLib.List<Mal.Val> iter =
102 (defns as Mal.List).vs;
103 iter != null; iter = iter.next.next) {
104 if (iter.next == null)
105 throw new Mal.Error.BAD_PARAMS(
106 "let*: expected an even-length list" +
107 " of definitions");
108 define_eval(iter.data, iter.next.data,
109 newenv, newenv);
110 }
111 } else if (defns is Mal.Vector) {
112 var vec = defns as Mal.Vector;
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)
118 define_eval(vec[i], vec[i+1], newenv, newenv);
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();
199 var root = new GC.Root(env); (void)root;
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 }