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