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