Implemented (reverse list).
[scheme.forth.jl.git] / scheme.4th
1 vocabulary scheme
2 scheme definitions
3
4 include term-colours.4th
5 include defer-is.4th
6 include float.4th
7
8 include debugging.4th
9
10 defer read
11 defer eval
12 defer print
13
14 defer collect-garbage
15
16 \ ------ Types ------
17
18 variable nexttype
19 0 nexttype !
20 : make-type
21     create nexttype @ ,
22     1 nexttype +!
23     does> @ ;
24
25 make-type fixnum-type
26 make-type realnum-type
27 make-type boolean-type
28 make-type character-type
29 make-type string-type
30 make-type nil-type
31 make-type none-type
32 make-type pair-type
33 make-type symbol-type
34 make-type primitive-proc-type
35 make-type compound-proc-type
36 make-type fileport-type
37 : istype? ( obj type -- obj bool )
38     over = ;
39
40 \ ---- List-structured memory ---- {{{
41
42 10000 constant scheme-memsize
43
44 create car-cells scheme-memsize allot
45 create car-type-cells scheme-memsize allot
46 create cdr-cells scheme-memsize allot
47 create cdr-type-cells scheme-memsize allot
48
49 create nextfrees scheme-memsize allot
50 :noname
51     scheme-memsize 0 do
52         i 1+ nextfrees i + !
53     loop
54 ; execute
55         
56 variable nextfree
57 0 nextfree !
58
59 : inc-nextfree
60     nextfrees nextfree @ + @
61     nextfree !
62
63     nextfree @ scheme-memsize >= if
64         collect-garbage
65     then
66
67     nextfree @ scheme-memsize >= if
68         fg red bold
69         ." Out of memory! Aborting."
70         reset-term abort
71     then
72 ;
73
74 : cons ( car-obj cdr-obj -- pair-obj )
75     cdr-type-cells nextfree @ + !
76     cdr-cells nextfree @ + !
77     car-type-cells nextfree @ + !
78     car-cells nextfree @ + !
79
80     nextfree @ pair-type
81     inc-nextfree
82 ;
83
84 : car ( pair-obj -- car-obj )
85     drop
86     dup car-cells + @ swap
87     car-type-cells + @
88 ;
89
90 : cdr ( pair-obj -- car-obj )
91     drop
92     dup cdr-cells + @ swap
93     cdr-type-cells + @
94 ;
95
96 : set-car! ( obj pair-obj -- )
97     drop dup
98     rot swap  car-type-cells + !
99     car-cells + !
100 ;
101
102 : set-cdr! ( obj pair-obj -- )
103     drop dup
104     rot swap  cdr-type-cells + !
105     cdr-cells + !
106 ;
107
108 : caar car car ;
109 : cadr cdr car ;
110 : cdar car cdr ;
111 : cddr cdr cdr ;
112
113 : nil 0 nil-type ;
114 : nil? nil-type istype? ;
115
116 : none 0 none-type ;
117 : none? none-type istype? ;
118
119 : objvar create nil swap , , ;
120
121 : value@ ( objvar -- val ) @ ;
122 : type@ ( objvar -- type ) 1+ @ ;
123 : value! ( newval objvar -- ) ! ;
124 : type! ( newtype objvar -- ) 1+ ! ;
125 : obj! ( newobj objvar -- ) dup rot swap 1+ ! ! ; 
126 : obj@ ( objvar -- obj ) dup @ swap 1+ @ ; 
127
128 : objeq? ( obj obj -- bool )
129     rot = -rot = and ;
130
131 : 2rot ( a1 a2 b1 b2 c1 c2 -- b1 b2 c1 c2 a1 a2 )
132     >R >R ( a1 a2 b1 b2 )
133     2swap ( b1 b2 a1 a2 )
134     R> R> ( b1 b2 a1 a2 c1 c2 )
135     2swap
136 ;
137
138 : -2rot ( a1 a2 b1 b2 c1 c2 -- c1 c2 a1 a2 b1 b2 )
139     2swap ( a1 a2 c1 c2 b1 b2 )
140     >R >R ( a1 a2 c1 c2 )
141     2swap ( c1 c2 a1 a2 )
142     R> R>
143 ;
144
145 \ }}}
146
147 \ ---- Port I/O ----  {{{
148
149 : fileport>fid ( fileport -- fid )
150     drop ;
151
152 : fid>fileport ( fid -- fileport )
153     fileport-type ;
154
155 : open-input-file ( addr n -- fileport )
156     r/o open-file drop fid>fileport
157 ;
158
159 : close-port ( fileport -- )
160     fileport>fid close-file drop
161 ;
162
163 objvar console-i/o-port
164 0 fileport-type console-i/o-port obj!
165
166 objvar current-input-port
167 console-i/o-port obj@ current-input-port obj!
168
169 : read-port ( fileport -- obj )
170     current-input-port obj!
171     read ;
172
173 : read-console ( -- obj )
174     console-i/o-port obj@ read-port ;
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 define        define-symbol
267 create-symbol define-macro  define-macro-symbol
268 create-symbol set!          set!-symbol
269 create-symbol ok            ok-symbol
270 create-symbol if            if-symbol
271 create-symbol lambda        lambda-symbol
272 create-symbol λ             λ-symbol
273 create-symbol begin         begin-symbol
274
275 \ }}}
276
277 \ ---- Environments ---- {{{
278
279 : enclosing-env ( env -- env )
280     cdr ;
281
282 : first-frame ( env -- frame )
283     car ;
284
285 : make-frame ( vars vals -- frame )
286     cons ;
287
288 : frame-vars ( frame -- vars )
289     car ;
290
291 : frame-vals ( frame -- vals )
292     cdr ;
293
294 : add-binding ( var val frame -- )
295     2swap 2over frame-vals cons
296     2over set-cdr!
297     2swap 2over frame-vars cons
298     2swap set-car!
299 ;
300
301 : extend-env ( vars vals env -- env )
302     >R >R
303     make-frame
304     R> R>
305     cons
306 ;
307
308 objvar vars
309 objvar vals
310
311 : get-vars-vals-frame ( var frame -- bool )
312     2dup frame-vars vars obj!
313     frame-vals vals obj!
314
315     begin
316         vars obj@ nil objeq? false =
317     while
318         2dup vars obj@ car objeq? if
319             2drop true
320             exit
321         then
322
323         vars obj@ cdr vars obj!
324         vals obj@ cdr vals obj!
325     repeat
326
327     2drop false
328 ;
329
330 : get-vars-vals ( var env -- vars? vals? bool )
331
332     begin
333         nil? false =
334     while
335         2over 2over first-frame
336         get-vars-vals-frame if
337             2drop 2drop
338             vars obj@ vals obj@ true
339             exit
340         then
341
342         enclosing-env
343     repeat
344
345     2drop 2drop
346     false
347 ;
348
349 hide vars
350 hide vals
351
352 : lookup-var ( var env -- val )
353     get-vars-vals if
354         2swap 2drop car
355     else
356         bold fg red ." Tried to read unbound variable." reset-term cr abort
357     then
358 ;
359
360 : set-var ( var val env -- )
361     >R >R 2swap R> R> ( val var env )
362     get-vars-vals if
363         2swap 2drop ( val vals )
364         set-car!
365     else
366         bold fg red ." Tried to set unbound variable." reset-term cr abort
367     then
368 ;
369
370 objvar env
371
372 : define-var ( var val env -- )
373     env obj! 
374
375     2over env obj@ ( var val var env )
376     get-vars-vals if
377         2swap 2drop ( var val vals )
378         set-car!
379         2drop
380     else
381         env obj@
382         first-frame ( var val frame )
383         add-binding
384     then
385 ;
386
387 hide env
388
389 : make-procedure ( params body env -- proc )
390     nil
391     cons cons cons
392     drop compound-proc-type
393 ;
394
395 objvar global-env
396 nil nil nil extend-env
397 global-env obj!
398
399 \ }}}
400
401 \ ---- Primitives ---- {{{
402
403 : make-primitive ( cfa -- )
404     bl word
405     count
406
407     \ 2dup ." Defining primitive " type ." ..." cr
408
409     cstr>charlist
410     charlist>symbol
411   
412     rot primitive-proc-type ( var prim )
413     global-env obj@ define-var
414 ;
415
416 : arg-count-error
417             bold fg red ." Incorrect argument count." reset-term cr
418             abort
419 ;
420
421 : ensure-arg-count ( args n -- )
422     dup 0= if
423         drop nil objeq? false = if
424             arg-count-error
425         then
426     else
427         -rot nil? if
428             arg-count-error
429         then
430         
431         cdr rot 1- recurse
432     then
433 ;
434
435 : arg-type-error
436             bold fg red ." Incorrect argument type." reset-term cr
437             abort
438 ;
439
440 : ensure-arg-type ( arg type -- arg )
441     istype? false = if
442         arg-type-error
443     then
444 ;
445
446
447 \ }}}
448
449 \ ---- Macros ---- {{{
450
451 objvar macro-table
452
453 ( Look up macro in macro table. Returns nil if
454   no macro is found. )
455 : lookup-macro ( name_symbol -- proc )
456     macro-table obj@
457
458     begin
459         nil? false =
460     while
461         2over 2over
462         car car objeq? if
463             2swap 2drop
464             car cdr
465             exit
466         then
467
468         cdr
469     repeat
470
471     2swap 2drop
472 ;
473
474 : make-macro ( name_symbol params body env -- )
475     make-procedure
476
477     2swap ( proc name_symbol )
478
479     macro-table obj@
480
481     begin
482         nil? false =
483     while
484         2over 2over ( proc name table name table )
485         car car objeq? if
486             2swap 2drop ( proc table )
487             car ( proc entry )
488             set-cdr!
489             exit
490         then
491
492         cdr
493     repeat
494
495     2drop
496
497     2swap cons
498     macro-table obj@ cons
499     macro-table obj!
500 ;
501
502 \ }}}
503
504 \ ---- Read ---- {{{
505
506 variable parse-idx
507 variable stored-parse-idx
508 create parse-str 161 allot
509 variable parse-str-span
510
511 create parse-idx-stack 10 allot 
512 variable parse-idx-sp
513 parse-idx-stack parse-idx-sp !
514
515 : push-parse-idx
516     parse-idx @ parse-idx-sp @ !
517     1 parse-idx-sp +!
518 ;
519
520 : pop-parse-idx
521     parse-idx-sp @ parse-idx-stack <= abort" Parse index stack underflow."
522
523     1 parse-idx-sp -!
524
525     parse-idx-sp @ @ parse-idx ! ;
526
527
528 : append-newline
529     '\n' parse-str parse-str-span @ + !
530     1 parse-str-span +! ;
531
532 : append-eof
533     4 parse-str parse-str-span @ + !
534     1 parse-str-span +!  ;
535
536 : empty-parse-str
537     0 parse-str-span !
538     0 parse-idx ! ;
539
540 : getline
541     current-input-port obj@ console-i/o-port obj@ objeq? if
542         parse-str 160 expect cr
543         span @ parse-str-span !
544     else
545         parse-str 160 current-input-port obj@ fileport>fid read-line
546         drop swap parse-str-span !
547
548         parse-str-span @ 0= and if append-eof then
549     then
550     append-newline
551     0 parse-idx ! ;
552
553 : inc-parse-idx
554     1 parse-idx +! ;
555
556 : dec-parse-idx
557     1 parse-idx -! ;
558
559 : charavailable? ( -- bool )
560     parse-str-span @ parse-idx @ > ;
561
562 : nextchar ( -- char )
563     charavailable? false = if getline then
564     parse-str parse-idx @ + @ ;
565
566 : '\t' 9 ;
567 : whitespace? ( -- bool )
568     nextchar BL = 
569     nextchar '\n' =
570     nextchar '\t' =
571     or or ;
572
573 : EOF 4 ; 
574 : eof? ( -- bool )
575     nextchar EOF = ;
576
577 : delim? ( -- bool )
578     whitespace?
579     nextchar [char] ( = or
580     nextchar [char] ) = or
581 ;
582
583 : commentstart? ( -- bool )
584     nextchar [char] ; = ;
585
586 : eatspaces
587
588     false \ Indicates whether or not we're eating a comment
589
590     begin
591         dup whitespace? or commentstart? or
592     while
593         dup nextchar '\n' = and if
594             invert \ Stop eating comment
595         else
596             dup false = commentstart? and if   
597                 invert \ Begin eating comment
598             then
599         then
600
601         inc-parse-idx
602     repeat
603     drop
604 ;
605
606 : digit? ( -- bool )
607     nextchar [char] 0 >=
608     nextchar [char] 9 <=
609     and ;
610
611 : minus? ( -- bool )
612     nextchar [char] - = ;
613
614 : plus? ( -- bool )
615     nextchar [char] + = ;
616
617 : fixnum? ( -- bool )
618     minus? plus? or if
619         inc-parse-idx
620
621         delim? if
622             dec-parse-idx
623             false exit
624         else
625             dec-parse-idx
626         then
627     else
628         digit? false = if
629             false exit
630         then
631     then
632
633     push-parse-idx
634     inc-parse-idx
635
636     begin digit? while
637         inc-parse-idx
638     repeat
639
640     delim? pop-parse-idx
641 ;
642
643 : realnum? ( -- bool )
644     push-parse-idx
645
646     minus? plus? or if
647         inc-parse-idx
648     then
649
650     \ Record starting parse idx:
651     \ Want to detect whether any characters (following +/-) were eaten.
652     parse-idx @
653
654     begin digit? while
655             inc-parse-idx
656     repeat
657
658     [char] . nextchar = if
659         inc-parse-idx
660         begin digit? while
661                 inc-parse-idx
662         repeat
663     then
664
665     [char] e nextchar = [char] E nextchar = or if
666         inc-parse-idx
667
668         minus? plus? or if
669             inc-parse-idx
670         then
671
672         digit? invert if
673             drop pop-parse-idx false exit
674         then
675
676         begin digit? while
677                 inc-parse-idx
678         repeat
679     then
680
681     \ This is a real number if characters were
682     \ eaten and the next characer is a delimiter.
683     parse-idx @ < delim? and
684
685     pop-parse-idx
686 ;
687
688 : boolean? ( -- bool )
689     nextchar [char] # <> if false exit then
690
691     push-parse-idx
692     inc-parse-idx
693
694     nextchar [char] t <>
695     nextchar [char] f <>
696     and if pop-parse-idx false exit then
697
698     inc-parse-idx
699     delim? if
700         pop-parse-idx
701         true
702     else
703         pop-parse-idx
704         false
705     then
706 ;
707
708 : str-equiv? ( str -- bool )
709
710     push-parse-idx
711
712     true -rot
713
714     swap dup rot + swap
715
716     do
717         i @ nextchar <> if
718             drop false
719             leave
720         then
721
722         inc-parse-idx
723     loop
724
725     delim? false = if drop false then
726
727     pop-parse-idx
728 ;
729
730 : character? ( -- bool )
731     nextchar [char] # <> if false exit then
732
733     push-parse-idx
734     inc-parse-idx
735
736     nextchar [char] \ <> if pop-parse-idx false exit then
737
738     inc-parse-idx
739
740     S" newline" str-equiv? if pop-parse-idx true exit then
741     S" space" str-equiv? if pop-parse-idx true exit then
742     S" tab" str-equiv? if pop-parse-idx true exit then
743
744     charavailable? false = if pop-parse-idx false exit then
745
746     pop-parse-idx true
747 ;
748
749 : pair? ( -- bool )
750     nextchar [char] ( = ;
751
752 : string? ( -- bool )
753     nextchar [char] " = ;
754
755 : readfixnum ( -- num-atom )
756     plus? minus? or if
757         minus?
758         inc-parse-idx
759     else
760         false
761     then
762
763     0
764
765     begin digit? while
766         10 * nextchar [char] 0 - +
767         inc-parse-idx
768     repeat
769
770     swap if negate then
771
772     fixnum-type
773 ;
774
775 : readrealnum ( -- realnum )
776
777     \ Remember that at this point we're guaranteed to
778     \ have a parsable real on this line.
779
780     parse-str parse-idx @ +
781
782     begin delim? false = while
783             inc-parse-idx
784     repeat
785
786     parse-str parse-idx @ + over -
787
788     float-parse
789
790     realnum-type
791 ;
792
793 : readbool ( -- bool-obj )
794     inc-parse-idx
795     
796     nextchar [char] f = if
797         false
798     else
799         true
800     then
801
802     inc-parse-idx
803
804     boolean-type
805 ;
806
807 : readchar ( -- char-obj )
808     inc-parse-idx
809     inc-parse-idx
810
811     S" newline" str-equiv? if 7 parse-idx +! '\n' character-type exit then
812     S" space" str-equiv? if 5 parse-idx +! bl character-type exit then
813     S" tab" str-equiv? if 3 parse-idx +! 9 character-type exit then
814
815     nextchar character-type
816
817     inc-parse-idx
818 ;
819
820 : readstring ( -- charlist )
821     nextchar [char] " = if
822         inc-parse-idx
823
824         delim? false = if
825             bold fg red
826             ." No delimiter following right double quote. Aborting." cr
827             reset-term abort
828         then
829
830         dec-parse-idx
831
832         0 nil-type exit
833     then
834
835     nextchar [char] \ = if
836         inc-parse-idx
837         nextchar case
838             [char] n of '\n' endof
839             [char] " of [char] " endof
840             [char] \
841         endcase
842     else
843         nextchar
844     then
845     inc-parse-idx character-type
846
847     recurse
848
849     cons
850 ;
851
852 : readsymbol ( -- charlist )
853     delim? if nil exit then
854
855     nextchar inc-parse-idx character-type
856
857     recurse
858
859     cons
860 ;
861
862 : readpair ( -- pairobj )
863     eatspaces
864
865     \ Empty lists
866     nextchar [char] ) = if
867         inc-parse-idx
868
869         delim? false = if
870             bold fg red
871             ." No delimiter following right paren. Aborting." cr
872             reset-term abort
873         then
874
875         dec-parse-idx
876
877         0 nil-type exit
878     then
879
880     \ Read first pair element
881     read
882
883     \ Pairs
884     eatspaces
885     nextchar [char] . = if
886         inc-parse-idx
887
888         delim? false = if
889             bold fg red
890             ." No delimiter following '.'. Aborting." cr
891             reset-term abort
892         then
893
894         eatspaces read
895     else
896         recurse
897     then
898
899     eatspaces
900
901     cons
902 ;
903
904 \ Parse a scheme expression
905 :noname ( -- obj )
906
907     eatspaces
908
909     fixnum? if
910         readfixnum
911         exit
912     then
913
914     realnum? if
915         readrealnum
916         exit
917     then
918
919     boolean? if
920         readbool
921         exit
922     then
923
924     character? if
925         readchar
926         exit
927     then
928
929     string? if
930         inc-parse-idx
931
932         readstring
933         drop string-type
934
935         nextchar [char] " <> if
936             bold red ." Missing closing double-quote." reset-term cr
937             abort
938         then
939
940         inc-parse-idx
941
942         exit
943     then
944
945     pair? if
946         inc-parse-idx
947
948         eatspaces
949
950         readpair
951
952         eatspaces
953
954         nextchar [char] ) <> if
955             bold red ." Missing closing paren." reset-term cr
956             abort
957         then
958
959         inc-parse-idx
960
961         exit
962     then
963
964     nextchar [char] ' = if
965         inc-parse-idx
966         quote-symbol recurse nil cons cons exit
967     then
968
969     eof? if
970         EOF character-type
971         inc-parse-idx
972         exit
973     then
974
975     \ Anything else is parsed as a symbol
976     readsymbol charlist>symbol
977
978     \ Replace λ with lambda
979     2dup λ-symbol objeq? if
980         2drop lambda-symbol
981     then
982     
983
984 ; is read
985
986 \ }}}
987
988 \ ---- Eval ---- {{{
989
990 : self-evaluating? ( obj -- obj bool )
991     boolean-type istype? if true exit then
992     fixnum-type istype? if true exit then
993     realnum-type istype? if true exit then
994     character-type istype? if true exit then
995     string-type istype? if true exit then
996     nil-type istype? if true exit then
997     none-type istype? if true exit then
998
999     false
1000 ;
1001
1002 : tagged-list? ( obj tag-obj -- obj bool )
1003     2over 
1004     pair-type istype? false = if
1005         2drop 2drop false
1006     else
1007         car objeq?
1008     then ;
1009
1010 : quote? ( obj -- obj bool )
1011     quote-symbol tagged-list?  ;
1012
1013 : quote-body ( quote-obj -- quote-body-obj )
1014     cadr ;
1015
1016 : variable? ( obj -- obj bool )
1017     symbol-type istype? ;
1018
1019 : definition? ( obj -- obj bool )
1020     define-symbol tagged-list? ;
1021
1022 : make-lambda ( params body -- lambda-exp )
1023     lambda-symbol -2rot cons cons ;
1024
1025 ( Handles iterative expansion of defines in
1026   terms of nested lambdas. Most Schemes only
1027   handle one iteration of expansion! )
1028 : definition-var-val ( obj -- var val )
1029
1030     cdr 2dup cdr 2swap car ( val var )
1031
1032     begin
1033         symbol-type istype? false =
1034     while
1035         2dup cdr 2swap car ( val formals var' )
1036         -2rot 2swap ( var' formals val )
1037         make-lambda nil cons ( var' val' )
1038         2swap ( val' var' )
1039     repeat
1040
1041     2swap car
1042 ;
1043
1044 : eval-definition ( obj env -- res )
1045     2dup 2rot ( env env obj )
1046     definition-var-val ( env env var val )
1047     2rot eval  ( env var val )
1048
1049     2rot ( var val env )
1050     define-var
1051
1052     ok-symbol
1053 ;
1054
1055 : assignment? ( obj -- obj bool )
1056     set!-symbol tagged-list? ;
1057
1058 : assignment-var ( obj -- var )
1059     cdr car ;
1060     
1061 : assignment-val ( obj -- val )
1062     cdr cdr car ;
1063
1064 : eval-assignment ( obj env -- res )
1065     2swap 
1066     2over 2over ( env obj env obj )
1067     assignment-val 2swap ( env obj valexp env )
1068     eval  ( env obj val )
1069     
1070     2swap assignment-var 2swap ( env var val )
1071
1072     2rot ( var val env )
1073     set-var
1074
1075     ok-symbol
1076 ;
1077
1078 : macro-definition? ( obj -- obj bool )
1079     define-macro-symbol tagged-list? ;
1080
1081 : macro-definition-name ( exp -- mname )
1082     cdr car car ;
1083
1084 : macro-definition-params ( exp -- params )
1085     cdr car cdr ;
1086
1087 : macro-definition-body ( exp -- body )
1088     cdr cdr ;
1089
1090 objvar env
1091 : eval-define-macro ( obj env -- res )
1092     env obj!
1093
1094     2dup macro-definition-name 2swap ( name obj )
1095     2dup macro-definition-params 2swap ( name params obj )
1096     macro-definition-body ( name params body )
1097
1098     env obj@ ( name params body env )
1099
1100     make-macro
1101
1102     ok-symbol
1103 ;
1104 hide env
1105
1106 : if? ( obj -- obj bool )
1107     if-symbol tagged-list? ;
1108
1109 : if-predicate ( ifobj -- pred )
1110     cdr car ;
1111
1112 : if-consequent ( ifobj -- conseq )
1113     cdr cdr car ;
1114
1115 : if-alternative ( ifobj -- alt|false )
1116     cdr cdr cdr
1117     nil? if
1118         2drop false
1119     else
1120         car
1121     then ;
1122
1123 : false? ( boolobj -- boolean )
1124     boolean-type istype? if
1125         false boolean-type objeq?
1126     else
1127         2drop false
1128     then
1129 ;
1130
1131 : true? ( boolobj -- bool )
1132     false? invert ;
1133
1134 : lambda? ( obj -- obj bool )
1135     lambda-symbol tagged-list? ;
1136
1137 : lambda-parameters ( obj -- params )
1138     cdr car ;
1139
1140 : lambda-body ( obj -- body )
1141     cdr cdr ;
1142
1143 : begin? ( obj -- obj bool )
1144     begin-symbol tagged-list? ;
1145
1146 : begin-actions ( obj -- actions )
1147     cdr ;
1148
1149 : eval-sequence ( explist env -- finalexp env )
1150     ( Evaluates all bar the final expressions in
1151       an an expression list. The final expression
1152       is returned to allow for tail optimization. )
1153
1154     2swap ( env explist )
1155
1156     \ Abort on empty list
1157     nil? if
1158         2drop none
1159         2swap exit
1160     then
1161
1162     begin
1163         2dup cdr ( env explist nextexplist )
1164         nil? false =
1165     while
1166         -2rot car 2over ( nextexplist env exp env )
1167         eval
1168         2drop \ discard result
1169         2swap ( env nextexplist )
1170     repeat
1171
1172     2drop car 2swap ( finalexp env )
1173 ;
1174
1175 : application? ( obj -- obj bool )
1176     pair-type istype? ;
1177
1178 : operator ( obj -- operator )
1179     car ;
1180
1181 : operands ( obj -- operands )
1182     cdr ;
1183
1184 : nooperands? ( operands -- bool )
1185     nil objeq? ;
1186
1187 : first-operand ( operands -- operand )
1188     car ;
1189
1190 : rest-operands ( operands -- other-operands )
1191     cdr ;
1192
1193 : list-of-vals ( args env -- vals )
1194     2swap
1195
1196     2dup nooperands? if
1197         2swap 2drop
1198     else
1199         2over 2over first-operand 2swap eval
1200         -2rot rest-operands 2swap recurse
1201         cons
1202     then
1203 ;
1204
1205 : procedure-params ( proc -- params )
1206     drop pair-type car ;
1207
1208 : procedure-body ( proc -- body )
1209     drop pair-type cdr car ;
1210
1211 : procedure-env ( proc -- body )
1212     drop pair-type cdr cdr car ;
1213
1214 ( Ensure terminating symbol arg name is handled
1215   specially to allow for variadic procedures. )
1216 : flatten-proc-args ( argvals argnames -- argvals' argnames' )
1217     nil? if
1218         2over nil? false = if
1219             bold fg red ." Too many arguments supplied to compound method. Aborting." reset-term cr
1220             abort
1221         else
1222             2drop
1223         then
1224         exit
1225     then
1226
1227     symbol-type istype? if
1228         nil cons
1229         2swap
1230         nil cons
1231         2swap
1232         exit
1233     then
1234
1235     2over
1236     nil? if
1237         bold fg red ." Too few arguments supplied to compound method. Aborting." reset-term cr
1238         abort
1239     else
1240         cdr
1241     then
1242
1243     2over cdr
1244
1245     recurse ( argvals argnames argvals'' argnames'' )
1246     2rot car 2swap cons  ( argvals argvals'' argnames' )
1247     2rot car 2rot cons ( argnames' argvals' )
1248     2swap
1249 ;
1250
1251 : apply ( proc argvals -- result )
1252         2swap dup case
1253             primitive-proc-type of
1254                 drop execute     
1255             endof
1256
1257             compound-proc-type of
1258                 2dup procedure-body ( argvals proc body )
1259                 -2rot 2dup procedure-params ( body argvals proc argnames )
1260                 -2rot procedure-env ( body argnames argvals procenv )
1261
1262                 -2rot 2swap
1263                 flatten-proc-args
1264                 2swap 2rot
1265
1266                 extend-env ( body env )
1267
1268                 eval-sequence
1269
1270                 R> drop ['] eval goto-deferred  \ Tail call optimization
1271             endof
1272
1273             bold fg red ." Object not applicable. Aborting." reset-term cr
1274             abort
1275         endcase
1276 ;
1277
1278 : macro-expand ( proc expbody -- result )
1279     2swap
1280     2dup procedure-body ( expbody proc procbody )
1281     -2rot 2dup procedure-params ( procbody expbody proc argnames )
1282     -2rot procedure-env ( procbody argnames expbody procenv )
1283     
1284     -2rot 2swap
1285     flatten-proc-args
1286     2swap 2rot
1287
1288     extend-env eval-sequence eval
1289 ;
1290
1291 :noname ( obj env -- result )
1292     2swap
1293
1294     self-evaluating? if
1295         2swap 2drop
1296         exit
1297     then
1298
1299     quote? if
1300         quote-body
1301         2swap 2drop
1302         exit
1303     then
1304
1305     variable? if
1306         2swap lookup-var
1307         exit
1308     then
1309
1310     definition? if
1311         2swap eval-definition
1312         exit
1313     then
1314
1315     assignment? if
1316         2swap eval-assignment
1317         exit
1318     then
1319
1320     macro-definition? if
1321         2swap eval-define-macro
1322         exit
1323     then
1324
1325     if? if
1326         2over 2over
1327         if-predicate
1328         2swap eval 
1329
1330         true? if
1331             if-consequent
1332         else
1333             if-alternative
1334         then
1335
1336         2swap
1337         ['] eval goto-deferred
1338     then
1339
1340     lambda? if
1341         2dup lambda-parameters
1342         2swap lambda-body
1343         2rot make-procedure
1344         exit
1345     then
1346
1347     begin? if
1348         begin-actions 2swap
1349         eval-sequence
1350         ['] eval goto-deferred
1351     then
1352
1353     application? if
1354
1355         2over 2over ( env exp env exp )
1356         operator ( env exp env opname )
1357
1358         2dup lookup-macro nil? false = if
1359              \ Macro function evaluation
1360
1361             ( env exp env opname mproc )
1362             2swap 2drop -2rot 2drop cdr ( env mproc body )
1363
1364             2dup print cr
1365             macro-expand
1366             2dup print cr
1367
1368             2swap
1369             ['] eval goto-deferred
1370         else
1371            \ Regular function application
1372
1373             2drop ( env exp env opname )
1374
1375             2swap eval ( env exp proc )
1376
1377             -2rot ( proc env exp )
1378             operands 2swap ( proc operands env )
1379             list-of-vals ( proc argvals )
1380
1381             apply
1382             exit
1383         then
1384     then
1385
1386     bold fg red ." Error evaluating expression - unrecognized type. Aborting." reset-term cr
1387     abort
1388 ; is eval
1389
1390 \ }}}
1391
1392 \ ---- Print ---- {{{
1393
1394 : printfixnum ( fixnumobj -- ) drop 0 .R ;
1395
1396 : printrealnum ( realnumobj -- ) drop float-print ;
1397
1398 : printbool ( numobj -- )
1399     drop if
1400         ." #t"
1401     else
1402         ." #f"
1403     then
1404 ;
1405
1406 : printchar ( charobj -- )
1407     drop
1408     case
1409         9 of ." #\tab" endof
1410         bl of ." #\space" endof
1411         '\n' of ." #\newline" endof
1412         
1413         dup ." #\" emit
1414     endcase
1415 ;
1416
1417 : (printstring) ( stringobj -- )
1418     nil-type istype? if 2drop exit then
1419
1420     2dup car drop dup
1421     case
1422         '\n' of ." \n" drop endof
1423         [char] \ of ." \\" drop endof
1424         [char] " of [char] \ emit [char] " emit drop endof
1425         emit
1426     endcase
1427
1428     cdr recurse
1429 ;
1430 : printstring ( stringobj -- )
1431     [char] " emit
1432     (printstring)
1433     [char] " emit ;
1434
1435 : printsymbol ( symbolobj -- )
1436     nil-type istype? if 2drop exit then
1437
1438     2dup car drop emit
1439     cdr recurse
1440 ;
1441
1442 : printnil ( nilobj -- )
1443     2drop ." ()" ;
1444
1445 : printpair ( pairobj -- )
1446     2dup
1447     car print
1448     cdr
1449     nil-type istype? if 2drop exit then
1450     pair-type istype? if space recurse exit then
1451     ."  . " print
1452 ;
1453
1454 : printprim ( primobj -- )
1455     2drop ." <primitive procedure>" ;
1456
1457 : printcomp ( primobj -- )
1458     2drop ." <compound procedure>" ;
1459
1460 : printnone ( noneobj -- )
1461     2drop ." Unspecified return value" ;
1462
1463 :noname ( obj -- )
1464     fixnum-type istype? if printfixnum exit then
1465     realnum-type istype? if printrealnum exit then
1466     boolean-type istype? if printbool exit then
1467     character-type istype? if printchar exit then
1468     string-type istype? if printstring exit then
1469     symbol-type istype? if printsymbol exit then
1470     nil-type istype? if printnil exit then
1471     pair-type istype? if ." (" printpair ." )" exit then
1472     primitive-proc-type istype? if printprim exit then
1473     compound-proc-type istype? if printcomp exit then
1474     none-type istype? if printnone exit then
1475
1476     bold fg red ." Error printing expression - unrecognized type. Aborting" reset-term cr
1477     abort
1478 ; is print
1479
1480 \ }}}
1481
1482 \ ---- Garbage Collection ---- {{{
1483
1484 variable gc-enabled
1485 false gc-enabled !
1486
1487 variable gc-stack-depth
1488
1489 : enable-gc
1490     depth gc-stack-depth !
1491     true gc-enabled ! ;
1492
1493 : disable-gc
1494     false gc-enabled ! ;
1495
1496 : gc-enabled?
1497     gc-enabled @ ;
1498
1499 : pairlike? ( obj -- obj bool )
1500     pair-type istype? if true exit then
1501     string-type istype? if true exit then
1502     symbol-type istype? if true exit then
1503     compound-proc-type istype? if true exit then
1504
1505     false
1506 ;
1507
1508 : pairlike-marked? ( obj -- obj bool )
1509     over nextfrees + @ 0=
1510 ;
1511
1512 : mark-pairlike ( obj -- obj )
1513         over nextfrees + 0 swap !
1514 ;
1515
1516 : gc-unmark ( -- )
1517     scheme-memsize 0 do
1518         1 nextfrees i + !
1519     loop
1520 ;
1521
1522 : gc-mark-obj ( obj -- )
1523
1524     pairlike? invert if 2drop exit then
1525     pairlike-marked? if 2drop exit then
1526
1527     mark-pairlike
1528
1529     drop pair-type 2dup
1530
1531     car recurse
1532     cdr recurse
1533 ;
1534
1535 : gc-sweep
1536     scheme-memsize nextfree !
1537     0 scheme-memsize 1- do
1538         nextfrees i + @ 0<> if
1539             nextfree @ nextfrees i + !
1540             i nextfree !
1541         then
1542     -1 +loop
1543 ;
1544
1545 \ Following a GC, this gives the amount of free memory
1546 : gc-count-marked
1547     0
1548     scheme-memsize 0 do
1549         nextfrees i + @ 0= if 1+ then
1550     loop
1551 ;
1552
1553 \ Debugging word - helps spot memory that is retained
1554 : gc-zero-unmarked
1555     scheme-memsize 0 do
1556         nextfrees i + @ 0<> if
1557             0 car-cells i + !
1558             0 cdr-cells i + !
1559         then
1560     loop
1561 ;
1562
1563 :noname
1564     \ ." GC! "
1565
1566     gc-unmark
1567
1568     symbol-table obj@ gc-mark-obj
1569     macro-table obj@ gc-mark-obj
1570     global-env obj@ gc-mark-obj
1571
1572     depth gc-stack-depth @ do
1573         PSP0 i + 1 + @
1574         PSP0 i + 2 + @
1575
1576         gc-mark-obj
1577     2 +loop
1578
1579     gc-sweep
1580
1581     \ ." (" gc-count-marked . ." pairs marked as used.)" cr
1582 ; is collect-garbage
1583
1584 \ }}}
1585
1586 \ ---- Loading files ---- {{{
1587
1588 : charlist>cstr ( charlist addr -- n )
1589
1590     dup 2swap ( origaddr addr charlist )
1591
1592     begin 
1593         nil? false =
1594     while
1595         2dup cdr 2swap car 
1596         drop ( origaddr addr charlist char )
1597         -rot 2swap ( origaddr charlist addr char )
1598         over !
1599         1+ -rot ( origaddr nextaddr charlist )
1600     repeat
1601
1602     2drop ( origaddr finaladdr ) 
1603     swap -
1604 ;
1605
1606 : load ( addr n -- finalResult )
1607     open-input-file
1608
1609     empty-parse-str
1610
1611     ok-symbol ( port res )
1612
1613     begin
1614         2over read-port ( port res obj )
1615
1616         2dup EOF character-type objeq? if
1617             2drop 2swap close-port
1618             exit
1619         then
1620
1621         2swap 2drop ( port obj )
1622
1623         global-env obj@ eval ( port res )
1624     again
1625 ;
1626
1627 \ }}}
1628
1629 \ ---- Standard Library ---- {{{
1630
1631     include scheme-primitives.4th
1632
1633     s" scheme-library.scm" load 2drop
1634     
1635 \ }}}
1636
1637 \ ---- REPL ----
1638
1639 : repl
1640     cr ." Welcome to scheme.forth.jl!" cr
1641        ." Use Ctrl-D to exit." cr
1642
1643     empty-parse-str
1644
1645     enable-gc
1646
1647     begin
1648         cr bold fg green ." > " reset-term
1649         read-console
1650
1651         2dup EOF character-type objeq? if
1652             2drop
1653             bold fg blue ." Moriturus te saluto." reset-term cr
1654             exit
1655         then
1656
1657         global-env obj@ eval
1658
1659         fg cyan ." ; " print reset-term
1660     again
1661 ;
1662
1663 forth definitions
1664
1665 \ vim:fdm=marker