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