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