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