95c621a646818731e4715418d42ab3fb517e8c00
[scheme.forth.jl.git] / scheme.4th
1 vocabulary scheme
2 scheme definitions
3
4 include term-colours.4th
5 include defer-is.4th
6 include catch-throw.4th
7 include float.4th
8
9 include debugging.4th
10
11 defer read
12 defer eval
13 defer print
14
15 defer collect-garbage
16
17 \ ---- Types ---- {{{
18
19 variable nexttype
20 0 nexttype !
21 : make-type
22     create nexttype @ ,
23     1 nexttype +!
24     does> @ ;
25
26 make-type fixnum-type
27 make-type realnum-type
28 make-type boolean-type
29 make-type character-type
30 make-type string-type
31 make-type nil-type
32 make-type none-type
33 make-type pair-type
34 make-type symbol-type
35 make-type primitive-proc-type
36 make-type compound-proc-type
37 make-type fileport-type
38 : istype? ( obj type -- obj bool )
39     over = ;
40
41 \ }}}
42
43 \ ---- Exceptions ---- {{{
44
45 variable nextexception
46 1 nextexception !
47 : make-exception 
48     create nextexception @ ,
49     1 nextexception +!
50     does> @ ;
51
52 make-exception recoverable-exception
53 make-exception unrecoverable-exception
54
55 : display-exception-msg ( addr count -- )
56     bold fg red
57     ." Exception: "
58     type
59     reset-term ;
60
61 : throw" immediate 
62     [compile] s"
63
64     ['] rot , ['] dup ,
65
66     [compile] if
67         ['] -rot ,
68         ['] display-exception-msg ,
69     [compile] then
70
71     ['] throw ,
72 ;
73
74 \ }}}
75
76 \ ---- List-structured memory ---- {{{
77
78 10000 constant scheme-memsize
79
80 create car-cells scheme-memsize allot
81 create car-type-cells scheme-memsize allot
82 create cdr-cells scheme-memsize allot
83 create cdr-type-cells scheme-memsize allot
84
85 create nextfrees scheme-memsize allot
86 :noname
87     scheme-memsize 0 do
88         i 1+ nextfrees i + !
89     loop
90 ; execute
91         
92 variable nextfree
93 0 nextfree !
94
95 : inc-nextfree
96     nextfrees nextfree @ + @
97     nextfree !
98
99     nextfree @ scheme-memsize >= if
100         collect-garbage
101     then
102
103     nextfree @ scheme-memsize >= if
104         unrecoverable-exception throw s" Out of memory!"
105     then
106 ;
107
108 : cons ( car-obj cdr-obj -- pair-obj )
109     cdr-type-cells nextfree @ + !
110     cdr-cells nextfree @ + !
111     car-type-cells nextfree @ + !
112     car-cells nextfree @ + !
113
114     nextfree @ pair-type
115     inc-nextfree
116 ;
117
118 : car ( pair-obj -- car-obj )
119     drop
120     dup car-cells + @ swap
121     car-type-cells + @
122 ;
123
124 : cdr ( pair-obj -- car-obj )
125     drop
126     dup cdr-cells + @ swap
127     cdr-type-cells + @
128 ;
129
130 : set-car! ( obj pair-obj -- )
131     drop dup
132     rot swap  car-type-cells + !
133     car-cells + !
134 ;
135
136 : set-cdr! ( obj pair-obj -- )
137     drop dup
138     rot swap  cdr-type-cells + !
139     cdr-cells + !
140 ;
141
142 : caar car car ;
143 : cadr cdr car ;
144 : cdar car cdr ;
145 : cddr cdr cdr ;
146
147 : nil 0 nil-type ;
148 : nil? nil-type istype? ;
149
150 : none 0 none-type ;
151 : none? none-type istype? ;
152
153 : objvar create nil swap , , ;
154
155 : value@ ( objvar -- val ) @ ;
156 : type@ ( objvar -- type ) 1+ @ ;
157 : value! ( newval objvar -- ) ! ;
158 : type! ( newtype objvar -- ) 1+ ! ;
159 : obj! ( newobj objvar -- ) dup rot swap 1+ ! ! ; 
160 : obj@ ( objvar -- obj ) dup @ swap 1+ @ ; 
161
162 : objeq? ( obj obj -- bool )
163     rot = -rot = and ;
164
165 : 2rot ( a1 a2 b1 b2 c1 c2 -- b1 b2 c1 c2 a1 a2 )
166     >R >R ( a1 a2 b1 b2 )
167     2swap ( b1 b2 a1 a2 )
168     R> R> ( b1 b2 a1 a2 c1 c2 )
169     2swap
170 ;
171
172 : -2rot ( a1 a2 b1 b2 c1 c2 -- c1 c2 a1 a2 b1 b2 )
173     2swap ( a1 a2 c1 c2 b1 b2 )
174     >R >R ( a1 a2 c1 c2 )
175     2swap ( c1 c2 a1 a2 )
176     R> R>
177 ;
178
179 \ }}}
180
181 \ ---- Port I/O ----  {{{
182
183 : fileport>fid ( fileport -- fid )
184     drop ;
185
186 : fid>fileport ( fid -- fileport )
187     fileport-type ;
188
189 : open-input-file ( addr n -- fileport )
190     r/o open-file drop fid>fileport
191 ;
192
193 : close-port ( fileport -- )
194     fileport>fid close-file drop
195 ;
196
197 objvar console-i/o-port
198 0 fileport-type console-i/o-port obj!
199
200 objvar current-input-port
201 console-i/o-port obj@ current-input-port obj!
202
203 : read-port ( fileport -- obj )
204     current-input-port obj!
205     read ;
206
207 : read-console ( -- obj )
208     console-i/o-port obj@ read-port ;
209
210 \ }}}
211
212 \ ---- Pre-defined symbols ---- {{{
213
214 objvar symbol-table
215
216 : duplicate-charlist ( charlist -- copy )
217     nil? false = if
218         2dup car 2swap cdr recurse cons
219     then ;
220
221 : charlist-equiv ( charlist charlist -- bool )
222
223     2over 2over
224
225     \ One or both nil
226     nil? -rot 2drop
227     if
228         nil? -rot 2drop
229         if
230             2drop 2drop true exit
231         else
232             2drop 2drop false exit
233         then
234     else
235         nil? -rot 2drop
236         if
237             2drop 2drop false exit
238         then
239     then
240
241     2over 2over
242
243     \ Neither nil
244     car drop -rot car drop = if
245             cdr 2swap cdr recurse
246         else
247             2drop 2drop false
248     then
249 ;
250
251 : charlist>symbol ( charlist -- symbol-obj )
252
253     symbol-table obj@
254
255     begin
256         nil? false =
257     while
258         2over 2over
259         car drop pair-type
260         charlist-equiv if
261             2swap 2drop
262             car
263             exit
264         else
265             cdr
266         then
267     repeat
268
269     2drop
270     drop symbol-type 2dup
271     symbol-table obj@ cons
272     symbol-table obj!
273 ;
274
275
276 : cstr>charlist ( addr n -- charlist )
277     dup 0= if
278         2drop nil
279     else
280         2dup drop @ character-type 2swap
281         swap 1+ swap 1-
282         recurse
283
284         cons
285     then
286 ;
287
288 : create-symbol ( -- )
289     bl word
290     count
291
292     cstr>charlist
293     charlist>symbol
294
295     create swap , ,
296     does> dup @ swap 1+ @
297 ;
298
299 create-symbol quote             quote-symbol
300 create-symbol quasiquote        quasiquote-symbol
301 create-symbol unquote           unquote-symbol
302 create-symbol unquote-splicing  unquote-splicing-symbol
303 create-symbol define            define-symbol
304 create-symbol define-macro      define-macro-symbol
305 create-symbol set!              set!-symbol
306 create-symbol ok                ok-symbol
307 create-symbol if                if-symbol
308 create-symbol lambda            lambda-symbol
309 create-symbol Î»                 Î»-symbol
310 create-symbol begin             begin-symbol
311
312 \ }}}
313
314 \ ---- Environments ---- {{{
315
316 : enclosing-env ( env -- env )
317     cdr ;
318
319 : first-frame ( env -- frame )
320     car ;
321
322 : make-frame ( vars vals -- frame )
323     cons ;
324
325 : frame-vars ( frame -- vars )
326     car ;
327
328 : frame-vals ( frame -- vals )
329     cdr ;
330
331 : add-binding ( var val frame -- )
332     2swap 2over frame-vals cons
333     2over set-cdr!
334     2swap 2over frame-vars cons
335     2swap set-car!
336 ;
337
338 : extend-env ( vars vals env -- env )
339     >R >R
340     make-frame
341     R> R>
342     cons
343 ;
344
345 objvar vars
346 objvar vals
347
348 : get-vars-vals-frame ( var frame -- bool )
349     2dup frame-vars vars obj!
350     frame-vals vals obj!
351
352     begin
353         vars obj@ nil objeq? false =
354     while
355         2dup vars obj@ car objeq? if
356             2drop true
357             exit
358         then
359
360         vars obj@ cdr vars obj!
361         vals obj@ cdr vals obj!
362     repeat
363
364     2drop false
365 ;
366
367 : get-vars-vals ( var env -- vars? vals? bool )
368
369     begin
370         nil? false =
371     while
372         2over 2over first-frame
373         get-vars-vals-frame if
374             2drop 2drop
375             vars obj@ vals obj@ true
376             exit
377         then
378
379         enclosing-env
380     repeat
381
382     2drop 2drop
383     false
384 ;
385
386 hide vars
387 hide vals
388
389 : lookup-var ( var env -- val )
390     get-vars-vals if
391         2swap 2drop car
392     else
393         recoverable-exception throw" Tried to read unbound variable."
394     then
395 ;
396
397 : set-var ( var val env -- )
398     >R >R 2swap R> R> ( val var env )
399     get-vars-vals if
400         2swap 2drop ( val vals )
401         set-car!
402     else
403         recoverable-exception throw" Tried to set unbound variable."
404     then
405 ;
406
407 objvar env
408
409 : define-var ( var val env -- )
410     env obj! 
411
412     2over env obj@ ( var val var env )
413     get-vars-vals if
414         2swap 2drop ( var val vals )
415         set-car!
416         2drop
417     else
418         env obj@
419         first-frame ( var val frame )
420         add-binding
421     then
422 ;
423
424 hide env
425
426 : make-procedure ( params body env -- proc )
427     nil
428     cons cons cons
429     drop compound-proc-type
430 ;
431
432 objvar global-env
433 nil nil nil extend-env
434 global-env obj!
435
436 \ }}}
437
438 \ ---- Primitives ---- {{{
439
440 : make-primitive ( cfa -- )
441     bl word
442     count
443
444     \ 2dup ." Defining primitive " type ." ..." cr
445
446     cstr>charlist
447     charlist>symbol
448   
449     rot primitive-proc-type ( var prim )
450     global-env obj@ define-var
451 ;
452
453 : ensure-arg-count ( args n -- )
454     dup 0= if
455         drop nil objeq? false = if
456             recoverable-exception throw" Too many arguments for primitive procedure."
457         then
458     else
459         -rot nil? if
460             recoverable-exception throw" Too few arguments for primitive procedure."
461         then
462         
463         cdr rot 1- recurse
464     then
465 ;
466
467 : arg-type-error
468             bold fg red ." Incorrect argument type." reset-term cr
469             abort
470 ;
471
472 : ensure-arg-type ( arg type -- arg )
473     istype? false = if
474         recoverable-exception throw" Incorrect argument type for primitive procedure."
475     then
476 ;
477
478
479 \ }}}
480
481 \ ---- Macros ---- {{{
482
483 objvar macro-table
484
485 ( Look up macro in macro table. Returns nil if
486   no macro is found. )
487 : lookup-macro ( name_symbol -- proc )
488     macro-table obj@
489
490     begin
491         nil? false =
492     while
493         2over 2over
494         car car objeq? if
495             2swap 2drop
496             car cdr
497             exit
498         then
499
500         cdr
501     repeat
502
503     2swap 2drop
504 ;
505
506 : make-macro ( name_symbol params body env -- )
507     make-procedure
508
509     2swap ( proc name_symbol )
510
511     macro-table obj@
512
513     begin
514         nil? false =
515     while
516         2over 2over ( proc name table name table )
517         car car objeq? if
518             2swap 2drop ( proc table )
519             car ( proc entry )
520             set-cdr!
521             exit
522         then
523
524         cdr
525     repeat
526
527     2drop
528
529     2swap cons
530     macro-table obj@ cons
531     macro-table obj!
532 ;
533
534 \ }}}
535
536 \ ---- Read ---- {{{
537
538 variable parse-idx
539 variable stored-parse-idx
540 create parse-str 161 allot
541 variable parse-str-span
542
543 create parse-idx-stack 10 allot 
544 variable parse-idx-sp
545 parse-idx-stack parse-idx-sp !
546
547 : push-parse-idx
548     parse-idx @ parse-idx-sp @ !
549     1 parse-idx-sp +!
550 ;
551
552 : pop-parse-idx
553     parse-idx-sp @ parse-idx-stack <= abort" Parse index stack underflow."
554
555     1 parse-idx-sp -!
556
557     parse-idx-sp @ @ parse-idx ! ;
558
559
560 : append-newline
561     '\n' parse-str parse-str-span @ + !
562     1 parse-str-span +! ;
563
564 : append-eof
565     4 parse-str parse-str-span @ + !
566     1 parse-str-span +!  ;
567
568 : empty-parse-str
569     0 parse-str-span !
570     0 parse-idx ! ;
571
572 : getline
573     current-input-port obj@ console-i/o-port obj@ objeq? if
574         parse-str 160 expect cr
575         span @ parse-str-span !
576     else
577         parse-str 160 current-input-port obj@ fileport>fid read-line
578         drop swap parse-str-span !
579
580         parse-str-span @ 0= and if append-eof then
581     then
582     append-newline
583     0 parse-idx ! ;
584
585 : inc-parse-idx
586     1 parse-idx +! ;
587
588 : dec-parse-idx
589     1 parse-idx -! ;
590
591 : charavailable? ( -- bool )
592     parse-str-span @ parse-idx @ > ;
593
594 : nextchar ( -- char )
595     charavailable? false = if getline then
596     parse-str parse-idx @ + @ ;
597
598 : '\t' 9 ;
599 : whitespace? ( -- bool )
600     nextchar BL = 
601     nextchar '\n' =
602     nextchar '\t' =
603     or or ;
604
605 : EOF 4 ; 
606 : eof? ( -- bool )
607     nextchar EOF = ;
608
609 : delim? ( -- bool )
610     whitespace?
611     nextchar [char] ( = or
612     nextchar [char] ) = or
613 ;
614
615 : commentstart? ( -- bool )
616     nextchar [char] ; = ;
617
618 : eatspaces
619
620     false \ Indicates whether or not we're eating a comment
621
622     begin
623         dup whitespace? or commentstart? or
624     while
625         dup nextchar '\n' = and if
626             invert \ Stop eating comment
627         else
628             dup false = commentstart? and if   
629                 invert \ Begin eating comment
630             then
631         then
632
633         inc-parse-idx
634     repeat
635     drop
636 ;
637
638 : digit? ( -- bool )
639     nextchar [char] 0 >=
640     nextchar [char] 9 <=
641     and ;
642
643 : minus? ( -- bool )
644     nextchar [char] - = ;
645
646 : plus? ( -- bool )
647     nextchar [char] + = ;
648
649 : fixnum? ( -- bool )
650     minus? plus? or if
651         inc-parse-idx
652
653         delim? if
654             dec-parse-idx
655             false exit
656         else
657             dec-parse-idx
658         then
659     else
660         digit? false = if
661             false exit
662         then
663     then
664
665     push-parse-idx
666     inc-parse-idx
667
668     begin digit? while
669         inc-parse-idx
670     repeat
671
672     delim? pop-parse-idx
673 ;
674
675 : realnum? ( -- bool )
676     push-parse-idx
677
678     minus? plus? or if
679         inc-parse-idx
680     then
681
682     \ Record starting parse idx:
683     \ Want to detect whether any characters (following +/-) were eaten.
684     parse-idx @
685
686     begin digit? while
687             inc-parse-idx
688     repeat
689
690     [char] . nextchar = if
691         inc-parse-idx
692         begin digit? while
693                 inc-parse-idx
694         repeat
695     then
696
697     [char] e nextchar = [char] E nextchar = or if
698         inc-parse-idx
699
700         minus? plus? or if
701             inc-parse-idx
702         then
703
704         digit? invert if
705             drop pop-parse-idx false exit
706         then
707
708         begin digit? while
709                 inc-parse-idx
710         repeat
711     then
712
713     \ This is a real number if characters were
714     \ eaten and the next characer is a delimiter.
715     parse-idx @ < delim? and
716
717     pop-parse-idx
718 ;
719
720 : boolean? ( -- bool )
721     nextchar [char] # <> if false exit then
722
723     push-parse-idx
724     inc-parse-idx
725
726     nextchar [char] t <>
727     nextchar [char] f <>
728     and if pop-parse-idx false exit then
729
730     inc-parse-idx
731     delim? if
732         pop-parse-idx
733         true
734     else
735         pop-parse-idx
736         false
737     then
738 ;
739
740 : str-equiv? ( str -- bool )
741
742     push-parse-idx
743
744     true -rot
745
746     swap dup rot + swap
747
748     do
749         i @ nextchar <> if
750             drop false
751             leave
752         then
753
754         inc-parse-idx
755     loop
756
757     delim? false = if drop false then
758
759     pop-parse-idx
760 ;
761
762 : character? ( -- bool )
763     nextchar [char] # <> if false exit then
764
765     push-parse-idx
766     inc-parse-idx
767
768     nextchar [char] \ <> if pop-parse-idx false exit then
769
770     inc-parse-idx
771
772     S" newline" str-equiv? if pop-parse-idx true exit then
773     S" space" str-equiv? if pop-parse-idx true exit then
774     S" tab" str-equiv? if pop-parse-idx true exit then
775
776     charavailable? false = if pop-parse-idx false exit then
777
778     pop-parse-idx true
779 ;
780
781 : pair? ( -- bool )
782     nextchar [char] ( = ;
783
784 : string? ( -- bool )
785     nextchar [char] " = ;
786
787 : readfixnum ( -- num-atom )
788     plus? minus? or if
789         minus?
790         inc-parse-idx
791     else
792         false
793     then
794
795     0
796
797     begin digit? while
798         10 * nextchar [char] 0 - +
799         inc-parse-idx
800     repeat
801
802     swap if negate then
803
804     fixnum-type
805 ;
806
807 : readrealnum ( -- realnum )
808
809     \ Remember that at this point we're guaranteed to
810     \ have a parsable real on this line.
811
812     parse-str parse-idx @ +
813
814     begin delim? false = while
815             inc-parse-idx
816     repeat
817
818     parse-str parse-idx @ + over -
819
820     float-parse
821
822     realnum-type
823 ;
824
825 : readbool ( -- bool-obj )
826     inc-parse-idx
827     
828     nextchar [char] f = if
829         false
830     else
831         true
832     then
833
834     inc-parse-idx
835
836     boolean-type
837 ;
838
839 : readchar ( -- char-obj )
840     inc-parse-idx
841     inc-parse-idx
842
843     S" newline" str-equiv? if 7 parse-idx +! '\n' character-type exit then
844     S" space" str-equiv? if 5 parse-idx +! bl character-type exit then
845     S" tab" str-equiv? if 3 parse-idx +! 9 character-type exit then
846
847     nextchar character-type
848
849     inc-parse-idx
850 ;
851
852 : readstring ( -- charlist )
853     nextchar [char] " = if
854         inc-parse-idx
855
856         delim? false = if
857             bold fg red
858             ." No delimiter following right double quote. Aborting." cr
859             reset-term abort
860         then
861
862         dec-parse-idx
863
864         0 nil-type exit
865     then
866
867     nextchar [char] \ = if
868         inc-parse-idx
869         nextchar case
870             [char] n of '\n' endof
871             [char] " of [char] " endof
872             [char] \
873         endcase
874     else
875         nextchar
876     then
877     inc-parse-idx character-type
878
879     recurse
880
881     cons
882 ;
883
884 : readsymbol ( -- charlist )
885     delim? if nil exit then
886
887     nextchar inc-parse-idx character-type
888
889     recurse
890
891     cons
892 ;
893
894 : readpair ( -- pairobj )
895     eatspaces
896
897     \ Empty lists
898     nextchar [char] ) = if
899         inc-parse-idx
900
901         delim? false = if
902             bold fg red
903             ." No delimiter following right paren. Aborting." cr
904             reset-term abort
905         then
906
907         dec-parse-idx
908
909         0 nil-type exit
910     then
911
912     \ Read first pair element
913     read
914
915     \ Pairs
916     eatspaces
917     nextchar [char] . = if
918         inc-parse-idx
919
920         delim? false = if
921             bold fg red
922             ." No delimiter following '.'. Aborting." cr
923             reset-term abort
924         then
925
926         eatspaces read
927     else
928         recurse
929     then
930
931     eatspaces
932
933     cons
934 ;
935
936 \ Parse a scheme expression
937 :noname ( -- obj )
938
939     eatspaces
940
941     fixnum? if
942         readfixnum
943         exit
944     then
945
946     realnum? if
947         readrealnum
948         exit
949     then
950
951     boolean? if
952         readbool
953         exit
954     then
955
956     character? if
957         readchar
958         exit
959     then
960
961     string? if
962         inc-parse-idx
963
964         readstring
965         drop string-type
966
967         nextchar [char] " <> if
968             bold red ." Missing closing double-quote." reset-term cr
969             abort
970         then
971
972         inc-parse-idx
973
974         exit
975     then
976
977     pair? if
978         inc-parse-idx
979
980         eatspaces
981
982         readpair
983
984         eatspaces
985
986         nextchar [char] ) <> if
987             bold red ." Missing closing paren." reset-term cr
988             abort
989         then
990
991         inc-parse-idx
992
993         exit
994     then
995
996     nextchar [char] ' = if
997         inc-parse-idx
998         quote-symbol recurse nil cons cons exit
999     then
1000
1001     nextchar [char] ` = if
1002         inc-parse-idx
1003         quasiquote-symbol recurse nil cons cons exit
1004     then
1005
1006     nextchar [char] , = if
1007         inc-parse-idx
1008         nextchar [char] @ = if
1009             inc-parse-idx
1010             unquote-splicing-symbol recurse nil cons cons exit
1011         else
1012             unquote-symbol recurse nil cons cons exit
1013         then
1014     then
1015
1016     eof? if
1017         EOF character-type
1018         inc-parse-idx
1019         exit
1020     then
1021
1022     \ Anything else is parsed as a symbol
1023     readsymbol charlist>symbol
1024
1025     \ Replace Î» with lambda
1026     2dup Î»-symbol objeq? if
1027         2drop lambda-symbol
1028     then
1029     
1030
1031 ; is read
1032
1033 \ }}}
1034
1035 \ ---- Eval ---- {{{
1036
1037 : self-evaluating? ( obj -- obj bool )
1038     boolean-type istype? if true exit then
1039     fixnum-type istype? if true exit then
1040     realnum-type istype? if true exit then
1041     character-type istype? if true exit then
1042     string-type istype? if true exit then
1043     nil-type istype? if true exit then
1044     none-type istype? if true exit then
1045
1046     false
1047 ;
1048
1049 : tagged-list? ( obj tag-obj -- obj bool )
1050     2over 
1051     pair-type istype? false = if
1052         2drop 2drop false
1053     else
1054         car objeq?
1055     then ;
1056
1057 : quote? ( obj -- obj bool )
1058     quote-symbol tagged-list?  ;
1059
1060 : quote-body ( quote-obj -- quote-body-obj )
1061     cadr ;
1062
1063 : quasiquote? ( obj -- obj bool )
1064     quasiquote-symbol tagged-list? ;
1065
1066 : unquote? ( obj -- obj bool )
1067     unquote-symbol tagged-list? ;
1068
1069 : unquote-splicing? ( obj -- obj bool )
1070     unquote-splicing-symbol tagged-list? ;
1071
1072 : eval-unquote ( env obj -- res )
1073     cdr ( env args )
1074
1075     nil? if
1076         recoverable-exception throw" no arguments to unquote."
1077     then
1078
1079     2dup cdr
1080     nil? false = if
1081         recoverable-exception throw" too many arguments to unquote."
1082     then
1083
1084     2drop car 2swap eval
1085 ;
1086
1087 ( Create a new list from elements of l1 consed on to l2 )
1088 : join-lists ( l2 l1 -- l3 )
1089     nil? if 2drop exit then
1090
1091     2dup car
1092     -2rot cdr
1093     recurse cons
1094 ;
1095
1096 defer eval-quasiquote-item
1097 : eval-quasiquote-pair ( env obj -- res )
1098     2over 2over ( env obj env obj )
1099
1100     cdr eval-quasiquote-item
1101
1102     -2rot car ( cdritem env objcar )
1103
1104     unquote-splicing? if
1105         eval-unquote ( cdritems caritem )
1106
1107         2swap nil? if
1108             2drop
1109         else
1110             2swap join-lists
1111         then
1112     else
1113         eval-quasiquote-item ( cdritems caritem )
1114         2swap cons
1115     then
1116
1117 ;
1118
1119 :noname ( env obj )
1120     nil? if
1121         2swap 2drop exit
1122     then
1123
1124     unquote? if
1125         eval-unquote exit
1126     then
1127
1128     pair-type istype? if
1129         eval-quasiquote-pair exit
1130     then
1131
1132     2swap 2drop
1133 ; is eval-quasiquote-item
1134
1135 : eval-quasiquote ( obj env -- res )
1136     2swap cdr ( env args )
1137
1138     nil? if
1139         recoverable-exception throw" no arguments to quasiquote."
1140     then
1141
1142     2dup cdr ( env args args-cdr )
1143     nil? false = if
1144         recoverable-exception throw" too many arguments to quasiquote."
1145     then
1146
1147     2drop car ( env arg )
1148
1149     eval-quasiquote-item
1150 ;
1151
1152 : variable? ( obj -- obj bool )
1153     symbol-type istype? ;
1154
1155 : definition? ( obj -- obj bool )
1156     define-symbol tagged-list? ;
1157
1158 : make-lambda ( params body -- lambda-exp )
1159     lambda-symbol -2rot cons cons ;
1160
1161 ( Handles iterative expansion of defines in
1162   terms of nested lambdas. Most Schemes only
1163   handle one iteration of expansion! )
1164 : definition-var-val ( obj -- var val )
1165
1166     cdr 2dup cdr 2swap car ( val var )
1167
1168     begin
1169         symbol-type istype? false =
1170     while
1171         2dup cdr 2swap car ( val formals var' )
1172         -2rot 2swap ( var' formals val )
1173         make-lambda nil cons ( var' val' )
1174         2swap ( val' var' )
1175     repeat
1176
1177     2swap car
1178 ;
1179
1180 : eval-definition ( obj env -- res )
1181     2dup 2rot ( env env obj )
1182     definition-var-val ( env env var val )
1183     2rot eval  ( env var val )
1184
1185     2rot ( var val env )
1186     define-var
1187
1188     ok-symbol
1189 ;
1190
1191 : assignment? ( obj -- obj bool )
1192     set!-symbol tagged-list? ;
1193
1194 : assignment-var ( obj -- var )
1195     cdr car ;
1196     
1197 : assignment-val ( obj -- val )
1198     cdr cdr car ;
1199
1200 : eval-assignment ( obj env -- res )
1201     2swap 
1202     2over 2over ( env obj env obj )
1203     assignment-val 2swap ( env obj valexp env )
1204     eval  ( env obj val )
1205     
1206     2swap assignment-var 2swap ( env var val )
1207
1208     2rot ( var val env )
1209     set-var
1210
1211     ok-symbol
1212 ;
1213
1214 : macro-definition? ( obj -- obj bool )
1215     define-macro-symbol tagged-list? ;
1216
1217 : macro-definition-name ( exp -- mname )
1218     cdr car car ;
1219
1220 : macro-definition-params ( exp -- params )
1221     cdr car cdr ;
1222
1223 : macro-definition-body ( exp -- body )
1224     cdr cdr ;
1225
1226 objvar env
1227 : eval-define-macro ( obj env -- res )
1228     env obj!
1229
1230     2dup macro-definition-name 2swap ( name obj )
1231     2dup macro-definition-params 2swap ( name params obj )
1232     macro-definition-body ( name params body )
1233
1234     env obj@ ( name params body env )
1235
1236     make-macro
1237
1238     ok-symbol
1239 ;
1240 hide env
1241
1242 : if? ( obj -- obj bool )
1243     if-symbol tagged-list? ;
1244
1245 : if-predicate ( ifobj -- pred )
1246     cdr car ;
1247
1248 : if-consequent ( ifobj -- conseq )
1249     cdr cdr car ;
1250
1251 : if-alternative ( ifobj -- alt|none )
1252     cdr cdr cdr
1253     nil? if
1254         2drop none
1255     else
1256         car
1257     then ;
1258
1259 : false? ( boolobj -- boolean )
1260     boolean-type istype? if
1261         false boolean-type objeq?
1262     else
1263         2drop false
1264     then
1265 ;
1266
1267 : true? ( boolobj -- bool )
1268     false? invert ;
1269
1270 : lambda? ( obj -- obj bool )
1271     lambda-symbol tagged-list? ;
1272
1273 : lambda-parameters ( obj -- params )
1274     cdr car ;
1275
1276 : lambda-body ( obj -- body )
1277     cdr cdr ;
1278
1279 : begin? ( obj -- obj bool )
1280     begin-symbol tagged-list? ;
1281
1282 : begin-actions ( obj -- actions )
1283     cdr ;
1284
1285 : eval-sequence ( explist env -- finalexp env )
1286     ( Evaluates all bar the final expressions in
1287       an an expression list. The final expression
1288       is returned to allow for tail optimization. )
1289
1290     2swap ( env explist )
1291
1292     \ Abort on empty list
1293     nil? if
1294         2drop none
1295         2swap exit
1296     then
1297
1298     begin
1299         2dup cdr ( env explist nextexplist )
1300         nil? false =
1301     while
1302         -2rot car 2over ( nextexplist env exp env )
1303         eval
1304         2drop \ discard result
1305         2swap ( env nextexplist )
1306     repeat
1307
1308     2drop car 2swap ( finalexp env )
1309 ;
1310
1311 : application? ( obj -- obj bool )
1312     pair-type istype? ;
1313
1314 : operator ( obj -- operator )
1315     car ;
1316
1317 : operands ( obj -- operands )
1318     cdr ;
1319
1320 : nooperands? ( operands -- bool )
1321     nil objeq? ;
1322
1323 : first-operand ( operands -- operand )
1324     car ;
1325
1326 : rest-operands ( operands -- other-operands )
1327     cdr ;
1328
1329 : list-of-vals ( args env -- vals )
1330     2swap
1331
1332     2dup nooperands? if
1333         2swap 2drop
1334     else
1335         2over 2over first-operand 2swap eval
1336         -2rot rest-operands 2swap recurse
1337         cons
1338     then
1339 ;
1340
1341 : procedure-params ( proc -- params )
1342     drop pair-type car ;
1343
1344 : procedure-body ( proc -- body )
1345     drop pair-type cdr car ;
1346
1347 : procedure-env ( proc -- body )
1348     drop pair-type cdr cdr car ;
1349
1350 ( Ensure terminating symbol arg name is handled
1351   specially to allow for variadic procedures. )
1352 : flatten-proc-args ( argvals argnames -- argvals' argnames' )
1353     nil? if
1354         2over nil? false = if
1355             recoverable-exception throw" Too many arguments for compound procedure."
1356         else
1357             2drop
1358         then
1359         exit
1360     then
1361
1362     symbol-type istype? if
1363         nil cons
1364         2swap
1365         nil cons
1366         2swap
1367         exit
1368     then
1369
1370     2over
1371     nil? if
1372         recoverable-exception throw" Too few arguments for compound procedure."
1373     else
1374         cdr
1375     then
1376
1377     2over cdr
1378
1379     recurse ( argvals argnames argvals'' argnames'' )
1380     2rot car 2swap cons  ( argvals argvals'' argnames' )
1381     2rot car 2rot cons ( argnames' argvals' )
1382     2swap
1383 ;
1384
1385 : apply ( proc argvals -- result )
1386         2swap dup case
1387             primitive-proc-type of
1388                 drop execute     
1389             endof
1390
1391             compound-proc-type of
1392                 2dup procedure-body ( argvals proc body )
1393                 -2rot 2dup procedure-params ( body argvals proc argnames )
1394                 -2rot procedure-env ( body argnames argvals procenv )
1395
1396                 -2rot 2swap
1397                 flatten-proc-args
1398                 2swap 2rot
1399
1400                 extend-env ( body env )
1401
1402                 eval-sequence
1403
1404                 R> drop ['] eval goto-deferred  \ Tail call optimization
1405             endof
1406
1407             recoverable-exception throw" Object not applicable."
1408         endcase
1409 ;
1410
1411 ( Simply evaluates the given procedure with expbody as its argument. )
1412 : macro-expand ( proc expbody -- result )
1413     2swap
1414     2dup procedure-body ( expbody proc procbody )
1415     -2rot 2dup procedure-params ( procbody expbody proc argnames )
1416     -2rot procedure-env ( procbody argnames expbody procenv )
1417     
1418     -2rot 2swap
1419     flatten-proc-args
1420     2swap 2rot
1421
1422     extend-env eval-sequence eval
1423 ;
1424
1425 :noname ( obj env -- result )
1426     2swap
1427
1428     self-evaluating? if
1429         2swap 2drop
1430         exit
1431     then
1432
1433     quote? if
1434         quote-body
1435         2swap 2drop
1436         exit
1437     then
1438
1439     quasiquote? if
1440         2swap eval-quasiquote
1441         exit
1442     then
1443
1444     variable? if
1445         2swap lookup-var
1446         exit
1447     then
1448
1449     definition? if
1450         2swap eval-definition
1451         exit
1452     then
1453
1454     assignment? if
1455         2swap eval-assignment
1456         exit
1457     then
1458
1459     macro-definition? if
1460         2swap eval-define-macro
1461         exit
1462     then
1463
1464     if? if
1465         2over 2over
1466         if-predicate
1467         2swap eval 
1468
1469         true? if
1470             if-consequent
1471         else
1472             if-alternative
1473         then
1474
1475         2swap
1476         ['] eval goto-deferred
1477     then
1478
1479     lambda? if
1480         2dup lambda-parameters
1481         2swap lambda-body
1482         2rot make-procedure
1483         exit
1484     then
1485
1486     begin? if
1487         begin-actions 2swap
1488         eval-sequence
1489         ['] eval goto-deferred
1490     then
1491
1492     application? if
1493
1494         2over 2over ( env exp env exp )
1495         operator ( env exp env opname )
1496
1497         2dup lookup-macro nil? false = if
1498              \ Macro function evaluation
1499
1500             ( env exp env opname mproc )
1501             2swap 2drop -2rot 2drop cdr ( env mproc body )
1502
1503             macro-expand
1504
1505             2swap
1506             ['] eval goto-deferred
1507         else
1508            \ Regular function application
1509
1510             2drop ( env exp env opname )
1511
1512             2swap eval ( env exp proc )
1513
1514             -2rot ( proc env exp )
1515             operands 2swap ( proc operands env )
1516             list-of-vals ( proc argvals )
1517
1518             apply
1519             exit
1520         then
1521     then
1522
1523     recoverable-exception throw" Tried to evaluate object with unknown type."
1524 ; is eval
1525
1526 \ }}}
1527
1528 \ ---- Print ---- {{{
1529
1530 : printfixnum ( fixnumobj -- ) drop 0 .R ;
1531
1532 : printrealnum ( realnumobj -- ) drop float-print ;
1533
1534 : printbool ( numobj -- )
1535     drop if
1536         ." #t"
1537     else
1538         ." #f"
1539     then
1540 ;
1541
1542 : printchar ( charobj -- )
1543     drop
1544     case
1545         9 of ." #\tab" endof
1546         bl of ." #\space" endof
1547         '\n' of ." #\newline" endof
1548         
1549         dup ." #\" emit
1550     endcase
1551 ;
1552
1553 : (printstring) ( stringobj -- )
1554     nil? if 2drop exit then
1555
1556     2dup car drop dup
1557     case
1558         '\n' of ." \n" drop endof
1559         [char] \ of ." \\" drop endof
1560         [char] " of [char] \ emit [char] " emit drop endof
1561         emit
1562     endcase
1563
1564     cdr recurse
1565 ;
1566 : printstring ( stringobj -- )
1567     [char] " emit
1568     (printstring)
1569     [char] " emit ;
1570
1571 : printsymbol ( symbolobj -- )
1572     nil-type istype? if 2drop exit then
1573
1574     2dup car drop emit
1575     cdr recurse
1576 ;
1577
1578 : printnil ( nilobj -- )
1579     2drop ." ()" ;
1580
1581 : printpair ( pairobj -- )
1582     2dup
1583     car print
1584     cdr
1585     nil-type istype? if 2drop exit then
1586     pair-type istype? if space recurse exit then
1587     ."  . " print
1588 ;
1589
1590 : printprim ( primobj -- )
1591     2drop ." <primitive procedure>" ;
1592
1593 : printcomp ( primobj -- )
1594     2drop ." <compound procedure>" ;
1595
1596 : printnone ( noneobj -- )
1597     2drop ." Unspecified return value" ;
1598
1599 : printport ( port -- )
1600     2drop ." <port>" ;
1601
1602 :noname ( obj -- )
1603     fixnum-type istype? if printfixnum exit then
1604     realnum-type istype? if printrealnum exit then
1605     boolean-type istype? if printbool exit then
1606     character-type istype? if printchar exit then
1607     string-type istype? if printstring exit then
1608     symbol-type istype? if printsymbol exit then
1609     nil-type istype? if printnil exit then
1610     pair-type istype? if ." (" printpair ." )" exit then
1611     primitive-proc-type istype? if printprim exit then
1612     compound-proc-type istype? if printcomp exit then
1613     none-type istype? if printnone exit then
1614
1615     recoverable-exception throw" Tried to print object with unknown type."
1616 ; is print
1617
1618 \ }}}
1619
1620 \ ---- Garbage Collection ---- {{{
1621
1622 variable gc-enabled
1623 false gc-enabled !
1624
1625 variable gc-stack-depth
1626
1627 : enable-gc
1628     depth gc-stack-depth !
1629     true gc-enabled ! ;
1630
1631 : disable-gc
1632     false gc-enabled ! ;
1633
1634 : gc-enabled?
1635     gc-enabled @ ;
1636
1637 : pairlike? ( obj -- obj bool )
1638     pair-type istype? if true exit then
1639     string-type istype? if true exit then
1640     symbol-type istype? if true exit then
1641     compound-proc-type istype? if true exit then
1642
1643     false
1644 ;
1645
1646 : pairlike-marked? ( obj -- obj bool )
1647     over nextfrees + @ 0=
1648 ;
1649
1650 : mark-pairlike ( obj -- obj )
1651         over nextfrees + 0 swap !
1652 ;
1653
1654 : gc-unmark ( -- )
1655     scheme-memsize 0 do
1656         1 nextfrees i + !
1657     loop
1658 ;
1659
1660 : gc-mark-obj ( obj -- )
1661
1662     pairlike? invert if 2drop exit then
1663     pairlike-marked? if 2drop exit then
1664
1665     mark-pairlike
1666
1667     drop pair-type 2dup
1668
1669     car recurse
1670     cdr recurse
1671 ;
1672
1673 : gc-sweep
1674     scheme-memsize nextfree !
1675     0 scheme-memsize 1- do
1676         nextfrees i + @ 0<> if
1677             nextfree @ nextfrees i + !
1678             i nextfree !
1679         then
1680     -1 +loop
1681 ;
1682
1683 \ Following a GC, this gives the amount of free memory
1684 : gc-count-marked
1685     0
1686     scheme-memsize 0 do
1687         nextfrees i + @ 0= if 1+ then
1688     loop
1689 ;
1690
1691 \ Debugging word - helps spot memory that is retained
1692 : gc-zero-unmarked
1693     scheme-memsize 0 do
1694         nextfrees i + @ 0<> if
1695             0 car-cells i + !
1696             0 cdr-cells i + !
1697         then
1698     loop
1699 ;
1700
1701 :noname
1702     \ ." GC! "
1703
1704     gc-unmark
1705
1706     symbol-table obj@ gc-mark-obj
1707     macro-table obj@ gc-mark-obj
1708     global-env obj@ gc-mark-obj
1709
1710     depth gc-stack-depth @ do
1711         PSP0 i + 1 + @
1712         PSP0 i + 2 + @
1713
1714         gc-mark-obj
1715     2 +loop
1716
1717     gc-sweep
1718
1719     \ ." (" gc-count-marked . ." pairs marked as used.)" cr
1720 ; is collect-garbage
1721
1722 \ }}}
1723
1724 \ ---- Loading files ---- {{{
1725
1726 : charlist>cstr ( charlist addr -- n )
1727
1728     dup 2swap ( origaddr addr charlist )
1729
1730     begin 
1731         nil? false =
1732     while
1733         2dup cdr 2swap car 
1734         drop ( origaddr addr charlist char )
1735         -rot 2swap ( origaddr charlist addr char )
1736         over !
1737         1+ -rot ( origaddr nextaddr charlist )
1738     repeat
1739
1740     2drop ( origaddr finaladdr ) 
1741     swap -
1742 ;
1743
1744 : load ( addr n -- finalResult )
1745     open-input-file
1746
1747     empty-parse-str
1748
1749     ok-symbol ( port res )
1750
1751     begin
1752         2over read-port ( port res obj )
1753
1754         2dup EOF character-type objeq? if
1755             2drop 2swap close-port
1756             exit
1757         then
1758
1759         2swap 2drop ( port obj )
1760
1761         global-env obj@ eval ( port res )
1762     again
1763 ;
1764
1765 \ }}}
1766
1767 \ ---- Standard Library ---- {{{
1768
1769     include scheme-primitives.4th
1770
1771     s" scheme-library.scm" load 2drop
1772     
1773 \ }}}
1774
1775 \ ---- REPL ----
1776
1777 ( REPL calls REPL-BODY in a loop until repl-body returns true. )
1778 : repl-body ( -- bool )
1779     cr bold fg green ." > " reset-term
1780
1781     read-console
1782
1783     2dup EOF character-type objeq? if
1784         2drop
1785         bold fg blue ." Moriturus te saluto." reset-term cr
1786         true exit
1787     then
1788
1789     global-env obj@ eval
1790
1791     fg cyan ." ; " print reset-term
1792
1793     false
1794 ;
1795
1796 : repl
1797     cr ." Welcome to scheme.forth.jl!" cr
1798        ." Use Ctrl-D to exit." cr
1799
1800     empty-parse-str
1801
1802     enable-gc
1803
1804     begin
1805         ['] repl-body catch
1806         case
1807             recoverable-exception of false endof
1808             unrecoverable-exception of true endof
1809
1810             throw false
1811         endcase
1812     until
1813 ;
1814
1815 forth definitions
1816
1817 \ vim:fdm=marker