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 | ||
b3ec2290 KR |
12 | // Symbols used in this module. |
13 | // | |
425305df KR |
14 | private let kValDef = make_symbol("def!") |
15 | private let kValDo = make_symbol("do") | |
16 | private let kValFn = make_symbol("fn*") | |
17 | private let kValIf = make_symbol("if") | |
18 | private let kValLet = make_symbol("let*") | |
19 | private let kValTry = make_symbol("try*") | |
20 | ||
21 | private let kSymbolDef = as_symbol(kValDef) | |
22 | private let kSymbolDo = as_symbol(kValDo) | |
23 | private let kSymbolFn = as_symbol(kValFn) | |
24 | private let kSymbolIf = as_symbol(kValIf) | |
25 | private let kSymbolLet = as_symbol(kValLet) | |
2539e6af KR |
26 | |
27 | // Parse the string into an AST. | |
28 | // | |
425305df KR |
29 | private func READ(str: String) throws -> MalVal { |
30 | return try read_str(str) | |
2539e6af KR |
31 | } |
32 | ||
33 | // Perform a simple evaluation of the `ast` object. If it's a symbol, | |
34 | // dereference it and return its value. If it's a collection, call EVAL on all | |
35 | // elements (or just the values, in the case of the hashmap). Otherwise, return | |
36 | // the object unchanged. | |
37 | // | |
425305df KR |
38 | private func eval_ast(ast: MalVal, _ env: Environment) throws -> MalVal { |
39 | if let symbol = as_symbolQ(ast) { | |
40 | guard let val = env.get(symbol) else { | |
41 | try throw_error("'\(symbol)' not found") // Specific text needed to match MAL unit tests | |
42 | } | |
43 | return val | |
44 | } | |
45 | if let list = as_listQ(ast) { | |
46 | var result = [MalVal]() | |
47 | result.reserveCapacity(Int(list.count)) | |
48 | for item in list { | |
49 | let eval = try EVAL(item, env) | |
50 | result.append(eval) | |
51 | } | |
52 | return make_list(result) | |
2539e6af | 53 | } |
425305df KR |
54 | if let vec = as_vectorQ(ast) { |
55 | var result = [MalVal]() | |
56 | result.reserveCapacity(Int(vec.count)) | |
57 | for item in vec { | |
58 | let eval = try EVAL(item, env) | |
59 | result.append(eval) | |
60 | } | |
61 | return make_vector(result) | |
62 | } | |
63 | if let hash = as_hashmapQ(ast) { | |
64 | var result = [MalVal]() | |
65 | result.reserveCapacity(Int(hash.count) * 2) | |
66 | for (k, v) in hash { | |
67 | let new_v = try EVAL(v, env) | |
68 | result.append(k) | |
69 | result.append(new_v) | |
70 | } | |
71 | return make_hashmap(result) | |
72 | } | |
73 | return ast | |
2539e6af KR |
74 | } |
75 | ||
b3ec2290 KR |
76 | // EVALuate "def!". |
77 | // | |
425305df KR |
78 | private func eval_def(list: MalSequence, _ env: Environment) throws -> MalVal { |
79 | guard list.count == 3 else { | |
80 | try throw_error("expected 2 arguments to def!, got \(list.count - 1)") | |
b3ec2290 | 81 | } |
425305df KR |
82 | let arg1 = try! list.nth(1) |
83 | let arg2 = try! list.nth(2) | |
84 | guard let sym = as_symbolQ(arg1) else { | |
85 | try throw_error("expected symbol for first argument to def!") | |
b3ec2290 | 86 | } |
425305df | 87 | let value = try EVAL(arg2, env) |
b3ec2290 KR |
88 | return env.set(sym, value) |
89 | } | |
90 | ||
91 | // EVALuate "let*". | |
92 | // | |
425305df KR |
93 | private func eval_let(list: MalSequence, _ env: Environment) throws -> MalVal { |
94 | guard list.count == 3 else { | |
95 | try throw_error("expected 2 arguments to let*, got \(list.count - 1)") | |
b3ec2290 | 96 | } |
425305df KR |
97 | let arg1 = try! list.nth(1) |
98 | let arg2 = try! list.nth(2) | |
99 | guard let bindings = as_sequenceQ(arg1) else { | |
100 | try throw_error("expected list for first argument to let*") | |
b3ec2290 | 101 | } |
425305df KR |
102 | guard bindings.count % 2 == 0 else { |
103 | try throw_error("expected even number of elements in bindings to let*, got \(bindings.count)") | |
b3ec2290 | 104 | } |
425305df KR |
105 | let new_env = Environment(outer: env) |
106 | for var index: MalIntType = 0; index < bindings.count; index += 2 { | |
107 | let binding_name = try! bindings.nth(index) | |
108 | let binding_value = try! bindings.nth(index + 1) | |
109 | guard let binding_symbol = as_symbolQ(binding_name) else { | |
110 | try throw_error("expected symbol for first element in binding pair") | |
b3ec2290 | 111 | } |
425305df | 112 | let evaluated_value = try EVAL(binding_value, new_env) |
b3ec2290 KR |
113 | new_env.set(binding_symbol, evaluated_value) |
114 | } | |
425305df | 115 | return try EVAL(arg2, new_env) |
b3ec2290 KR |
116 | } |
117 | ||
118 | // EVALuate "do". | |
119 | // | |
425305df KR |
120 | private func eval_do(list: MalSequence, _ env: Environment) throws -> MalVal { |
121 | let evaluated_ast = try eval_ast(list.rest(), env) | |
122 | let evaluated_seq = as_sequence(evaluated_ast) | |
b3ec2290 KR |
123 | return evaluated_seq.last() |
124 | } | |
125 | ||
126 | // EVALuate "if". | |
127 | // | |
425305df KR |
128 | private func eval_if(list: MalSequence, _ env: Environment) throws -> MalVal { |
129 | guard list.count >= 3 else { | |
130 | try throw_error("expected at least 2 arguments to if, got \(list.count - 1)") | |
b3ec2290 | 131 | } |
425305df KR |
132 | let cond_result = try EVAL(try! list.nth(1), env) |
133 | var new_ast: MalVal | |
b3ec2290 | 134 | if is_truthy(cond_result) { |
425305df | 135 | new_ast = try! list.nth(2) |
b3ec2290 | 136 | } else if list.count == 4 { |
425305df | 137 | new_ast = try! list.nth(3) |
b3ec2290 | 138 | } else { |
425305df | 139 | return make_nil() |
b3ec2290 | 140 | } |
425305df | 141 | return try EVAL(new_ast, env) |
b3ec2290 KR |
142 | } |
143 | ||
144 | // EVALuate "fn*". | |
145 | // | |
425305df KR |
146 | private func eval_fn(list: MalSequence, _ env: Environment) throws -> MalVal { |
147 | guard list.count == 3 else { | |
148 | try throw_error("expected 2 arguments to fn*, got \(list.count - 1)") | |
b3ec2290 | 149 | } |
425305df KR |
150 | guard let seq = as_sequenceQ(try! list.nth(1)) else { |
151 | try throw_error("expected list or vector for first argument to fn*") | |
b3ec2290 | 152 | } |
425305df | 153 | return make_closure((eval: EVAL, args: seq, body: try! list.nth(2), env: env)) |
b3ec2290 KR |
154 | } |
155 | ||
2539e6af KR |
156 | // Walk the AST and completely evaluate it, handling macro expansions, special |
157 | // forms and function calls. | |
158 | // | |
425305df | 159 | private func EVAL(ast: MalVal, _ env: Environment) throws -> MalVal { |
2539e6af | 160 | |
b3ec2290 | 161 | if !is_list(ast) { |
2539e6af | 162 | |
b3ec2290 | 163 | // Not a list -- just evaluate and return. |
2539e6af | 164 | |
425305df | 165 | let answer = try eval_ast(ast, env) |
b3ec2290 KR |
166 | return answer |
167 | } | |
2539e6af | 168 | |
b3ec2290 | 169 | // Special handling if it's a list. |
2539e6af | 170 | |
425305df | 171 | let list = as_list(ast) |
2539e6af | 172 | |
b3ec2290 | 173 | if list.isEmpty { |
425305df | 174 | return ast |
b3ec2290 | 175 | } |
2539e6af | 176 | |
b3ec2290 KR |
177 | // Check for special forms, where we want to check the operation |
178 | // before evaluating all of the parameters. | |
2539e6af | 179 | |
b3ec2290 | 180 | let arg0 = list.first() |
425305df | 181 | if let fn_symbol = as_symbolQ(arg0) { |
2539e6af | 182 | |
b3ec2290 | 183 | switch fn_symbol { |
425305df KR |
184 | case kSymbolDef: return try eval_def(list, env) |
185 | case kSymbolLet: return try eval_let(list, env) | |
186 | case kSymbolDo: return try eval_do(list, env) | |
187 | case kSymbolIf: return try eval_if(list, env) | |
188 | case kSymbolFn: return try eval_fn(list, env) | |
b3ec2290 | 189 | default: break |
2539e6af | 190 | } |
b3ec2290 KR |
191 | } |
192 | ||
193 | // Standard list to be applied. Evaluate all the elements first. | |
194 | ||
425305df | 195 | let eval = try eval_ast(ast, env) |
2539e6af | 196 | |
b3ec2290 KR |
197 | // The result had better be a list and better be non-empty. |
198 | ||
425305df | 199 | let eval_list = as_list(eval) |
b3ec2290 | 200 | if eval_list.isEmpty { |
425305df | 201 | return eval |
b3ec2290 | 202 | } |
2539e6af | 203 | |
b3ec2290 KR |
204 | // Get the first element of the list and execute it. |
205 | ||
206 | let first = eval_list.first() | |
425305df | 207 | let rest = as_sequence(eval_list.rest()) |
b3ec2290 | 208 | |
425305df KR |
209 | if let fn = as_builtinQ(first) { |
210 | let answer = try fn.apply(rest) | |
b3ec2290 | 211 | return answer |
425305df KR |
212 | } else if let fn = as_closureQ(first) { |
213 | let new_env = Environment(outer: fn.env) | |
214 | let _ = try new_env.set_bindings(fn.args, with_exprs: rest) | |
215 | let answer = try EVAL(fn.body, new_env) | |
b3ec2290 | 216 | return answer |
2539e6af KR |
217 | } |
218 | ||
b3ec2290 KR |
219 | // The first element wasn't a function to be executed. Return an |
220 | // error saying so. | |
2539e6af | 221 | |
425305df | 222 | try throw_error("first list item does not evaluate to a function: \(first)") |
2539e6af KR |
223 | } |
224 | ||
225 | // Convert the value into a human-readable string for printing. | |
226 | // | |
425305df | 227 | private func PRINT(exp: MalVal) -> String { |
2539e6af KR |
228 | return pr_str(exp, true) |
229 | } | |
230 | ||
231 | // Perform the READ and EVAL steps. Useful for when you don't care about the | |
232 | // printable result. | |
233 | // | |
425305df KR |
234 | private func RE(text: String, _ env: Environment) -> MalVal? { |
235 | if !text.isEmpty { | |
236 | do { | |
237 | let ast = try READ(text) | |
238 | do { | |
239 | return try EVAL(ast, env) | |
240 | } catch let error as MalException { | |
241 | print("Error evaluating input: \(error)") | |
242 | } catch { | |
243 | print("Error evaluating input: \(error)") | |
244 | } | |
245 | } catch let error as MalException { | |
246 | print("Error parsing input: \(error)") | |
247 | } catch { | |
248 | print("Error parsing input: \(error)") | |
249 | } | |
2539e6af | 250 | } |
425305df | 251 | return nil |
2539e6af KR |
252 | } |
253 | ||
254 | // Perform the full READ/EVAL/PRINT, returning a printable string. | |
255 | // | |
425305df | 256 | private func REP(text: String, _ env: Environment) -> String? { |
2539e6af KR |
257 | let exp = RE(text, env) |
258 | if exp == nil { return nil } | |
259 | return PRINT(exp!) | |
260 | } | |
261 | ||
262 | // Perform the full REPL. | |
263 | // | |
425305df | 264 | private func REPL(env: Environment) { |
2539e6af KR |
265 | while true { |
266 | if let text = _readline("user> ") { | |
267 | if let output = REP(text, env) { | |
425305df | 268 | print("\(output)") |
2539e6af KR |
269 | } |
270 | } else { | |
425305df | 271 | print("") |
2539e6af KR |
272 | break |
273 | } | |
274 | } | |
275 | } | |
276 | ||
277 | func main() { | |
425305df | 278 | let env = Environment(outer: nil) |
2539e6af KR |
279 | |
280 | load_history_file() | |
281 | load_builtins(env) | |
282 | ||
283 | RE("(def! not (fn* (a) (if a false true)))", env) | |
284 | REPL(env) | |
285 | ||
286 | save_history_file() | |
287 | } |