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