Elm step A: implemented GC. MAL tests are failing..
[jackhill/mal.git] / elm / Core.elm
1 module Core exposing (..)
2
3 import Types exposing (..)
4 import Env
5 import Eval
6 import Printer exposing (printString)
7 import Array
8 import Dict
9 import IO exposing (IO(..))
10 import Reader
11 import Utils exposing (zip)
12 import Time
13 import Task
14
15
16 ns : Env
17 ns =
18 let
19 makeFn =
20 CoreFunc >> MalFunction
21
22 binaryOp fn retType args =
23 case args of
24 [ MalInt x, MalInt y ] ->
25 Eval.succeed (retType (fn x y))
26
27 _ ->
28 Eval.fail "unsupported arguments"
29
30 {- list -}
31 list =
32 Eval.succeed << MalList
33
34 {- list? -}
35 isList args =
36 case args of
37 [ MalList _ ] ->
38 Eval.succeed (MalBool True)
39
40 _ ->
41 Eval.succeed (MalBool False)
42
43 {- empty? -}
44 isEmpty args =
45 case args of
46 [ MalList list ] ->
47 Eval.succeed <| MalBool (List.isEmpty list)
48
49 [ MalVector vec ] ->
50 Eval.succeed <| MalBool (Array.isEmpty vec)
51
52 _ ->
53 Eval.fail "unsupported arguments"
54
55 {- count -}
56 count args =
57 case args of
58 [ MalNil ] ->
59 Eval.succeed (MalInt 0)
60
61 [ MalList list ] ->
62 Eval.succeed <| MalInt (List.length list)
63
64 [ MalVector vec ] ->
65 Eval.succeed <| MalInt (Array.length vec)
66
67 _ ->
68 Eval.fail "unsupported arguments"
69
70 equalLists a b =
71 case ( a, b ) of
72 ( [], [] ) ->
73 True
74
75 ( x :: xs, y :: ys ) ->
76 if deepEquals x y then
77 equalLists xs ys
78 else
79 False
80
81 _ ->
82 False
83
84 compareListTo list other =
85 case other of
86 MalList otherList ->
87 equalLists list otherList
88
89 MalVector vec ->
90 equalLists list (Array.toList vec)
91
92 _ ->
93 False
94
95 equalMaps a b =
96 if Dict.keys a /= Dict.keys b then
97 False
98 else
99 zip (Dict.values a) (Dict.values b)
100 |> List.map (uncurry deepEquals)
101 |> List.all identity
102
103 deepEquals a b =
104 case ( a, b ) of
105 ( MalList list, MalList otherList ) ->
106 equalLists list otherList
107
108 ( MalList list, MalVector vec ) ->
109 equalLists list (Array.toList vec)
110
111 ( MalList _, _ ) ->
112 False
113
114 ( MalVector vec, MalList list ) ->
115 equalLists (Array.toList vec) list
116
117 ( MalVector vec, MalVector otherVec ) ->
118 equalLists (Array.toList vec) (Array.toList otherVec)
119
120 ( MalVector _, _ ) ->
121 False
122
123 ( MalMap map, MalMap otherMap ) ->
124 equalMaps map otherMap
125
126 ( MalMap _, _ ) ->
127 False
128
129 ( _, MalMap _ ) ->
130 False
131
132 _ ->
133 a == b
134
135 {- = -}
136 equals args =
137 case args of
138 [ a, b ] ->
139 Eval.succeed <| MalBool (deepEquals a b)
140
141 _ ->
142 Eval.fail "unsupported arguments"
143
144 {- pr-str -}
145 prStr args =
146 Eval.withEnv
147 (\env ->
148 args
149 |> List.map (printString env True)
150 |> String.join " "
151 |> MalString
152 |> Eval.succeed
153 )
154
155 {- str -}
156 str args =
157 Eval.withEnv
158 (\env ->
159 args
160 |> List.map (printString env False)
161 |> String.join ""
162 |> MalString
163 |> Eval.succeed
164 )
165
166 {- helper function to write a string to stdout -}
167 writeLine str =
168 Eval.io (IO.writeLine str)
169 (\msg ->
170 case msg of
171 LineWritten ->
172 Eval.succeed MalNil
173
174 _ ->
175 Eval.fail "wrong IO, expected LineWritten"
176 )
177
178 prn args =
179 Eval.withEnv
180 (\env ->
181 args
182 |> List.map (printString env True)
183 |> String.join " "
184 |> writeLine
185 )
186
187 println args =
188 Eval.withEnv
189 (\env ->
190 args
191 |> List.map (printString env False)
192 |> String.join " "
193 |> writeLine
194 )
195
196 printEnv args =
197 case args of
198 [] ->
199 Eval.withEnv (Printer.printEnv >> writeLine)
200
201 _ ->
202 Eval.fail "unsupported arguments"
203
204 readString args =
205 case args of
206 [ MalString str ] ->
207 case Reader.readString str of
208 Ok Nothing ->
209 Eval.succeed MalNil
210
211 Ok (Just ast) ->
212 Eval.succeed ast
213
214 Err msg ->
215 Eval.fail msg
216
217 _ ->
218 Eval.fail "unsupported arguments"
219
220 slurp args =
221 case args of
222 [ MalString filename ] ->
223 Eval.io (IO.readFile filename)
224 (\msg ->
225 case msg of
226 FileRead contents ->
227 Eval.succeed <| MalString contents
228
229 Exception msg ->
230 Eval.fail msg
231
232 _ ->
233 Eval.fail "wrong IO, expected FileRead"
234 )
235
236 _ ->
237 Eval.fail "unsupported arguments"
238
239 atom args =
240 case args of
241 [ value ] ->
242 Eval.withEnv
243 (\env ->
244 case Env.newAtom value env of
245 ( newEnv, atomId ) ->
246 Eval.setEnv newEnv
247 |> Eval.map (\_ -> MalAtom atomId)
248 )
249
250 _ ->
251 Eval.fail "unsupported arguments"
252
253 isAtom args =
254 case args of
255 [ MalAtom _ ] ->
256 Eval.succeed <| MalBool True
257
258 _ ->
259 Eval.succeed <| MalBool False
260
261 deref args =
262 case args of
263 [ MalAtom atomId ] ->
264 Eval.withEnv (Env.getAtom atomId >> Eval.succeed)
265
266 _ ->
267 Eval.fail "unsupported arguments"
268
269 reset args =
270 case args of
271 [ MalAtom atomId, value ] ->
272 Eval.modifyEnv (Env.setAtom atomId value)
273 |> Eval.map (always value)
274
275 _ ->
276 Eval.fail "unsupported arguments"
277
278 {- helper function for calling a core or user function -}
279 callFn func args =
280 case func of
281 CoreFunc fn ->
282 fn args
283
284 UserFunc { eagerFn } ->
285 eagerFn args
286
287 swap args =
288 case args of
289 (MalAtom atomId) :: (MalFunction func) :: args ->
290 Eval.withEnv
291 (\env ->
292 let
293 value =
294 Env.getAtom atomId env
295 in
296 callFn func (value :: args)
297 )
298 |> Eval.andThen
299 (\res ->
300 Eval.modifyEnv (Env.setAtom atomId res)
301 |> Eval.map (always res)
302 )
303
304 _ ->
305 Eval.fail "unsupported arguments"
306
307 gc args =
308 Eval.withEnv (Env.gc MalNil >> Printer.printEnv >> writeLine)
309
310 setDebug enabled =
311 Eval.modifyEnv
312 (\env ->
313 { env | debug = enabled }
314 )
315 |> Eval.andThen (\_ -> Eval.succeed MalNil)
316
317 debug args =
318 case args of
319 [ MalBool value ] ->
320 setDebug value
321
322 _ ->
323 Eval.withEnv
324 (\env ->
325 Eval.succeed (MalBool env.debug)
326 )
327
328 typeof args =
329 case args of
330 [ MalInt _ ] ->
331 Eval.succeed <| MalSymbol "int"
332
333 [ MalBool _ ] ->
334 Eval.succeed <| MalSymbol "bool"
335
336 [ MalString _ ] ->
337 Eval.succeed <| MalSymbol "string"
338
339 [ MalKeyword _ ] ->
340 Eval.succeed <| MalSymbol "keyword"
341
342 [ MalSymbol _ ] ->
343 Eval.succeed <| MalSymbol "symbol"
344
345 [ MalNil ] ->
346 Eval.succeed <| MalSymbol "nil"
347
348 [ MalList _ ] ->
349 Eval.succeed <| MalSymbol "vector"
350
351 [ MalVector _ ] ->
352 Eval.succeed <| MalSymbol "vector"
353
354 [ MalMap _ ] ->
355 Eval.succeed <| MalSymbol "vector"
356
357 [ MalFunction _ ] ->
358 Eval.succeed <| MalSymbol "function"
359
360 [ MalAtom _ ] ->
361 Eval.succeed <| MalSymbol "atom"
362
363 _ ->
364 Eval.fail "unsupported arguments"
365
366 cons args =
367 case args of
368 [ e, MalList list ] ->
369 Eval.succeed <| MalList (e :: list)
370
371 [ e, MalVector vec ] ->
372 Eval.succeed <| MalList (e :: (Array.toList vec))
373
374 _ ->
375 Eval.fail "unsupported arguments"
376
377 concat args =
378 let
379 go arg acc =
380 case arg of
381 MalList list ->
382 Eval.succeed (acc ++ list)
383
384 MalVector vec ->
385 Eval.succeed (acc ++ Array.toList vec)
386
387 _ ->
388 Eval.fail "unsupported arguments"
389 in
390 List.foldl (go >> Eval.andThen) (Eval.succeed []) args
391 |> Eval.map MalList
392
393 nth args =
394 let
395 get list index =
396 if index < 0 then
397 Nothing
398 else if index == 0 then
399 List.head list
400 else
401 case list of
402 [] ->
403 Nothing
404
405 _ :: rest ->
406 get rest (index - 1)
407
408 make res =
409 case res of
410 Just value ->
411 Eval.succeed value
412
413 Nothing ->
414 Eval.fail "index out of bounds"
415 in
416 case args of
417 [ MalList list, MalInt index ] ->
418 make <| get list index
419
420 [ MalVector vec, MalInt index ] ->
421 make <| Array.get index vec
422
423 _ ->
424 Eval.fail "unsupported arguments"
425
426 first args =
427 let
428 make =
429 Eval.succeed << Maybe.withDefault MalNil
430 in
431 case args of
432 [ MalNil ] ->
433 Eval.succeed MalNil
434
435 [ MalList list ] ->
436 make <| List.head list
437
438 [ MalVector vec ] ->
439 make <| Array.get 0 vec
440
441 _ ->
442 Eval.fail "unsupported arguments"
443
444 rest args =
445 case args of
446 [ MalNil ] ->
447 Eval.succeed <| MalList []
448
449 [ MalList [] ] ->
450 Eval.succeed <| MalList []
451
452 [ MalList (head :: tail) ] ->
453 Eval.succeed <| MalList tail
454
455 [ MalVector vec ] ->
456 Array.toList vec
457 |> List.tail
458 |> Maybe.withDefault []
459 |> MalList
460 |> Eval.succeed
461
462 _ ->
463 Eval.fail "unsupported arguments"
464
465 throw args =
466 case args of
467 ex :: _ ->
468 Eval.throw ex
469
470 _ ->
471 Eval.fail "undefined exception"
472
473 apply args =
474 case args of
475 (MalFunction func) :: rest ->
476 case List.reverse rest of
477 (MalList last) :: middle ->
478 callFn func ((List.reverse middle) ++ last)
479
480 (MalVector last) :: middle ->
481 callFn func
482 ((List.reverse middle)
483 ++ (Array.toList last)
484 )
485
486 _ ->
487 Eval.fail "apply expected the last argument to be a list or vector"
488
489 _ ->
490 Eval.fail "unsupported arguments"
491
492 map args =
493 let
494 go func list acc =
495 case list of
496 [] ->
497 Eval.succeed <| MalList <| List.reverse acc
498
499 inv :: rest ->
500 callFn func [ inv ]
501 |> Eval.andThen
502 (\outv ->
503 go func rest (outv :: acc)
504 )
505 in
506 case args of
507 [ MalFunction func, MalList list ] ->
508 go func list []
509
510 [ MalFunction func, MalVector vec ] ->
511 go func (Array.toList vec) []
512
513 _ ->
514 Eval.fail "unsupported arguments"
515
516 isNil args =
517 Eval.succeed <|
518 MalBool <|
519 case args of
520 MalNil :: _ ->
521 True
522
523 _ ->
524 False
525
526 isTrue args =
527 Eval.succeed <|
528 MalBool <|
529 case args of
530 (MalBool True) :: _ ->
531 True
532
533 _ ->
534 False
535
536 isFalse args =
537 Eval.succeed <|
538 MalBool <|
539 case args of
540 (MalBool False) :: _ ->
541 True
542
543 _ ->
544 False
545
546 isSymbol args =
547 Eval.succeed <|
548 MalBool <|
549 case args of
550 (MalSymbol _) :: _ ->
551 True
552
553 _ ->
554 False
555
556 isKeyword args =
557 Eval.succeed <|
558 MalBool <|
559 case args of
560 (MalKeyword _) :: _ ->
561 True
562
563 _ ->
564 False
565
566 isVector args =
567 Eval.succeed <|
568 MalBool <|
569 case args of
570 (MalVector _) :: _ ->
571 True
572
573 _ ->
574 False
575
576 isMap args =
577 Eval.succeed <|
578 MalBool <|
579 case args of
580 (MalMap _) :: _ ->
581 True
582
583 _ ->
584 False
585
586 isString args =
587 Eval.succeed <|
588 MalBool <|
589 case args of
590 (MalString _) :: _ ->
591 True
592
593 _ ->
594 False
595
596 isSequential args =
597 Eval.succeed <|
598 MalBool <|
599 case args of
600 (MalList _) :: _ ->
601 True
602
603 (MalVector _) :: _ ->
604 True
605
606 _ ->
607 False
608
609 symbol args =
610 case args of
611 [ MalString str ] ->
612 Eval.succeed <| MalSymbol str
613
614 _ ->
615 Eval.fail "unsupported arguments"
616
617 keyword args =
618 case args of
619 [ MalString str ] ->
620 Eval.succeed <| MalKeyword (String.cons ':' str)
621
622 _ ->
623 Eval.fail "unsupported arguments"
624
625 vector args =
626 Eval.succeed <| MalVector <| Array.fromList args
627
628 parseKey key =
629 case key of
630 MalString str ->
631 Ok str
632
633 MalKeyword keyword ->
634 Ok <| String.cons keywordPrefix keyword
635
636 _ ->
637 Err "map key must be a symbol or keyword"
638
639 buildMap list acc =
640 case list of
641 [] ->
642 Eval.succeed <| MalMap acc
643
644 key :: value :: rest ->
645 parseKey key
646 |> Eval.fromResult
647 |> Eval.andThen
648 (\key ->
649 buildMap rest (Dict.insert key value acc)
650 )
651
652 _ ->
653 Eval.fail "expected an even number of key-value pairs"
654
655 hashMap args =
656 buildMap args Dict.empty
657
658 assoc args =
659 case args of
660 (MalMap dict) :: rest ->
661 buildMap rest dict
662
663 _ ->
664 Eval.fail "unsupported arguments"
665
666 dissoc args =
667 let
668 go keys acc =
669 case keys of
670 [] ->
671 Eval.succeed <| MalMap acc
672
673 key :: rest ->
674 parseKey key
675 |> Eval.fromResult
676 |> Eval.andThen
677 (\key ->
678 go rest (Dict.remove key acc)
679 )
680 in
681 case args of
682 (MalMap dict) :: keys ->
683 go keys dict
684
685 _ ->
686 Eval.fail "unsupported arguments"
687
688 get args =
689 case args of
690 [ MalNil, key ] ->
691 Eval.succeed MalNil
692
693 [ MalMap dict, key ] ->
694 parseKey key
695 |> Eval.fromResult
696 |> Eval.map
697 (\key ->
698 Dict.get key dict
699 |> Maybe.withDefault MalNil
700 )
701
702 _ ->
703 Eval.fail "unsupported arguments"
704
705 contains args =
706 case args of
707 [ MalMap dict, key ] ->
708 parseKey key
709 |> Eval.fromResult
710 |> Eval.map (\key -> Dict.member key dict)
711 |> Eval.map MalBool
712
713 _ ->
714 Eval.fail "unsupported arguments"
715
716 unparseKey key =
717 case String.uncons key of
718 Just ( prefix, rest ) ->
719 if prefix == keywordPrefix then
720 MalKeyword rest
721 else
722 MalString key
723
724 _ ->
725 MalString key
726
727 keys args =
728 case args of
729 [ MalMap dict ] ->
730 Dict.keys dict
731 |> List.map unparseKey
732 |> MalList
733 |> Eval.succeed
734
735 _ ->
736 Eval.fail "unsupported arguments"
737
738 vals args =
739 case args of
740 [ MalMap dict ] ->
741 Dict.values dict
742 |> MalList
743 |> Eval.succeed
744
745 _ ->
746 Eval.fail "unsupported arguments"
747
748 readLine args =
749 case args of
750 [ MalString prompt ] ->
751 Eval.io (IO.readLine prompt)
752 (\msg ->
753 case msg of
754 LineRead (Just line) ->
755 Eval.succeed (MalString line)
756
757 LineRead Nothing ->
758 Eval.succeed MalNil
759
760 _ ->
761 Eval.fail "wrong IO, expected LineRead"
762 )
763
764 _ ->
765 Eval.fail "unsupported arguments"
766
767 withMeta args =
768 case args of
769 [ MalFunction (UserFunc func), meta ] ->
770 Eval.succeed <| MalFunction <| UserFunc { func | meta = Just meta }
771
772 _ ->
773 Eval.fail "with-meta expected a user function and a map"
774
775 meta args =
776 case args of
777 [ MalFunction (UserFunc { meta }) ] ->
778 Eval.succeed (Maybe.withDefault MalNil meta)
779
780 _ ->
781 Eval.succeed MalNil
782
783 conj args =
784 case args of
785 (MalList list) :: rest ->
786 Eval.succeed <|
787 MalList <|
788 (List.reverse rest)
789 ++ list
790
791 (MalVector vec) :: rest ->
792 Eval.succeed <|
793 MalVector <|
794 Array.append
795 vec
796 (Array.fromList rest)
797
798 _ ->
799 Eval.fail "unsupported arguments"
800
801 seq args =
802 case args of
803 [ MalNil ] ->
804 Eval.succeed MalNil
805
806 [ MalList [] ] ->
807 Eval.succeed MalNil
808
809 [ MalString "" ] ->
810 Eval.succeed MalNil
811
812 [ (MalList _) as list ] ->
813 Eval.succeed list
814
815 [ MalVector vec ] ->
816 Eval.succeed <|
817 if Array.isEmpty vec then
818 MalNil
819 else
820 MalList <| Array.toList vec
821
822 [ MalString str ] ->
823 Eval.succeed <|
824 MalList <|
825 (String.toList str
826 |> List.map String.fromChar
827 |> List.map MalString
828 )
829
830 _ ->
831 Eval.fail "unsupported arguments"
832
833 requestTime =
834 Task.perform (GotTime >> Ok >> Input) Time.now
835
836 timeMs args =
837 case args of
838 [] ->
839 Eval.io requestTime
840 (\msg ->
841 case msg of
842 GotTime time ->
843 Time.inMilliseconds time
844 |> floor
845 |> MalInt
846 |> Eval.succeed
847
848 _ ->
849 Eval.fail "wrong IO, expected GotTime"
850 )
851
852 _ ->
853 Eval.fail "time-ms takes no arguments"
854 in
855 Env.global
856 |> Env.set "+" (makeFn <| binaryOp (+) MalInt)
857 |> Env.set "-" (makeFn <| binaryOp (-) MalInt)
858 |> Env.set "*" (makeFn <| binaryOp (*) MalInt)
859 |> Env.set "/" (makeFn <| binaryOp (//) MalInt)
860 |> Env.set "<" (makeFn <| binaryOp (<) MalBool)
861 |> Env.set ">" (makeFn <| binaryOp (>) MalBool)
862 |> Env.set "<=" (makeFn <| binaryOp (<=) MalBool)
863 |> Env.set ">=" (makeFn <| binaryOp (>=) MalBool)
864 |> Env.set "list" (makeFn list)
865 |> Env.set "list?" (makeFn isList)
866 |> Env.set "empty?" (makeFn isEmpty)
867 |> Env.set "count" (makeFn count)
868 |> Env.set "=" (makeFn equals)
869 |> Env.set "pr-str" (makeFn prStr)
870 |> Env.set "str" (makeFn str)
871 |> Env.set "prn" (makeFn prn)
872 |> Env.set "println" (makeFn println)
873 |> Env.set "pr-env" (makeFn printEnv)
874 |> Env.set "read-string" (makeFn readString)
875 |> Env.set "slurp" (makeFn slurp)
876 |> Env.set "atom" (makeFn atom)
877 |> Env.set "atom?" (makeFn isAtom)
878 |> Env.set "deref" (makeFn deref)
879 |> Env.set "reset!" (makeFn reset)
880 |> Env.set "swap!" (makeFn swap)
881 |> Env.set "gc" (makeFn gc)
882 |> Env.set "debug!" (makeFn debug)
883 |> Env.set "typeof" (makeFn typeof)
884 |> Env.set "cons" (makeFn cons)
885 |> Env.set "concat" (makeFn concat)
886 |> Env.set "nth" (makeFn nth)
887 |> Env.set "first" (makeFn first)
888 |> Env.set "rest" (makeFn rest)
889 |> Env.set "throw" (makeFn throw)
890 |> Env.set "apply" (makeFn apply)
891 |> Env.set "map" (makeFn map)
892 |> Env.set "nil?" (makeFn isNil)
893 |> Env.set "true?" (makeFn isTrue)
894 |> Env.set "false?" (makeFn isFalse)
895 |> Env.set "symbol?" (makeFn isSymbol)
896 |> Env.set "keyword?" (makeFn isKeyword)
897 |> Env.set "vector?" (makeFn isVector)
898 |> Env.set "map?" (makeFn isMap)
899 |> Env.set "string?" (makeFn isString)
900 |> Env.set "sequential?" (makeFn isSequential)
901 |> Env.set "symbol" (makeFn symbol)
902 |> Env.set "keyword" (makeFn keyword)
903 |> Env.set "vector" (makeFn vector)
904 |> Env.set "hash-map" (makeFn hashMap)
905 |> Env.set "assoc" (makeFn assoc)
906 |> Env.set "dissoc" (makeFn dissoc)
907 |> Env.set "get" (makeFn get)
908 |> Env.set "contains?" (makeFn contains)
909 |> Env.set "keys" (makeFn keys)
910 |> Env.set "vals" (makeFn vals)
911 |> Env.set "readline" (makeFn readLine)
912 |> Env.set "with-meta" (makeFn withMeta)
913 |> Env.set "meta" (makeFn meta)
914 |> Env.set "conj" (makeFn conj)
915 |> Env.set "seq" (makeFn seq)
916 |> Env.set "time-ms" (makeFn timeMs)