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