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