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