Change quasiquote algorithm
[jackhill/mal.git] / impls / tcl / core.tcl
CommitLineData
54d9903c
DM
1proc mal_equal {a} {
2 bool_new [equal_q [lindex $a 0] [lindex $a 1]]
3}
4
5set ::mal_exception_obj 0
6proc mal_throw {a} {
7 set ::mal_exception_obj [lindex $a 0]
8 error "__MalException__"
9}
10
11proc mal_nil_q {a} {
12 bool_new [nil_q [lindex $a 0]]
13}
14
15proc mal_true_q {a} {
16 bool_new [true_q [lindex $a 0]]
17}
18
19proc mal_false_q {a} {
20 bool_new [false_q [lindex $a 0]]
21}
22
23proc mal_symbol {a} {
24 symbol_new [obj_val [lindex $a 0]]
25}
26
27proc mal_symbol_q {a} {
28 bool_new [symbol_q [lindex $a 0]]
29}
30
0e198b2c
DM
31proc mal_string_q {a} {
32 bool_new [string_q [lindex $a 0]]
33}
34
54d9903c
DM
35proc mal_keyword {a} {
36 keyword_new [obj_val [lindex $a 0]]
37}
38
39proc mal_keyword_q {a} {
40 bool_new [keyword_q [lindex $a 0]]
41}
42
c91c8de9
DM
43proc mal_number_q {a} {
44 bool_new [integer_q [lindex $a 0]]
45}
46
47proc mal_fn_q {a} {
48 set f [lindex $a 0]
49 switch [obj_type $f] {
50 function { return [bool_new [expr {![macro_q $f]}]] }
51 nativefunction { return $::mal_true }
52 default { return $::mal_false }
53 }
54}
55
56proc mal_macro_q {a} {
57 bool_new [macro_q [lindex $a 0]]
58}
59
54d9903c
DM
60proc render_array {arr readable delim} {
61 set res {}
62 foreach e $arr {
63 lappend res [pr_str $e $readable]
64 }
65 join $res $delim
66}
67
68proc mal_pr_str {a} {
69 string_new [render_array $a 1 " "]
70}
71
72proc mal_str {a} {
73 string_new [render_array $a 0 ""]
74}
75
76proc mal_prn {a} {
77 puts [render_array $a 1 " "]
78 return $::mal_nil
79}
80
81proc mal_println {a} {
82 puts [render_array $a 0 " "]
83 return $::mal_nil
84}
85
86proc mal_read_string {a} {
87 read_str [obj_val [lindex $a 0]]
88}
89
90proc mal_readline {a} {
91 set prompt [obj_val [lindex $a 0]]
92 set res [_readline $prompt]
93 if {[lindex $res 0] == "EOF"} {
94 return $::mal_nil
95 }
96 string_new [lindex $res 1]
97}
98
99proc mal_slurp {a} {
100 set filename [obj_val [lindex $a 0]]
101 set file [open $filename]
102 set content [read $file]
103 close $file
104 string_new $content
105}
106
107proc mal_lt {a} {
108 bool_new [expr {[obj_val [lindex $a 0]] < [obj_val [lindex $a 1]]}]
109}
110
111proc mal_lte {a} {
112 bool_new [expr {[obj_val [lindex $a 0]] <= [obj_val [lindex $a 1]]}]
113}
114
115proc mal_gt {a} {
116 bool_new [expr {[obj_val [lindex $a 0]] > [obj_val [lindex $a 1]]}]
117}
118
119proc mal_gte {a} {
120 bool_new [expr {[obj_val [lindex $a 0]] >= [obj_val [lindex $a 1]]}]
121}
122
123proc mal_add {a} {
124 integer_new [expr {[obj_val [lindex $a 0]] + [obj_val [lindex $a 1]]}]
125}
126
127proc mal_sub {a} {
128 integer_new [expr {[obj_val [lindex $a 0]] - [obj_val [lindex $a 1]]}]
129}
130
131proc mal_mul {a} {
132 integer_new [expr {[obj_val [lindex $a 0]] * [obj_val [lindex $a 1]]}]
133}
134
135proc mal_div {a} {
136 integer_new [expr {[obj_val [lindex $a 0]] / [obj_val [lindex $a 1]]}]
137}
138
139proc mal_time_ms {a} {
140 integer_new [clock milliseconds]
141}
142
143proc mal_list {a} {
144 list_new $a
145}
146
147proc mal_list_q {a} {
148 bool_new [list_q [lindex $a 0]]
149}
150
151proc mal_vector {a} {
152 vector_new $a
153}
154
155proc mal_vector_q {a} {
156 bool_new [vector_q [lindex $a 0]]
157}
158
159proc mal_hash_map {a} {
160 set d [dict create]
161 foreach {k v} $a {
162 dict set d [obj_val $k] $v
163 }
164 hashmap_new $d
165}
166
167proc mal_map_q {a} {
168 bool_new [hashmap_q [lindex $a 0]]
169}
170
171proc mal_assoc {a} {
172 set d [dict create]
173 dict for {k v} [obj_val [lindex $a 0]] {
174 dict set d $k $v
175 }
176 foreach {k v} [lrange $a 1 end] {
177 dict set d [obj_val $k] $v
178 }
179 hashmap_new $d
180}
181
182proc mal_dissoc {a} {
183 set d [dict create]
184 dict for {k v} [obj_val [lindex $a 0]] {
185 dict set d $k $v
186 }
187 foreach k [lrange $a 1 end] {
188 dict unset d [obj_val $k]
189 }
190 hashmap_new $d
191}
192
193proc mal_get {a} {
194 lassign $a hashmap_obj key_obj
195 if {[dict exists [obj_val $hashmap_obj] [obj_val $key_obj]]} {
196 dict get [obj_val $hashmap_obj] [obj_val $key_obj]
197 } else {
198 return $::mal_nil
199 }
200}
201
202proc mal_contains_q {a} {
203 lassign $a hashmap_obj key_obj
204 bool_new [dict exists [obj_val $hashmap_obj] [obj_val $key_obj]]
205}
206
207proc mal_keys {a} {
208 set res {}
209 foreach k [dict keys [obj_val [lindex $a 0]]] {
210 lappend res [string_new $k]
211 }
212 list_new $res
213}
214
215proc mal_vals {a} {
216 list_new [dict values [obj_val [lindex $a 0]]]
217}
218
219proc mal_sequential_q {a} {
220 bool_new [sequential_q [lindex $a 0]]
221}
222
223proc mal_cons {a} {
224 lassign $a head lst
225 list_new [concat [list $head] [obj_val $lst]]
226}
227
228proc mal_concat {a} {
229 set res {}
230 foreach lst $a {
231 if {[nil_q $lst]} {
232 continue
233 }
234 set res [concat $res [obj_val $lst]]
235 }
236 list_new $res
237}
238
fbfe6784
NB
239proc mal_vec {a} {
240 lassign $a a0
241 if {[vector_q $a0]} {
242 return $a0
243 } elseif {[list_q $a0]} {
244 return [vector_new [obj_val $a0]]
245 } else {
246 error "vec requires list or vector"
247 }
248}
249
54d9903c
DM
250proc mal_nth {a} {
251 lassign $a lst_obj index_obj
252 set index [obj_val $index_obj]
253 set lst [obj_val $lst_obj]
254 if {$index >= [llength $lst]} {
255 error "nth: index out of range"
256 }
257 lindex $lst $index
258}
259
260proc mal_first {a} {
261 lassign $a lst
262 if {[nil_q $lst] || [llength [obj_val $lst]] == 0} {
263 return $::mal_nil
264 }
265 lindex [obj_val $lst] 0
266}
267
268proc mal_rest {a} {
269 lassign $a lst
270 list_new [lrange [obj_val $lst] 1 end]
271}
272
273proc mal_empty_q {a} {
274 bool_new [expr {[llength [obj_val [lindex $a 0]]] == 0}]
275}
276
277proc mal_count {a} {
278 integer_new [llength [obj_val [lindex $a 0]]]
279}
280
281proc mal_apply {a} {
282 set f [lindex $a 0]
283 if {[llength $a] > 1} {
284 set mid_args [lrange $a 1 end-1]
285 set last_list [lindex $a end]
286 set apply_args [concat $mid_args [obj_val $last_list]]
287 } else {
288 set apply_args {}
289 }
290
291 switch [obj_type $f] {
292 function {
293 set funcdict [obj_val $f]
294 set body [dict get $funcdict body]
295 set env [dict get $funcdict env]
296 set binds [dict get $funcdict binds]
297 set funcenv [Env new $env $binds $apply_args]
298 return [EVAL $body $funcenv]
299 }
300 nativefunction {
301 set body [concat [list [obj_val $f]] {$a}]
302 set lambda [list {a} $body]
303 return [apply $lambda $apply_args]
304 }
305 default {
306 error "Not a function"
307 }
308 }
309}
310
311proc mal_map {a} {
312 lassign $a f seq
313 set res {}
314 foreach item [obj_val $seq] {
315 set mappeditem [mal_apply [list $f [list_new [list $item]]]]
316 lappend res $mappeditem
317 }
318 list_new $res
319}
320
321proc mal_conj {a} {
322 lassign $a a0
323 if {[list_q $a0]} {
324 set lst $a0
325 foreach item [lrange $a 1 end] {
326 set lst [mal_cons [list $item $lst]]
327 }
328 return $lst
329 } elseif {[vector_q $a0]} {
330 set res [obj_val $a0]
331 foreach item [lrange $a 1 end] {
332 lappend res $item
333 }
334 vector_new $res
335 } else {
336 error "conj requires list or vector"
337 }
338}
339
0e198b2c
DM
340proc mal_seq {a} {
341 lassign $a a0
342 if {[string_q $a0]} {
343 set str [obj_val $a0]
344 if {$str == ""} {
345 return $::mal_nil
346 }
347 set res {}
348 foreach char [split $str {}] {
349 lappend res [string_new $char]
350 }
351 list_new $res
352 } elseif {[list_q $a0]} {
353 if {[llength [obj_val $a0]] == 0} {
354 return $::mal_nil
355 }
356 return $a0
357 } elseif {[vector_q $a0]} {
358 if {[llength [obj_val $a0]] == 0} {
359 return $::mal_nil
360 }
361 list_new [obj_val $a0]
362 } elseif {[nil_q $a0]} {
363 return $::mal_nil
364 } else {
365 error "seq requires string or list or vector or nil"
366 }
367}
368
54d9903c
DM
369proc mal_meta {a} {
370 obj_meta [lindex $a 0]
371}
372
373proc mal_with_meta {a} {
374 lassign $a a0 a1
375 obj_new [obj_type $a0] [obj_val $a0] $a1
376}
377
378proc mal_atom {a} {
379 atom_new [lindex $a 0]
380}
381
382proc mal_atom_q {a} {
383 bool_new [atom_q [lindex $a 0]]
384}
385
386proc mal_deref {a} {
387 obj_val [lindex $a 0]
388}
389
390proc mal_reset_bang {a} {
391 lassign $a a0 a1
392 obj_set_val $a0 $a1
393}
394
395proc mal_swap_bang {a} {
396 lassign $a a0 f
397 set apply_args [concat [list [obj_val $a0]] [lrange $a 2 end]]
398 set newval [mal_apply [list $f [list_new $apply_args]]]
399 mal_reset_bang [list $a0 $newval]
400}
401
402set core_ns [dict create \
403 "=" [nativefunction_new mal_equal] \
404 "throw" [nativefunction_new mal_throw] \
405 \
406 "nil?" [nativefunction_new mal_nil_q] \
407 "true?" [nativefunction_new mal_true_q] \
408 "false?" [nativefunction_new mal_false_q] \
409 "symbol" [nativefunction_new mal_symbol] \
410 "symbol?" [nativefunction_new mal_symbol_q] \
0e198b2c 411 "string?" [nativefunction_new mal_string_q] \
54d9903c
DM
412 "keyword" [nativefunction_new mal_keyword] \
413 "keyword?" [nativefunction_new mal_keyword_q] \
c91c8de9
DM
414 "number?" [nativefunction_new mal_number_q] \
415 "fn?" [nativefunction_new mal_fn_q] \
416 "macro?" [nativefunction_new mal_macro_q] \
54d9903c
DM
417 \
418 "pr-str" [nativefunction_new mal_pr_str] \
419 "str" [nativefunction_new mal_str] \
420 "prn" [nativefunction_new mal_prn] \
421 "println" [nativefunction_new mal_println] \
422 "read-string" [nativefunction_new mal_read_string] \
423 "readline" [nativefunction_new mal_readline] \
424 "slurp" [nativefunction_new mal_slurp] \
425 \
426 "<" [nativefunction_new mal_lt] \
427 "<=" [nativefunction_new mal_lte] \
428 ">" [nativefunction_new mal_gt] \
429 ">=" [nativefunction_new mal_gte] \
430 "+" [nativefunction_new mal_add] \
431 "-" [nativefunction_new mal_sub] \
432 "*" [nativefunction_new mal_mul] \
433 "/" [nativefunction_new mal_div] \
434 "time-ms" [nativefunction_new mal_time_ms] \
435 \
436 "list" [nativefunction_new mal_list] \
437 "list?" [nativefunction_new mal_list_q] \
438 "vector" [nativefunction_new mal_vector] \
439 "vector?" [nativefunction_new mal_vector_q] \
440 "hash-map" [nativefunction_new mal_hash_map] \
441 "map?" [nativefunction_new mal_map_q] \
442 "assoc" [nativefunction_new mal_assoc] \
443 "dissoc" [nativefunction_new mal_dissoc] \
444 "get" [nativefunction_new mal_get] \
445 "contains?" [nativefunction_new mal_contains_q] \
446 "keys" [nativefunction_new mal_keys] \
447 "vals" [nativefunction_new mal_vals] \
448 \
449 "sequential?" [nativefunction_new mal_sequential_q] \
450 "cons" [nativefunction_new mal_cons] \
451 "concat" [nativefunction_new mal_concat] \
fbfe6784 452 "vec" [nativefunction_new mal_vec] \
54d9903c
DM
453 "nth" [nativefunction_new mal_nth] \
454 "first" [nativefunction_new mal_first] \
455 "rest" [nativefunction_new mal_rest] \
456 "empty?" [nativefunction_new mal_empty_q] \
457 "count" [nativefunction_new mal_count] \
458 "apply" [nativefunction_new mal_apply] \
459 "map" [nativefunction_new mal_map] \
460 \
461 "conj" [nativefunction_new mal_conj] \
0e198b2c 462 "seq" [nativefunction_new mal_seq] \
54d9903c
DM
463 \
464 "meta" [nativefunction_new mal_meta] \
465 "with-meta" [nativefunction_new mal_with_meta] \
466 "atom" [nativefunction_new mal_atom] \
467 "atom?" [nativefunction_new mal_atom_q] \
468 "deref" [nativefunction_new mal_deref] \
469 "reset!" [nativefunction_new mal_reset_bang] \
470 "swap!" [nativefunction_new mal_swap_bang] \
471]