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