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