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