Merge branch 'wasamasa-elisp'
[jackhill/mal.git] / swift / step3_env.swift
CommitLineData
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
10import Foundation
11
b3ec2290
KR
12// Symbols used in this module.
13//
425305df
KR
14private let kValDef = make_symbol("def!")
15private let kValLet = make_symbol("let*")
16private let kValTry = make_symbol("try*")
17
18private let kSymbolDef = as_symbol(kValDef)
19private let kSymbolLet = as_symbol(kValLet)
2539e6af
KR
20
21// Parse the string into an AST.
22//
425305df
KR
23private 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
32private 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
72private 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
87private 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 115private 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 175private 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
182private 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 204private 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 212private 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
225func 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}