vala: make Env's internal storage private.
[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 {
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}