Commit | Line | Data |
---|---|---|
2539e6af KR |
1 | //****************************************************************************** |
2 | // MAL - core | |
3 | //****************************************************************************** | |
4 | ||
5 | import Foundation | |
6 | ||
425305df KR |
7 | // This is a simple type distinct from all MalVal types so that we can pass a |
8 | // sequence to a function and be able to distinguish between those functions | |
9 | // that want a sequence as a parameter and those that want a sequence that holds | |
10 | // the rest of the function parameters. | |
11 | // | |
12 | final class MalVarArgs { | |
13 | init(_ value: MalSequence) { self.value = value } | |
14 | init(_ value: MalVal) { self.value = as_sequence(value) } | |
15 | let value: MalSequence | |
16 | } | |
2539e6af | 17 | |
425305df | 18 | private func fn_eq(obj1: MalVal, obj2: MalVal) throws -> Bool { |
2539e6af KR |
19 | return obj1 == obj2 |
20 | } | |
21 | ||
425305df KR |
22 | private func fn_throw(exception: MalVal) throws -> MalVal { |
23 | try throw_error(exception) | |
2539e6af KR |
24 | } |
25 | ||
425305df | 26 | private func fn_nilQ(obj: MalVal) throws -> Bool { |
2539e6af KR |
27 | return is_nil(obj) |
28 | } | |
29 | ||
425305df | 30 | private func fn_trueQ(obj: MalVal) throws -> Bool { |
2539e6af KR |
31 | return is_true(obj) |
32 | } | |
33 | ||
425305df | 34 | private func fn_falseQ(obj: MalVal) throws -> Bool { |
2539e6af KR |
35 | return is_false(obj) |
36 | } | |
37 | ||
c391c80c JM |
38 | private func fn_stringQ(obj: MalVal) throws -> Bool { |
39 | return is_string(obj) | |
40 | } | |
41 | ||
425305df KR |
42 | private func fn_symbol(s: String) throws -> MalVal { |
43 | return make_symbol(s) | |
2539e6af KR |
44 | } |
45 | ||
425305df | 46 | private func fn_symbolQ(obj: MalVal) throws -> Bool { |
2539e6af KR |
47 | return is_symbol(obj) |
48 | } | |
49 | ||
425305df | 50 | private func fn_keyword(s: MalVal) throws -> MalVal { |
2539e6af KR |
51 | if is_keyword(s) { |
52 | return s | |
53 | } | |
54 | if is_string(s) { | |
425305df | 55 | return make_keyword(as_string(s)) |
2539e6af | 56 | } |
425305df | 57 | try throw_error("expected string or keyword") |
2539e6af KR |
58 | } |
59 | ||
425305df | 60 | private func fn_keywordQ(obj: MalVal) throws -> Bool { |
2539e6af KR |
61 | return is_keyword(obj) |
62 | } | |
63 | ||
c3c9f348 JM |
64 | private func fn_numberQ(obj: MalVal) throws -> Bool { |
65 | return is_integer(obj) || is_float(obj) | |
66 | } | |
67 | ||
68 | private func fn_functionQ(obj: MalVal) throws -> Bool { | |
69 | return is_function(obj) | |
70 | } | |
71 | ||
72 | private func fn_macroQ(obj: MalVal) throws -> Bool { | |
73 | return is_macro(obj) | |
74 | } | |
75 | ||
425305df KR |
76 | private func fn_prstr(args: MalVarArgs) throws -> String { |
77 | let args_str_array = args.value.map { pr_str($0, true) } | |
78 | return args_str_array.joinWithSeparator(" ") | |
2539e6af KR |
79 | } |
80 | ||
425305df KR |
81 | private func fn_str(args: MalVarArgs) throws -> String { |
82 | let args_str_array = args.value.map { pr_str($0, false) } | |
83 | return args_str_array.joinWithSeparator("") | |
2539e6af KR |
84 | } |
85 | ||
425305df KR |
86 | private func fn_prn(args: MalVarArgs) { |
87 | let args_str_array = args.value.map { pr_str($0, true) } | |
88 | let args_str = args_str_array.joinWithSeparator(" ") | |
89 | print(args_str) | |
2539e6af KR |
90 | } |
91 | ||
425305df KR |
92 | private func fn_println(args: MalVarArgs) { |
93 | let args_str_array = args.value.map { pr_str($0, false) } | |
94 | let args_str = args_str_array.joinWithSeparator(" ") | |
95 | print(args_str) | |
2539e6af KR |
96 | } |
97 | ||
425305df KR |
98 | private func fn_readstring(s: String) throws -> MalVal { |
99 | return try read_str(s) | |
2539e6af KR |
100 | } |
101 | ||
425305df | 102 | private func fn_readline(s: String) throws -> String? { |
2539e6af KR |
103 | return _readline(s) |
104 | } | |
105 | ||
425305df KR |
106 | private func fn_slurp(s: String) throws -> MalVal { |
107 | do { | |
108 | let result = try String(contentsOfFile: s, encoding: NSUTF8StringEncoding) | |
109 | return make_string(result) | |
110 | } catch let error as NSError { | |
111 | try throw_error("unknown error reading file \(error)") | |
2539e6af | 112 | } |
2539e6af KR |
113 | } |
114 | ||
425305df | 115 | private func fn_lt(arg1: MalIntType, arg2: MalIntType) throws -> Bool { |
2539e6af KR |
116 | return arg1 < arg2 |
117 | } | |
118 | ||
425305df | 119 | private func fn_lte(arg1: MalIntType, arg2: MalIntType) throws -> Bool { |
2539e6af KR |
120 | return arg1 <= arg2 |
121 | } | |
122 | ||
425305df | 123 | private func fn_gt(arg1: MalIntType, arg2: MalIntType) throws -> Bool { |
2539e6af KR |
124 | return arg1 > arg2 |
125 | } | |
126 | ||
425305df | 127 | private func fn_gte(arg1: MalIntType, arg2: MalIntType) throws -> Bool { |
2539e6af KR |
128 | return arg1 >= arg2 |
129 | } | |
130 | ||
425305df | 131 | private func fn_add(arg1: MalIntType, arg2: MalIntType) throws -> MalIntType { |
2539e6af KR |
132 | return arg1 + arg2 |
133 | } | |
134 | ||
425305df | 135 | private func fn_subtract(arg1: MalIntType, arg2: MalIntType) throws -> MalIntType { |
2539e6af KR |
136 | return arg1 - arg2 |
137 | } | |
138 | ||
425305df | 139 | private func fn_multiply(arg1: MalIntType, arg2: MalIntType) throws -> MalIntType { |
2539e6af KR |
140 | return arg1 * arg2 |
141 | } | |
142 | ||
425305df | 143 | private func fn_divide(arg1: MalIntType, arg2: MalIntType) throws -> MalIntType { |
2539e6af KR |
144 | return arg1 / arg2 |
145 | } | |
146 | ||
425305df KR |
147 | private func fn_timems() throws -> MalIntType { |
148 | var time = timeval(tv_sec: 0, tv_usec: 0) | |
2539e6af KR |
149 | let res = gettimeofday(&time, nil) |
150 | if res == 0 { | |
425305df | 151 | return (MalIntType(time.tv_sec) * 1_000_000 + MalIntType(time.tv_usec)) / 1000 |
2539e6af KR |
152 | } |
153 | return -1 | |
154 | } | |
155 | ||
425305df KR |
156 | private func fn_list(args: MalVarArgs) throws -> MalVal { |
157 | return make_list(args.value) | |
2539e6af KR |
158 | } |
159 | ||
425305df | 160 | private func fn_listQ(obj: MalVal) throws -> Bool { |
2539e6af KR |
161 | return is_list(obj) |
162 | } | |
163 | ||
425305df KR |
164 | private func fn_vector(args: MalVarArgs) throws -> MalVal { |
165 | return make_vector(args.value) | |
2539e6af KR |
166 | } |
167 | ||
425305df | 168 | private func fn_vectorQ(obj: MalVal) throws -> Bool { |
2539e6af KR |
169 | return is_vector(obj) |
170 | } | |
171 | ||
425305df KR |
172 | private func fn_hashmap(args: MalVarArgs) throws -> MalVal { |
173 | return make_hashmap(args.value) | |
2539e6af KR |
174 | } |
175 | ||
425305df | 176 | private func fn_hashmapQ(obj: MalVal) throws -> Bool { |
2539e6af KR |
177 | return is_hashmap(obj) |
178 | } | |
179 | ||
425305df KR |
180 | private func fn_assoc(hash: MalHashMap, args: MalVarArgs) throws -> MalVal { |
181 | guard args.value.count % 2 == 0 else { | |
182 | try throw_error("expected even number of elements, got \(args.value.count)") | |
2539e6af KR |
183 | } |
184 | var new_dictionary = hash.hash | |
425305df KR |
185 | for var index: MalIntType = 0; index < args.value.count; index += 2 { |
186 | new_dictionary[try! args.value.nth(index)] = try! args.value.nth(index + 1) | |
2539e6af | 187 | } |
425305df | 188 | return make_hashmap(new_dictionary) |
2539e6af KR |
189 | } |
190 | ||
425305df KR |
191 | private func fn_dissoc(hash: MalHashMap, args: MalVarArgs) throws -> MalVal { |
192 | var new_dictionary = hash.hash | |
193 | for value in args.value { | |
194 | new_dictionary.removeValueForKey(value) | |
195 | } | |
196 | return make_hashmap(new_dictionary) | |
2539e6af KR |
197 | } |
198 | ||
425305df KR |
199 | private func fn_get(obj: MalVal, key: MalVal) throws -> MalVal { |
200 | if let as_vec = as_vectorQ(obj) { | |
201 | guard let index = as_integerQ(key) else { | |
202 | try throw_error("expected integer key for get(vector), got \(key)") | |
203 | } | |
204 | let n = as_inttype(index) | |
205 | guard n >= as_vec.count else { try throw_error("index out of range: \(n) >= \(as_vec.count)") } | |
206 | return try! as_vec.nth(n) | |
2539e6af | 207 | } |
425305df KR |
208 | if let as_hash = as_hashmapQ(obj) { |
209 | if let value = as_hash.value_for(key) { return value } | |
210 | return make_nil() | |
2539e6af KR |
211 | } |
212 | if is_nil(obj) { | |
213 | return obj | |
214 | } | |
425305df | 215 | try throw_error("get called on unsupported type: \(obj)") |
2539e6af KR |
216 | } |
217 | ||
425305df KR |
218 | private func fn_containsQ(obj: MalVal, key: MalVal) throws -> MalVal { |
219 | if let as_vec = as_vectorQ(obj) { | |
220 | guard let index = as_integerQ(key) else { | |
221 | try throw_error("expected integer key for contains(vector), got \(key)") | |
222 | } | |
223 | let n = as_inttype(index) | |
224 | return n < as_vec.count ? make_true() : make_false() | |
2539e6af | 225 | } |
425305df KR |
226 | if let as_hash = as_hashmapQ(obj) { |
227 | return as_hash.value_for(key) != nil ? make_true() : make_false() | |
2539e6af | 228 | } |
425305df | 229 | try throw_error("contains? called on unsupported type: \(obj)") |
2539e6af KR |
230 | } |
231 | ||
425305df KR |
232 | private func fn_keys(hash: MalHashMap) throws -> MalVal { |
233 | return hash.keys | |
2539e6af KR |
234 | } |
235 | ||
425305df KR |
236 | private func fn_values(hash: MalHashMap) throws -> MalVal { |
237 | return hash.values | |
2539e6af KR |
238 | } |
239 | ||
425305df | 240 | private func fn_sequentialQ(obj: MalVal) throws -> Bool { |
2539e6af KR |
241 | return is_sequence(obj) |
242 | } | |
243 | ||
425305df KR |
244 | private func fn_cons(first: MalVal, rest: MalSequence) throws -> MalVal { |
245 | return rest.cons(first) | |
2539e6af KR |
246 | } |
247 | ||
425305df KR |
248 | private func fn_concat(args: MalVarArgs) throws -> MalVal { |
249 | var result = make_list() | |
250 | for arg in args.value { | |
251 | guard let arg_as_seq = as_sequenceQ(arg) else { | |
252 | try throw_error("expected list, got \(arg)") | |
253 | } | |
254 | result = try! as_sequence(result).concat(arg_as_seq) | |
2539e6af | 255 | } |
425305df | 256 | return result |
2539e6af KR |
257 | } |
258 | ||
425305df KR |
259 | private func fn_nth(list: MalSequence, index: MalIntType) throws -> MalVal { |
260 | return try list.nth(index) | |
2539e6af KR |
261 | } |
262 | ||
425305df | 263 | private func fn_first(arg: MalVal) throws -> MalVal { |
2539e6af KR |
264 | if is_nil(arg) { |
265 | return arg | |
266 | } | |
425305df | 267 | if let list = as_sequenceQ(arg) { |
2539e6af KR |
268 | return list.first() |
269 | } | |
425305df | 270 | try throw_error("expected list, got \(arg)") |
2539e6af KR |
271 | } |
272 | ||
b5f4363f DM |
273 | private func fn_rest(arg: MalVal) throws -> MalVal { |
274 | if is_nil(arg) { | |
275 | return make_list() | |
276 | } | |
277 | if let seq = as_sequenceQ(arg) { | |
278 | return seq.rest() | |
279 | } | |
280 | try throw_error("expected sequence, got \(arg)") | |
2539e6af KR |
281 | } |
282 | ||
425305df KR |
283 | private func fn_emptyQ(obj: MalVal) throws -> Bool { |
284 | if let list = as_sequenceQ(obj) { | |
2539e6af KR |
285 | return list.isEmpty |
286 | } | |
287 | return true | |
288 | } | |
289 | ||
425305df | 290 | private func fn_count(obj: MalVal) throws -> MalIntType { |
2539e6af KR |
291 | if is_nil(obj) { |
292 | return 0 | |
293 | } | |
425305df KR |
294 | if let as_seq = as_sequenceQ(obj) { |
295 | return as_seq.count | |
2539e6af | 296 | } |
425305df KR |
297 | if let as_hash = as_hashmapQ(obj) { |
298 | return as_hash.count | |
2539e6af | 299 | } |
425305df KR |
300 | if let as_str = as_stringQ(obj) { |
301 | return MalIntType(as_stringtype(as_str).characters.count) | |
2539e6af KR |
302 | } |
303 | return 0 | |
304 | } | |
305 | ||
425305df KR |
306 | private func fn_apply(args: MalVarArgs) throws -> MalVal { |
307 | guard args.value.count >= 2 else { | |
308 | try throw_error("expected at least 2 arguments to apply, got \(args.value.count)") | |
309 | } | |
310 | ||
311 | let first = args.value.first() | |
312 | let middle = args.value.range_from(1, to: args.value.count - 1) | |
313 | let last = args.value.last() | |
314 | ||
315 | guard let fn = as_functionQ(first) else { | |
316 | try throw_error("expected function for first argument to apply, got \(first)") | |
317 | } | |
318 | guard let seq = as_sequenceQ(last) else { | |
319 | try throw_error("expected sequence for last argument to apply, got \(last)") | |
320 | } | |
321 | let exprs = try! as_sequence(middle).concat(seq) | |
322 | return try fn.apply(as_sequence(exprs)) | |
2539e6af KR |
323 | } |
324 | ||
425305df | 325 | private func fn_map(fn: MalFunction, list: MalSequence) throws -> MalVal { |
2539e6af | 326 | var result = [MalVal]() |
425305df KR |
327 | result.reserveCapacity(Int(list.count)) |
328 | for var index: MalIntType = 0; index < list.count; ++index { | |
329 | let apply_res = try fn.apply(as_sequence(make_list_from(try! list.nth(index)))) | |
2539e6af KR |
330 | result.append(apply_res) |
331 | } | |
425305df | 332 | return make_list(result) |
2539e6af KR |
333 | } |
334 | ||
425305df KR |
335 | private func fn_conj(first: MalSequence, rest: MalVarArgs) throws -> MalVal { |
336 | return try first.conj(rest.value) | |
2539e6af KR |
337 | } |
338 | ||
c391c80c JM |
339 | private func fn_seq(seq: MalVal) throws -> MalVal { |
340 | if let list = as_listQ(seq) { | |
341 | return list.count > 0 ? list : make_nil() | |
342 | } else if let vector = as_vectorQ(seq) { | |
343 | return vector.count > 0 ? make_list(vector) : make_nil() | |
344 | } else if let str = as_stringQ(seq) { | |
345 | if str.string.characters.count == 0 { return make_nil() } | |
346 | return make_list(str.string.characters.map { make_string(String($0)) }) | |
347 | } else if is_nil(seq) { | |
348 | return make_nil() | |
349 | } else { | |
350 | try throw_error("seq: called with non-sequence") | |
351 | } | |
352 | return seq | |
353 | } | |
354 | ||
425305df KR |
355 | private func fn_meta(obj: MalVal) throws -> MalVal { |
356 | if let meta = get_meta(obj) { | |
357 | return meta | |
358 | } | |
359 | ||
360 | return make_nil() | |
2539e6af KR |
361 | } |
362 | ||
425305df KR |
363 | private func fn_withmeta(form: MalVal, meta: MalVal) throws -> MalVal { |
364 | return with_meta(form, meta) | |
2539e6af KR |
365 | } |
366 | ||
425305df KR |
367 | private func fn_atom(obj: MalVal) throws -> MalVal { |
368 | return make_atom(obj) | |
2539e6af KR |
369 | } |
370 | ||
425305df | 371 | private func fn_atomQ(obj: MalVal) throws -> Bool { |
2539e6af KR |
372 | return is_atom(obj) |
373 | } | |
374 | ||
425305df KR |
375 | private func fn_deref(atom: MalAtom) throws -> MalVal { |
376 | return atom.object | |
2539e6af KR |
377 | } |
378 | ||
425305df KR |
379 | private func fn_resetBang(atom: MalAtom, obj: MalVal) throws -> MalVal { |
380 | return atom.set_object(obj) | |
2539e6af KR |
381 | } |
382 | ||
425305df KR |
383 | private func fn_swapBang(let atom: MalAtom, fn: MalFunction, rest: MalVarArgs) throws -> MalVal { |
384 | var new_args = make_list_from(atom.object) | |
385 | new_args = try as_sequence(new_args).concat(rest.value) | |
386 | let result = try fn.apply(as_sequence(new_args)) | |
387 | return atom.set_object(result) | |
2539e6af KR |
388 | } |
389 | ||
390 | //****************************************************************************** | |
391 | // | |
392 | // The facility for invoking built-in functions makes use of a name -> | |
393 | // function-pointer table (defined down below). The function-pointers accept a | |
394 | // sequence of MalVals and return a MalVal as a result. Each built-in function | |
395 | // that does actual work, on the other hand, may expect a different set of | |
396 | // parameters of different types, and may naturally return a result of any type. | |
397 | // In order to convert between these two types of interfaces, we have these | |
425305df | 398 | // unwrap_args functions. These functions implement the (MalSequence) -> MalVal |
2539e6af KR |
399 | // interface expected by EVAL, and convert that information into Ints, Strings, |
400 | // etc. expected by the built-in functions. | |
401 | // | |
402 | //****************************************************************************** | |
403 | ||
425305df KR |
404 | private func with_one_parameter(args: MalSequence, @noescape fn: (MalVal) throws -> MalVal) throws -> MalVal { |
405 | guard args.count >= 1 else { try throw_error("expected at least 1 parameter, got \(args.count)") } | |
406 | let arg1 = try! args.nth(0) | |
407 | return try fn(arg1) | |
2539e6af KR |
408 | } |
409 | ||
425305df KR |
410 | private func with_two_parameters(args: MalSequence, @noescape fn: (MalVal, MalVal) throws -> MalVal) throws -> MalVal { |
411 | guard args.count >= 2 else { try throw_error("expected at least 2 parameter, got \(args.count)") } | |
412 | let arg1 = try! args.nth(0) | |
413 | let arg2 = try! args.nth(1) | |
414 | return try fn(arg1, arg2) | |
2539e6af KR |
415 | } |
416 | ||
417 | // ========== 0-parameter functions ========== | |
418 | ||
425305df | 419 | // () -> MalIntType |
2539e6af | 420 | |
425305df KR |
421 | private func unwrap_args(args: MalSequence, @noescape forFunction fn: () throws -> MalIntType) throws -> MalVal { |
422 | return make_integer(try fn()) | |
2539e6af KR |
423 | } |
424 | ||
425 | // () -> MalVal | |
426 | ||
425305df KR |
427 | private func unwrap_args(args: MalSequence, @noescape forFunction fn: () throws -> MalVal) throws -> MalVal { |
428 | return try fn() | |
2539e6af KR |
429 | } |
430 | ||
431 | // ========== 1-parameter functions ========== | |
432 | ||
425305df KR |
433 | // (MalAtom) -> MalVal |
434 | ||
435 | private func unwrap_args(args: MalSequence, @noescape forFunction fn: (MalAtom) throws -> MalVal) throws -> MalVal { | |
436 | return try with_one_parameter(args) { (arg1) -> MalVal in | |
437 | guard let atom = as_atomQ(arg1) else { | |
438 | try throw_error("expected atom, got \(arg1)") | |
439 | } | |
440 | return try fn(atom) | |
441 | } | |
442 | } | |
443 | ||
2539e6af KR |
444 | // (MalHashMap) -> MalVal |
445 | ||
425305df KR |
446 | private func unwrap_args(args: MalSequence, @noescape forFunction fn: (MalHashMap) throws -> MalVal) throws -> MalVal { |
447 | return try with_one_parameter(args) { (arg1) -> MalVal in | |
448 | guard let hash = as_hashmapQ(arg1) else { | |
449 | try throw_error("expected hashmap, got \(arg1)") | |
450 | } | |
451 | return try fn(hash) | |
2539e6af KR |
452 | } |
453 | } | |
454 | ||
455 | // (MalSequence) -> MalVal | |
456 | ||
425305df KR |
457 | private func unwrap_args(args: MalSequence, @noescape forFunction fn: (MalSequence) throws -> MalVal) throws -> MalVal { |
458 | return try with_one_parameter(args) { (arg1) -> MalVal in | |
459 | guard let seq = as_sequenceQ(arg1) else { | |
460 | try throw_error("expected list, got \(arg1)") | |
461 | } | |
462 | return try fn(seq) | |
2539e6af KR |
463 | } |
464 | } | |
465 | ||
466 | // (MalVal) -> Bool | |
467 | ||
425305df KR |
468 | private func unwrap_args(args: MalSequence, @noescape forFunction fn: (MalVal) throws -> Bool) throws -> MalVal { |
469 | return try with_one_parameter(args) { (arg1) -> MalVal in | |
470 | return try fn(arg1) ? make_true() : make_false() | |
2539e6af KR |
471 | } |
472 | } | |
473 | ||
425305df | 474 | // (MalVal) -> MalIntType |
2539e6af | 475 | |
425305df KR |
476 | private func unwrap_args(args: MalSequence, @noescape forFunction fn: (MalVal) throws -> MalIntType) throws -> MalVal { |
477 | return try with_one_parameter(args) { (arg1) -> MalVal in | |
478 | return make_integer(try fn(arg1)) | |
2539e6af KR |
479 | } |
480 | } | |
481 | ||
482 | // (MalVal) -> MalVal | |
483 | ||
425305df KR |
484 | func unwrap_args(args: MalSequence, @noescape forFunction fn: (MalVal) throws -> MalVal) throws -> MalVal { |
485 | return try with_one_parameter(args) { (arg1) -> MalVal in | |
486 | return try fn(arg1) | |
2539e6af KR |
487 | } |
488 | } | |
489 | ||
490 | // (String) -> MalVal | |
491 | ||
425305df KR |
492 | private func unwrap_args(args: MalSequence, @noescape forFunction fn: (String) throws -> MalVal) throws -> MalVal { |
493 | return try with_one_parameter(args) { (arg1) -> MalVal in | |
494 | guard let str = as_stringQ(arg1) else { | |
495 | try throw_error("expected string, got \(arg1)") | |
496 | } | |
497 | return try fn(as_stringtype(str)) | |
2539e6af KR |
498 | } |
499 | } | |
500 | ||
501 | // (String) -> MalVal? | |
502 | ||
425305df KR |
503 | private func unwrap_args(args: MalSequence, @noescape forFunction fn: (String) throws -> MalVal?) throws -> MalVal { |
504 | return try with_one_parameter(args) { (arg1) -> MalVal in | |
505 | guard let str = as_stringQ(arg1) else { | |
506 | try throw_error("expected string, got \(arg1)") | |
507 | } | |
508 | let res = try fn(as_stringtype(str)) | |
509 | return res != nil ? res! : make_nil() | |
2539e6af KR |
510 | } |
511 | } | |
512 | ||
513 | // (String) -> String | |
514 | ||
425305df KR |
515 | private func unwrap_args(args: MalSequence, @noescape forFunction fn: (String) throws -> String) throws -> MalVal { |
516 | return try with_one_parameter(args) { (arg1) -> MalVal in | |
517 | guard let str = as_stringQ(arg1) else { | |
518 | try throw_error("expected string, got \(arg1)") | |
519 | } | |
520 | return make_string(try fn(as_stringtype(str))) | |
2539e6af KR |
521 | } |
522 | } | |
523 | ||
524 | // (String) -> String? | |
525 | ||
425305df KR |
526 | private func unwrap_args(args: MalSequence, @noescape forFunction fn: (String) throws -> String?) throws -> MalVal { |
527 | return try with_one_parameter(args) { (arg1) -> MalVal in | |
528 | guard let str = as_stringQ(arg1) else { | |
529 | try throw_error("expected string, got \(arg1)") | |
530 | } | |
531 | let res = try fn(as_stringtype(str)) | |
532 | return res != nil ? make_string(res!) : make_nil() | |
2539e6af KR |
533 | } |
534 | } | |
535 | ||
536 | // ========== 2-parameter functions ========== | |
537 | ||
425305df | 538 | // (MalIntType, MalIntType) -> Bool |
2539e6af | 539 | |
425305df KR |
540 | private func unwrap_args(args: MalSequence, @noescape forFunction fn: (MalIntType, MalIntType) throws -> Bool) throws -> MalVal { |
541 | return try with_two_parameters(args) { (arg1, arg2) -> MalVal in | |
542 | guard let int1 = as_integerQ(arg1) else { | |
543 | try throw_error("expected number, got \(arg1)") | |
544 | } | |
545 | guard let int2 = as_integerQ(arg2) else { | |
546 | try throw_error("expected number, got \(arg2)") | |
547 | } | |
548 | return try fn(as_inttype(int1), as_inttype(int2)) ? make_true() : make_false() | |
2539e6af KR |
549 | } |
550 | } | |
551 | ||
425305df | 552 | // (MalIntType, MalIntType) -> MalIntType |
2539e6af | 553 | |
425305df KR |
554 | private func unwrap_args(args: MalSequence, @noescape forFunction fn: (MalIntType, MalIntType) throws -> MalIntType) throws -> MalVal { |
555 | return try with_two_parameters(args) { (arg1, arg2) -> MalVal in | |
556 | guard let int1 = as_integerQ(arg1) else { | |
557 | try throw_error("expected number, got \(arg1)") | |
558 | } | |
559 | guard let int2 = as_integerQ(arg2) else { | |
560 | try throw_error("expected number, got \(arg2)") | |
561 | } | |
562 | return make_integer(try fn(as_inttype(int1), as_inttype(int2))) | |
2539e6af KR |
563 | } |
564 | } | |
565 | ||
566 | // (MalAtom, MalVal) -> MalVal | |
567 | ||
425305df KR |
568 | private func unwrap_args(args: MalSequence, @noescape forFunction fn: (MalAtom, MalVal) throws -> MalVal) throws -> MalVal { |
569 | return try with_two_parameters(args) { (arg1, arg2) -> MalVal in | |
570 | guard let atom = as_atomQ(arg1) else { | |
571 | try throw_error("expected atom, got \(arg1)") | |
572 | } | |
573 | return try fn(atom, arg2) | |
2539e6af KR |
574 | } |
575 | } | |
576 | ||
577 | // (MalFunction, MalSequence) -> MalVal | |
578 | ||
425305df KR |
579 | private func unwrap_args(args: MalSequence, @noescape forFunction fn: (MalFunction, MalSequence) throws -> MalVal) throws -> MalVal { |
580 | return try with_two_parameters(args) { (arg1, arg2) -> MalVal in | |
581 | guard let fn1 = as_functionQ(arg1) else { | |
582 | try throw_error("expected function, got \(arg1)") | |
583 | } | |
584 | guard let seq2 = as_sequenceQ(arg2) else { | |
585 | try throw_error("expected sequence, got \(arg2)") | |
586 | } | |
587 | return try fn(fn1, seq2) | |
2539e6af KR |
588 | } |
589 | } | |
590 | ||
425305df | 591 | // (MalSequence, MalIntType) -> MalVal |
2539e6af | 592 | |
425305df KR |
593 | private func unwrap_args(args: MalSequence, @noescape forFunction fn: (MalSequence, MalIntType) throws -> MalVal) throws -> MalVal { |
594 | return try with_two_parameters(args) { (arg1, arg2) -> MalVal in | |
595 | guard let seq = as_sequenceQ(arg1) else { | |
596 | try throw_error("expected sequence, got \(arg1)") | |
597 | } | |
598 | guard let int = as_integerQ(arg2) else { | |
599 | try throw_error("expected number, got \(arg2)") | |
600 | } | |
601 | return try fn(seq, as_inttype(int)) | |
2539e6af KR |
602 | } |
603 | } | |
604 | ||
605 | // (MalVal, MalSequence) -> MalVal | |
606 | ||
425305df KR |
607 | private func unwrap_args(args: MalSequence, @noescape forFunction fn: (MalVal, MalSequence) throws -> MalVal) throws -> MalVal { |
608 | return try with_two_parameters(args) { (arg1, arg2) -> MalVal in | |
609 | guard let seq = as_sequenceQ(arg2) else { | |
610 | try throw_error("expected sequence, got \(arg2)") | |
611 | } | |
612 | return try fn(arg1, seq) | |
2539e6af KR |
613 | } |
614 | } | |
615 | ||
616 | // (MalVal, MalVal) -> Bool | |
617 | ||
425305df KR |
618 | private func unwrap_args(args: MalSequence, @noescape forFunction fn: (MalVal, MalVal) throws -> Bool) throws -> MalVal { |
619 | return try with_two_parameters(args) { (arg1, arg2) -> MalVal in | |
620 | return try fn(arg1, arg2) ? make_true() : make_false() | |
2539e6af KR |
621 | } |
622 | } | |
623 | ||
624 | // (MalVal, MalVal) -> MalVal | |
625 | ||
425305df KR |
626 | private func unwrap_args(args: MalSequence, @noescape forFunction fn: (MalVal, MalVal) throws -> MalVal) throws -> MalVal { |
627 | return try with_two_parameters(args) { (arg1, arg2) -> MalVal in | |
628 | return try fn(arg1, arg2) | |
2539e6af KR |
629 | } |
630 | } | |
631 | ||
632 | // ========== Variadic functions ========== | |
633 | ||
634 | // (MalVarArgs) -> () | |
635 | ||
425305df KR |
636 | private func unwrap_args(args: MalSequence, @noescape forFunction fn: (MalVarArgs) throws -> ()) throws -> MalVal { |
637 | try fn(MalVarArgs(args)) | |
638 | return make_nil() | |
2539e6af KR |
639 | } |
640 | ||
641 | // (MalVarArgs) -> String | |
642 | ||
425305df KR |
643 | private func unwrap_args(args: MalSequence, @noescape forFunction fn: (MalVarArgs) throws -> String) throws -> MalVal { |
644 | return make_string(try fn(MalVarArgs(args))) | |
2539e6af KR |
645 | } |
646 | ||
647 | // (MalVarArgs) -> MalVal | |
648 | ||
425305df KR |
649 | private func unwrap_args(args: MalSequence, @noescape forFunction fn: (MalVarArgs) throws -> MalVal) throws -> MalVal { |
650 | return try fn(MalVarArgs(args)) | |
2539e6af KR |
651 | } |
652 | ||
653 | // (MalAtom, MalFunction, MalVarArgs) -> MalVal | |
654 | ||
425305df KR |
655 | private func unwrap_args(args: MalSequence, @noescape forFunction fn: (MalAtom, MalFunction, MalVarArgs) throws -> MalVal) throws -> MalVal { |
656 | return try with_two_parameters(args) { (arg1, arg2) -> MalVal in | |
657 | guard let atom = as_atomQ(arg1) else { | |
658 | try throw_error("expected atom, got \(arg1)") | |
659 | } | |
660 | guard let fn2 = as_functionQ(arg2) else { | |
661 | try throw_error("expected function, got \(arg2)") | |
662 | } | |
663 | return try fn(atom, fn2, MalVarArgs(as_sequence(args.rest()).rest())) | |
2539e6af KR |
664 | } |
665 | } | |
666 | ||
667 | // (MalHashMap, MalVarArgs) -> MalVal | |
668 | ||
425305df KR |
669 | private func unwrap_args(args: MalSequence, @noescape forFunction fn: (MalHashMap, MalVarArgs) throws -> MalVal) throws -> MalVal { |
670 | return try with_one_parameter(args) { (arg1) -> MalVal in | |
671 | guard let hash = as_hashmapQ(arg1) else { | |
672 | try throw_error("expected hashmap, got \(arg1)") | |
673 | } | |
674 | return try fn(hash, MalVarArgs(args.rest())) | |
2539e6af KR |
675 | } |
676 | } | |
677 | ||
678 | // (MalSequence, MalVarArgs) -> MalVal | |
679 | ||
425305df KR |
680 | private func unwrap_args(args: MalSequence, @noescape forFunction fn: (MalSequence, MalVarArgs) throws -> MalVal) throws -> MalVal { |
681 | return try with_one_parameter(args) { (arg1) -> MalVal in | |
682 | guard let seq = as_sequenceQ(arg1) else { | |
683 | try throw_error("expected sequence, got \(arg1)") | |
684 | } | |
685 | return try fn(seq, MalVarArgs(args.rest())) | |
2539e6af KR |
686 | } |
687 | } | |
688 | ||
689 | // *o*o*o*o*o*o*o*o*o*o*o*o*o*o*o*o*o*o*o*o*o*o*o*o*o*o*o*o*o*o*o*o*o*o*o*o*o*o*o*o*o*o*o*o* | |
690 | ||
425305df KR |
691 | let ns: [String: MalBuiltin.Signature] = [ |
692 | "=": { try unwrap_args($0, forFunction: fn_eq) }, | |
693 | "throw": { try unwrap_args($0, forFunction: fn_throw) }, | |
694 | ||
695 | "nil?": { try unwrap_args($0, forFunction: fn_nilQ) }, | |
696 | "true?": { try unwrap_args($0, forFunction: fn_trueQ) }, | |
697 | "false?": { try unwrap_args($0, forFunction: fn_falseQ) }, | |
c391c80c | 698 | "string?": { try unwrap_args($0, forFunction: fn_stringQ) }, |
425305df KR |
699 | "symbol": { try unwrap_args($0, forFunction: fn_symbol) }, |
700 | "symbol?": { try unwrap_args($0, forFunction: fn_symbolQ) }, | |
701 | "keyword": { try unwrap_args($0, forFunction: fn_keyword) }, | |
702 | "keyword?": { try unwrap_args($0, forFunction: fn_keywordQ) }, | |
c3c9f348 JM |
703 | "number?": { try unwrap_args($0, forFunction: fn_numberQ) }, |
704 | "fn?": { try unwrap_args($0, forFunction: fn_functionQ) }, | |
705 | "macro?": { try unwrap_args($0, forFunction: fn_macroQ) }, | |
425305df KR |
706 | |
707 | "pr-str": { try unwrap_args($0, forFunction: fn_prstr) }, | |
708 | "str": { try unwrap_args($0, forFunction: fn_str) }, | |
709 | "prn": { try unwrap_args($0, forFunction: fn_prn) }, | |
710 | "println": { try unwrap_args($0, forFunction: fn_println) }, | |
711 | "read-string": { try unwrap_args($0, forFunction: fn_readstring) }, | |
712 | "readline": { try unwrap_args($0, forFunction: fn_readline) }, | |
713 | "slurp": { try unwrap_args($0, forFunction: fn_slurp) }, | |
714 | ||
715 | "<": { try unwrap_args($0, forFunction: fn_lt) }, | |
716 | "<=": { try unwrap_args($0, forFunction: fn_lte) }, | |
717 | ">": { try unwrap_args($0, forFunction: fn_gt) }, | |
718 | ">=": { try unwrap_args($0, forFunction: fn_gte) }, | |
719 | "+": { try unwrap_args($0, forFunction: fn_add) }, | |
720 | "-": { try unwrap_args($0, forFunction: fn_subtract) }, | |
721 | "*": { try unwrap_args($0, forFunction: fn_multiply) }, | |
722 | "/": { try unwrap_args($0, forFunction: fn_divide) }, | |
723 | "time-ms": { try unwrap_args($0, forFunction: fn_timems) }, | |
724 | ||
725 | "list": { try unwrap_args($0, forFunction: fn_list) }, | |
726 | "list?": { try unwrap_args($0, forFunction: fn_listQ) }, | |
727 | "vector": { try unwrap_args($0, forFunction: fn_vector) }, | |
728 | "vector?": { try unwrap_args($0, forFunction: fn_vectorQ) }, | |
729 | "hash-map": { try unwrap_args($0, forFunction: fn_hashmap) }, | |
730 | "map?": { try unwrap_args($0, forFunction: fn_hashmapQ) }, | |
731 | "assoc": { try unwrap_args($0, forFunction: fn_assoc) }, | |
732 | "dissoc": { try unwrap_args($0, forFunction: fn_dissoc) }, | |
733 | "get": { try unwrap_args($0, forFunction: fn_get) }, | |
734 | "contains?": { try unwrap_args($0, forFunction: fn_containsQ) }, | |
735 | "keys": { try unwrap_args($0, forFunction: fn_keys) }, | |
736 | "vals": { try unwrap_args($0, forFunction: fn_values) }, | |
737 | ||
738 | "sequential?": { try unwrap_args($0, forFunction: fn_sequentialQ) }, | |
739 | "cons": { try unwrap_args($0, forFunction: fn_cons) }, | |
740 | "concat": { try unwrap_args($0, forFunction: fn_concat) }, | |
741 | "nth": { try unwrap_args($0, forFunction: fn_nth) }, | |
742 | "first": { try unwrap_args($0, forFunction: fn_first) }, | |
743 | "rest": { try unwrap_args($0, forFunction: fn_rest) }, | |
744 | "empty?": { try unwrap_args($0, forFunction: fn_emptyQ) }, | |
745 | "count": { try unwrap_args($0, forFunction: fn_count) }, | |
746 | "apply": { try unwrap_args($0, forFunction: fn_apply) }, | |
747 | "map": { try unwrap_args($0, forFunction: fn_map) }, | |
c391c80c | 748 | |
425305df | 749 | "conj": { try unwrap_args($0, forFunction: fn_conj) }, |
c391c80c | 750 | "seq": { try unwrap_args($0, forFunction: fn_seq) }, |
425305df KR |
751 | |
752 | "meta": { try unwrap_args($0, forFunction: fn_meta) }, | |
753 | "with-meta": { try unwrap_args($0, forFunction: fn_withmeta) }, | |
754 | "atom": { try unwrap_args($0, forFunction: fn_atom) }, | |
755 | "atom?": { try unwrap_args($0, forFunction: fn_atomQ) }, | |
756 | "deref": { try unwrap_args($0, forFunction: fn_deref) }, | |
757 | "reset!": { try unwrap_args($0, forFunction: fn_resetBang) }, | |
758 | "swap!": { try unwrap_args($0, forFunction: fn_swapBang) }, | |
2539e6af KR |
759 | ] |
760 | ||
761 | func load_builtins(env: Environment) { | |
762 | for (name, fn) in ns { | |
425305df | 763 | env.set(as_symbol(make_symbol(name)), make_builtin(fn)) |
2539e6af KR |
764 | } |
765 | } |