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