Working on restore-return-stack
[scheme.forth.jl.git] / src / scheme.4th
1 vocabulary scheme
2 scheme definitions
3
4 include term-colours.4th
5 include defer-is.4th
6 include goto.4th
7 include catch-throw.4th
8 include integer.4th
9 include float.4th
10
11 include debugging.4th
12
13 defer read
14 defer expand
15 defer analyze
16 defer eval
17 defer print
18
19 defer collect-garbage
20
21 \ ---- Types ---- {{{
22
23 variable nexttype
24 0 nexttype !
25 : make-type
26     create nexttype @ ,
27     1 nexttype +!
28     does> @ ;
29
30 make-type fixnum-type
31 make-type flonum-type
32 make-type ratnum-type
33 make-type boolean-type
34 make-type character-type
35 make-type string-type
36 make-type nil-type
37 make-type none-type
38 make-type pair-type
39 make-type symbol-type
40 make-type primitive-proc-type
41 make-type compound-proc-type
42 make-type continuation-type
43 make-type port-type
44 : istype? ( obj type -- obj bool )
45     over = ;
46
47 \ }}}
48
49 \ ---- Exceptions ---- {{{
50
51 variable nextexception
52 1 nextexception !
53 : make-exception 
54     create nextexception @ ,
55     1 nextexception +!
56     does> @ ;
57
58 : except-message:
59     bold fg red
60     ." Exception: "
61 ;
62
63 make-exception recoverable-exception
64 make-exception unrecoverable-exception
65
66 : throw reset-term cr throw ;
67
68 \ }}}
69
70 \ ---- List-structured memory ---- {{{
71
72 20000 constant scheme-memsize
73
74 create car-cells scheme-memsize allot
75 create car-type-cells scheme-memsize allot
76 create cdr-cells scheme-memsize allot
77 create cdr-type-cells scheme-memsize allot
78
79 create nextfrees scheme-memsize allot
80 :noname
81     scheme-memsize 0 do
82         i 1+ nextfrees i + !
83     loop
84 ; execute
85         
86 variable nextfree
87 0 nextfree !
88
89 : inc-nextfree
90     nextfrees nextfree @ + @
91     nextfree !
92
93     nextfree @ scheme-memsize >= if
94       collect-garbage
95     then
96
97     nextfree @ scheme-memsize >= if
98         except-message: ." Out of memory!" unrecoverable-exception throw
99     then
100 ;
101
102 : cons ( car-obj cdr-obj -- pair-obj )
103     cdr-type-cells nextfree @ + !
104     cdr-cells nextfree @ + !
105     car-type-cells nextfree @ + !
106     car-cells nextfree @ + !
107
108     nextfree @ pair-type
109     inc-nextfree
110 ;
111
112 : car ( pair-obj -- car-obj )
113     drop
114     dup car-cells + @ swap
115     car-type-cells + @
116 ;
117
118 : cdr ( pair-obj -- car-obj )
119     drop
120     dup cdr-cells + @ swap
121     cdr-type-cells + @
122 ;
123
124 : set-car! ( obj pair-obj -- )
125     drop dup
126     rot swap  car-type-cells + !
127     car-cells + !
128 ;
129
130 : set-cdr! ( obj pair-obj -- )
131     drop dup
132     rot swap  cdr-type-cells + !
133     cdr-cells + !
134 ;
135
136 variable object-stack-base
137 : init-object-stack-base
138   depth object-stack-base ! ;
139
140 : nil 0 nil-type ;
141 : nil? nil-type istype? ;
142
143 : none 0 none-type ;
144 : none? none-type istype? ;
145
146 : objvar create nil swap , , ;
147
148 : value@ ( objvar -- val ) @ ;
149 : type@ ( objvar -- type ) 1+ @ ;
150 : value! ( newval objvar -- ) ! ;
151 : type! ( newtype objvar -- ) 1+ ! ;
152 : obj! ( newobj objvar -- ) dup rot swap 1+ ! ! ; 
153 : obj@ ( objvar -- obj ) dup @ swap 1+ @ ; 
154
155 : objeq? ( obj obj -- bool )
156     rot = -rot = and ;
157
158 : 2rot ( a1 a2 b1 b2 c1 c2 -- b1 b2 c1 c2 a1 a2 )
159     >R >R ( a1 a2 b1 b2 )
160     2swap ( b1 b2 a1 a2 )
161     R> R> ( b1 b2 a1 a2 c1 c2 )
162     2swap
163 ;
164
165 : -2rot ( a1 a2 b1 b2 c1 c2 -- c1 c2 a1 a2 b1 b2 )
166     2swap ( a1 a2 c1 c2 b1 b2 )
167     >R >R ( a1 a2 c1 c2 )
168     2swap ( c1 c2 a1 a2 )
169     R> R>
170 ;
171
172 : 2pick ( an bn an-1 bn-1 ... a0 b0 n -- an bn an-1 bn-1 ... a0 b0 an bn )
173     2* 1+ dup
174     >R pick R> pick ;
175
176 \ }}}
177
178 \ ---- Pre-defined symbols ---- {{{
179
180 objvar symbol-table
181
182 : duplicate-charlist ( charlist -- copy )
183     nil? false = if
184         2dup car 2swap cdr recurse cons
185     then ;
186
187 : charlist-equiv ( charlist charlist -- bool )
188
189     2over 2over
190
191     \ One or both nil
192     nil? -rot 2drop
193     if
194         nil? -rot 2drop
195         if
196             2drop 2drop true exit
197         else
198             2drop 2drop false exit
199         then
200     else
201         nil? -rot 2drop
202         if
203             2drop 2drop false exit
204         then
205     then
206
207     2over 2over
208
209     \ Neither nil
210     car drop -rot car drop = if
211             cdr 2swap cdr recurse
212         else
213             2drop 2drop false
214     then
215 ;
216
217 : charlist>symbol ( charlist -- symbol-obj )
218
219     symbol-table obj@
220
221     begin
222         nil? false =
223     while
224         2over 2over
225         car drop pair-type
226         charlist-equiv if
227             2swap 2drop
228             car
229             exit
230         else
231             cdr
232         then
233     repeat
234
235     2drop
236     drop symbol-type 2dup
237     symbol-table obj@ cons
238     symbol-table obj!
239 ;
240
241
242 : cstr>charlist ( addr n -- charlist )
243     dup 0= if
244         2drop nil
245     else
246         2dup drop @ character-type 2swap
247         swap 1+ swap 1-
248         recurse
249
250         cons
251     then
252 ;
253
254 : create-symbol ( -- )
255     bl word
256     count
257
258     cstr>charlist
259     charlist>symbol
260
261     create swap , ,
262     does> dup @ swap 1+ @
263 ;
264
265 create-symbol quote             quote-symbol
266 create-symbol quasiquote        quasiquote-symbol
267 create-symbol unquote           unquote-symbol
268 create-symbol unquote-splicing  unquote-splicing-symbol
269 create-symbol define            define-symbol
270 create-symbol define-macro      define-macro-symbol
271 create-symbol set!              set!-symbol
272 create-symbol ok                ok-symbol
273 create-symbol if                if-symbol
274 create-symbol lambda            lambda-symbol
275 create-symbol Î»                 Î»-symbol
276 create-symbol eof               eof-symbol
277 create-symbol no-match          no-match-symbol
278
279 \ Symbol to be bound to welcome message procedure by library
280 create-symbol welcome           welcome-symbol
281
282 \ }}}
283
284 \ ---- Port I/O ----  {{{
285
286 ( Ports are pairs with the fid in the car and the peek buffer in the cdr. )
287
288 : fileport>fid ( fileport -- fid )
289     drop pair-type car drop ;
290
291 : get-last-peek ( fileport -- char/nil )
292     drop pair-type cdr ;
293
294 : set-last-peek ( char/nil fileport -- )
295     drop pair-type set-cdr!
296 ;
297
298 : fid>fileport ( fid -- fileport )
299     fixnum-type nil cons drop port-type ;
300
301 : open-input-file ( addr n -- fileport )
302     r/o open-file drop fid>fileport
303 ;
304
305 : close-port ( fileport -- )
306     fileport>fid close-file drop
307 ;
308
309 objvar console-i/o-port
310 0 fixnum-type nil cons drop port-type console-i/o-port obj!
311
312 objvar current-input-port
313 console-i/o-port obj@ current-input-port obj!
314
315 : read-char ( port -- char ) 
316     2dup get-last-peek nil? if
317         2drop
318         2dup console-i/o-port obj@ objeq? if
319             2drop
320             key character-type
321         else
322             fileport>fid pad 1 rot read-file 0= if
323                 eof-symbol
324             else
325                 pad @ character-type
326             then
327         then
328     else
329         nil 2rot set-cdr!
330     then
331 ;
332
333 : peek-char ( port -- char )
334     2dup get-last-peek nil? if
335         2drop 2dup read-char
336         2dup 2rot set-last-peek
337     else
338         2swap 2drop
339     then
340 ;
341
342 variable read-line-buffer-span
343 variable read-line-buffer-offset
344
345 ( Hack to save original read-line while we transition to new one. )
346 : orig-read-line immediate
347     ['] read-line , ;
348
349 : read-line ( port -- string )
350
351     2dup get-last-peek
352     nil? if
353         2drop
354         0 read-line-buffer-offset !
355     else
356         2over nil 2swap set-last-peek
357         2dup drop '\n' = if
358             2drop nil nil cons exit
359         else
360             drop pad !
361             1 read-line-buffer-offset !
362         then
363     then
364
365     2dup console-i/o-port obj@ objeq? if
366         2drop
367         pad read-line-buffer-offset @ + 200 expect cr
368         span @ read-line-buffer-offset @ + read-line-buffer-span !
369     else
370         pad read-line-buffer-offset @ + 200 2over fileport>fid orig-read-line
371         drop swap read-line-buffer-offset @ + read-line-buffer-span !
372     then
373
374     nil
375     
376     begin
377         read-line-buffer-span @ 0>
378     while
379         pad read-line-buffer-span @ 1- + @ character-type 2swap cons
380         -1 read-line-buffer-span +!
381     repeat
382
383     nil? if
384         nil cons drop string-type
385     else
386         drop string-type
387     then
388 ;
389
390 : read-port ( fileport -- obj )
391     current-input-port obj!
392     read ;
393
394 : read-console ( -- obj )
395     console-i/o-port obj@ read-port ;
396
397 \ }}}
398
399 \ ---- Environments ---- {{{
400
401 : enclosing-env ( env -- env )
402     cdr ;
403
404 : first-frame ( env -- frame )
405     car ;
406
407 : make-frame ( vars vals -- frame )
408     cons ;
409
410 : add-frame-to-env ( frame env -- env )
411     cons ;
412
413 : frame-vars ( frame -- vars )
414     car ;
415
416 : frame-vals ( frame -- vals )
417     cdr ;
418
419 : add-binding ( var val frame -- )
420     2swap 2over frame-vals cons
421     2over set-cdr!
422     2swap 2over frame-vars cons
423     2swap set-car!
424 ;
425
426 : extend-env ( vars vals env -- env )
427     -2rot make-frame
428     2swap add-frame-to-env
429 ;
430
431 : get-vals-frame ( var frame -- vals | nil )
432     2dup frame-vars 
433     2swap frame-vals ( var vars vals )
434
435     begin
436         nil? false =
437     while
438
439         -2rot ( vals var vars )
440         2over 2over car objeq? if
441             2drop 2drop
442             exit
443         then
444
445         cdr 2rot cdr
446     repeat
447
448     2drop 2drop 2drop
449     nil
450 ;
451
452 : get-vals ( var env -- vals | nil )
453
454     begin
455         nil? false =
456     while
457         2over 2over first-frame
458         get-vals-frame nil? false = if
459             2swap 2drop 2swap 2drop
460             exit
461         then
462
463         2drop
464
465         enclosing-env
466     repeat
467
468     2swap 2drop
469 ;
470
471 objvar var \ Used only for error messages
472 : lookup-var ( var env -- val )
473     2over var obj!
474     
475     get-vals nil? if
476         except-message: ." tried to read unbound variable '" var obj@ print ." '."
477         recoverable-exception throw
478     then
479
480     car
481 ;
482
483 : set-var ( var val env -- )
484     2rot 2dup var obj! ( val env var )
485     2swap ( val var env )
486     get-vals nil? if
487         except-message: ." tried to set unbound variable '" var obj@ print ." '."
488         recoverable-exception throw
489     else
490         ( val vals )
491         set-car!
492     then
493 ;
494 hide var
495
496 : define-var ( var val env -- )
497     first-frame ( var val frame )
498     2rot 2swap 2over 2over ( val var frame var frame )
499
500     get-vals-frame nil? if
501         2drop
502         -2rot 2swap 2rot
503         add-binding
504     else
505         ( val var frame vals )
506         2swap 2drop 2swap 2drop
507         set-car!
508     then
509 ;
510
511 : make-procedure ( params body env -- proc )
512     nil
513     cons cons cons
514     drop compound-proc-type
515 ;
516
517 objvar global-env
518 nil nil nil extend-env
519 global-env obj!
520
521 \ }}}
522
523 \ ---- Continuations ---- {{{
524
525 : cons-return-stack ( -- listobj )
526   rsp@ 1- rsp0  = if
527     nil exit
528   then
529
530   nil rsp@ 1- rsp0 do
531     i 1+ @ fixnum-type 2swap cons
532   loop
533
534   rsp@ 1- rsp0 - fixnum-type 2swap cons
535 ;
536
537 : cons-param-stack ( -- listobj )
538   nil 
539
540   depth 2- object-stack-base @ = if
541     exit
542   then
543
544   depth 2- object-stack-base @ do
545         PSP0 i + 1 + @
546         PSP0 i + 2 + @
547
548         2swap cons
549     2 +loop
550
551   depth 2- fixnum-type 2swap cons
552 ;
553
554 : make-continuation
555
556   cons-param-stack
557   cons-return-stack
558   cons drop continuation-type
559 ;
560
561 : continuation->pstack-list
562   drop pair-type car ;
563
564 : continuation->rstack-list
565   drop pair-type cdr ;
566
567 : restore-param-stack ( continuation -- obj_stack )
568   continuation->pstack-list
569   2dup >R >R
570
571   ( Allocate stack space first using psp!,
572     then copy objects from list. )
573
574   car drop
575   object-stack-base @ psp0 + + psp!
576
577   R> R> 2dup cdr
578   2swap
579   car drop 2- 0 swap do
580
581       2dup car
582       PSP0 object-stack-base @ + i + 2 + !
583       PSP0 object-stack-base @ + i + 1 + !
584       cdr
585
586   -2 +loop
587
588   2drop
589 ;
590
591 : restore-return-stack ( continuation -- )
592     R> \ store top of return stack on PS
593     continuation->rstack-list
594     2dup >R >R
595
596     ( Allocate stack space first using rsp!,
597       then copy objects from list. )
598
599     car drop
600     rsp0 + rsp!
601
602     R> R> 2dup cdr
603     2swap
604     car drop 0 swap do
605         2dup car drop
606         rsp0 i + 1 + !
607         cdr
608     1- +loop
609
610     2drop
611     trace
612     >R \ restore original top of return stack
613 ;
614
615 : restore-continuation ( continuation -- )
616   \ TODO: replace current parameter and return stacks with
617   \ contents of continuation object.
618
619     2dup >R >R
620     restore-param-stack
621     R> R>
622     restore-return-stack
623 ;
624
625 \ }}}
626
627 \ ---- Primitives ---- {{{
628
629 : make-primitive ( cfa -- )
630     bl word
631     count
632
633     cstr>charlist
634     charlist>symbol
635   
636     rot primitive-proc-type ( var prim )
637     global-env obj@ define-var
638 ;
639
640 : ensure-arg-count ( args n -- )
641     dup 0= if
642         drop nil objeq? false = if
643             except-message: ." Too many arguments for primitive procedure." recoverable-exception throw
644         then
645     else
646         -rot nil? if
647             except-message: ." Too few arguments for primitive procedure." recoverable-exception  throw
648         then
649         
650         cdr rot 1- recurse
651     then
652 ;
653
654 : ensure-arg-type-and-count ( tn tn-1 ... t2 t1 args n -- )
655     dup 0= if
656         drop nil objeq? false = if
657             except-message: ." Too many arguments for primitive procedure." recoverable-exception throw
658         then
659     else
660         -rot nil? if
661             except-message: ." Too few arguments for primitive procedure." recoverable-exception throw
662         then
663
664         2dup cdr 2swap car ( ... t1 n args' arg1 )
665         2rot 1- swap 2swap rot ( ... args' n-1 arg1 t1 )
666         istype? false = if
667             except-message: ." Incorrect type for primitive procedure." recoverable-exception throw
668         then
669
670         2drop recurse
671     then
672
673 ;
674
675 : push-args-to-stack ( args -- arg1 arg2 ... argn )
676     begin
677         nil? false =
678     while
679         2dup car 2swap cdr
680     repeat
681
682     2drop
683 ;
684
685 : add-fa-checks ( cfa n -- cfa' )
686     here current @ 1+ dup @ , !
687     0 ,
688     here -rot
689     docol ,
690     ['] 2dup , ['] lit , , ['] ensure-arg-count ,
691     ['] push-args-to-stack ,
692     ['] lit , , ['] execute ,
693     ['] exit ,
694 ;
695
696 : add-fa-type-checks ( cfa t1 t2 ... tn n -- cfa' )
697     here current @ 1+ dup @ , !
698     0 ,
699     here >R
700     docol ,
701     ['] 2dup ,
702     ['] >R , ['] >R ,
703
704     dup ( cfa t1 t2 ... tn n m )
705     
706     begin
707         ?dup 0>
708     while
709         rot ['] lit , , ( cfa t1 t2 ... tn-1 n m )
710         1-
711     repeat
712
713     ['] R> , ['] R> ,
714
715     ['] lit , , ['] ensure-arg-type-and-count ,
716
717     ['] push-args-to-stack ,
718     ['] lit , , ['] execute ,
719     ['] exit ,
720
721     R>
722 ;
723
724 : make-fa-primitive ( cfa n -- )
725     add-fa-checks make-primitive ;
726
727 : make-fa-type-primitive ( cfa t1 t2 ... tn n -- )
728     add-fa-type-checks make-primitive ;
729
730 : arg-type-error
731             bold fg red ." Incorrect argument type." reset-term cr
732             abort
733 ;
734
735 : ensure-arg-type ( arg type -- arg )
736     istype? false = if
737         except-message: ." Incorrect argument type for primitive procedure." recoverable-exception throw
738     then
739 ;
740
741
742 \ }}}
743
744 \ ---- Macros ---- {{{
745
746 objvar macro-table
747
748 ( Look up macro in macro table. Returns nil if
749   no macro is found. )
750 : lookup-macro ( name_symbol -- proc )
751
752     symbol-type istype? invert if
753         \ Early exit if argument is not a symbol
754         2drop nil exit
755     then
756     
757     macro-table obj@
758
759     begin
760         nil? false =
761     while
762         2over 2over
763         car car objeq? if
764             2swap 2drop
765             car cdr
766             exit
767         then
768
769         cdr
770     repeat
771
772     2swap 2drop
773 ;
774
775 : make-macro ( name_symbol params body env -- )
776     make-procedure
777
778     2swap ( proc name_symbol )
779
780     macro-table obj@
781
782     begin
783         nil? false =
784     while
785         2over 2over ( proc name table name table )
786         car car objeq? if
787             2swap 2drop ( proc table )
788             car ( proc entry )
789             set-cdr!
790             exit
791         then
792
793         cdr
794     repeat
795
796     2drop
797
798     2swap cons
799     macro-table obj@ cons
800     macro-table obj!
801 ;
802
803 \ }}}
804
805 \ ---- Read ---- {{{
806
807 variable parse-idx
808 variable stored-parse-idx
809 create parse-str 161 allot
810 variable parse-str-span
811
812 create parse-idx-stack 10 allot 
813 variable parse-idx-sp
814 parse-idx-stack parse-idx-sp !
815
816 : push-parse-idx
817     parse-idx @ parse-idx-sp @ !
818     1 parse-idx-sp +!
819 ;
820
821 : pop-parse-idx
822     parse-idx-sp @ parse-idx-stack <= abort" Parse index stack underflow."
823
824     1 parse-idx-sp -!
825
826     parse-idx-sp @ @ parse-idx ! ;
827
828
829 : append-newline
830     '\n' parse-str parse-str-span @ + !
831     1 parse-str-span +! ;
832
833 : append-eof
834     4 parse-str parse-str-span @ + !
835     1 parse-str-span +!  ;
836
837 : empty-parse-str
838     0 parse-str-span !
839     0 parse-idx ! ;
840
841 : getline
842     current-input-port obj@ console-i/o-port obj@ objeq? if
843         parse-str 160 expect cr
844         span @ parse-str-span !
845     else
846         parse-str 160 current-input-port obj@ fileport>fid orig-read-line
847         drop swap parse-str-span !
848
849         parse-str-span @ 0= and if append-eof then
850     then
851     append-newline
852     0 parse-idx ! ;
853
854 : inc-parse-idx
855     1 parse-idx +! ;
856
857 : dec-parse-idx
858     1 parse-idx -! ;
859
860 : charavailable? ( -- bool )
861     parse-str-span @ parse-idx @ > ;
862
863 : nextchar ( -- char )
864     charavailable? false = if getline then
865     parse-str parse-idx @ + @ ;
866
867 : '\t' 9 ;
868 : whitespace? ( -- bool )
869     nextchar BL = 
870     nextchar '\n' =
871     nextchar '\t' =
872     or or ;
873
874 : EOF 4 ; 
875 : eof? ( -- bool )
876     nextchar EOF = ;
877
878 : delim? ( -- bool )
879     whitespace?
880     nextchar [char] ( = or
881     nextchar [char] ) = or
882 ;
883
884 : commentstart? ( -- bool )
885     nextchar [char] ; = ;
886
887 : eatspaces
888
889     false \ Indicates whether or not we're eating a comment
890
891     begin
892         dup whitespace? or commentstart? or
893     while
894         dup nextchar '\n' = and if
895             invert \ Stop eating comment
896         else
897             dup false = commentstart? and if   
898                 invert \ Begin eating comment
899             then
900         then
901
902         inc-parse-idx
903     repeat
904     drop
905 ;
906
907 : digit? ( -- bool )
908     nextchar [char] 0 >=
909     nextchar [char] 9 <=
910     and ;
911
912 : minus? ( -- bool )
913     nextchar [char] - = ;
914
915 : plus? ( -- bool )
916     nextchar [char] + = ;
917
918 : fixnum? ( -- bool )
919     minus? plus? or if
920         inc-parse-idx
921
922         delim? if
923             dec-parse-idx
924             false exit
925         else
926             dec-parse-idx
927         then
928     else
929         digit? false = if
930             false exit
931         then
932     then
933
934     push-parse-idx
935     inc-parse-idx
936
937     begin digit? while
938         inc-parse-idx
939     repeat
940
941     delim? pop-parse-idx
942 ;
943
944 : flonum? ( -- bool )
945     push-parse-idx
946
947     minus? plus? or if
948         inc-parse-idx
949     then
950
951     \ Record starting parse idx:
952     \ Want to detect whether any characters (following +/-) were eaten.
953     parse-idx @
954
955     begin digit? while
956             inc-parse-idx
957     repeat
958
959     [char] . nextchar = if
960         inc-parse-idx
961         begin digit? while
962                 inc-parse-idx
963         repeat
964     then
965
966     [char] e nextchar = [char] E nextchar = or if
967         inc-parse-idx
968
969         minus? plus? or if
970             inc-parse-idx
971         then
972
973         digit? invert if
974             drop pop-parse-idx false exit
975         then
976
977         begin digit? while
978                 inc-parse-idx
979         repeat
980     then
981
982     \ This is a real number if characters were
983     \ eaten and the next characer is a delimiter.
984     parse-idx @ < delim? and
985
986     pop-parse-idx
987 ;
988
989 : ratnum? ( -- bool )
990     push-parse-idx
991
992     minus? plus? or if
993         inc-parse-idx
994     then
995
996     digit? invert if
997         pop-parse-idx false exit
998     else
999         inc-parse-idx
1000     then
1001
1002     begin digit? while
1003         inc-parse-idx
1004     repeat
1005
1006     [char] / nextchar <> if
1007         pop-parse-idx false exit
1008     else
1009         inc-parse-idx
1010     then
1011
1012     digit? invert if
1013         pop-parse-idx false exit
1014     else
1015         inc-parse-idx
1016     then
1017
1018     begin digit? while
1019         inc-parse-idx
1020     repeat
1021
1022     delim? pop-parse-idx
1023 ;
1024
1025 : boolean? ( -- bool )
1026     nextchar [char] # <> if false exit then
1027
1028     push-parse-idx
1029     inc-parse-idx
1030
1031     nextchar [char] t <>
1032     nextchar [char] f <>
1033     and if pop-parse-idx false exit then
1034
1035     inc-parse-idx
1036     delim? if
1037         pop-parse-idx
1038         true
1039     else
1040         pop-parse-idx
1041         false
1042     then
1043 ;
1044
1045 : str-equiv? ( str -- bool )
1046
1047     push-parse-idx
1048
1049     true -rot
1050
1051     swap dup rot + swap
1052
1053     do
1054         i @ nextchar <> if
1055             drop false
1056             leave
1057         then
1058
1059         inc-parse-idx
1060     loop
1061
1062     delim? false = if drop false then
1063
1064     pop-parse-idx
1065 ;
1066
1067 : character? ( -- bool )
1068     nextchar [char] # <> if false exit then
1069
1070     push-parse-idx
1071     inc-parse-idx
1072
1073     nextchar [char] \ <> if pop-parse-idx false exit then
1074
1075     inc-parse-idx
1076
1077     S" newline" str-equiv? if pop-parse-idx true exit then
1078     S" space" str-equiv? if pop-parse-idx true exit then
1079     S" tab" str-equiv? if pop-parse-idx true exit then
1080
1081     charavailable? false = if pop-parse-idx false exit then
1082
1083     pop-parse-idx true
1084 ;
1085
1086 : pair? ( -- bool )
1087     nextchar [char] ( = ;
1088
1089 : string? ( -- bool )
1090     nextchar [char] " = ;
1091
1092 : readfixnum ( -- fixnum )
1093     plus? minus? or if
1094         minus?
1095         inc-parse-idx
1096     else
1097         false
1098     then
1099
1100     0
1101
1102     begin digit? while
1103         10 * nextchar [char] 0 - +
1104         inc-parse-idx
1105     repeat
1106
1107     swap if negate then
1108
1109     fixnum-type
1110 ;
1111
1112 : readflonum ( -- flonum )
1113     readfixnum drop
1114     dup 0< swap abs i->f
1115
1116     [char] . nextchar = if
1117         inc-parse-idx
1118
1119         10.0 ( f exp )
1120
1121         begin digit? while
1122             nextchar [char] 0 - i->f ( f exp d )
1123             over f/ rot f+ ( exp f' )
1124             swap 10.0 f* ( f' exp' )
1125             inc-parse-idx
1126         repeat
1127
1128         drop
1129     then
1130
1131     [char] e nextchar = [char] E nextchar = or if
1132         inc-parse-idx
1133         10.0
1134         readfixnum drop i->f
1135         f^ f*
1136     then
1137
1138     swap if
1139         -1.0 f*
1140     then
1141
1142     flonum-type
1143 ;
1144
1145 : make-rational ( fixnum fixnum -- ratnum|fixnum )
1146     drop swap drop
1147     simplify
1148
1149     dup 1 = if
1150         drop fixnum-type
1151     else
1152         fixnum-type swap fixnum-type
1153         cons drop ratnum-type
1154     then
1155 ;
1156
1157 : readratnum ( -- ratnum )
1158     readfixnum inc-parse-idx readfixnum
1159     make-rational
1160 ;
1161
1162 : readbool ( -- bool-obj )
1163     inc-parse-idx
1164     
1165     nextchar [char] f = if
1166         false
1167     else
1168         true
1169     then
1170
1171     inc-parse-idx
1172
1173     boolean-type
1174 ;
1175
1176 : readchar ( -- char-obj )
1177     inc-parse-idx
1178     inc-parse-idx
1179
1180     S" newline" str-equiv? if 7 parse-idx +! '\n' character-type exit then
1181     S" space" str-equiv? if 5 parse-idx +! bl character-type exit then
1182     S" tab" str-equiv? if 3 parse-idx +! 9 character-type exit then
1183
1184     nextchar character-type
1185
1186     inc-parse-idx
1187 ;
1188
1189 : readstring ( -- charlist )
1190
1191     nil nil
1192
1193     begin
1194         nextchar [char] " <>
1195     while
1196         nextchar [char] \ = if
1197             inc-parse-idx
1198             nextchar case
1199                 [char] n of '\n' endof
1200                 [char] " of [char] " endof
1201                 [char] \
1202             endcase
1203         else
1204             nextchar
1205         then
1206         inc-parse-idx character-type
1207         nil cons
1208
1209         ( firstchar prevchar thischar )
1210
1211         2swap nil? if
1212             2drop 2swap 2drop 2dup  ( thischar thischar )
1213         else
1214             ( firstchar thischar prevchar )
1215             2over 2swap  set-cdr! ( firstchar thischar )
1216         then
1217     repeat
1218
1219     \ Discard previous character
1220     2drop
1221
1222     inc-parse-idx
1223     delim? false = if
1224         bold fg red
1225         ." No delimiter following right double quote. Aborting." cr
1226         reset-term abort
1227     then
1228
1229     dec-parse-idx
1230
1231     nil? if
1232         nil cons
1233     then
1234     drop string-type
1235 ;
1236
1237 : readsymbol ( -- charlist )
1238     delim? if nil exit then
1239
1240     nextchar inc-parse-idx character-type
1241
1242     recurse
1243
1244     cons
1245 ;
1246
1247 : readpair ( -- pairobj )
1248     eatspaces
1249
1250     \ Empty lists
1251     nextchar [char] ) = if
1252         inc-parse-idx
1253
1254         delim? false = if
1255             bold fg red
1256             ." No delimiter following right paren. Aborting." cr
1257             reset-term abort
1258         then
1259
1260         dec-parse-idx
1261
1262         0 nil-type exit
1263     then
1264
1265     \ Read first pair element
1266     read
1267
1268     \ Pairs
1269     eatspaces
1270     nextchar [char] . = if
1271         inc-parse-idx
1272
1273         delim? false = if
1274             bold fg red
1275             ." No delimiter following '.'. Aborting." cr
1276             reset-term abort
1277         then
1278
1279         eatspaces read
1280     else
1281         recurse
1282     then
1283
1284     eatspaces
1285
1286     cons
1287 ;
1288
1289 \ Parse a scheme expression
1290 :noname ( -- obj )
1291
1292     eatspaces
1293
1294     fixnum? if
1295         readfixnum
1296         exit
1297     then
1298
1299     flonum? if
1300         readflonum
1301         exit
1302     then
1303
1304     ratnum? if
1305         readratnum
1306         exit
1307     then
1308
1309     boolean? if
1310         readbool
1311         exit
1312     then
1313
1314     character? if
1315         readchar
1316         exit
1317     then
1318
1319     string? if
1320         inc-parse-idx
1321
1322         readstring
1323
1324         nextchar [char] " <> if
1325             bold red ." Missing closing double-quote." reset-term cr
1326             abort
1327         then
1328
1329         inc-parse-idx
1330
1331         exit
1332     then
1333
1334     pair? if
1335         inc-parse-idx
1336
1337         eatspaces
1338
1339         readpair
1340
1341         eatspaces
1342
1343         nextchar [char] ) <> if
1344             bold red ." Missing closing paren." reset-term cr
1345             abort
1346         then
1347
1348         inc-parse-idx
1349
1350         exit
1351     then
1352
1353     nextchar [char] ' = if
1354         inc-parse-idx
1355         quote-symbol recurse nil cons cons exit
1356     then
1357
1358     nextchar [char] ` = if
1359         inc-parse-idx
1360         quasiquote-symbol recurse nil cons cons exit
1361     then
1362
1363     nextchar [char] , = if
1364         inc-parse-idx
1365         nextchar [char] @ = if
1366             inc-parse-idx
1367             unquote-splicing-symbol recurse nil cons cons exit
1368         else
1369             unquote-symbol recurse nil cons cons exit
1370         then
1371     then
1372
1373     eof? if
1374         EOF character-type
1375         inc-parse-idx
1376         exit
1377     then
1378
1379     nextchar [char] ) = if
1380         inc-parse-idx
1381         except-message: ." unmatched closing parenthesis." recoverable-exception throw
1382     then
1383
1384     \ Anything else is parsed as a symbol
1385     readsymbol charlist>symbol
1386
1387     \ Replace Î» with lambda
1388     2dup Î»-symbol objeq? if
1389         2drop lambda-symbol
1390     then
1391     
1392
1393 ; is read
1394
1395 \ }}}
1396
1397 \ ---- Syntax ---- {{{
1398
1399 : self-evaluating? ( obj -- obj bool )
1400     boolean-type istype? if true exit then
1401     fixnum-type istype? if true exit then
1402     flonum-type istype? if true exit then
1403     ratnum-type istype? if true exit then
1404     character-type istype? if true exit then
1405     string-type istype? if true exit then
1406     nil-type istype? if true exit then
1407     none-type istype? if true exit then
1408
1409     false
1410 ;
1411
1412 : tagged-list? ( obj tag-obj -- obj bool )
1413     2over 
1414     pair-type istype? false = if
1415         2drop 2drop false
1416     else
1417         car objeq?
1418     then ;
1419
1420 : quote? ( obj -- obj bool )
1421     quote-symbol tagged-list?  ;
1422
1423 : quote-body ( quote-obj -- quote-body-obj )
1424     cdr car ;
1425
1426 : variable? ( obj -- obj bool )
1427     symbol-type istype? ;
1428
1429 : definition? ( obj -- obj bool )
1430     define-symbol tagged-list? ;
1431
1432 : definition-var ( obj -- var )
1433     cdr car ;
1434
1435 : definition-val ( obj -- val )
1436     cdr cdr car ;
1437
1438 : assignment? ( obj -- obj bool )
1439     set!-symbol tagged-list? ;
1440
1441 : assignment-var ( obj -- var )
1442     cdr car ;
1443     
1444 : assignment-val ( obj -- val )
1445     cdr cdr car ;
1446
1447 : macro-definition? ( obj -- obj bool )
1448     define-macro-symbol tagged-list? ;
1449
1450 : macro-definition-name ( exp -- mname )
1451     cdr car car ;
1452
1453 : macro-definition-params ( exp -- params )
1454     cdr car cdr ;
1455
1456 : macro-definition-body ( exp -- body )
1457     cdr cdr ;
1458
1459 : if? ( obj -- obj bool )
1460     if-symbol tagged-list? ;
1461
1462 : if-predicate ( ifobj -- pred )
1463     cdr car ;
1464
1465 : if-consequent ( ifobj -- conseq )
1466     cdr cdr car ;
1467
1468 : if-alternative ( ifobj -- alt|none )
1469     cdr cdr cdr
1470     nil? if
1471         2drop none
1472     else
1473         car
1474     then ;
1475
1476 : false? ( boolobj -- boolean )
1477     boolean-type istype? if
1478         false boolean-type objeq?
1479     else
1480         2drop false
1481     then
1482 ;
1483
1484 : true? ( boolobj -- bool )
1485     false? invert ;
1486
1487 : lambda? ( obj -- obj bool )
1488     lambda-symbol tagged-list? ;
1489
1490 : lambda-parameters ( obj -- params )
1491     cdr car ;
1492
1493 : lambda-body ( obj -- body )
1494     cdr cdr ;
1495
1496 : application? ( obj -- obj bool )
1497     pair-type istype? ;
1498
1499 : operator ( obj -- operator )
1500     car ;
1501
1502 : operands ( obj -- operands )
1503     cdr ;
1504
1505 : nooperands? ( operands -- bool )
1506     nil objeq? ;
1507
1508 : first-operand ( operands -- operand )
1509     car ;
1510
1511 : rest-operands ( operands -- other-operands )
1512     cdr ;
1513
1514 : procedure-params ( proc -- params )
1515     drop pair-type car ;
1516
1517 : procedure-body ( proc -- body )
1518     drop pair-type cdr car ;
1519
1520 : procedure-env ( proc -- body )
1521     drop pair-type cdr cdr car ;
1522
1523 ( Ensure terminating symbol arg name is handled
1524   specially to allow for variadic procedures. )
1525 : flatten-proc-args ( argvals argnames -- argvals' argnames' )
1526     nil? if
1527         2over nil? false = if
1528             except-message: ." Too many arguments for compound procedure." recoverable-exception throw
1529         else
1530             2drop
1531         then
1532         exit
1533     then
1534
1535     symbol-type istype? if
1536         nil cons
1537         2swap
1538         nil cons
1539         2swap
1540         exit
1541     then
1542
1543     2over
1544     nil? if
1545         except-message: ." Too few arguments for compound procedure." recoverable-exception throw
1546     else
1547         cdr
1548     then
1549
1550     2over cdr
1551
1552     recurse ( argvals argnames argvals'' argnames'' )
1553     2rot car 2swap cons  ( argvals argvals'' argnames' )
1554     2rot car 2rot cons ( argnames' argvals' )
1555     2swap
1556 ;
1557
1558 \ }}}
1559
1560 \ ---- Analyze ---- {{{
1561
1562 : evaluate-eproc ( eproc env --- res )
1563
1564     >R >R
1565
1566     begin
1567         nil? invert
1568     while
1569         2dup car
1570         2swap cdr
1571     repeat
1572     
1573     2drop \ get rid of null
1574
1575     R> R> 2swap
1576
1577     \ Final element of eproc list is primitive procedure
1578     drop \ dump type signifier
1579
1580     goto \ jump straight to primitive procedure (executor)
1581 ;
1582
1583 : self-evaluating-executor ( exp env -- exp )
1584     2drop ;
1585
1586 : analyze-self-evaluating ( exp --- eproc )
1587     ['] self-evaluating-executor primitive-proc-type
1588     nil cons cons
1589 ;
1590
1591 : quote-executor ( exp env -- exp )
1592     2drop ;
1593
1594 : analyze-quoted ( exp -- eproc )
1595     quote-body
1596
1597     ['] quote-executor primitive-proc-type
1598     nil cons cons
1599 ;
1600
1601 : variable-executor ( var env -- val )
1602     lookup-var ;
1603
1604 : analyze-variable ( exp -- eproc )
1605     ['] variable-executor primitive-proc-type
1606     nil cons cons
1607 ;
1608
1609 : definition-executor ( var val-eproc env -- ok )
1610     2swap 2over ( var env val-eproc env )
1611     evaluate-eproc 2swap ( var val env )
1612     define-var
1613     ok-symbol
1614 ;
1615
1616 : analyze-definition ( exp -- eproc )
1617     2dup definition-var
1618     2swap definition-val analyze
1619
1620     ['] definition-executor primitive-proc-type
1621     nil cons cons cons
1622 ;
1623
1624 : assignment-executor ( var val-eproc env -- ok )
1625     2swap 2over ( var env val-eproc env )
1626     evaluate-eproc 2swap ( var val env )
1627     set-var
1628     ok-symbol
1629 ;
1630
1631 : analyze-assignment ( exp -- eproc )
1632     2dup assignment-var
1633     2swap assignment-val analyze ( var val-eproc )
1634
1635     ['] assignment-executor primitive-proc-type
1636     nil cons cons cons
1637 ;
1638
1639 : sequence-executor ( eproc-list env -- res )
1640     2swap
1641
1642     begin
1643         2dup cdr ( env elist elist-rest)
1644         nil? invert
1645     while
1646         -2rot car 2over ( elist-rest env elist-head env )
1647         evaluate-eproc  ( elist-rest env head-res )
1648         2drop 2swap     ( env elist-rest )
1649     repeat
1650
1651     2drop car 2swap
1652     ['] evaluate-eproc goto
1653 ;
1654
1655
1656 : (analyze-sequence) ( explist -- eproc-list )
1657     nil? if exit then
1658
1659     2dup car analyze
1660     2swap cdr recurse
1661
1662     cons
1663 ;
1664
1665 : analyze-sequence ( explist -- eproc )
1666     (analyze-sequence)
1667     ['] sequence-executor primitive-proc-type
1668     nil cons cons
1669 ;
1670
1671
1672 : macro-definition-executor  ( name params bproc env -- ok )
1673     make-macro ok-symbol
1674 ;
1675
1676 : analyze-macro-definition ( exp -- eproc )
1677     2dup macro-definition-name
1678     2swap 2dup macro-definition-params
1679     2swap macro-definition-body analyze-sequence
1680
1681     ['] macro-definition-executor primitive-proc-type
1682     nil cons cons cons cons
1683 ;
1684
1685 : if-executor ( cproc aproc pproc env -- res )
1686     2swap 2over ( cproc aproc env pproc env -- res )
1687     evaluate-eproc
1688
1689     true? if
1690         2swap 2drop
1691     else
1692         2rot 2drop
1693     then
1694
1695     ['] evaluate-eproc goto
1696 ;
1697
1698 : analyze-if ( exp -- eproc )
1699     2dup if-consequent analyze
1700     2swap 2dup if-alternative analyze
1701     2swap if-predicate analyze
1702
1703     ['] if-executor primitive-proc-type
1704     nil cons cons cons cons
1705 ;
1706
1707 : lambda-executor ( params bproc env -- res )
1708     make-procedure
1709     ( Although this is packaged up as a regular compound procedure,
1710       the "body" element contains an _eproc_ to be evaluated in an
1711       environment resulting from extending env with the parameter
1712       bindings. )
1713 ;
1714
1715 : analyze-lambda ( exp -- eproc )
1716     2dup lambda-parameters
1717     2swap lambda-body
1718
1719     nil? if
1720         except-message: ." encountered lambda with an empty body." recoverable-exception throw
1721     then
1722
1723     analyze-sequence
1724
1725     ['] lambda-executor primitive-proc-type
1726     nil cons cons cons
1727 ;
1728
1729 : operand-eproc-list ( operands -- eprocs )
1730     nil? invert if
1731         2dup car analyze
1732         2swap cdr recurse
1733         cons
1734     then
1735 ;
1736
1737 : evaluate-operand-eprocs ( env aprocs -- vals )
1738     nil? if
1739         2swap 2drop
1740     else
1741         2over 2over car 2swap evaluate-eproc ( env aprocs thisval )
1742         -2rot cdr recurse ( thisval restvals )
1743         cons
1744     then
1745 ;
1746
1747 : apply ( vals proc )
1748     dup case
1749         primitive-proc-type of
1750             drop execute
1751         endof
1752
1753         compound-proc-type of
1754                 2dup procedure-body ( argvals proc bproc )
1755                 -2rot 2dup procedure-params ( bproc argvals proc argnames )
1756                 -2rot procedure-env ( bproc argnames argvals procenv )
1757
1758                 -2rot 2swap
1759                 flatten-proc-args
1760                 2swap 2rot
1761
1762                 extend-env ( bproc env )
1763
1764                ['] evaluate-eproc goto
1765         endof
1766
1767         continuation-type of
1768           \ TODO: Apply continuation
1769         endof
1770
1771         except-message: ." object '" drop print ." ' not applicable." recoverable-exception throw
1772     endcase
1773 ;
1774
1775 : application-executor ( operator-proc arg-procs env -- res )
1776     2rot 2over ( aprocs env fproc env )
1777     evaluate-eproc ( aprocs env proc )
1778
1779     -2rot 2swap ( proc env aprocs )
1780     evaluate-operand-eprocs ( proc vals )
1781
1782     2swap ( vals proc )
1783
1784     ['] apply goto
1785 ;
1786
1787 : analyze-application ( exp -- eproc )
1788     2dup operator analyze
1789     2swap operands operand-eproc-list
1790
1791     ['] application-executor primitive-proc-type
1792     nil cons cons cons
1793 ;
1794
1795 :noname ( exp --- eproc )
1796
1797     self-evaluating? if analyze-self-evaluating exit then
1798
1799     quote? if analyze-quoted exit then
1800     
1801     variable? if analyze-variable exit then
1802
1803     definition? if analyze-definition exit then
1804
1805     assignment? if analyze-assignment exit then
1806
1807     macro-definition? if analyze-macro-definition exit then
1808
1809     if? if analyze-if exit then
1810
1811     lambda? if analyze-lambda exit then
1812
1813     application? if analyze-application exit then
1814
1815     except-message: ." tried to analyze unknown expression type." recoverable-exception throw
1816
1817 ; is analyze
1818
1819 \ }}}
1820
1821 \ ---- Macro Expansion ---- {{{
1822
1823 ( Simply evaluates the given procedure with expbody as its argument. )
1824 : macro-eval ( proc expbody -- result )
1825     2swap
1826     2dup procedure-body ( expbody proc bproc )
1827     -2rot 2dup procedure-params ( bproc expbody proc argnames )
1828     -2rot procedure-env ( bproc argnames expbody procenv )
1829     
1830     -2rot 2swap
1831     flatten-proc-args
1832     2swap 2rot
1833
1834     extend-env ( bproc env )
1835
1836     ['] evaluate-eproc goto
1837 ;
1838
1839 : expand-macro ( exp -- result )
1840     pair-type istype? invert if exit then
1841
1842     2dup car symbol-type istype? invert if 2drop exit then
1843
1844     lookup-macro nil? if 2drop exit then
1845
1846     2over cdr macro-eval
1847
1848     2dup no-match-symbol objeq? if
1849         2drop exit
1850     else
1851         2swap 2drop
1852     then
1853
1854     R> drop ['] expand goto-deferred
1855 ;
1856
1857 : expand-definition ( exp -- result )
1858     define-symbol 2swap
1859
1860     2dup definition-var
1861     2swap definition-val expand
1862     nil ( define var val' nil )
1863
1864     cons cons cons ;
1865
1866 : expand-assignment ( exp -- result )
1867     set!-symbol 2swap
1868
1869     2dup assignment-var
1870     2swap assignment-val expand
1871     nil ( define var val' nil )
1872
1873     cons cons cons ;
1874
1875 : expand-list ( exp -- res )
1876     nil? if exit then
1877
1878     2dup car expand
1879     2swap cdr recurse
1880
1881     cons ;
1882
1883 : macro-definition-nameparams
1884     cdr car ;
1885
1886 : expand-define-macro ( exp -- res )
1887     define-macro-symbol 2swap
1888     2dup macro-definition-nameparams
1889     2swap macro-definition-body expand-list
1890
1891     cons cons ;
1892
1893 : expand-lambda ( exp -- res )
1894     lambda-symbol 2swap
1895     2dup lambda-parameters
1896     2swap lambda-body expand-list
1897
1898     cons cons ;
1899
1900 : expand-if ( exp -- res )
1901     if-symbol 2swap
1902     
1903     2dup if-predicate expand
1904     2swap 2dup if-consequent expand
1905     2swap if-alternative none? if
1906         2drop nil
1907     else
1908         expand nil cons
1909     then
1910
1911     cons cons cons ;
1912
1913 : expand-application ( exp -- res )
1914     2dup operator expand
1915     2swap operands expand-list
1916
1917     cons ;
1918
1919 :noname ( exp -- result )
1920     expand-macro
1921
1922     self-evaluating? if exit then
1923
1924     quote? if exit then
1925
1926     definition? if expand-definition exit then
1927
1928     assignment? if expand-assignment exit then
1929
1930     macro-definition? if expand-define-macro exit then
1931
1932     lambda? if expand-lambda exit then
1933
1934     if? if expand-if exit then
1935
1936     application? if expand-application exit then
1937
1938 ; is expand
1939
1940 \ }}}
1941
1942 :noname ( exp env -- res )
1943     2swap expand analyze 2swap evaluate-eproc
1944 ; is eval
1945
1946 \ ---- Print ---- {{{
1947
1948 : printfixnum ( fixnum -- ) drop 0 .R ;
1949
1950 : printflonum ( flonum -- ) drop f. ;
1951
1952 : printratnum ( ratnum -- )
1953     drop pair-type 2dup
1954     car print ." /" cdr print
1955 ;
1956
1957 : printbool ( bool -- )
1958     drop if
1959         ." #t"
1960     else
1961         ." #f"
1962     then
1963 ;
1964
1965 : printchar ( charobj -- )
1966     drop
1967     case
1968         9 of ." #\tab" endof
1969         bl of ." #\space" endof
1970         '\n' of ." #\newline" endof
1971         
1972         dup ." #\" emit
1973     endcase
1974 ;
1975
1976 : (printstring) ( stringobj -- )
1977     nil? if 2drop exit then
1978
1979     2dup car drop dup
1980     case
1981         '\n' of ." \n" drop endof
1982         [char] \ of ." \\" drop endof
1983         [char] " of [char] \ emit [char] " emit drop endof
1984         emit
1985     endcase
1986
1987     cdr recurse
1988 ;
1989 : printstring ( stringobj -- )
1990     [char] " emit
1991     (printstring)
1992     [char] " emit ;
1993
1994 : printsymbol ( symbolobj -- )
1995     nil-type istype? if 2drop exit then
1996
1997     2dup car drop emit
1998     cdr recurse
1999 ;
2000
2001 : printnil ( nilobj -- )
2002     2drop ." ()" ;
2003
2004 : printpair ( pairobj -- )
2005     2dup
2006     car print
2007     cdr
2008     nil-type istype? if 2drop exit then
2009     pair-type istype? if space recurse exit then
2010     ."  . " print
2011 ;
2012
2013 : printprim ( primobj -- )
2014     2drop ." <primitive procedure>" ;
2015
2016 : printcomp ( primobj -- )
2017     2drop ." <compound procedure>" ;
2018
2019 : printcont ( primobj --)
2020     2drop ." <continuation>" ;
2021
2022 : printnone ( noneobj -- )
2023     2drop ." Unspecified return value" ;
2024
2025 : printport ( port -- )
2026     2drop ." <port>" ;
2027
2028 :noname ( obj -- )
2029     fixnum-type istype? if printfixnum exit then
2030     flonum-type istype? if printflonum exit then
2031     ratnum-type istype? if printratnum exit then
2032     boolean-type istype? if printbool exit then
2033     character-type istype? if printchar exit then
2034     string-type istype? if printstring exit then
2035     symbol-type istype? if printsymbol exit then
2036     nil-type istype? if printnil exit then
2037     pair-type istype? if ." (" printpair ." )" exit then
2038     primitive-proc-type istype? if printprim exit then
2039     compound-proc-type istype? if printcomp exit then
2040     continuation-type istype? if printcont exit then
2041     none-type istype? if printnone exit then
2042     port-type istype? if printport exit then
2043
2044     except-message: ." tried to print object with unknown type." recoverable-exception throw
2045 ; is print
2046
2047 \ }}}
2048
2049 \ ---- Garbage Collection ---- {{{
2050
2051 ( Notes on garbage collection:
2052   This is a mark-sweep garbage collector, invoked by cons.
2053   The roots of the object tree used by the marking routine 
2054   include all objects in the parameter stack, and several
2055   other fixed roots such as global-env, symbol-table, macro-table,
2056   and the console-i/o-port.
2057
2058   NO OTHER OBJECTS WILL BE MARKED!
2059
2060   This places implicit restrictions on when cons can be invoked.
2061   Invoking cons when live objects are stored on the return stack
2062   or in other variables than the above will result in possible
2063   memory corruption if the cons triggers the GC. )
2064
2065
2066 : pairlike? ( obj -- obj bool )
2067     pair-type istype? if true exit then
2068     string-type istype? if true exit then
2069     symbol-type istype? if true exit then
2070     compound-proc-type istype? if true exit then
2071     port-type istype? if true exit then
2072
2073     false
2074 ;
2075
2076 : pairlike-marked? ( obj -- obj bool )
2077     over nextfrees + @ 0=
2078 ;
2079
2080 : mark-pairlike ( obj -- obj )
2081         over nextfrees + 0 swap !
2082 ;
2083
2084 : gc-unmark ( -- )
2085     scheme-memsize 0 do
2086         1 nextfrees i + !
2087     loop
2088 ;
2089
2090 : gc-mark-obj ( obj -- )
2091
2092     pairlike? invert if 2drop exit then
2093     pairlike-marked? if 2drop exit then
2094
2095     mark-pairlike
2096
2097     drop pair-type 2dup
2098
2099     car recurse
2100     cdr recurse
2101 ;
2102
2103 : gc-sweep
2104     scheme-memsize nextfree !
2105     0 scheme-memsize 1- do
2106         nextfrees i + @ 0<> if
2107             nextfree @ nextfrees i + !
2108             i nextfree !
2109         then
2110     -1 +loop
2111 ;
2112
2113 \ Following a GC, this gives the amount of free memory
2114 : gc-count-marked
2115     0
2116     scheme-memsize 0 do
2117         nextfrees i + @ 0= if 1+ then
2118     loop
2119 ;
2120
2121 \ Debugging word - helps spot memory that is retained
2122 : gc-zero-unmarked
2123     scheme-memsize 0 do
2124         nextfrees i + @ 0<> if
2125             0 car-cells i + !
2126             0 cdr-cells i + !
2127         then
2128     loop
2129 ;
2130
2131 :noname
2132     \ ." GC! "
2133
2134     gc-unmark
2135
2136     symbol-table obj@ gc-mark-obj
2137     macro-table obj@ gc-mark-obj
2138     console-i/o-port obj@ gc-mark-obj
2139     global-env obj@ gc-mark-obj
2140
2141     depth object-stack-base @ do
2142         PSP0 i + 1 + @
2143         PSP0 i + 2 + @
2144
2145         gc-mark-obj
2146     2 +loop
2147
2148     gc-sweep
2149
2150     \ ." (" gc-count-marked . ." pairs marked as used.)" cr
2151 ; is collect-garbage
2152
2153 \ }}}
2154
2155 xxxx
2156
2157 \ ---- Loading files ---- {{{
2158
2159 : load ( addr n -- finalResult )
2160     open-input-file
2161
2162     empty-parse-str
2163
2164     ok-symbol ( port res )
2165
2166     begin
2167         \ DEBUG
2168         \ bold fg blue ." READ from " 2over drop . ." ==> " reset-term
2169
2170         2over read-port ( port res obj )
2171
2172         \ DEBUG
2173         \ 2dup print cr
2174
2175         2dup EOF character-type objeq? if
2176             2drop 2swap close-port
2177             exit
2178         then
2179
2180         2swap 2drop ( port obj )
2181
2182         global-env obj@ eval ( port res )
2183     again
2184 ;
2185
2186 \ }}}
2187
2188 \ ---- Standard Library ---- {{{
2189
2190     include scheme-primitives.4th
2191
2192     init-object-stack-base
2193     s" scheme-library.scm" load 2drop
2194     
2195 \ }}}
2196
2197 \ ---- REPL ----
2198
2199 ( REPL calls REPL-BODY in a loop until repl-body returns true. )
2200 : repl-body ( -- bool )
2201     cr bold fg green ." > " reset-term
2202
2203     read-console
2204
2205     2dup EOF character-type objeq? if
2206         2drop
2207         bold fg blue ." Moriturus te saluto." reset-term cr
2208         true exit
2209     then
2210
2211     global-env obj@ eval
2212
2213     fg cyan ." ; " print reset-term
2214
2215     false
2216 ;
2217
2218 : repl
2219     empty-parse-str
2220
2221     init-object-stack-base
2222
2223     \ Display welcome message
2224     welcome-symbol nil cons global-env obj@ eval 2drop
2225
2226     begin
2227         ['] repl-body catch
2228         case
2229             recoverable-exception of false endof
2230             unrecoverable-exception of true endof
2231
2232             throw false
2233         endcase
2234     until
2235 ;
2236
2237 forth definitions
2238
2239 \ vim:fdm=marker