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