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