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