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