Commit | Line | Data |
---|---|---|
2539e6af KR |
1 | //****************************************************************************** |
2 | // MAL - step 4 - if/fn/do | |
3 | //****************************************************************************** | |
4 | // This file is automatically generated from templates/step.swift. Rather than | |
5 | // editing it directly, it's probably better to edit templates/step.swift and | |
6 | // regenerate this file. Otherwise, your change might be lost if/when someone | |
7 | // else performs that process. | |
8 | //****************************************************************************** | |
9 | ||
10 | import Foundation | |
11 | ||
12 | let kSymbolDef = MalSymbol(symbol: "def!") | |
13 | let kSymbolDo = MalSymbol(symbol: "do") | |
14 | let kSymbolFunction = MalSymbol(symbol: "fn*") | |
15 | let kSymbolIf = MalSymbol(symbol: "if") | |
16 | let kSymbolLet = MalSymbol(symbol: "let*") | |
17 | ||
18 | // Parse the string into an AST. | |
19 | // | |
20 | func READ(str: String) -> MalVal { | |
21 | return read_str(str) | |
22 | } | |
23 | ||
24 | // Perform a simple evaluation of the `ast` object. If it's a symbol, | |
25 | // dereference it and return its value. If it's a collection, call EVAL on all | |
26 | // elements (or just the values, in the case of the hashmap). Otherwise, return | |
27 | // the object unchanged. | |
28 | // | |
29 | func eval_ast(ast: MalVal, env: Environment) -> MalVal { | |
30 | if is_symbol(ast) { | |
31 | let symbol = ast as MalSymbol | |
32 | if let val = env.get(symbol) { | |
33 | return val | |
34 | } | |
35 | return MalError(message: "'\(symbol)' not found") // Specific text needed to match MAL unit tests | |
36 | } | |
37 | if is_list(ast) { | |
38 | let list = ast as MalList | |
39 | var result = [MalVal]() | |
40 | result.reserveCapacity(list.count) | |
41 | for item in list { | |
42 | let eval = EVAL(item, env) | |
43 | if is_error(eval) { return eval } | |
44 | result.append(eval) | |
45 | } | |
46 | return MalList(array: result) | |
47 | } | |
48 | if is_vector(ast) { | |
49 | let vec = ast as MalVector | |
50 | var result = [MalVal]() | |
51 | result.reserveCapacity(vec.count) | |
52 | for item in vec { | |
53 | let eval = EVAL(item, env) | |
54 | if is_error(eval) { return eval } | |
55 | result.append(eval) | |
56 | } | |
57 | return MalVector(array: result) | |
58 | } | |
59 | if is_hashmap(ast) { | |
60 | let hash = ast as MalHashMap | |
61 | var result = [MalVal]() | |
62 | result.reserveCapacity(hash.count * 2) | |
63 | for (k, v) in hash { | |
64 | let new_v = EVAL(v, env) | |
65 | if is_error(new_v) { return new_v } | |
66 | result.append(k) | |
67 | result.append(new_v) | |
68 | } | |
69 | return MalHashMap(array: result) | |
70 | } | |
71 | return ast | |
72 | } | |
73 | ||
74 | // Walk the AST and completely evaluate it, handling macro expansions, special | |
75 | // forms and function calls. | |
76 | // | |
77 | func EVAL(var ast: MalVal, var env: Environment) -> MalVal { | |
78 | if is_error(ast) { return ast } | |
79 | ||
80 | // Special handling if it's a list. | |
81 | ||
82 | if is_list(ast) { | |
83 | var list = ast as MalList | |
84 | ||
85 | if list.isEmpty { | |
86 | return ast | |
87 | } | |
88 | ||
89 | let arg1 = list.first() | |
90 | if is_symbol(arg1) { | |
91 | let fn_symbol = arg1 as MalSymbol | |
92 | ||
93 | // Check for special forms, where we want to check the operation | |
94 | // before evaluating all of the parameters. | |
95 | ||
96 | if fn_symbol == kSymbolDef { | |
97 | if list.count != 3 { | |
98 | return MalError(message: "expected 2 arguments to def!, got \(list.count - 1)") | |
99 | } | |
100 | let arg1 = list[1] | |
101 | let arg2 = list[2] | |
102 | if !is_symbol(arg1) { | |
103 | return MalError(message: "expected symbol for first argument to def!") | |
104 | } | |
105 | let sym = arg1 as MalSymbol | |
106 | let value = EVAL(arg2, env) | |
107 | if is_error(value) { return value } | |
108 | return env.set(sym, value) | |
109 | } else if fn_symbol == kSymbolLet { | |
110 | if list.count != 3 { | |
111 | return MalError(message: "expected 2 arguments to let*, got \(list.count - 1)") | |
112 | } | |
113 | let arg1 = list[1] | |
114 | let arg2 = list[2] | |
115 | if !is_sequence(arg1) { | |
116 | return MalError(message: "expected list for first argument to let*") | |
117 | } | |
118 | let bindings = arg1 as MalSequence | |
119 | if bindings.count % 2 == 1 { | |
120 | return MalError(message: "expected even number of elements in bindings to let*, got \(bindings.count)") | |
121 | } | |
122 | var new_env = Environment(outer: env) | |
123 | for var index = 0; index < bindings.count; index += 2 { | |
124 | let binding_name = bindings[index] | |
125 | let binding_value = bindings[index + 1] | |
126 | ||
127 | if !is_symbol(binding_name) { | |
128 | return MalError(message: "expected symbol for first element in binding pair") | |
129 | } | |
130 | let binding_symbol = binding_name as MalSymbol | |
131 | let evaluated_value = EVAL(binding_value, new_env) | |
132 | if is_error(evaluated_value) { return evaluated_value } | |
133 | new_env.set(binding_symbol, evaluated_value) | |
134 | } | |
135 | return EVAL(arg2, new_env) | |
136 | } else if fn_symbol == kSymbolDo { | |
137 | let evaluated_ast = eval_ast(list.rest(), env) | |
138 | if is_error(evaluated_ast) { return evaluated_ast } | |
139 | let evaluated_seq = evaluated_ast as MalSequence | |
140 | return evaluated_seq.last() | |
141 | } else if fn_symbol == kSymbolIf { | |
142 | if list.count < 3 { | |
143 | return MalError(message: "expected at least 2 arguments to if, got \(list.count - 1)") | |
144 | } | |
145 | let cond_result = EVAL(list[1], env) | |
146 | var new_ast = MalVal() | |
147 | if is_truthy(cond_result) { | |
148 | new_ast = list[2] | |
149 | } else if list.count == 4 { | |
150 | new_ast = list[3] | |
151 | } else { | |
152 | return MalNil() | |
153 | } | |
154 | return EVAL(new_ast, env) | |
155 | } else if fn_symbol == kSymbolFunction { | |
156 | if list.count != 3 { | |
157 | return MalError(message: "expected 2 arguments to fn*, got \(list.count - 1)") | |
158 | } | |
159 | if !is_sequence(list[1]) { | |
160 | return MalError(message: "expected list or vector for first argument to fn*") | |
161 | } | |
162 | return MalClosure(eval: EVAL, args:list[1] as MalSequence, body:list[2], env:env) | |
163 | } | |
164 | } | |
165 | ||
166 | // Standard list to be applied. Evaluate all the elements first. | |
167 | ||
168 | let eval = eval_ast(ast, env) | |
169 | if is_error(eval) { return eval } | |
170 | ||
171 | // The result had better be a list and better be non-empty. | |
172 | ||
173 | let eval_list = eval as MalList | |
174 | if eval_list.isEmpty { | |
175 | return eval_list | |
176 | } | |
177 | ||
178 | // Get the first element of the list and execute it. | |
179 | ||
180 | let first = eval_list.first() | |
181 | let rest = eval_list.rest() | |
182 | ||
183 | if is_builtin(first) { | |
184 | let fn = first as MalBuiltin | |
185 | let answer = fn.apply(rest) | |
186 | return answer | |
187 | } else if is_closure(first) { | |
188 | let fn = first as MalClosure | |
189 | var new_env = Environment(outer: fn.env) | |
190 | let result = new_env.set_bindings(fn.args, with_exprs:rest) | |
191 | if is_error(result) { return result } | |
192 | let answer = EVAL(fn.body, new_env) | |
193 | return answer | |
194 | } | |
195 | ||
196 | // The first element wasn't a function to be executed. Return an | |
197 | // error saying so. | |
198 | ||
199 | return MalError(message: "first list item does not evaluate to a function: \(first)") | |
200 | } | |
201 | ||
202 | // Not a list -- just evaluate and return. | |
203 | ||
204 | let answer = eval_ast(ast, env) | |
205 | return answer | |
206 | } | |
207 | ||
208 | // Convert the value into a human-readable string for printing. | |
209 | // | |
210 | func PRINT(exp: MalVal) -> String? { | |
211 | if is_error(exp) { return nil } | |
212 | return pr_str(exp, true) | |
213 | } | |
214 | ||
215 | // Perform the READ and EVAL steps. Useful for when you don't care about the | |
216 | // printable result. | |
217 | // | |
218 | func RE(text: String, env: Environment) -> MalVal? { | |
219 | if text.isEmpty { return nil } | |
220 | let ast = READ(text) | |
221 | if is_error(ast) { | |
222 | println("Error parsing input: \(ast)") | |
223 | return nil | |
224 | } | |
225 | let exp = EVAL(ast, env) | |
226 | if is_error(exp) { | |
227 | println("Error evaluating input: \(exp)") | |
228 | return nil | |
229 | } | |
230 | return exp | |
231 | } | |
232 | ||
233 | // Perform the full READ/EVAL/PRINT, returning a printable string. | |
234 | // | |
235 | func REP(text: String, env: Environment) -> String? { | |
236 | let exp = RE(text, env) | |
237 | if exp == nil { return nil } | |
238 | return PRINT(exp!) | |
239 | } | |
240 | ||
241 | // Perform the full REPL. | |
242 | // | |
243 | func REPL(env: Environment) { | |
244 | while true { | |
245 | if let text = _readline("user> ") { | |
246 | if let output = REP(text, env) { | |
247 | println("\(output)") | |
248 | } | |
249 | } else { | |
250 | println() | |
251 | break | |
252 | } | |
253 | } | |
254 | } | |
255 | ||
256 | func main() { | |
257 | var env = Environment(outer: nil) | |
258 | ||
259 | load_history_file() | |
260 | load_builtins(env) | |
261 | ||
262 | RE("(def! not (fn* (a) (if a false true)))", env) | |
263 | REPL(env) | |
264 | ||
265 | save_history_file() | |
266 | } |