Working on quasiquote.
[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
1070 : eval-unquote ( env obj -- res )
1071     cdr ( env args )
1072
1073     nil? if
1074         recoverable-exception throw" no arguments to unquote."
1075     then
1076
1077     2dup cdr
1078     nil? false = if
1079         recoverable-exception throw" too many arguments to unquote."
1080     then
1081
1082     2drop car 2swap eval
1083 ;
1084
1085 : (eval-quasiquote) ( env obj -- res )
1086     nil? if
1087         2swap 2drop exit
1088     then
1089
1090     2over 2over car ( env obj env obj-car )
1091
1092     unquote? if
1093         eval-unquote
1094     else
1095         2swap 2drop
1096     then
1097
1098     -2rot cdr ( caritem env cdr )
1099     recurse ( caritem cdritems )
1100     cons
1101 ;
1102
1103 : eval-quasiquote ( obj env -- res )
1104     2swap cdr ( env args )
1105
1106     nil? if
1107         recoverable-exception throw" no arguments to quasiquote."
1108     then
1109
1110     2dup cdr ( env args args-cdr )
1111     nil? false = if
1112         recoverable-exception throw" too many arguments to quasiquote."
1113     then
1114
1115     2drop car ( env arg )
1116
1117     unquote? if
1118         eval-unquote exit
1119     then
1120
1121     pair-type istype? if
1122         (eval-quasiquote) exit
1123     then
1124
1125     2swap 2drop ;
1126
1127 : variable? ( obj -- obj bool )
1128     symbol-type istype? ;
1129
1130 : definition? ( obj -- obj bool )
1131     define-symbol tagged-list? ;
1132
1133 : make-lambda ( params body -- lambda-exp )
1134     lambda-symbol -2rot cons cons ;
1135
1136 ( Handles iterative expansion of defines in
1137   terms of nested lambdas. Most Schemes only
1138   handle one iteration of expansion! )
1139 : definition-var-val ( obj -- var val )
1140
1141     cdr 2dup cdr 2swap car ( val var )
1142
1143     begin
1144         symbol-type istype? false =
1145     while
1146         2dup cdr 2swap car ( val formals var' )
1147         -2rot 2swap ( var' formals val )
1148         make-lambda nil cons ( var' val' )
1149         2swap ( val' var' )
1150     repeat
1151
1152     2swap car
1153 ;
1154
1155 : eval-definition ( obj env -- res )
1156     2dup 2rot ( env env obj )
1157     definition-var-val ( env env var val )
1158     2rot eval  ( env var val )
1159
1160     2rot ( var val env )
1161     define-var
1162
1163     ok-symbol
1164 ;
1165
1166 : assignment? ( obj -- obj bool )
1167     set!-symbol tagged-list? ;
1168
1169 : assignment-var ( obj -- var )
1170     cdr car ;
1171     
1172 : assignment-val ( obj -- val )
1173     cdr cdr car ;
1174
1175 : eval-assignment ( obj env -- res )
1176     2swap 
1177     2over 2over ( env obj env obj )
1178     assignment-val 2swap ( env obj valexp env )
1179     eval  ( env obj val )
1180     
1181     2swap assignment-var 2swap ( env var val )
1182
1183     2rot ( var val env )
1184     set-var
1185
1186     ok-symbol
1187 ;
1188
1189 : macro-definition? ( obj -- obj bool )
1190     define-macro-symbol tagged-list? ;
1191
1192 : macro-definition-name ( exp -- mname )
1193     cdr car car ;
1194
1195 : macro-definition-params ( exp -- params )
1196     cdr car cdr ;
1197
1198 : macro-definition-body ( exp -- body )
1199     cdr cdr ;
1200
1201 objvar env
1202 : eval-define-macro ( obj env -- res )
1203     env obj!
1204
1205     2dup macro-definition-name 2swap ( name obj )
1206     2dup macro-definition-params 2swap ( name params obj )
1207     macro-definition-body ( name params body )
1208
1209     env obj@ ( name params body env )
1210
1211     make-macro
1212
1213     ok-symbol
1214 ;
1215 hide env
1216
1217 : if? ( obj -- obj bool )
1218     if-symbol tagged-list? ;
1219
1220 : if-predicate ( ifobj -- pred )
1221     cdr car ;
1222
1223 : if-consequent ( ifobj -- conseq )
1224     cdr cdr car ;
1225
1226 : if-alternative ( ifobj -- alt|false )
1227     cdr cdr cdr
1228     nil? if
1229         2drop false
1230     else
1231         car
1232     then ;
1233
1234 : false? ( boolobj -- boolean )
1235     boolean-type istype? if
1236         false boolean-type objeq?
1237     else
1238         2drop false
1239     then
1240 ;
1241
1242 : true? ( boolobj -- bool )
1243     false? invert ;
1244
1245 : lambda? ( obj -- obj bool )
1246     lambda-symbol tagged-list? ;
1247
1248 : lambda-parameters ( obj -- params )
1249     cdr car ;
1250
1251 : lambda-body ( obj -- body )
1252     cdr cdr ;
1253
1254 : begin? ( obj -- obj bool )
1255     begin-symbol tagged-list? ;
1256
1257 : begin-actions ( obj -- actions )
1258     cdr ;
1259
1260 : eval-sequence ( explist env -- finalexp env )
1261     ( Evaluates all bar the final expressions in
1262       an an expression list. The final expression
1263       is returned to allow for tail optimization. )
1264
1265     2swap ( env explist )
1266
1267     \ Abort on empty list
1268     nil? if
1269         2drop none
1270         2swap exit
1271     then
1272
1273     begin
1274         2dup cdr ( env explist nextexplist )
1275         nil? false =
1276     while
1277         -2rot car 2over ( nextexplist env exp env )
1278         eval
1279         2drop \ discard result
1280         2swap ( env nextexplist )
1281     repeat
1282
1283     2drop car 2swap ( finalexp env )
1284 ;
1285
1286 : application? ( obj -- obj bool )
1287     pair-type istype? ;
1288
1289 : operator ( obj -- operator )
1290     car ;
1291
1292 : operands ( obj -- operands )
1293     cdr ;
1294
1295 : nooperands? ( operands -- bool )
1296     nil objeq? ;
1297
1298 : first-operand ( operands -- operand )
1299     car ;
1300
1301 : rest-operands ( operands -- other-operands )
1302     cdr ;
1303
1304 : list-of-vals ( args env -- vals )
1305     2swap
1306
1307     2dup nooperands? if
1308         2swap 2drop
1309     else
1310         2over 2over first-operand 2swap eval
1311         -2rot rest-operands 2swap recurse
1312         cons
1313     then
1314 ;
1315
1316 : procedure-params ( proc -- params )
1317     drop pair-type car ;
1318
1319 : procedure-body ( proc -- body )
1320     drop pair-type cdr car ;
1321
1322 : procedure-env ( proc -- body )
1323     drop pair-type cdr cdr car ;
1324
1325 ( Ensure terminating symbol arg name is handled
1326   specially to allow for variadic procedures. )
1327 : flatten-proc-args ( argvals argnames -- argvals' argnames' )
1328     nil? if
1329         2over nil? false = if
1330             recoverable-exception throw" Too many arguments for compound procedure."
1331         else
1332             2drop
1333         then
1334         exit
1335     then
1336
1337     symbol-type istype? if
1338         nil cons
1339         2swap
1340         nil cons
1341         2swap
1342         exit
1343     then
1344
1345     2over
1346     nil? if
1347         recoverable-exception throw" Too few arguments for compound procedure."
1348     else
1349         cdr
1350     then
1351
1352     2over cdr
1353
1354     recurse ( argvals argnames argvals'' argnames'' )
1355     2rot car 2swap cons  ( argvals argvals'' argnames' )
1356     2rot car 2rot cons ( argnames' argvals' )
1357     2swap
1358 ;
1359
1360 : apply ( proc argvals -- result )
1361         2swap dup case
1362             primitive-proc-type of
1363                 drop execute     
1364             endof
1365
1366             compound-proc-type of
1367                 2dup procedure-body ( argvals proc body )
1368                 -2rot 2dup procedure-params ( body argvals proc argnames )
1369                 -2rot procedure-env ( body argnames argvals procenv )
1370
1371                 -2rot 2swap
1372                 flatten-proc-args
1373                 2swap 2rot
1374
1375                 extend-env ( body env )
1376
1377                 eval-sequence
1378
1379                 R> drop ['] eval goto-deferred  \ Tail call optimization
1380             endof
1381
1382             recoverable-exception throw" Object not applicable."
1383         endcase
1384 ;
1385
1386 : macro-expand ( proc expbody -- result )
1387     2swap
1388     2dup procedure-body ( expbody proc procbody )
1389     -2rot 2dup procedure-params ( procbody expbody proc argnames )
1390     -2rot procedure-env ( procbody argnames expbody procenv )
1391     
1392     -2rot 2swap
1393     flatten-proc-args
1394     2swap 2rot
1395
1396     extend-env eval-sequence eval
1397 ;
1398
1399 :noname ( obj env -- result )
1400     2swap
1401
1402     self-evaluating? if
1403         2swap 2drop
1404         exit
1405     then
1406
1407     quote? if
1408         quote-body
1409         2swap 2drop
1410         exit
1411     then
1412
1413     quasiquote? if
1414         2swap eval-quasiquote
1415         exit
1416     then
1417
1418     variable? if
1419         2swap lookup-var
1420         exit
1421     then
1422
1423     definition? if
1424         2swap eval-definition
1425         exit
1426     then
1427
1428     assignment? if
1429         2swap eval-assignment
1430         exit
1431     then
1432
1433     macro-definition? if
1434         2swap eval-define-macro
1435         exit
1436     then
1437
1438     if? if
1439         2over 2over
1440         if-predicate
1441         2swap eval 
1442
1443         true? if
1444             if-consequent
1445         else
1446             if-alternative
1447         then
1448
1449         2swap
1450         ['] eval goto-deferred
1451     then
1452
1453     lambda? if
1454         2dup lambda-parameters
1455         2swap lambda-body
1456         2rot make-procedure
1457         exit
1458     then
1459
1460     begin? if
1461         begin-actions 2swap
1462         eval-sequence
1463         ['] eval goto-deferred
1464     then
1465
1466     application? if
1467
1468         2over 2over ( env exp env exp )
1469         operator ( env exp env opname )
1470
1471         2dup lookup-macro nil? false = if
1472              \ Macro function evaluation
1473
1474             ( env exp env opname mproc )
1475             2swap 2drop -2rot 2drop cdr ( env mproc body )
1476
1477             macro-expand
1478
1479             2swap
1480             ['] eval goto-deferred
1481         else
1482            \ Regular function application
1483
1484             2drop ( env exp env opname )
1485
1486             2swap eval ( env exp proc )
1487
1488             -2rot ( proc env exp )
1489             operands 2swap ( proc operands env )
1490             list-of-vals ( proc argvals )
1491
1492             apply
1493             exit
1494         then
1495     then
1496
1497     recoverable-exception throw" Tried to evaluate object with unknown type."
1498 ; is eval
1499
1500 \ }}}
1501
1502 \ ---- Print ---- {{{
1503
1504 : printfixnum ( fixnumobj -- ) drop 0 .R ;
1505
1506 : printrealnum ( realnumobj -- ) drop float-print ;
1507
1508 : printbool ( numobj -- )
1509     drop if
1510         ." #t"
1511     else
1512         ." #f"
1513     then
1514 ;
1515
1516 : printchar ( charobj -- )
1517     drop
1518     case
1519         9 of ." #\tab" endof
1520         bl of ." #\space" endof
1521         '\n' of ." #\newline" endof
1522         
1523         dup ." #\" emit
1524     endcase
1525 ;
1526
1527 : (printstring) ( stringobj -- )
1528     nil? if 2drop exit then
1529
1530     2dup car drop dup
1531     case
1532         '\n' of ." \n" drop endof
1533         [char] \ of ." \\" drop endof
1534         [char] " of [char] \ emit [char] " emit drop endof
1535         emit
1536     endcase
1537
1538     cdr recurse
1539 ;
1540 : printstring ( stringobj -- )
1541     [char] " emit
1542     (printstring)
1543     [char] " emit ;
1544
1545 : printsymbol ( symbolobj -- )
1546     nil-type istype? if 2drop exit then
1547
1548     2dup car drop emit
1549     cdr recurse
1550 ;
1551
1552 : printnil ( nilobj -- )
1553     2drop ." ()" ;
1554
1555 : printpair ( pairobj -- )
1556     2dup
1557     car print
1558     cdr
1559     nil-type istype? if 2drop exit then
1560     pair-type istype? if space recurse exit then
1561     ."  . " print
1562 ;
1563
1564 : printprim ( primobj -- )
1565     2drop ." <primitive procedure>" ;
1566
1567 : printcomp ( primobj -- )
1568     2drop ." <compound procedure>" ;
1569
1570 : printnone ( noneobj -- )
1571     2drop ." Unspecified return value" ;
1572
1573 :noname ( obj -- )
1574     fixnum-type istype? if printfixnum exit then
1575     realnum-type istype? if printrealnum exit then
1576     boolean-type istype? if printbool exit then
1577     character-type istype? if printchar exit then
1578     string-type istype? if printstring exit then
1579     symbol-type istype? if printsymbol exit then
1580     nil-type istype? if printnil exit then
1581     pair-type istype? if ." (" printpair ." )" exit then
1582     primitive-proc-type istype? if printprim exit then
1583     compound-proc-type istype? if printcomp exit then
1584     none-type istype? if printnone exit then
1585
1586     recoverable-exception throw" Tried to print object with unknown type."
1587 ; is print
1588
1589 \ }}}
1590
1591 \ ---- Garbage Collection ---- {{{
1592
1593 variable gc-enabled
1594 false gc-enabled !
1595
1596 variable gc-stack-depth
1597
1598 : enable-gc
1599     depth gc-stack-depth !
1600     true gc-enabled ! ;
1601
1602 : disable-gc
1603     false gc-enabled ! ;
1604
1605 : gc-enabled?
1606     gc-enabled @ ;
1607
1608 : pairlike? ( obj -- obj bool )
1609     pair-type istype? if true exit then
1610     string-type istype? if true exit then
1611     symbol-type istype? if true exit then
1612     compound-proc-type istype? if true exit then
1613
1614     false
1615 ;
1616
1617 : pairlike-marked? ( obj -- obj bool )
1618     over nextfrees + @ 0=
1619 ;
1620
1621 : mark-pairlike ( obj -- obj )
1622         over nextfrees + 0 swap !
1623 ;
1624
1625 : gc-unmark ( -- )
1626     scheme-memsize 0 do
1627         1 nextfrees i + !
1628     loop
1629 ;
1630
1631 : gc-mark-obj ( obj -- )
1632
1633     pairlike? invert if 2drop exit then
1634     pairlike-marked? if 2drop exit then
1635
1636     mark-pairlike
1637
1638     drop pair-type 2dup
1639
1640     car recurse
1641     cdr recurse
1642 ;
1643
1644 : gc-sweep
1645     scheme-memsize nextfree !
1646     0 scheme-memsize 1- do
1647         nextfrees i + @ 0<> if
1648             nextfree @ nextfrees i + !
1649             i nextfree !
1650         then
1651     -1 +loop
1652 ;
1653
1654 \ Following a GC, this gives the amount of free memory
1655 : gc-count-marked
1656     0
1657     scheme-memsize 0 do
1658         nextfrees i + @ 0= if 1+ then
1659     loop
1660 ;
1661
1662 \ Debugging word - helps spot memory that is retained
1663 : gc-zero-unmarked
1664     scheme-memsize 0 do
1665         nextfrees i + @ 0<> if
1666             0 car-cells i + !
1667             0 cdr-cells i + !
1668         then
1669     loop
1670 ;
1671
1672 :noname
1673     \ ." GC! "
1674
1675     gc-unmark
1676
1677     symbol-table obj@ gc-mark-obj
1678     macro-table obj@ gc-mark-obj
1679     global-env obj@ gc-mark-obj
1680
1681     depth gc-stack-depth @ do
1682         PSP0 i + 1 + @
1683         PSP0 i + 2 + @
1684
1685         gc-mark-obj
1686     2 +loop
1687
1688     gc-sweep
1689
1690     \ ." (" gc-count-marked . ." pairs marked as used.)" cr
1691 ; is collect-garbage
1692
1693 \ }}}
1694
1695 \ ---- Loading files ---- {{{
1696
1697 : charlist>cstr ( charlist addr -- n )
1698
1699     dup 2swap ( origaddr addr charlist )
1700
1701     begin 
1702         nil? false =
1703     while
1704         2dup cdr 2swap car 
1705         drop ( origaddr addr charlist char )
1706         -rot 2swap ( origaddr charlist addr char )
1707         over !
1708         1+ -rot ( origaddr nextaddr charlist )
1709     repeat
1710
1711     2drop ( origaddr finaladdr ) 
1712     swap -
1713 ;
1714
1715 : load ( addr n -- finalResult )
1716     open-input-file
1717
1718     empty-parse-str
1719
1720     ok-symbol ( port res )
1721
1722     begin
1723         2over read-port ( port res obj )
1724
1725         2dup EOF character-type objeq? if
1726             2drop 2swap close-port
1727             exit
1728         then
1729
1730         2swap 2drop ( port obj )
1731
1732         global-env obj@ eval ( port res )
1733     again
1734 ;
1735
1736 \ }}}
1737
1738 \ ---- Standard Library ---- {{{
1739
1740     include scheme-primitives.4th
1741
1742     s" scheme-library.scm" load 2drop
1743     
1744 \ }}}
1745
1746 \ ---- REPL ----
1747
1748 ( REPL calls REPL-BODY in a loop until repl-body returns true. )
1749 : repl-body ( -- bool )
1750     cr bold fg green ." > " reset-term
1751
1752     read-console
1753
1754     2dup EOF character-type objeq? if
1755         2drop
1756         bold fg blue ." Moriturus te saluto." reset-term cr
1757         true exit
1758     then
1759
1760     global-env obj@ eval
1761
1762     fg cyan ." ; " print reset-term
1763
1764     false
1765 ;
1766
1767 : repl
1768     cr ." Welcome to scheme.forth.jl!" cr
1769        ." Use Ctrl-D to exit." cr
1770
1771     empty-parse-str
1772
1773     enable-gc
1774
1775     begin
1776         ['] repl-body catch
1777         case
1778             recoverable-exception of false endof
1779             unrecoverable-exception of true endof
1780
1781             throw false
1782         endcase
1783     until
1784 ;
1785
1786 forth definitions
1787
1788 \ vim:fdm=marker