4 func READ(_ input: String) throws -> MalData {
5 return try read_str(input)
8 func EVAL(_ anAst: MalData, env anEnv: Env) throws -> MalData {
9 func is_pair(_ ast: MalData) -> Bool { // not used
10 return (ast is [MalData]) && (ast.count != 0)
12 func quasiquote(_ ast: MalData) -> MalData {
13 let list = ast.listForm
15 return [Symbol("quote"), ast]
17 if let sym = list[0] as? Symbol, sym.name == "unquote" {
20 let innerList = list[0].listForm
21 if !innerList.isEmpty, let sym = innerList[0] as? Symbol, sym.name == "splice-unquote" {
22 return [Symbol("concat"), innerList[1], quasiquote(list.dropFirst().listForm)]
24 return [Symbol("cons"), quasiquote(list[0]), quasiquote(list.dropFirst().listForm)]
26 func macroexpand(_ anAst: MalData, env: Env) throws -> MalData {
27 func isMacro_call(_ ast: MalData, env: Env) -> Bool { // not used
28 if let list = ast as? [MalData],
29 let symbol = list[0] as? Symbol,
30 let fn = try? env.get(forKey: symbol) as? Function {
31 return fn?.isMacro ?? false
37 while let list = ast as? [MalData],
38 let symbol = list[0] as? Symbol,
39 let fn = try? env.get(forKey: symbol) as? Function,
40 let isMacro = fn?.isMacro, isMacro == true {
41 ast = try fn!.fn(List(list.dropFirst()))
47 var ast = anAst, env = anEnv
51 if (ast as! [MalData]).isEmpty { return ast }
52 ast = try macroexpand(ast, env: env)
53 guard let list = ast as? [MalData] else { return try eval_ast(ast, env: env) }
54 guard !list.isEmpty else { return list }
55 if let sym = list[0] as? Symbol {
58 let value = try EVAL(list[2], env: env), key = list[1] as! Symbol
59 env.set(value, forKey: key)
62 let fn = try EVAL(list[2], env: env) as! Function, key = list[1] as! Symbol
63 let macro = Function(withFunction: fn, isMacro: true)
64 env.set(macro, forKey: key)
67 let newEnv = Env(outer: env), expr = list[2]
68 let bindings = list[1].listForm
69 for i in stride(from: 0, to: bindings.count-1, by: 2) {
70 let key = bindings[i], value = bindings[i+1]
71 let result = try EVAL(value, env: newEnv)
72 newEnv.set(result, forKey: key as! Symbol)
78 try _ = list.dropFirst().dropLast().map { try EVAL($0, env: env) }
79 ast = list.last ?? Nil()
82 let predicate = try EVAL(list[1], env: env)
83 if predicate as? Bool == false || predicate is Nil {
84 ast = list.count>3 ? list[3] : Nil()
90 let fn = {(params: [MalData]) -> MalData in
91 let newEnv = Env(binds: (list[1].listForm as! [Symbol]), exprs: params, outer: env)
92 return try EVAL(list[2], env: newEnv)
94 return Function(ast: list[2], params: (list[1].listForm as! [Symbol]), env:env , fn: fn)
98 ast = quasiquote(list[1])
101 return try macroexpand(list[1], env: env)
106 // not a symbol. maybe: function, list, or some wrong type
107 let evaluated = try eval_ast(list, env: env) as! [MalData]
108 guard let function = evaluated[0] as? Function else {
109 throw MalError.SymbolNotFound(list[0] as? Symbol ?? Symbol("Symbol"))
111 if let fnAst = function.ast { // a full fn
113 env = Env(binds: function.params!, exprs: evaluated.dropFirst().listForm, outer: function.env!)
114 } else { // normal function
115 return try function.fn(evaluated.dropFirst().listForm)
119 let vector = ast as! ContiguousArray<MalData>
120 return try ContiguousArray(vector.map { element in try EVAL(element, env: env) })
122 let hashMap = ast as! HashMap<String, MalData>
123 return try hashMap.mapValues { value in try EVAL(value, env: env) }
125 return try eval_ast(ast, env: env)
130 func PRINT(_ input: MalData) -> String {
131 return pr_str(input, print_readably: true)
134 @discardableResult func rep(_ input: String, env: Env) throws -> String {
135 return try PRINT(EVAL(READ(input), env: env))
138 func eval_ast(_ ast: MalData, env: Env) throws -> MalData {
139 switch ast.dataType {
141 let sym = ast as! Symbol
142 if let function = try? env.get(forKey: sym) {
145 throw MalError.SymbolNotFound(sym)
148 let list = ast as! [MalData]
149 return try list.map { element in try EVAL(element, env: env) }
151 return (ast as! Atom).value
160 for (key, value) in ns {
161 repl_env.set(Function(fn: value), forKey: Symbol(key))
163 repl_env.set(Function(fn: { try EVAL($0[0], env: repl_env) }), forKey: Symbol("eval"))
164 repl_env.set([], forKey: Symbol("*ARGV*"))
166 try rep("(def! not (fn* (a) (if a false true)))", env: repl_env)
167 try rep("(def! load-file (fn* (f) (eval (read-string (str \"(do \" (slurp f) \"\nnil)\")))))", env: repl_env)
168 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)))))))", env: repl_env)
170 if CommandLine.argc > 1 {
171 let fileName = CommandLine.arguments[1],
172 args = List(CommandLine.arguments.dropFirst(2))
173 repl_env.set(args, forKey: Symbol("*ARGV*"))
174 try rep("(load-file \"\(fileName)\")", env: repl_env)
179 print("user> ", terminator: "")
180 if let input = readLine(strippingNewline: true) {
181 guard input != "" else { continue }
183 try print(rep(input, env: repl_env))
184 } catch let error as MalError {