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