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