Move implementations into impls/ dir
[jackhill/mal.git] / impls / swift4 / Sources / step8_macros / main.swift
1
2 import Foundation
3
4 func READ(_ input: String) throws -> MalData {
5 return try read_str(input)
6 }
7
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)
11 }
12 func quasiquote(_ ast: MalData) -> MalData {
13 let list = ast.listForm
14 if list.isEmpty {
15 return [Symbol("quote"), ast]
16 }
17 if let sym = list[0] as? Symbol, sym.name == "unquote" {
18 return list[1]
19 }
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)]
23 }
24 return [Symbol("cons"), quasiquote(list[0]), quasiquote(list.dropFirst().listForm)]
25 }
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
32 }
33 return false
34 }
35
36 var ast = anAst
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()))
42 }
43 return ast
44 }
45
46 /// Apply
47 var ast = anAst, env = anEnv
48 while true {
49 switch ast.dataType {
50 case .List:
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 {
56 switch sym.name {
57 case "def!":
58 let value = try EVAL(list[2], env: env), key = list[1] as! Symbol
59 env.set(value, forKey: key)
60 return value
61 case "defmacro!":
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)
65 return macro
66 case "let*":
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)
73 }
74 env = newEnv
75 ast = expr
76 continue
77 case "do":
78 try _ = list.dropFirst().dropLast().map { try EVAL($0, env: env) }
79 ast = list.last ?? Nil()
80 continue
81 case "if":
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()
85 } else {
86 ast = list[2]
87 }
88 continue
89 case "fn*":
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)
93 }
94 return Function(ast: list[2], params: (list[1].listForm as! [Symbol]), env:env , fn: fn)
95 case "quote":
96 return list[1]
97 case "quasiquote":
98 ast = quasiquote(list[1])
99 continue
100 case "macroexpand":
101 return try macroexpand(list[1], env: env)
102 default:
103 break
104 }
105 }
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"))
110 }
111 if let fnAst = function.ast { // a full fn
112 ast = fnAst
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)
116 }
117 continue
118 case .Vector:
119 let vector = ast as! ContiguousArray<MalData>
120 return try ContiguousArray(vector.map { element in try EVAL(element, env: env) })
121 case .HashMap:
122 let hashMap = ast as! HashMap<String, MalData>
123 return try hashMap.mapValues { value in try EVAL(value, env: env) }
124 default:
125 return try eval_ast(ast, env: env)
126 }
127 }
128 }
129
130 func PRINT(_ input: MalData) -> String {
131 return pr_str(input, print_readably: true)
132 }
133
134 @discardableResult func rep(_ input: String, env: Env) throws -> String {
135 return try PRINT(EVAL(READ(input), env: env))
136 }
137
138 func eval_ast(_ ast: MalData, env: Env) throws -> MalData {
139 switch ast.dataType {
140 case .Symbol:
141 let sym = ast as! Symbol
142 if let function = try? env.get(forKey: sym) {
143 return function
144 } else {
145 throw MalError.SymbolNotFound(sym)
146 }
147 case .List:
148 let list = ast as! [MalData]
149 return try list.map { element in try EVAL(element, env: env) }
150 case .Atom:
151 return (ast as! Atom).value
152 default:
153 return ast
154 }
155 }
156
157
158
159 var repl_env = Env()
160 for (key, value) in ns {
161 repl_env.set(Function(fn: value), forKey: Symbol(key))
162 }
163 repl_env.set(Function(fn: { try EVAL($0[0], env: repl_env) }), forKey: Symbol("eval"))
164 repl_env.set([], forKey: Symbol("*ARGV*"))
165
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)
169
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)
175 exit(0)
176 }
177
178 while true {
179 print("user> ", terminator: "")
180 if let input = readLine(strippingNewline: true) {
181 guard input != "" else { continue }
182 do {
183 try print(rep(input, env: repl_env))
184 } catch let error as MalError {
185 print(error.info())
186 }
187 } else {
188 exit(0);
189 }
190 }