forked from kanaka/mal
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathtypes.swift
212 lines (186 loc) · 6.08 KB
/
types.swift
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
enum MalError: Error {
case Reader(msg: String)
case General(msg: String)
case MalException(obj: MalVal)
}
class MutableAtom {
var val: MalVal
init(val: MalVal) {
self.val = val
}
}
enum MalVal {
case MalNil
case MalTrue
case MalFalse
case MalInt(Int)
case MalFloat(Float)
case MalString(String)
case MalSymbol(String)
case MalList(Array<MalVal>, meta: Array<MalVal>?)
case MalVector(Array<MalVal>, meta: Array<MalVal>?)
case MalHashMap(Dictionary<String,MalVal>, meta: Array<MalVal>?)
// TODO: internal MalVals are wrapped in arrays because otherwise
// compiler throws a fault
case MalFunc((Array<MalVal>) throws -> MalVal,
ast: Array<MalVal>?,
env: Env?,
params: Array<MalVal>?,
macro: Bool,
meta: Array<MalVal>?)
case MalAtom(MutableAtom)
}
typealias MV = MalVal
// General functions
func wraptf(_ a: Bool) -> MalVal {
return a ? MV.MalTrue : MV.MalFalse
}
// equality functions
func cmp_seqs(_ a: Array<MalVal>, _ b: Array<MalVal>) -> Bool {
if a.count != b.count { return false }
var idx = a.startIndex
while idx < a.endIndex {
if !equal_Q(a[idx], b[idx]) { return false }
idx = a.index(after:idx)
}
return true
}
func cmp_maps(_ a: Dictionary<String,MalVal>,
_ b: Dictionary<String,MalVal>) -> Bool {
if a.count != b.count { return false }
for (k,v1) in a {
if b[k] == nil { return false }
if !equal_Q(v1, b[k]!) { return false }
}
return true
}
func equal_Q(_ a: MalVal, _ b: MalVal) -> Bool {
switch (a, b) {
case (MV.MalNil, MV.MalNil): return true
case (MV.MalFalse, MV.MalFalse): return true
case (MV.MalTrue, MV.MalTrue): return true
case (MV.MalInt(let i1), MV.MalInt(let i2)): return i1 == i2
case (MV.MalString(let s1), MV.MalString(let s2)): return s1 == s2
case (MV.MalSymbol(let s1), MV.MalSymbol(let s2)): return s1 == s2
case (MV.MalList(let l1,_), MV.MalList(let l2,_)):
return cmp_seqs(l1, l2)
case (MV.MalList(let l1,_), MV.MalVector(let l2,_)):
return cmp_seqs(l1, l2)
case (MV.MalVector(let l1,_), MV.MalList(let l2,_)):
return cmp_seqs(l1, l2)
case (MV.MalVector(let l1,_), MV.MalVector(let l2,_)):
return cmp_seqs(l1, l2)
case (MV.MalHashMap(let d1,_), MV.MalHashMap(let d2,_)):
return cmp_maps(d1, d2)
default:
return false
}
}
// list and vector functions
func list(_ lst: Array<MalVal>) -> MalVal {
return MV.MalList(lst, meta:nil)
}
func list(_ lst: Array<MalVal>, meta: MalVal) -> MalVal {
return MV.MalList(lst, meta:[meta])
}
func vector(_ lst: Array<MalVal>) -> MalVal {
return MV.MalVector(lst, meta:nil)
}
func vector(_ lst: Array<MalVal>, meta: MalVal) -> MalVal {
return MV.MalVector(lst, meta:[meta])
}
// hash-map functions
func _assoc(_ src: Dictionary<String,MalVal>, _ mvs: Array<MalVal>)
throws -> Dictionary<String,MalVal> {
var d = src
if mvs.count % 2 != 0 {
throw MalError.General(msg: "Odd number of args to assoc_BANG")
}
var pos = mvs.startIndex
while pos < mvs.count {
switch (mvs[pos], mvs[pos+1]) {
case (MV.MalString(let k), let mv):
d[k] = mv
default:
throw MalError.General(msg: "Invalid _assoc call")
}
pos += 2
}
return d
}
func _dissoc(_ src: Dictionary<String,MalVal>, _ mvs: Array<MalVal>)
throws -> Dictionary<String,MalVal> {
var d = src
for mv in mvs {
switch mv {
case MV.MalString(let k): d.removeValue(forKey: k)
default: throw MalError.General(msg: "Invalid _dissoc call")
}
}
return d
}
func hash_map(_ dict: Dictionary<String,MalVal>) -> MalVal {
return MV.MalHashMap(dict, meta:nil)
}
func hash_map(_ dict: Dictionary<String,MalVal>, meta:MalVal) -> MalVal {
return MV.MalHashMap(dict, meta:[meta])
}
func hash_map(_ arr: Array<MalVal>) throws -> MalVal {
let d = Dictionary<String,MalVal>();
return MV.MalHashMap(try _assoc(d, arr), meta:nil)
}
// function functions
func malfunc(_ fn: @escaping (Array<MalVal>) throws -> MalVal) -> MalVal {
return MV.MalFunc(fn, ast: nil, env: nil, params: nil,
macro: false, meta: nil)
}
func malfunc(_ fn: @escaping (Array<MalVal>) throws -> MalVal,
ast: Array<MalVal>?,
env: Env?,
params: Array<MalVal>?) -> MalVal {
return MV.MalFunc(fn, ast: ast, env: env, params: params,
macro: false, meta: nil)
}
func malfunc(_ fn: @escaping (Array<MalVal>) throws -> MalVal,
ast: Array<MalVal>?,
env: Env?,
params: Array<MalVal>?,
macro: Bool,
meta: MalVal?) -> MalVal {
return MV.MalFunc(fn, ast: ast, env: env, params: params,
macro: macro, meta: meta != nil ? [meta!] : nil)
}
func malfunc(_ fn: @escaping (Array<MalVal>) throws -> MalVal,
ast: Array<MalVal>?,
env: Env?,
params: Array<MalVal>?,
macro: Bool,
meta: Array<MalVal>?) -> MalVal {
return MV.MalFunc(fn, ast: ast, env: env, params: params,
macro: macro, meta: meta)
}
// sequence functions
func _rest(_ a: MalVal) throws -> Array<MalVal> {
switch a {
case MV.MalList(let lst,_):
let start = lst.index(after: lst.startIndex)
let slc = lst[start..<lst.endIndex]
return Array(slc)
case MV.MalVector(let lst,_):
let start = lst.index(after: lst.startIndex)
let slc = lst[start..<lst.endIndex]
return Array(slc)
default:
throw MalError.General(msg: "Invalid rest call")
}
}
func rest(_ a: MalVal) throws -> MalVal {
return list(try _rest(a))
}
func _nth(_ a: MalVal, _ idx: Int) throws -> MalVal {
switch a {
case MV.MalList(let l,_): return l[l.startIndex.advanced(by: idx)]
case MV.MalVector(let l,_): return l[l.startIndex.advanced(by: idx)]
default: throw MalError.General(msg: "Invalid nth call")
}
}