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