crystal, logo: Add number?, fn?, macro?
[jackhill/mal.git] / crystal / core.cr
1 require "time"
2
3 require "./types"
4 require "./error"
5 require "./printer"
6 require "./reader"
7 require "./readline"
8
9 module Mal
10
11 macro calc_op(op)
12 -> (args : Array(Mal::Type)) {
13 x, y = args[0].unwrap, args[1].unwrap
14 eval_error "invalid arguments for binary operator {{op.id}}" unless x.is_a?(Int64) && y.is_a?(Int64)
15 Mal::Type.new(x {{op.id}} y)
16 }
17 end
18
19 def self.list(args)
20 args.to_mal
21 end
22
23 def self.list?(args)
24 args.first.unwrap.is_a? Mal::List
25 end
26
27 def self.empty?(args)
28 a = args.first.unwrap
29 a.is_a?(Array) ? a.empty? : false
30 end
31
32 def self.count(args)
33 a = args.first.unwrap
34 case a
35 when Array
36 a.size.to_i64
37 when Nil
38 0i64
39 else
40 eval_error "invalid argument for function 'count'"
41 end
42 end
43
44 def self.pr_str_(args)
45 args.map{|a| pr_str(a)}.join(" ")
46 end
47
48 def self.str(args)
49 args.map{|a| pr_str(a, false)}.join
50 end
51
52 def self.prn(args)
53 puts self.pr_str_(args)
54 nil
55 end
56
57 def self.println(args)
58 puts args.map{|a| pr_str(a, false)}.join(" ")
59 nil
60 end
61
62 def self.read_string(args)
63 head = args.first.unwrap
64 eval_error "argument of read-str must be string" unless head.is_a? String
65 read_str head
66 end
67
68 def self.slurp(args)
69 head = args.first.unwrap
70 eval_error "argument of slurp must be string" unless head.is_a? String
71 begin
72 File.read head
73 rescue e : Errno
74 eval_error "no such file"
75 end
76 end
77
78 def self.cons(args)
79 head, tail = args[0] as Mal::Type, args[1].unwrap
80 eval_error "2nd arg of cons must be list" unless tail.is_a? Array
81 ([head] + tail).to_mal
82 end
83
84 def self.concat(args)
85 args.each_with_object(Mal::List.new) do |arg, list|
86 a = arg.unwrap
87 eval_error "arguments of concat must be list" unless a.is_a?(Array)
88 a.each{|e| list << e}
89 end
90 end
91
92 def self.nth(args)
93 a0, a1 = args[0].unwrap, args[1].unwrap
94 eval_error "1st argument of nth must be list or vector" unless a0.is_a? Array
95 eval_error "2nd argument of nth must be integer" unless a1.is_a? Int64
96 a0[a1]
97 end
98
99 def self.first(args)
100 a0 = args[0].unwrap
101
102 return nil if a0.nil?
103 eval_error "1st argument of first must be list or vector or nil" unless a0.is_a? Array
104 a0.empty? ? nil : a0.first
105 end
106
107 def self.rest(args)
108 a0 = args[0].unwrap
109
110 return Mal::List.new if a0.nil?
111 eval_error "1st argument of first must be list or vector or nil" unless a0.is_a? Array
112 return Mal::List.new if a0.empty?
113 a0[1..-1].to_mal
114 end
115
116 def self.apply(args)
117 eval_error "apply must take at least 2 arguments" unless args.size >= 2
118
119 head = args.first.unwrap
120 last = args.last.unwrap
121
122 eval_error "last argument of apply must be list or vector" unless last.is_a? Array
123
124 case head
125 when Mal::Closure
126 head.fn.call(args[1..-2] + last)
127 when Mal::Func
128 head.call(args[1..-2] + last)
129 else
130 eval_error "1st argument of apply must be function or closure"
131 end
132 end
133
134 def self.map(args)
135 func = args.first.unwrap
136 list = args[1].unwrap
137
138 eval_error "2nd argument of map must be list or vector" unless list.is_a? Array
139
140 f = case func
141 when Mal::Closure then func.fn
142 when Mal::Func then func
143 else eval_error "1st argument of map must be function"
144 end
145
146 list.each_with_object(Mal::List.new) do |elem, mapped|
147 mapped << f.call([elem])
148 end
149 end
150
151 def self.nil_value?(args)
152 args.first.unwrap.nil?
153 end
154
155 def self.true?(args)
156 a = args.first.unwrap
157 a.is_a?(Bool) && a
158 end
159
160 def self.false?(args)
161 a = args.first.unwrap
162 a.is_a?(Bool) && !a
163 end
164
165 def self.symbol?(args)
166 args.first.unwrap.is_a?(Mal::Symbol)
167 end
168
169 def self.symbol(args)
170 head = args.first.unwrap
171 eval_error "1st argument of symbol function must be string" unless head.is_a? String
172 Mal::Symbol.new head
173 end
174
175 def self.string?(args)
176 head = args.first.unwrap
177 head.is_a?(String) && (head.empty? || head[0] != '\u029e')
178 end
179
180 def self.keyword(args)
181 head = args.first.unwrap
182 eval_error "1st argument of symbol function must be string" unless head.is_a? String
183 "\u029e" + head
184 end
185
186 def self.keyword?(args)
187 head = args.first.unwrap
188 head.is_a?(String) && !head.empty? && head[0] == '\u029e'
189 end
190
191 def self.number?(args)
192 args.first.unwrap.is_a?(Int64)
193 end
194
195 def self.fn?(args)
196 return false if args.first.macro?
197 head = args.first.unwrap
198 head.is_a?(Mal::Func) || head.is_a?(Mal::Closure)
199 end
200
201 def self.macro?(args)
202 args.first.macro?
203 end
204
205 def self.vector(args)
206 args.to_mal(Mal::Vector)
207 end
208
209 def self.vector?(args)
210 args.first.unwrap.is_a? Mal::Vector
211 end
212
213 def self.hash_map(args)
214 eval_error "hash-map must take even number of arguments" unless args.size.even?
215 map = Mal::HashMap.new
216 args.each_slice(2) do |kv|
217 k = kv[0].unwrap
218 eval_error "key must be string" unless k.is_a? String
219 map[k] = kv[1]
220 end
221 map
222 end
223
224 def self.map?(args)
225 args.first.unwrap.is_a? Mal::HashMap
226 end
227
228 def self.assoc(args)
229 head = args.first.unwrap
230 eval_error "1st argument of assoc must be hashmap" unless head.is_a? Mal::HashMap
231 eval_error "assoc must take a list and even number of arguments" unless (args.size - 1).even?
232
233 map = Mal::HashMap.new
234 head.each{|k, v| map[k] = v}
235
236 args[1..-1].each_slice(2) do |kv|
237 k = kv[0].unwrap
238 eval_error "key must be string" unless k.is_a? String
239 map[k] = kv[1]
240 end
241
242 map
243 end
244
245 def self.dissoc(args)
246 head = args.first.unwrap
247 eval_error "1st argument of assoc must be hashmap" unless head.is_a? Mal::HashMap
248
249 map = Mal::HashMap.new
250 head.each{|k,v| map[k] = v}
251
252 args[1..-1].each do |arg|
253 key = arg.unwrap
254 eval_error "key must be string" unless key.is_a? String
255 map.delete key
256 end
257
258 map
259 end
260
261 def self.get(args)
262 a0, a1 = args[0].unwrap, args[1].unwrap
263 return nil unless a0.is_a? Mal::HashMap
264 eval_error "2nd argument of get must be string" unless a1.is_a? String
265
266 # a0[a1]? isn't available because type ofa0[a1] is infered NoReturn
267 a0.has_key?(a1) ? a0[a1] : nil
268 end
269
270 def self.contains?(args)
271 a0, a1 = args[0].unwrap, args[1].unwrap
272 eval_error "1st argument of get must be hashmap" unless a0.is_a? Mal::HashMap
273 eval_error "2nd argument of get must be string" unless a1.is_a? String
274 a0.has_key? a1
275 end
276
277 def self.keys(args)
278 head = args.first.unwrap
279 eval_error "1st argument of assoc must be hashmap" unless head.is_a? Mal::HashMap
280 head.keys.each_with_object(Mal::List.new){|e,l| l << Mal::Type.new(e)}
281 end
282
283 def self.vals(args)
284 head = args.first.unwrap
285 eval_error "1st argument of assoc must be hashmap" unless head.is_a? Mal::HashMap
286 head.values.to_mal
287 end
288
289 def self.sequential?(args)
290 args.first.unwrap.is_a? Array
291 end
292
293 def self.readline(args)
294 head = args.first.unwrap
295 eval_error "1st argument of readline must be string" unless head.is_a? String
296 my_readline head
297 end
298
299 def self.meta(args)
300 m = args.first.meta
301 m.nil? ? nil : m
302 end
303
304 def self.with_meta(args)
305 t = args.first.dup
306 t.meta = args[1]
307 t
308 end
309
310 def self.atom(args)
311 Mal::Atom.new args.first
312 end
313
314 def self.atom?(args)
315 args.first.unwrap.is_a? Mal::Atom
316 end
317
318 def self.deref(args)
319 head = args.first.unwrap
320 eval_error "1st argument of deref must be atom" unless head.is_a? Mal::Atom
321 head.val
322 end
323
324 def self.reset!(args)
325 head = args.first.unwrap
326 eval_error "1st argument of reset! must be atom" unless head.is_a? Mal::Atom
327 head.val = args[1]
328 end
329
330 def self.swap!(args)
331 atom = args.first.unwrap
332 eval_error "1st argument of swap! must be atom" unless atom.is_a? Mal::Atom
333
334 a = [atom.val] + args[2..-1]
335
336 func = args[1].unwrap
337 case func
338 when Mal::Func
339 atom.val = func.call a
340 when Mal::Closure
341 atom.val = func.fn.call a
342 else
343 eval_error "2nd argumetn of swap! must be function"
344 end
345 end
346
347 def self.conj(args)
348 seq = args.first.unwrap
349 case seq
350 when Mal::List
351 (args[1..-1].reverse + seq).to_mal
352 when Mal::Vector
353 (seq + args[1..-1]).to_mal(Mal::Vector)
354 else
355 eval_error "1st argument of conj must be list or vector"
356 end
357 end
358
359 def self.seq(args)
360 obj = args.first.unwrap
361 case obj
362 when nil
363 nil
364 when Mal::List
365 return nil if obj.empty?
366 obj
367 when Mal::Vector
368 return nil if obj.empty?
369 obj.to_mal
370 when String
371 return nil if obj.empty?
372 obj.split("").each_with_object(Mal::List.new){|e,l| l << Mal::Type.new(e)}
373 else
374 eval_error "argument of seq must be list or vector or string or nil"
375 end
376 end
377
378 def self.time_ms(args)
379 Time.now.epoch_ms.to_i64
380 end
381
382 # Note:
383 # Simply using ->self.some_func doesn't work
384 macro func(name)
385 -> (args : Array(Mal::Type)) { Mal::Type.new self.{{name.id}}(args) }
386 end
387
388 macro rel_op(op)
389 -> (args : Array(Mal::Type)) { Mal::Type.new (args[0] {{op.id}} args[1]) }
390 end
391
392 NS = {
393 "+" => calc_op(:+),
394 "-" => calc_op(:-),
395 "*" => calc_op(:*),
396 "/" => calc_op(:/),
397 "list" => func(:list),
398 "list?" => func(:list?),
399 "empty?" => func(:empty?),
400 "count" => func(:count),
401 "=" => rel_op(:==),
402 "<" => rel_op(:<),
403 ">" => rel_op(:>),
404 "<=" => rel_op(:<=),
405 ">=" => rel_op(:>=),
406 "pr-str" => func(:pr_str_),
407 "str" => func(:str),
408 "prn" => func(:prn),
409 "println" => func(:println),
410 "read-string" => func(:read_string),
411 "slurp" => func(:slurp),
412 "cons" => func(:cons),
413 "concat" => func(:concat),
414 "nth" => func(:nth),
415 "first" => func(:first),
416 "rest" => func(:rest),
417 "throw" => -> (args : Array(Mal::Type)) { raise Mal::RuntimeException.new args[0] },
418 "apply" => func(:apply),
419 "map" => func(:map),
420 "nil?" => func(:nil_value?),
421 "true?" => func(:true?),
422 "false?" => func(:false?),
423 "symbol?" => func(:symbol?),
424 "symbol" => func(:symbol),
425 "string?" => func(:string?),
426 "keyword" => func(:keyword),
427 "keyword?" => func(:keyword?),
428 "number?" => func(:number?),
429 "fn?" => func(:fn?),
430 "macro?" => func(:macro?),
431 "vector" => func(:vector),
432 "vector?" => func(:vector?),
433 "hash-map" => func(:hash_map),
434 "map?" => func(:map?),
435 "assoc" => func(:assoc),
436 "dissoc" => func(:dissoc),
437 "get" => func(:get),
438 "contains?" => func(:contains?),
439 "keys" => func(:keys),
440 "vals" => func(:vals),
441 "sequential?" => func(:sequential?),
442 "readline" => func(:readline),
443 "meta" => func(:meta),
444 "with-meta" => func(:with_meta),
445 "atom" => func(:atom),
446 "atom?" => func(:atom?),
447 "deref" => func(:deref),
448 "deref" => func(:deref),
449 "reset!" => func(:reset!),
450 "swap!" => func(:swap!),
451 "conj" => func(:conj),
452 "seq" => func(:seq),
453 "time-ms" => func(:time_ms),
454 } of String => Mal::Func
455
456 end