swift3: steps6-A, vectors, maps, keywords, meta.
[jackhill/mal.git] / swift3 / Sources / step8_macros / main.swift
CommitLineData
0eace3df
JM
1import Foundation
2
3// read
4func READ(str: String) throws -> MalVal {
5 return try read_str(str)
6}
7
8// eval
9func 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
17func 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
43func 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
65func 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
77func 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
94func 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// print
190func PRINT(exp: MalVal) -> String {
191 return pr_str(exp, true)
192}
193
194
195// repl
196func rep(str:String) throws -> String {
197 return PRINT(try EVAL(try READ(str), repl_env))
198}
199
200var repl_env: Env = try Env()
201
202// core.swift: defined using Swift
203for (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}
208try 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))
212let pargs = Process.arguments.map { MalVal.MalString($0) }
213// TODO: weird way to get empty list, fix this
214var args = pargs[pargs.startIndex..<pargs.startIndex]
215if pargs.startIndex.advancedBy(2) < pargs.endIndex {
216 args = pargs[pargs.startIndex.advancedBy(2)..<pargs.endIndex]
217}
218try repl_env.set(MalVal.MalSymbol("*ARGV*"), MalVal.MalList(Array(args)))
219
220// core.mal: defined using the language itself
221try rep("(def! not (fn* (a) (if a false true)))")
222try rep("(def! load-file (fn* (f) (eval (read-string (str \"(do \" (slurp f) \")\")))))")
223try 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)))))))")
224try 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
227if Process.arguments.count > 1 {
228 try rep("(load-file \"" + Process.arguments[1] + "\")")
229 exit(0)
230}
231
232while 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}