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