Return stack restoration working?
[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 : list->pad ( list -- n )
592
593     2dup car drop -rot \ keep length of list on stack
594     2dup cdr 2swap car drop \ get length from list
595
596     pad + 1- \ final dest addr
597     pad      \ initial dest addr
598     swap
599     do
600         2dup cdr 2swap car
601         drop i !
602     -1 +loop
603
604     2drop
605 ;
606
607 : restore-return-stack ( continuation -- )
608
609     trace
610
611     continuation->rstack-list
612     list->pad
613     dup
614     RSP0 + RSP! \ expand return stack to accommodate entries
615
616     0   \ initial offset
617     do
618         pad i + @ RSP0 i 1+ + !
619     loop
620
621     trace
622 ;
623
624 : restore-continuation ( continuation -- )
625   \ TODO: replace current parameter and return stacks with
626   \ contents of continuation object.
627
628     2dup >R >R
629     restore-param-stack
630
631     ." ====== PARAM STACK RESTORED ======" cr
632     trace
633     
634     R> R>
635     restore-return-stack
636 ;
637
638 \ }}}
639
640 \ ---- Primitives ---- {{{
641
642 : make-primitive ( cfa -- )
643     bl word
644     count
645
646     cstr>charlist
647     charlist>symbol
648   
649     rot primitive-proc-type ( var prim )
650     global-env obj@ define-var
651 ;
652
653 : ensure-arg-count ( args n -- )
654     dup 0= if
655         drop nil objeq? false = if
656             except-message: ." Too many arguments for primitive procedure." recoverable-exception throw
657         then
658     else
659         -rot nil? if
660             except-message: ." Too few arguments for primitive procedure." recoverable-exception  throw
661         then
662         
663         cdr rot 1- recurse
664     then
665 ;
666
667 : ensure-arg-type-and-count ( tn tn-1 ... t2 t1 args n -- )
668     dup 0= if
669         drop nil objeq? false = if
670             except-message: ." Too many arguments for primitive procedure." recoverable-exception throw
671         then
672     else
673         -rot nil? if
674             except-message: ." Too few arguments for primitive procedure." recoverable-exception throw
675         then
676
677         2dup cdr 2swap car ( ... t1 n args' arg1 )
678         2rot 1- swap 2swap rot ( ... args' n-1 arg1 t1 )
679         istype? false = if
680             except-message: ." Incorrect type for primitive procedure." recoverable-exception throw
681         then
682
683         2drop recurse
684     then
685
686 ;
687
688 : push-args-to-stack ( args -- arg1 arg2 ... argn )
689     begin
690         nil? false =
691     while
692         2dup car 2swap cdr
693     repeat
694
695     2drop
696 ;
697
698 : add-fa-checks ( cfa n -- cfa' )
699     here current @ 1+ dup @ , !
700     0 ,
701     here -rot
702     docol ,
703     ['] 2dup , ['] lit , , ['] ensure-arg-count ,
704     ['] push-args-to-stack ,
705     ['] lit , , ['] execute ,
706     ['] exit ,
707 ;
708
709 : add-fa-type-checks ( cfa t1 t2 ... tn n -- cfa' )
710     here current @ 1+ dup @ , !
711     0 ,
712     here >R
713     docol ,
714     ['] 2dup ,
715     ['] >R , ['] >R ,
716
717     dup ( cfa t1 t2 ... tn n m )
718     
719     begin
720         ?dup 0>
721     while
722         rot ['] lit , , ( cfa t1 t2 ... tn-1 n m )
723         1-
724     repeat
725
726     ['] R> , ['] R> ,
727
728     ['] lit , , ['] ensure-arg-type-and-count ,
729
730     ['] push-args-to-stack ,
731     ['] lit , , ['] execute ,
732     ['] exit ,
733
734     R>
735 ;
736
737 : make-fa-primitive ( cfa n -- )
738     add-fa-checks make-primitive ;
739
740 : make-fa-type-primitive ( cfa t1 t2 ... tn n -- )
741     add-fa-type-checks make-primitive ;
742
743 : arg-type-error
744             bold fg red ." Incorrect argument type." reset-term cr
745             abort
746 ;
747
748 : ensure-arg-type ( arg type -- arg )
749     istype? false = if
750         except-message: ." Incorrect argument type for primitive procedure." recoverable-exception throw
751     then
752 ;
753
754
755 \ }}}
756
757 \ ---- Macros ---- {{{
758
759 objvar macro-table
760
761 ( Look up macro in macro table. Returns nil if
762   no macro is found. )
763 : lookup-macro ( name_symbol -- proc )
764
765     symbol-type istype? invert if
766         \ Early exit if argument is not a symbol
767         2drop nil exit
768     then
769     
770     macro-table obj@
771
772     begin
773         nil? false =
774     while
775         2over 2over
776         car car objeq? if
777             2swap 2drop
778             car cdr
779             exit
780         then
781
782         cdr
783     repeat
784
785     2swap 2drop
786 ;
787
788 : make-macro ( name_symbol params body env -- )
789     make-procedure
790
791     2swap ( proc name_symbol )
792
793     macro-table obj@
794
795     begin
796         nil? false =
797     while
798         2over 2over ( proc name table name table )
799         car car objeq? if
800             2swap 2drop ( proc table )
801             car ( proc entry )
802             set-cdr!
803             exit
804         then
805
806         cdr
807     repeat
808
809     2drop
810
811     2swap cons
812     macro-table obj@ cons
813     macro-table obj!
814 ;
815
816 \ }}}
817
818 \ ---- Read ---- {{{
819
820 variable parse-idx
821 variable stored-parse-idx
822 create parse-str 161 allot
823 variable parse-str-span
824
825 create parse-idx-stack 10 allot 
826 variable parse-idx-sp
827 parse-idx-stack parse-idx-sp !
828
829 : push-parse-idx
830     parse-idx @ parse-idx-sp @ !
831     1 parse-idx-sp +!
832 ;
833
834 : pop-parse-idx
835     parse-idx-sp @ parse-idx-stack <= abort" Parse index stack underflow."
836
837     1 parse-idx-sp -!
838
839     parse-idx-sp @ @ parse-idx ! ;
840
841
842 : append-newline
843     '\n' parse-str parse-str-span @ + !
844     1 parse-str-span +! ;
845
846 : append-eof
847     4 parse-str parse-str-span @ + !
848     1 parse-str-span +!  ;
849
850 : empty-parse-str
851     0 parse-str-span !
852     0 parse-idx ! ;
853
854 : getline
855     current-input-port obj@ console-i/o-port obj@ objeq? if
856         parse-str 160 expect cr
857         span @ parse-str-span !
858     else
859         parse-str 160 current-input-port obj@ fileport>fid orig-read-line
860         drop swap parse-str-span !
861
862         parse-str-span @ 0= and if append-eof then
863     then
864     append-newline
865     0 parse-idx ! ;
866
867 : inc-parse-idx
868     1 parse-idx +! ;
869
870 : dec-parse-idx
871     1 parse-idx -! ;
872
873 : charavailable? ( -- bool )
874     parse-str-span @ parse-idx @ > ;
875
876 : nextchar ( -- char )
877     charavailable? false = if getline then
878     parse-str parse-idx @ + @ ;
879
880 : '\t' 9 ;
881 : whitespace? ( -- bool )
882     nextchar BL = 
883     nextchar '\n' =
884     nextchar '\t' =
885     or or ;
886
887 : EOF 4 ; 
888 : eof? ( -- bool )
889     nextchar EOF = ;
890
891 : delim? ( -- bool )
892     whitespace?
893     nextchar [char] ( = or
894     nextchar [char] ) = or
895 ;
896
897 : commentstart? ( -- bool )
898     nextchar [char] ; = ;
899
900 : eatspaces
901
902     false \ Indicates whether or not we're eating a comment
903
904     begin
905         dup whitespace? or commentstart? or
906     while
907         dup nextchar '\n' = and if
908             invert \ Stop eating comment
909         else
910             dup false = commentstart? and if   
911                 invert \ Begin eating comment
912             then
913         then
914
915         inc-parse-idx
916     repeat
917     drop
918 ;
919
920 : digit? ( -- bool )
921     nextchar [char] 0 >=
922     nextchar [char] 9 <=
923     and ;
924
925 : minus? ( -- bool )
926     nextchar [char] - = ;
927
928 : plus? ( -- bool )
929     nextchar [char] + = ;
930
931 : fixnum? ( -- bool )
932     minus? plus? or if
933         inc-parse-idx
934
935         delim? if
936             dec-parse-idx
937             false exit
938         else
939             dec-parse-idx
940         then
941     else
942         digit? false = if
943             false exit
944         then
945     then
946
947     push-parse-idx
948     inc-parse-idx
949
950     begin digit? while
951         inc-parse-idx
952     repeat
953
954     delim? pop-parse-idx
955 ;
956
957 : flonum? ( -- bool )
958     push-parse-idx
959
960     minus? plus? or if
961         inc-parse-idx
962     then
963
964     \ Record starting parse idx:
965     \ Want to detect whether any characters (following +/-) were eaten.
966     parse-idx @
967
968     begin digit? while
969             inc-parse-idx
970     repeat
971
972     [char] . nextchar = if
973         inc-parse-idx
974         begin digit? while
975                 inc-parse-idx
976         repeat
977     then
978
979     [char] e nextchar = [char] E nextchar = or if
980         inc-parse-idx
981
982         minus? plus? or if
983             inc-parse-idx
984         then
985
986         digit? invert if
987             drop pop-parse-idx false exit
988         then
989
990         begin digit? while
991                 inc-parse-idx
992         repeat
993     then
994
995     \ This is a real number if characters were
996     \ eaten and the next characer is a delimiter.
997     parse-idx @ < delim? and
998
999     pop-parse-idx
1000 ;
1001
1002 : ratnum? ( -- bool )
1003     push-parse-idx
1004
1005     minus? plus? or if
1006         inc-parse-idx
1007     then
1008
1009     digit? invert if
1010         pop-parse-idx false exit
1011     else
1012         inc-parse-idx
1013     then
1014
1015     begin digit? while
1016         inc-parse-idx
1017     repeat
1018
1019     [char] / nextchar <> if
1020         pop-parse-idx false exit
1021     else
1022         inc-parse-idx
1023     then
1024
1025     digit? invert if
1026         pop-parse-idx false exit
1027     else
1028         inc-parse-idx
1029     then
1030
1031     begin digit? while
1032         inc-parse-idx
1033     repeat
1034
1035     delim? pop-parse-idx
1036 ;
1037
1038 : boolean? ( -- bool )
1039     nextchar [char] # <> if false exit then
1040
1041     push-parse-idx
1042     inc-parse-idx
1043
1044     nextchar [char] t <>
1045     nextchar [char] f <>
1046     and if pop-parse-idx false exit then
1047
1048     inc-parse-idx
1049     delim? if
1050         pop-parse-idx
1051         true
1052     else
1053         pop-parse-idx
1054         false
1055     then
1056 ;
1057
1058 : str-equiv? ( str -- bool )
1059
1060     push-parse-idx
1061
1062     true -rot
1063
1064     swap dup rot + swap
1065
1066     do
1067         i @ nextchar <> if
1068             drop false
1069             leave
1070         then
1071
1072         inc-parse-idx
1073     loop
1074
1075     delim? false = if drop false then
1076
1077     pop-parse-idx
1078 ;
1079
1080 : character? ( -- bool )
1081     nextchar [char] # <> if false exit then
1082
1083     push-parse-idx
1084     inc-parse-idx
1085
1086     nextchar [char] \ <> if pop-parse-idx false exit then
1087
1088     inc-parse-idx
1089
1090     S" newline" str-equiv? if pop-parse-idx true exit then
1091     S" space" str-equiv? if pop-parse-idx true exit then
1092     S" tab" str-equiv? if pop-parse-idx true exit then
1093
1094     charavailable? false = if pop-parse-idx false exit then
1095
1096     pop-parse-idx true
1097 ;
1098
1099 : pair? ( -- bool )
1100     nextchar [char] ( = ;
1101
1102 : string? ( -- bool )
1103     nextchar [char] " = ;
1104
1105 : readfixnum ( -- fixnum )
1106     plus? minus? or if
1107         minus?
1108         inc-parse-idx
1109     else
1110         false
1111     then
1112
1113     0
1114
1115     begin digit? while
1116         10 * nextchar [char] 0 - +
1117         inc-parse-idx
1118     repeat
1119
1120     swap if negate then
1121
1122     fixnum-type
1123 ;
1124
1125 : readflonum ( -- flonum )
1126     readfixnum drop
1127     dup 0< swap abs i->f
1128
1129     [char] . nextchar = if
1130         inc-parse-idx
1131
1132         10.0 ( f exp )
1133
1134         begin digit? while
1135             nextchar [char] 0 - i->f ( f exp d )
1136             over f/ rot f+ ( exp f' )
1137             swap 10.0 f* ( f' exp' )
1138             inc-parse-idx
1139         repeat
1140
1141         drop
1142     then
1143
1144     [char] e nextchar = [char] E nextchar = or if
1145         inc-parse-idx
1146         10.0
1147         readfixnum drop i->f
1148         f^ f*
1149     then
1150
1151     swap if
1152         -1.0 f*
1153     then
1154
1155     flonum-type
1156 ;
1157
1158 : make-rational ( fixnum fixnum -- ratnum|fixnum )
1159     drop swap drop
1160     simplify
1161
1162     dup 1 = if
1163         drop fixnum-type
1164     else
1165         fixnum-type swap fixnum-type
1166         cons drop ratnum-type
1167     then
1168 ;
1169
1170 : readratnum ( -- ratnum )
1171     readfixnum inc-parse-idx readfixnum
1172     make-rational
1173 ;
1174
1175 : readbool ( -- bool-obj )
1176     inc-parse-idx
1177     
1178     nextchar [char] f = if
1179         false
1180     else
1181         true
1182     then
1183
1184     inc-parse-idx
1185
1186     boolean-type
1187 ;
1188
1189 : readchar ( -- char-obj )
1190     inc-parse-idx
1191     inc-parse-idx
1192
1193     S" newline" str-equiv? if 7 parse-idx +! '\n' character-type exit then
1194     S" space" str-equiv? if 5 parse-idx +! bl character-type exit then
1195     S" tab" str-equiv? if 3 parse-idx +! 9 character-type exit then
1196
1197     nextchar character-type
1198
1199     inc-parse-idx
1200 ;
1201
1202 : readstring ( -- charlist )
1203
1204     nil nil
1205
1206     begin
1207         nextchar [char] " <>
1208     while
1209         nextchar [char] \ = if
1210             inc-parse-idx
1211             nextchar case
1212                 [char] n of '\n' endof
1213                 [char] " of [char] " endof
1214                 [char] \
1215             endcase
1216         else
1217             nextchar
1218         then
1219         inc-parse-idx character-type
1220         nil cons
1221
1222         ( firstchar prevchar thischar )
1223
1224         2swap nil? if
1225             2drop 2swap 2drop 2dup  ( thischar thischar )
1226         else
1227             ( firstchar thischar prevchar )
1228             2over 2swap  set-cdr! ( firstchar thischar )
1229         then
1230     repeat
1231
1232     \ Discard previous character
1233     2drop
1234
1235     inc-parse-idx
1236     delim? false = if
1237         bold fg red
1238         ." No delimiter following right double quote. Aborting." cr
1239         reset-term abort
1240     then
1241
1242     dec-parse-idx
1243
1244     nil? if
1245         nil cons
1246     then
1247     drop string-type
1248 ;
1249
1250 : readsymbol ( -- charlist )
1251     delim? if nil exit then
1252
1253     nextchar inc-parse-idx character-type
1254
1255     recurse
1256
1257     cons
1258 ;
1259
1260 : readpair ( -- pairobj )
1261     eatspaces
1262
1263     \ Empty lists
1264     nextchar [char] ) = if
1265         inc-parse-idx
1266
1267         delim? false = if
1268             bold fg red
1269             ." No delimiter following right paren. Aborting." cr
1270             reset-term abort
1271         then
1272
1273         dec-parse-idx
1274
1275         0 nil-type exit
1276     then
1277
1278     \ Read first pair element
1279     read
1280
1281     \ Pairs
1282     eatspaces
1283     nextchar [char] . = if
1284         inc-parse-idx
1285
1286         delim? false = if
1287             bold fg red
1288             ." No delimiter following '.'. Aborting." cr
1289             reset-term abort
1290         then
1291
1292         eatspaces read
1293     else
1294         recurse
1295     then
1296
1297     eatspaces
1298
1299     cons
1300 ;
1301
1302 \ Parse a scheme expression
1303 :noname ( -- obj )
1304
1305     eatspaces
1306
1307     fixnum? if
1308         readfixnum
1309         exit
1310     then
1311
1312     flonum? if
1313         readflonum
1314         exit
1315     then
1316
1317     ratnum? if
1318         readratnum
1319         exit
1320     then
1321
1322     boolean? if
1323         readbool
1324         exit
1325     then
1326
1327     character? if
1328         readchar
1329         exit
1330     then
1331
1332     string? if
1333         inc-parse-idx
1334
1335         readstring
1336
1337         nextchar [char] " <> if
1338             bold red ." Missing closing double-quote." reset-term cr
1339             abort
1340         then
1341
1342         inc-parse-idx
1343
1344         exit
1345     then
1346
1347     pair? if
1348         inc-parse-idx
1349
1350         eatspaces
1351
1352         readpair
1353
1354         eatspaces
1355
1356         nextchar [char] ) <> if
1357             bold red ." Missing closing paren." reset-term cr
1358             abort
1359         then
1360
1361         inc-parse-idx
1362
1363         exit
1364     then
1365
1366     nextchar [char] ' = if
1367         inc-parse-idx
1368         quote-symbol recurse nil cons cons exit
1369     then
1370
1371     nextchar [char] ` = if
1372         inc-parse-idx
1373         quasiquote-symbol recurse nil cons cons exit
1374     then
1375
1376     nextchar [char] , = if
1377         inc-parse-idx
1378         nextchar [char] @ = if
1379             inc-parse-idx
1380             unquote-splicing-symbol recurse nil cons cons exit
1381         else
1382             unquote-symbol recurse nil cons cons exit
1383         then
1384     then
1385
1386     eof? if
1387         EOF character-type
1388         inc-parse-idx
1389         exit
1390     then
1391
1392     nextchar [char] ) = if
1393         inc-parse-idx
1394         except-message: ." unmatched closing parenthesis." recoverable-exception throw
1395     then
1396
1397     \ Anything else is parsed as a symbol
1398     readsymbol charlist>symbol
1399
1400     \ Replace Î» with lambda
1401     2dup Î»-symbol objeq? if
1402         2drop lambda-symbol
1403     then
1404     
1405
1406 ; is read
1407
1408 \ }}}
1409
1410 \ ---- Syntax ---- {{{
1411
1412 : self-evaluating? ( obj -- obj bool )
1413     boolean-type istype? if true exit then
1414     fixnum-type istype? if true exit then
1415     flonum-type istype? if true exit then
1416     ratnum-type istype? if true exit then
1417     character-type istype? if true exit then
1418     string-type istype? if true exit then
1419     nil-type istype? if true exit then
1420     none-type istype? if true exit then
1421
1422     false
1423 ;
1424
1425 : tagged-list? ( obj tag-obj -- obj bool )
1426     2over 
1427     pair-type istype? false = if
1428         2drop 2drop false
1429     else
1430         car objeq?
1431     then ;
1432
1433 : quote? ( obj -- obj bool )
1434     quote-symbol tagged-list?  ;
1435
1436 : quote-body ( quote-obj -- quote-body-obj )
1437     cdr car ;
1438
1439 : variable? ( obj -- obj bool )
1440     symbol-type istype? ;
1441
1442 : definition? ( obj -- obj bool )
1443     define-symbol tagged-list? ;
1444
1445 : definition-var ( obj -- var )
1446     cdr car ;
1447
1448 : definition-val ( obj -- val )
1449     cdr cdr car ;
1450
1451 : assignment? ( obj -- obj bool )
1452     set!-symbol tagged-list? ;
1453
1454 : assignment-var ( obj -- var )
1455     cdr car ;
1456     
1457 : assignment-val ( obj -- val )
1458     cdr cdr car ;
1459
1460 : macro-definition? ( obj -- obj bool )
1461     define-macro-symbol tagged-list? ;
1462
1463 : macro-definition-name ( exp -- mname )
1464     cdr car car ;
1465
1466 : macro-definition-params ( exp -- params )
1467     cdr car cdr ;
1468
1469 : macro-definition-body ( exp -- body )
1470     cdr cdr ;
1471
1472 : if? ( obj -- obj bool )
1473     if-symbol tagged-list? ;
1474
1475 : if-predicate ( ifobj -- pred )
1476     cdr car ;
1477
1478 : if-consequent ( ifobj -- conseq )
1479     cdr cdr car ;
1480
1481 : if-alternative ( ifobj -- alt|none )
1482     cdr cdr cdr
1483     nil? if
1484         2drop none
1485     else
1486         car
1487     then ;
1488
1489 : false? ( boolobj -- boolean )
1490     boolean-type istype? if
1491         false boolean-type objeq?
1492     else
1493         2drop false
1494     then
1495 ;
1496
1497 : true? ( boolobj -- bool )
1498     false? invert ;
1499
1500 : lambda? ( obj -- obj bool )
1501     lambda-symbol tagged-list? ;
1502
1503 : lambda-parameters ( obj -- params )
1504     cdr car ;
1505
1506 : lambda-body ( obj -- body )
1507     cdr cdr ;
1508
1509 : application? ( obj -- obj bool )
1510     pair-type istype? ;
1511
1512 : operator ( obj -- operator )
1513     car ;
1514
1515 : operands ( obj -- operands )
1516     cdr ;
1517
1518 : nooperands? ( operands -- bool )
1519     nil objeq? ;
1520
1521 : first-operand ( operands -- operand )
1522     car ;
1523
1524 : rest-operands ( operands -- other-operands )
1525     cdr ;
1526
1527 : procedure-params ( proc -- params )
1528     drop pair-type car ;
1529
1530 : procedure-body ( proc -- body )
1531     drop pair-type cdr car ;
1532
1533 : procedure-env ( proc -- body )
1534     drop pair-type cdr cdr car ;
1535
1536 ( Ensure terminating symbol arg name is handled
1537   specially to allow for variadic procedures. )
1538 : flatten-proc-args ( argvals argnames -- argvals' argnames' )
1539     nil? if
1540         2over nil? false = if
1541             except-message: ." Too many arguments for compound procedure." recoverable-exception throw
1542         else
1543             2drop
1544         then
1545         exit
1546     then
1547
1548     symbol-type istype? if
1549         nil cons
1550         2swap
1551         nil cons
1552         2swap
1553         exit
1554     then
1555
1556     2over
1557     nil? if
1558         except-message: ." Too few arguments for compound procedure." recoverable-exception throw
1559     else
1560         cdr
1561     then
1562
1563     2over cdr
1564
1565     recurse ( argvals argnames argvals'' argnames'' )
1566     2rot car 2swap cons  ( argvals argvals'' argnames' )
1567     2rot car 2rot cons ( argnames' argvals' )
1568     2swap
1569 ;
1570
1571 \ }}}
1572
1573 \ ---- Analyze ---- {{{
1574
1575 : evaluate-eproc ( eproc env --- res )
1576
1577     >R >R
1578
1579     begin
1580         nil? invert
1581     while
1582         2dup car
1583         2swap cdr
1584     repeat
1585     
1586     2drop \ get rid of null
1587
1588     R> R> 2swap
1589
1590     \ Final element of eproc list is primitive procedure
1591     drop \ dump type signifier
1592
1593     goto \ jump straight to primitive procedure (executor)
1594 ;
1595
1596 : self-evaluating-executor ( exp env -- exp )
1597     2drop ;
1598
1599 : analyze-self-evaluating ( exp --- eproc )
1600     ['] self-evaluating-executor primitive-proc-type
1601     nil cons cons
1602 ;
1603
1604 : quote-executor ( exp env -- exp )
1605     2drop ;
1606
1607 : analyze-quoted ( exp -- eproc )
1608     quote-body
1609
1610     ['] quote-executor primitive-proc-type
1611     nil cons cons
1612 ;
1613
1614 : variable-executor ( var env -- val )
1615     lookup-var ;
1616
1617 : analyze-variable ( exp -- eproc )
1618     ['] variable-executor primitive-proc-type
1619     nil cons cons
1620 ;
1621
1622 : definition-executor ( var val-eproc env -- ok )
1623     2swap 2over ( var env val-eproc env )
1624     evaluate-eproc 2swap ( var val env )
1625     define-var
1626     ok-symbol
1627 ;
1628
1629 : analyze-definition ( exp -- eproc )
1630     2dup definition-var
1631     2swap definition-val analyze
1632
1633     ['] definition-executor primitive-proc-type
1634     nil cons cons cons
1635 ;
1636
1637 : assignment-executor ( var val-eproc env -- ok )
1638     2swap 2over ( var env val-eproc env )
1639     evaluate-eproc 2swap ( var val env )
1640     set-var
1641     ok-symbol
1642 ;
1643
1644 : analyze-assignment ( exp -- eproc )
1645     2dup assignment-var
1646     2swap assignment-val analyze ( var val-eproc )
1647
1648     ['] assignment-executor primitive-proc-type
1649     nil cons cons cons
1650 ;
1651
1652 : sequence-executor ( eproc-list env -- res )
1653     2swap
1654
1655     begin
1656         2dup cdr ( env elist elist-rest)
1657         nil? invert
1658     while
1659         -2rot car 2over ( elist-rest env elist-head env )
1660         evaluate-eproc  ( elist-rest env head-res )
1661         2drop 2swap     ( env elist-rest )
1662     repeat
1663
1664     2drop car 2swap
1665     ['] evaluate-eproc goto
1666 ;
1667
1668
1669 : (analyze-sequence) ( explist -- eproc-list )
1670     nil? if exit then
1671
1672     2dup car analyze
1673     2swap cdr recurse
1674
1675     cons
1676 ;
1677
1678 : analyze-sequence ( explist -- eproc )
1679     (analyze-sequence)
1680     ['] sequence-executor primitive-proc-type
1681     nil cons cons
1682 ;
1683
1684
1685 : macro-definition-executor  ( name params bproc env -- ok )
1686     make-macro ok-symbol
1687 ;
1688
1689 : analyze-macro-definition ( exp -- eproc )
1690     2dup macro-definition-name
1691     2swap 2dup macro-definition-params
1692     2swap macro-definition-body analyze-sequence
1693
1694     ['] macro-definition-executor primitive-proc-type
1695     nil cons cons cons cons
1696 ;
1697
1698 : if-executor ( cproc aproc pproc env -- res )
1699     2swap 2over ( cproc aproc env pproc env -- res )
1700     evaluate-eproc
1701
1702     true? if
1703         2swap 2drop
1704     else
1705         2rot 2drop
1706     then
1707
1708     ['] evaluate-eproc goto
1709 ;
1710
1711 : analyze-if ( exp -- eproc )
1712     2dup if-consequent analyze
1713     2swap 2dup if-alternative analyze
1714     2swap if-predicate analyze
1715
1716     ['] if-executor primitive-proc-type
1717     nil cons cons cons cons
1718 ;
1719
1720 : lambda-executor ( params bproc env -- res )
1721     make-procedure
1722     ( Although this is packaged up as a regular compound procedure,
1723       the "body" element contains an _eproc_ to be evaluated in an
1724       environment resulting from extending env with the parameter
1725       bindings. )
1726 ;
1727
1728 : analyze-lambda ( exp -- eproc )
1729     2dup lambda-parameters
1730     2swap lambda-body
1731
1732     nil? if
1733         except-message: ." encountered lambda with an empty body." recoverable-exception throw
1734     then
1735
1736     analyze-sequence
1737
1738     ['] lambda-executor primitive-proc-type
1739     nil cons cons cons
1740 ;
1741
1742 : operand-eproc-list ( operands -- eprocs )
1743     nil? invert if
1744         2dup car analyze
1745         2swap cdr recurse
1746         cons
1747     then
1748 ;
1749
1750 : evaluate-operand-eprocs ( env aprocs -- vals )
1751     nil? if
1752         2swap 2drop
1753     else
1754         2over 2over car 2swap evaluate-eproc ( env aprocs thisval )
1755         -2rot cdr recurse ( thisval restvals )
1756         cons
1757     then
1758 ;
1759
1760 : apply ( vals proc )
1761     dup case
1762         primitive-proc-type of
1763             drop execute
1764         endof
1765
1766         compound-proc-type of
1767                 2dup procedure-body ( argvals proc bproc )
1768                 -2rot 2dup procedure-params ( bproc argvals proc argnames )
1769                 -2rot procedure-env ( bproc argnames argvals procenv )
1770
1771                 -2rot 2swap
1772                 flatten-proc-args
1773                 2swap 2rot
1774
1775                 extend-env ( bproc env )
1776
1777                ['] evaluate-eproc goto
1778         endof
1779
1780         continuation-type of
1781           \ TODO: Apply continuation
1782         endof
1783
1784         except-message: ." object '" drop print ." ' not applicable." recoverable-exception throw
1785     endcase
1786 ;
1787
1788 : application-executor ( operator-proc arg-procs env -- res )
1789     2rot 2over ( aprocs env fproc env )
1790     evaluate-eproc ( aprocs env proc )
1791
1792     -2rot 2swap ( proc env aprocs )
1793     evaluate-operand-eprocs ( proc vals )
1794
1795     2swap ( vals proc )
1796
1797     ['] apply goto
1798 ;
1799
1800 : analyze-application ( exp -- eproc )
1801     2dup operator analyze
1802     2swap operands operand-eproc-list
1803
1804     ['] application-executor primitive-proc-type
1805     nil cons cons cons
1806 ;
1807
1808 :noname ( exp --- eproc )
1809
1810     self-evaluating? if analyze-self-evaluating exit then
1811
1812     quote? if analyze-quoted exit then
1813     
1814     variable? if analyze-variable exit then
1815
1816     definition? if analyze-definition exit then
1817
1818     assignment? if analyze-assignment exit then
1819
1820     macro-definition? if analyze-macro-definition exit then
1821
1822     if? if analyze-if exit then
1823
1824     lambda? if analyze-lambda exit then
1825
1826     application? if analyze-application exit then
1827
1828     except-message: ." tried to analyze unknown expression type." recoverable-exception throw
1829
1830 ; is analyze
1831
1832 \ }}}
1833
1834 \ ---- Macro Expansion ---- {{{
1835
1836 ( Simply evaluates the given procedure with expbody as its argument. )
1837 : macro-eval ( proc expbody -- result )
1838     2swap
1839     2dup procedure-body ( expbody proc bproc )
1840     -2rot 2dup procedure-params ( bproc expbody proc argnames )
1841     -2rot procedure-env ( bproc argnames expbody procenv )
1842     
1843     -2rot 2swap
1844     flatten-proc-args
1845     2swap 2rot
1846
1847     extend-env ( bproc env )
1848
1849     ['] evaluate-eproc goto
1850 ;
1851
1852 : expand-macro ( exp -- result )
1853     pair-type istype? invert if exit then
1854
1855     2dup car symbol-type istype? invert if 2drop exit then
1856
1857     lookup-macro nil? if 2drop exit then
1858
1859     2over cdr macro-eval
1860
1861     2dup no-match-symbol objeq? if
1862         2drop exit
1863     else
1864         2swap 2drop
1865     then
1866
1867     R> drop ['] expand goto-deferred
1868 ;
1869
1870 : expand-definition ( exp -- result )
1871     define-symbol 2swap
1872
1873     2dup definition-var
1874     2swap definition-val expand
1875     nil ( define var val' nil )
1876
1877     cons cons cons ;
1878
1879 : expand-assignment ( exp -- result )
1880     set!-symbol 2swap
1881
1882     2dup assignment-var
1883     2swap assignment-val expand
1884     nil ( define var val' nil )
1885
1886     cons cons cons ;
1887
1888 : expand-list ( exp -- res )
1889     nil? if exit then
1890
1891     2dup car expand
1892     2swap cdr recurse
1893
1894     cons ;
1895
1896 : macro-definition-nameparams
1897     cdr car ;
1898
1899 : expand-define-macro ( exp -- res )
1900     define-macro-symbol 2swap
1901     2dup macro-definition-nameparams
1902     2swap macro-definition-body expand-list
1903
1904     cons cons ;
1905
1906 : expand-lambda ( exp -- res )
1907     lambda-symbol 2swap
1908     2dup lambda-parameters
1909     2swap lambda-body expand-list
1910
1911     cons cons ;
1912
1913 : expand-if ( exp -- res )
1914     if-symbol 2swap
1915     
1916     2dup if-predicate expand
1917     2swap 2dup if-consequent expand
1918     2swap if-alternative none? if
1919         2drop nil
1920     else
1921         expand nil cons
1922     then
1923
1924     cons cons cons ;
1925
1926 : expand-application ( exp -- res )
1927     2dup operator expand
1928     2swap operands expand-list
1929
1930     cons ;
1931
1932 :noname ( exp -- result )
1933     expand-macro
1934
1935     self-evaluating? if exit then
1936
1937     quote? if exit then
1938
1939     definition? if expand-definition exit then
1940
1941     assignment? if expand-assignment exit then
1942
1943     macro-definition? if expand-define-macro exit then
1944
1945     lambda? if expand-lambda exit then
1946
1947     if? if expand-if exit then
1948
1949     application? if expand-application exit then
1950
1951 ; is expand
1952
1953 \ }}}
1954
1955 :noname ( exp env -- res )
1956     2swap expand analyze 2swap evaluate-eproc
1957 ; is eval
1958
1959 \ ---- Print ---- {{{
1960
1961 : printfixnum ( fixnum -- ) drop 0 .R ;
1962
1963 : printflonum ( flonum -- ) drop f. ;
1964
1965 : printratnum ( ratnum -- )
1966     drop pair-type 2dup
1967     car print ." /" cdr print
1968 ;
1969
1970 : printbool ( bool -- )
1971     drop if
1972         ." #t"
1973     else
1974         ." #f"
1975     then
1976 ;
1977
1978 : printchar ( charobj -- )
1979     drop
1980     case
1981         9 of ." #\tab" endof
1982         bl of ." #\space" endof
1983         '\n' of ." #\newline" endof
1984         
1985         dup ." #\" emit
1986     endcase
1987 ;
1988
1989 : (printstring) ( stringobj -- )
1990     nil? if 2drop exit then
1991
1992     2dup car drop dup
1993     case
1994         '\n' of ." \n" drop endof
1995         [char] \ of ." \\" drop endof
1996         [char] " of [char] \ emit [char] " emit drop endof
1997         emit
1998     endcase
1999
2000     cdr recurse
2001 ;
2002 : printstring ( stringobj -- )
2003     [char] " emit
2004     (printstring)
2005     [char] " emit ;
2006
2007 : printsymbol ( symbolobj -- )
2008     nil-type istype? if 2drop exit then
2009
2010     2dup car drop emit
2011     cdr recurse
2012 ;
2013
2014 : printnil ( nilobj -- )
2015     2drop ." ()" ;
2016
2017 : printpair ( pairobj -- )
2018     2dup
2019     car print
2020     cdr
2021     nil-type istype? if 2drop exit then
2022     pair-type istype? if space recurse exit then
2023     ."  . " print
2024 ;
2025
2026 : printprim ( primobj -- )
2027     2drop ." <primitive procedure>" ;
2028
2029 : printcomp ( primobj -- )
2030     2drop ." <compound procedure>" ;
2031
2032 : printcont ( primobj --)
2033     2drop ." <continuation>" ;
2034
2035 : printnone ( noneobj -- )
2036     2drop ." Unspecified return value" ;
2037
2038 : printport ( port -- )
2039     2drop ." <port>" ;
2040
2041 :noname ( obj -- )
2042     fixnum-type istype? if printfixnum exit then
2043     flonum-type istype? if printflonum exit then
2044     ratnum-type istype? if printratnum exit then
2045     boolean-type istype? if printbool exit then
2046     character-type istype? if printchar exit then
2047     string-type istype? if printstring exit then
2048     symbol-type istype? if printsymbol exit then
2049     nil-type istype? if printnil exit then
2050     pair-type istype? if ." (" printpair ." )" exit then
2051     primitive-proc-type istype? if printprim exit then
2052     compound-proc-type istype? if printcomp exit then
2053     continuation-type istype? if printcont exit then
2054     none-type istype? if printnone exit then
2055     port-type istype? if printport exit then
2056
2057     except-message: ." tried to print object with unknown type." recoverable-exception throw
2058 ; is print
2059
2060 \ }}}
2061
2062 \ ---- Garbage Collection ---- {{{
2063
2064 ( Notes on garbage collection:
2065   This is a mark-sweep garbage collector, invoked by cons.
2066   The roots of the object tree used by the marking routine 
2067   include all objects in the parameter stack, and several
2068   other fixed roots such as global-env, symbol-table, macro-table,
2069   and the console-i/o-port.
2070
2071   NO OTHER OBJECTS WILL BE MARKED!
2072
2073   This places implicit restrictions on when cons can be invoked.
2074   Invoking cons when live objects are stored on the return stack
2075   or in other variables than the above will result in possible
2076   memory corruption if the cons triggers the GC. )
2077
2078
2079 : pairlike? ( obj -- obj bool )
2080     pair-type istype? if true exit then
2081     string-type istype? if true exit then
2082     symbol-type istype? if true exit then
2083     compound-proc-type istype? if true exit then
2084     port-type istype? if true exit then
2085
2086     false
2087 ;
2088
2089 : pairlike-marked? ( obj -- obj bool )
2090     over nextfrees + @ 0=
2091 ;
2092
2093 : mark-pairlike ( obj -- obj )
2094         over nextfrees + 0 swap !
2095 ;
2096
2097 : gc-unmark ( -- )
2098     scheme-memsize 0 do
2099         1 nextfrees i + !
2100     loop
2101 ;
2102
2103 : gc-mark-obj ( obj -- )
2104
2105     pairlike? invert if 2drop exit then
2106     pairlike-marked? if 2drop exit then
2107
2108     mark-pairlike
2109
2110     drop pair-type 2dup
2111
2112     car recurse
2113     cdr recurse
2114 ;
2115
2116 : gc-sweep
2117     scheme-memsize nextfree !
2118     0 scheme-memsize 1- do
2119         nextfrees i + @ 0<> if
2120             nextfree @ nextfrees i + !
2121             i nextfree !
2122         then
2123     -1 +loop
2124 ;
2125
2126 \ Following a GC, this gives the amount of free memory
2127 : gc-count-marked
2128     0
2129     scheme-memsize 0 do
2130         nextfrees i + @ 0= if 1+ then
2131     loop
2132 ;
2133
2134 \ Debugging word - helps spot memory that is retained
2135 : gc-zero-unmarked
2136     scheme-memsize 0 do
2137         nextfrees i + @ 0<> if
2138             0 car-cells i + !
2139             0 cdr-cells i + !
2140         then
2141     loop
2142 ;
2143
2144 :noname
2145     \ ." GC! "
2146
2147     gc-unmark
2148
2149     symbol-table obj@ gc-mark-obj
2150     macro-table obj@ gc-mark-obj
2151     console-i/o-port obj@ gc-mark-obj
2152     global-env obj@ gc-mark-obj
2153
2154     depth object-stack-base @ do
2155         PSP0 i + 1 + @
2156         PSP0 i + 2 + @
2157
2158         gc-mark-obj
2159     2 +loop
2160
2161     gc-sweep
2162
2163     \ ." (" gc-count-marked . ." pairs marked as used.)" cr
2164 ; is collect-garbage
2165
2166 \ }}}
2167
2168 \ DEBUGGING
2169 xxxx
2170
2171 \ ---- Loading files ---- {{{
2172
2173 : load ( addr n -- finalResult )
2174     open-input-file
2175
2176     empty-parse-str
2177
2178     ok-symbol ( port res )
2179
2180     begin
2181         \ DEBUG
2182         \ bold fg blue ." READ from " 2over drop . ." ==> " reset-term
2183
2184         2over read-port ( port res obj )
2185
2186         \ DEBUG
2187         \ 2dup print cr
2188
2189         2dup EOF character-type objeq? if
2190             2drop 2swap close-port
2191             exit
2192         then
2193
2194         2swap 2drop ( port obj )
2195
2196         global-env obj@ eval ( port res )
2197     again
2198 ;
2199
2200 \ }}}
2201
2202 \ ---- Standard Library ---- {{{
2203
2204     include scheme-primitives.4th
2205
2206     init-object-stack-base
2207     s" scheme-library.scm" load 2drop
2208     
2209 \ }}}
2210
2211 \ ---- REPL ----
2212
2213 ( REPL calls REPL-BODY in a loop until repl-body returns true. )
2214 : repl-body ( -- bool )
2215     cr bold fg green ." > " reset-term
2216
2217     read-console
2218
2219     2dup EOF character-type objeq? if
2220         2drop
2221         bold fg blue ." Moriturus te saluto." reset-term cr
2222         true exit
2223     then
2224
2225     global-env obj@ eval
2226
2227     fg cyan ." ; " print reset-term
2228
2229     false
2230 ;
2231
2232 : repl
2233     empty-parse-str
2234
2235     init-object-stack-base
2236
2237     \ Display welcome message
2238     welcome-symbol nil cons global-env obj@ eval 2drop
2239
2240     begin
2241         ['] repl-body catch
2242         case
2243             recoverable-exception of false endof
2244             unrecoverable-exception of true endof
2245
2246             throw false
2247         endcase
2248     until
2249 ;
2250
2251 forth definitions
2252
2253 \ vim:fdm=marker