Commit | Line | Data |
---|---|---|
0eace3df JM |
1 | import Foundation |
2 | ||
3 | // read | |
4 | func READ(str: String) throws -> MalVal { | |
5 | return try read_str(str) | |
6 | } | |
7 | ||
8 | // eval | |
9 | func is_pair(ast: MalVal) -> Bool { | |
10 | switch ast { | |
11 | case MalVal.MalList(let lst): return lst.count > 0 | |
12 | case MalVal.MalVector(let lst): return lst.count > 0 | |
13 | default: return false | |
14 | } | |
15 | } | |
16 | ||
17 | func quasiquote(ast: MalVal) -> MalVal { | |
18 | if !is_pair(ast) { | |
19 | return MalVal.MalList([MalVal.MalSymbol("quote"), ast]) | |
20 | } | |
21 | let a0 = try! _nth(ast, 0) | |
22 | switch a0 { | |
23 | case MalVal.MalSymbol("unquote"): | |
24 | return try! _nth(ast, 1) | |
25 | default: true // fallthrough | |
26 | } | |
27 | if is_pair(a0) { | |
28 | let a00 = try! _nth(a0, 0) | |
29 | switch a00 { | |
30 | case MalVal.MalSymbol("splice-unquote"): | |
31 | return MalVal.MalList([MalVal.MalSymbol("concat"), | |
32 | try! _nth(a0, 1), | |
33 | quasiquote(try! rest(ast))]) | |
34 | default: true // fallthrough | |
35 | } | |
36 | } | |
37 | ||
38 | return MalVal.MalList([MalVal.MalSymbol("cons"), | |
39 | quasiquote(a0), | |
40 | quasiquote(try! rest(ast))]) | |
41 | } | |
42 | ||
43 | func is_macro(ast: MalVal, _ env: Env) -> Bool { | |
44 | switch ast { | |
45 | case MalVal.MalList(let lst) where lst.count > 0: | |
46 | let a0 = lst[lst.startIndex] | |
47 | switch a0 { | |
48 | case MalVal.MalSymbol: | |
49 | let e = try! env.find(a0) | |
50 | if e != nil { | |
51 | let mac = try! e!.get(a0) | |
52 | switch mac { | |
53 | case MalVal.MalFunc(_,_,_,_,let macro,_): return macro | |
54 | default: return false | |
55 | } | |
56 | } else { | |
57 | return false | |
58 | } | |
59 | default: return false | |
60 | } | |
61 | default: return false | |
62 | } | |
63 | } | |
64 | ||
65 | func macroexpand(orig_ast: MalVal, _ env: Env) throws -> MalVal { | |
66 | var ast: MalVal = orig_ast | |
67 | while is_macro(ast, env) { | |
68 | switch try! env.get(try! _nth(ast, 0)) { | |
69 | case MalVal.MalFunc(let mac,_,_,_,_,_): | |
70 | ast = try mac(_rest(ast)) | |
71 | default: throw MalError.General(msg: "impossible state in macroexpand") | |
72 | } | |
73 | } | |
74 | return ast | |
75 | } | |
76 | ||
77 | func eval_ast(ast: MalVal, _ env: Env) throws -> MalVal { | |
78 | switch ast { | |
79 | case MalVal.MalSymbol: | |
80 | return try env.get(ast) | |
81 | case MalVal.MalList(let lst): | |
82 | return MalVal.MalList(try lst.map { try EVAL($0, env) }) | |
83 | case MalVal.MalVector(let lst): | |
84 | return MalVal.MalVector(try lst.map { try EVAL($0, env) }) | |
85 | case MalVal.MalHashMap(let dict): | |
86 | var new_dict = Dictionary<String,MalVal>() | |
87 | for (k,v) in dict { new_dict[k] = try EVAL(v, env) } | |
88 | return MalVal.MalHashMap(new_dict) | |
89 | default: | |
90 | return ast | |
91 | } | |
92 | } | |
93 | ||
94 | func EVAL(orig_ast: MalVal, _ orig_env: Env) throws -> MalVal { | |
95 | var ast = orig_ast, env = orig_env | |
96 | while true { | |
97 | switch ast { | |
98 | case MalVal.MalList: true | |
99 | default: return try eval_ast(ast, env) | |
100 | } | |
101 | ||
102 | ast = try macroexpand(ast, env) | |
103 | switch ast { | |
104 | case MalVal.MalList: true | |
105 | default: return try eval_ast(ast, env) | |
106 | } | |
107 | ||
108 | switch ast { | |
109 | case MalVal.MalList(let lst): | |
110 | switch lst[0] { | |
111 | case MalVal.MalSymbol("def!"): | |
112 | return try env.set(lst[1], try EVAL(lst[2], env)) | |
113 | case MalVal.MalSymbol("let*"): | |
114 | let let_env = try Env(env) | |
115 | var binds = Array<MalVal>() | |
116 | switch lst[1] { | |
117 | case MalVal.MalList(let l): binds = l | |
118 | case MalVal.MalVector(let l): binds = l | |
119 | default: | |
120 | throw MalError.General(msg: "Invalid let* bindings") | |
121 | } | |
122 | var idx = binds.startIndex | |
123 | while idx < binds.endIndex { | |
124 | let v = try EVAL(binds[idx.successor()], let_env) | |
125 | try let_env.set(binds[idx], v) | |
126 | idx = idx.successor().successor() | |
127 | } | |
128 | env = let_env | |
129 | ast = lst[2] // TCO | |
130 | case MalVal.MalSymbol("quote"): | |
131 | return lst[1] | |
132 | case MalVal.MalSymbol("quasiquote"): | |
133 | ast = quasiquote(lst[1]) // TCO | |
134 | case MalVal.MalSymbol("defmacro!"): | |
135 | var mac = try EVAL(lst[2], env) | |
136 | switch mac { | |
137 | case MalVal.MalFunc(let fn, let a, let e, let p, _, let m): | |
138 | mac = MalVal.MalFunc(fn,ast:a,env:e,params:p,macro:true,meta:m) | |
139 | default: throw MalError.General(msg: "invalid defmacro! form") | |
140 | } | |
141 | return try env.set(lst[1], mac) | |
142 | case MalVal.MalSymbol("macroexpand"): | |
143 | return try macroexpand(lst[1], env) | |
144 | case MalVal.MalSymbol("do"): | |
145 | let slc = lst[1..<lst.endIndex.predecessor()] | |
146 | try eval_ast(MalVal.MalList(Array(slc)), env) | |
147 | ast = lst[lst.endIndex.predecessor()] // TCO | |
148 | case MalVal.MalSymbol("if"): | |
149 | switch try EVAL(lst[1], env) { | |
150 | case MalVal.MalFalse, MalVal.MalNil: | |
151 | if lst.count > 3 { | |
152 | ast = lst[3] // TCO | |
153 | } else { | |
154 | return MalVal.MalNil | |
155 | } | |
156 | default: | |
157 | ast = lst[2] // TCO | |
158 | } | |
159 | case MalVal.MalSymbol("fn*"): | |
160 | return MalVal.MalFunc( { | |
161 | return try EVAL(lst[2], Env(env, binds: lst[1], | |
162 | exprs: MalVal.MalList($0))) | |
163 | }, ast:[lst[2]], env:env, params:[lst[1]], | |
164 | macro:false, meta:nil) | |
165 | default: | |
166 | switch try eval_ast(ast, env) { | |
167 | case MalVal.MalList(let elst): | |
168 | switch elst[0] { | |
169 | case MalVal.MalFunc(let fn, nil, _, _, _, _): | |
170 | let args = Array(elst[1..<elst.count]) | |
171 | return try fn(args) | |
172 | case MalVal.MalFunc(_, let a, let e, let p, _, _): | |
173 | let args = Array(elst[1..<elst.count]) | |
174 | env = try Env(e, binds: p![0], | |
175 | exprs: MalVal.MalList(args)) // TCO | |
176 | ast = a![0] // TCO | |
177 | default: | |
178 | throw MalError.General(msg: "Cannot apply on '\(elst[0])'") | |
179 | } | |
180 | default: throw MalError.General(msg: "Invalid apply") | |
181 | } | |
182 | } | |
183 | default: | |
184 | throw MalError.General(msg: "Invalid apply") | |
185 | } | |
186 | } | |
187 | } | |
188 | ||
189 | ||
190 | func PRINT(exp: MalVal) -> String { | |
191 | return pr_str(exp, true) | |
192 | } | |
193 | ||
194 | ||
195 | // repl | |
196 | func rep(str:String) throws -> String { | |
197 | return PRINT(try EVAL(try READ(str), repl_env)) | |
198 | } | |
199 | ||
200 | var repl_env: Env = try Env() | |
201 | ||
202 | // core.swift: defined using Swift | |
203 | for (k, fn) in core_ns { | |
204 | try repl_env.set(MalVal.MalSymbol(k), | |
205 | MalVal.MalFunc(fn,ast:nil,env:nil,params:nil, | |
206 | macro:false,meta:nil)) | |
207 | } | |
208 | try repl_env.set(MalVal.MalSymbol("eval"), | |
209 | MalVal.MalFunc({ try EVAL($0[0], repl_env) }, | |
210 | ast:nil,env:nil,params:nil, | |
211 | macro:false,meta:nil)) | |
212 | let pargs = Process.arguments.map { MalVal.MalString($0) } | |
213 | // TODO: weird way to get empty list, fix this | |
214 | var args = pargs[pargs.startIndex..<pargs.startIndex] | |
215 | if pargs.startIndex.advancedBy(2) < pargs.endIndex { | |
216 | args = pargs[pargs.startIndex.advancedBy(2)..<pargs.endIndex] | |
217 | } | |
218 | try repl_env.set(MalVal.MalSymbol("*ARGV*"), MalVal.MalList(Array(args))) | |
219 | ||
220 | // core.mal: defined using the language itself | |
221 | try rep("(def! not (fn* (a) (if a false true)))") | |
222 | try rep("(def! load-file (fn* (f) (eval (read-string (str \"(do \" (slurp f) \")\")))))") | |
223 | try rep("(defmacro! cond (fn* (& xs) (if (> (count xs) 0) (list 'if (first xs) (if (> (count xs) 1) (nth xs 1) (throw \"odd number of forms to cond\")) (cons 'cond (rest (rest xs)))))))") | |
224 | try rep("(defmacro! or (fn* (& xs) (if (empty? xs) nil (if (= 1 (count xs)) (first xs) `(let* (or_FIXME ~(first xs)) (if or_FIXME or_FIXME (or ~@(rest xs))))))))") | |
225 | ||
226 | ||
227 | if Process.arguments.count > 1 { | |
228 | try rep("(load-file \"" + Process.arguments[1] + "\")") | |
229 | exit(0) | |
230 | } | |
231 | ||
232 | while true { | |
233 | print("user> ", terminator: "") | |
234 | let line = readLine(stripNewline: true) | |
235 | if line == nil { break } | |
236 | if line == "" { continue } | |
237 | ||
238 | do { | |
239 | print(try rep(line!)) | |
240 | } catch (MalError.Reader(let msg)) { | |
241 | print("Error: \(msg)") | |
242 | } catch (MalError.General(let msg)) { | |
243 | print("Error: \(msg)") | |
244 | } | |
245 | } |