c1d42e8a9836caea4f8925e2d21e173434f71098
[scheme.forth.jl.git] / src / scheme.4th
1 vocabulary scheme
2 scheme definitions
3
4 include term-colours.4th
5 include defer-is.4th
6 include 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 flonum-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     cstr>charlist
440     charlist>symbol
441   
442     rot primitive-proc-type ( var prim )
443     global-env obj@ define-var
444 ;
445
446 : ensure-arg-count ( args n -- )
447     dup 0= if
448         drop nil objeq? false = if
449             recoverable-exception throw" Too many arguments for primitive procedure."
450         then
451     else
452         -rot nil? if
453             recoverable-exception throw" Too few arguments for primitive procedure."
454         then
455         
456         cdr rot 1- recurse
457     then
458 ;
459
460 : ensure-arg-type-and-count ( tn tn-1 ... t2 t1 args n -- )
461     dup 0= if
462         drop nil objeq? false = if
463             recoverable-exception throw" Too many arguments for primitive procedure."
464         then
465     else
466         -rot nil? if
467             recoverable-exception throw" Too few arguments for primitive procedure."
468         then
469
470         2dup cdr 2swap car ( ... t1 n args' arg1 )
471         2rot 1- swap 2swap rot ( ... args' n-1 arg1 t1 )
472         istype? false = if
473             recoverable-exception throw" Incorrect type for primitive procedure."
474         then
475
476         2drop recurse
477     then
478
479 ;
480
481 : push-args-to-stack ( args -- arg1 arg2 ... argn )
482     begin
483         nil? false =
484     while
485         2dup car 2swap cdr
486     repeat
487
488     2drop
489 ;
490
491 : add-fa-checks ( cfa n -- cfa' )
492     here current @ 1+ dup @ , !
493     0 ,
494     here -rot
495     docol ,
496     ['] 2dup , ['] lit , , ['] ensure-arg-count ,
497     ['] push-args-to-stack ,
498     ['] lit , , ['] execute ,
499     ['] exit ,
500 ;
501
502 : add-fa-type-checks ( cfa t1 t2 ... tn n -- cfa' )
503     here current @ 1+ dup @ , !
504     0 ,
505     here >R
506     docol ,
507     ['] 2dup ,
508     ['] >R , ['] >R ,
509
510     dup ( cfa t1 t2 ... tn n m )
511     
512     begin
513         ?dup 0>
514     while
515         rot ['] lit , , ( cfa t1 t2 ... tn-1 n m )
516         1-
517     repeat
518
519     ['] R> , ['] R> ,
520
521     ['] lit , , ['] ensure-arg-type-and-count ,
522
523     ['] push-args-to-stack ,
524     ['] lit , , ['] execute ,
525     ['] exit ,
526
527     R>
528 ;
529
530 : make-fa-primitive ( cfa n -- )
531     add-fa-checks make-primitive ;
532
533 : make-fa-type-primitive ( cfa t1 t2 ... tn n -- )
534     add-fa-type-checks make-primitive ;
535
536 : arg-type-error
537             bold fg red ." Incorrect argument type." reset-term cr
538             abort
539 ;
540
541 : ensure-arg-type ( arg type -- arg )
542     istype? false = if
543         recoverable-exception throw" Incorrect argument type for primitive procedure."
544     then
545 ;
546
547
548 \ }}}
549
550 \ ---- Macros ---- {{{
551
552 objvar macro-table
553
554 ( Look up macro in macro table. Returns nil if
555   no macro is found. )
556 : lookup-macro ( name_symbol -- proc )
557     macro-table obj@
558
559     begin
560         nil? false =
561     while
562         2over 2over
563         car car objeq? if
564             2swap 2drop
565             car cdr
566             exit
567         then
568
569         cdr
570     repeat
571
572     2swap 2drop
573 ;
574
575 : make-macro ( name_symbol params body env -- )
576     make-procedure
577
578     2swap ( proc name_symbol )
579
580     macro-table obj@
581
582     begin
583         nil? false =
584     while
585         2over 2over ( proc name table name table )
586         car car objeq? if
587             2swap 2drop ( proc table )
588             car ( proc entry )
589             set-cdr!
590             exit
591         then
592
593         cdr
594     repeat
595
596     2drop
597
598     2swap cons
599     macro-table obj@ cons
600     macro-table obj!
601 ;
602
603 \ }}}
604
605 \ ---- Read ---- {{{
606
607 variable parse-idx
608 variable stored-parse-idx
609 create parse-str 161 allot
610 variable parse-str-span
611
612 create parse-idx-stack 10 allot 
613 variable parse-idx-sp
614 parse-idx-stack parse-idx-sp !
615
616 : push-parse-idx
617     parse-idx @ parse-idx-sp @ !
618     1 parse-idx-sp +!
619 ;
620
621 : pop-parse-idx
622     parse-idx-sp @ parse-idx-stack <= abort" Parse index stack underflow."
623
624     1 parse-idx-sp -!
625
626     parse-idx-sp @ @ parse-idx ! ;
627
628
629 : append-newline
630     '\n' parse-str parse-str-span @ + !
631     1 parse-str-span +! ;
632
633 : append-eof
634     4 parse-str parse-str-span @ + !
635     1 parse-str-span +!  ;
636
637 : empty-parse-str
638     0 parse-str-span !
639     0 parse-idx ! ;
640
641 : getline
642     current-input-port obj@ console-i/o-port obj@ objeq? if
643         parse-str 160 expect cr
644         span @ parse-str-span !
645     else
646         parse-str 160 current-input-port obj@ fileport>fid read-line
647         drop swap parse-str-span !
648
649         parse-str-span @ 0= and if append-eof then
650     then
651     append-newline
652     0 parse-idx ! ;
653
654 : inc-parse-idx
655     1 parse-idx +! ;
656
657 : dec-parse-idx
658     1 parse-idx -! ;
659
660 : charavailable? ( -- bool )
661     parse-str-span @ parse-idx @ > ;
662
663 : nextchar ( -- char )
664     charavailable? false = if getline then
665     parse-str parse-idx @ + @ ;
666
667 : '\t' 9 ;
668 : whitespace? ( -- bool )
669     nextchar BL = 
670     nextchar '\n' =
671     nextchar '\t' =
672     or or ;
673
674 : EOF 4 ; 
675 : eof? ( -- bool )
676     nextchar EOF = ;
677
678 : delim? ( -- bool )
679     whitespace?
680     nextchar [char] ( = or
681     nextchar [char] ) = or
682 ;
683
684 : commentstart? ( -- bool )
685     nextchar [char] ; = ;
686
687 : eatspaces
688
689     false \ Indicates whether or not we're eating a comment
690
691     begin
692         dup whitespace? or commentstart? or
693     while
694         dup nextchar '\n' = and if
695             invert \ Stop eating comment
696         else
697             dup false = commentstart? and if   
698                 invert \ Begin eating comment
699             then
700         then
701
702         inc-parse-idx
703     repeat
704     drop
705 ;
706
707 : digit? ( -- bool )
708     nextchar [char] 0 >=
709     nextchar [char] 9 <=
710     and ;
711
712 : minus? ( -- bool )
713     nextchar [char] - = ;
714
715 : plus? ( -- bool )
716     nextchar [char] + = ;
717
718 : fixnum? ( -- bool )
719     minus? plus? or if
720         inc-parse-idx
721
722         delim? if
723             dec-parse-idx
724             false exit
725         else
726             dec-parse-idx
727         then
728     else
729         digit? false = if
730             false exit
731         then
732     then
733
734     push-parse-idx
735     inc-parse-idx
736
737     begin digit? while
738         inc-parse-idx
739     repeat
740
741     delim? pop-parse-idx
742 ;
743
744 : flonum? ( -- bool )
745     push-parse-idx
746
747     minus? plus? or if
748         inc-parse-idx
749     then
750
751     \ Record starting parse idx:
752     \ Want to detect whether any characters (following +/-) were eaten.
753     parse-idx @
754
755     begin digit? while
756             inc-parse-idx
757     repeat
758
759     [char] . nextchar = if
760         inc-parse-idx
761         begin digit? while
762                 inc-parse-idx
763         repeat
764     then
765
766     [char] e nextchar = [char] E nextchar = or if
767         inc-parse-idx
768
769         minus? plus? or if
770             inc-parse-idx
771         then
772
773         digit? invert if
774             drop pop-parse-idx false exit
775         then
776
777         begin digit? while
778                 inc-parse-idx
779         repeat
780     then
781
782     \ This is a real number if characters were
783     \ eaten and the next characer is a delimiter.
784     parse-idx @ < delim? and
785
786     pop-parse-idx
787 ;
788
789 : boolean? ( -- bool )
790     nextchar [char] # <> if false exit then
791
792     push-parse-idx
793     inc-parse-idx
794
795     nextchar [char] t <>
796     nextchar [char] f <>
797     and if pop-parse-idx false exit then
798
799     inc-parse-idx
800     delim? if
801         pop-parse-idx
802         true
803     else
804         pop-parse-idx
805         false
806     then
807 ;
808
809 : str-equiv? ( str -- bool )
810
811     push-parse-idx
812
813     true -rot
814
815     swap dup rot + swap
816
817     do
818         i @ nextchar <> if
819             drop false
820             leave
821         then
822
823         inc-parse-idx
824     loop
825
826     delim? false = if drop false then
827
828     pop-parse-idx
829 ;
830
831 : character? ( -- bool )
832     nextchar [char] # <> if false exit then
833
834     push-parse-idx
835     inc-parse-idx
836
837     nextchar [char] \ <> if pop-parse-idx false exit then
838
839     inc-parse-idx
840
841     S" newline" str-equiv? if pop-parse-idx true exit then
842     S" space" str-equiv? if pop-parse-idx true exit then
843     S" tab" str-equiv? if pop-parse-idx true exit then
844
845     charavailable? false = if pop-parse-idx false exit then
846
847     pop-parse-idx true
848 ;
849
850 : pair? ( -- bool )
851     nextchar [char] ( = ;
852
853 : string? ( -- bool )
854     nextchar [char] " = ;
855
856 : readfixnum ( -- num-atom )
857     plus? minus? or if
858         minus?
859         inc-parse-idx
860     else
861         false
862     then
863
864     0
865
866     begin digit? while
867         10 * nextchar [char] 0 - +
868         inc-parse-idx
869     repeat
870
871     swap if negate then
872
873     fixnum-type
874 ;
875
876 : readbool ( -- bool-obj )
877     inc-parse-idx
878     
879     nextchar [char] f = if
880         false
881     else
882         true
883     then
884
885     inc-parse-idx
886
887     boolean-type
888 ;
889
890 : readchar ( -- char-obj )
891     inc-parse-idx
892     inc-parse-idx
893
894     S" newline" str-equiv? if 7 parse-idx +! '\n' character-type exit then
895     S" space" str-equiv? if 5 parse-idx +! bl character-type exit then
896     S" tab" str-equiv? if 3 parse-idx +! 9 character-type exit then
897
898     nextchar character-type
899
900     inc-parse-idx
901 ;
902
903 : readstring ( -- charlist )
904     nextchar [char] " = if
905         inc-parse-idx
906
907         delim? false = if
908             bold fg red
909             ." No delimiter following right double quote. Aborting." cr
910             reset-term abort
911         then
912
913         dec-parse-idx
914
915         0 nil-type exit
916     then
917
918     nextchar [char] \ = if
919         inc-parse-idx
920         nextchar case
921             [char] n of '\n' endof
922             [char] " of [char] " endof
923             [char] \
924         endcase
925     else
926         nextchar
927     then
928     inc-parse-idx character-type
929
930     recurse
931
932     cons
933 ;
934
935 : readsymbol ( -- charlist )
936     delim? if nil exit then
937
938     nextchar inc-parse-idx character-type
939
940     recurse
941
942     cons
943 ;
944
945 : readpair ( -- pairobj )
946     eatspaces
947
948     \ Empty lists
949     nextchar [char] ) = if
950         inc-parse-idx
951
952         delim? false = if
953             bold fg red
954             ." No delimiter following right paren. Aborting." cr
955             reset-term abort
956         then
957
958         dec-parse-idx
959
960         0 nil-type exit
961     then
962
963     \ Read first pair element
964     read
965
966     \ Pairs
967     eatspaces
968     nextchar [char] . = if
969         inc-parse-idx
970
971         delim? false = if
972             bold fg red
973             ." No delimiter following '.'. Aborting." cr
974             reset-term abort
975         then
976
977         eatspaces read
978     else
979         recurse
980     then
981
982     eatspaces
983
984     cons
985 ;
986
987 \ Parse a scheme expression
988 :noname ( -- obj )
989
990     eatspaces
991
992     fixnum? if
993         readfixnum
994         exit
995     then
996
997     boolean? if
998         readbool
999         exit
1000     then
1001
1002     character? if
1003         readchar
1004         exit
1005     then
1006
1007     string? if
1008         inc-parse-idx
1009
1010         readstring
1011         drop string-type
1012
1013         nextchar [char] " <> if
1014             bold red ." Missing closing double-quote." reset-term cr
1015             abort
1016         then
1017
1018         inc-parse-idx
1019
1020         exit
1021     then
1022
1023     pair? if
1024         inc-parse-idx
1025
1026         eatspaces
1027
1028         readpair
1029
1030         eatspaces
1031
1032         nextchar [char] ) <> if
1033             bold red ." Missing closing paren." reset-term cr
1034             abort
1035         then
1036
1037         inc-parse-idx
1038
1039         exit
1040     then
1041
1042     nextchar [char] ' = if
1043         inc-parse-idx
1044         quote-symbol recurse nil cons cons exit
1045     then
1046
1047     nextchar [char] ` = if
1048         inc-parse-idx
1049         quasiquote-symbol recurse nil cons cons exit
1050     then
1051
1052     nextchar [char] , = if
1053         inc-parse-idx
1054         nextchar [char] @ = if
1055             inc-parse-idx
1056             unquote-splicing-symbol recurse nil cons cons exit
1057         else
1058             unquote-symbol recurse nil cons cons exit
1059         then
1060     then
1061
1062     eof? if
1063         EOF character-type
1064         inc-parse-idx
1065         exit
1066     then
1067
1068     \ Anything else is parsed as a symbol
1069     readsymbol charlist>symbol
1070
1071     \ Replace Î» with lambda
1072     2dup Î»-symbol objeq? if
1073         2drop lambda-symbol
1074     then
1075     
1076
1077 ; is read
1078
1079 \ }}}
1080
1081 \ ---- Eval ---- {{{
1082
1083 : self-evaluating? ( obj -- obj bool )
1084     boolean-type istype? if true exit then
1085     fixnum-type istype? if true exit then
1086     character-type istype? if true exit then
1087     string-type istype? if true exit then
1088     nil-type istype? if true exit then
1089     none-type istype? if true exit then
1090
1091     false
1092 ;
1093
1094 : tagged-list? ( obj tag-obj -- obj bool )
1095     2over 
1096     pair-type istype? false = if
1097         2drop 2drop false
1098     else
1099         car objeq?
1100     then ;
1101
1102 : quote? ( obj -- obj bool )
1103     quote-symbol tagged-list?  ;
1104
1105 : quote-body ( quote-obj -- quote-body-obj )
1106     cdr car ;
1107
1108 : quasiquote? ( obj -- obj bool )
1109     quasiquote-symbol tagged-list? ;
1110
1111 : unquote? ( obj -- obj bool )
1112     unquote-symbol tagged-list? ;
1113
1114 : unquote-splicing? ( obj -- obj bool )
1115     unquote-splicing-symbol tagged-list? ;
1116
1117 : eval-unquote ( env obj -- res )
1118     cdr ( env args )
1119
1120     nil? if
1121         recoverable-exception throw" no arguments to unquote."
1122     then
1123
1124     2dup cdr
1125     nil? false = if
1126         recoverable-exception throw" too many arguments to unquote."
1127     then
1128
1129     2drop car 2swap eval
1130 ;
1131
1132 ( Create a new list from elements of l1 consed on to l2 )
1133 : join-lists ( l2 l1 -- l3 )
1134     nil? if 2drop exit then
1135
1136     2dup car
1137     -2rot cdr
1138     recurse cons
1139 ;
1140
1141 defer eval-quasiquote-item
1142 : eval-quasiquote-pair ( env obj -- res )
1143     2over 2over ( env obj env obj )
1144
1145     cdr eval-quasiquote-item
1146
1147     -2rot car ( cdritem env objcar )
1148
1149     unquote-splicing? if
1150         eval-unquote ( cdritems caritem )
1151
1152         2swap nil? if
1153             2drop
1154         else
1155             2swap join-lists
1156         then
1157     else
1158         eval-quasiquote-item ( cdritems caritem )
1159         2swap cons
1160     then
1161
1162 ;
1163
1164 :noname ( env obj )
1165     nil? if
1166         2swap 2drop exit
1167     then
1168
1169     unquote? if
1170         eval-unquote exit
1171     then
1172
1173     pair-type istype? if
1174         eval-quasiquote-pair exit
1175     then
1176
1177     2swap 2drop
1178 ; is eval-quasiquote-item
1179
1180 : eval-quasiquote ( obj env -- res )
1181     2swap cdr ( env args )
1182
1183     nil? if
1184         recoverable-exception throw" no arguments to quasiquote."
1185     then
1186
1187     2dup cdr ( env args args-cdr )
1188     nil? false = if
1189         recoverable-exception throw" too many arguments to quasiquote."
1190     then
1191
1192     2drop car ( env arg )
1193
1194     eval-quasiquote-item
1195 ;
1196
1197 : variable? ( obj -- obj bool )
1198     symbol-type istype? ;
1199
1200 : definition? ( obj -- obj bool )
1201     define-symbol tagged-list? ;
1202
1203 : make-lambda ( params body -- lambda-exp )
1204     lambda-symbol -2rot cons cons ;
1205
1206 ( Handles iterative expansion of defines in
1207   terms of nested lambdas. Most Schemes only
1208   handle one iteration of expansion! )
1209 : definition-var-val ( obj -- var val )
1210
1211     cdr 2dup cdr 2swap car ( val var )
1212
1213     begin
1214         symbol-type istype? false =
1215     while
1216         2dup cdr 2swap car ( val formals var' )
1217         -2rot 2swap ( var' formals val )
1218         make-lambda nil cons ( var' val' )
1219         2swap ( val' var' )
1220     repeat
1221
1222     2swap car
1223 ;
1224
1225 : eval-definition ( obj env -- res )
1226     2dup 2rot ( env env obj )
1227     definition-var-val ( env env var val )
1228     2rot eval  ( env var val )
1229
1230     2rot ( var val env )
1231     define-var
1232
1233     ok-symbol
1234 ;
1235
1236 : assignment? ( obj -- obj bool )
1237     set!-symbol tagged-list? ;
1238
1239 : assignment-var ( obj -- var )
1240     cdr car ;
1241     
1242 : assignment-val ( obj -- val )
1243     cdr cdr car ;
1244
1245 : eval-assignment ( obj env -- res )
1246     2swap 
1247     2over 2over ( env obj env obj )
1248     assignment-val 2swap ( env obj valexp env )
1249     eval  ( env obj val )
1250     
1251     2swap assignment-var 2swap ( env var val )
1252
1253     2rot ( var val env )
1254     set-var
1255
1256     ok-symbol
1257 ;
1258
1259 : macro-definition? ( obj -- obj bool )
1260     define-macro-symbol tagged-list? ;
1261
1262 : macro-definition-name ( exp -- mname )
1263     cdr car car ;
1264
1265 : macro-definition-params ( exp -- params )
1266     cdr car cdr ;
1267
1268 : macro-definition-body ( exp -- body )
1269     cdr cdr ;
1270
1271 objvar env
1272 : eval-define-macro ( obj env -- res )
1273     env obj!
1274
1275     2dup macro-definition-name 2swap ( name obj )
1276     2dup macro-definition-params 2swap ( name params obj )
1277     macro-definition-body ( name params body )
1278
1279     env obj@ ( name params body env )
1280
1281     make-macro
1282
1283     ok-symbol
1284 ;
1285 hide env
1286
1287 : if? ( obj -- obj bool )
1288     if-symbol tagged-list? ;
1289
1290 : if-predicate ( ifobj -- pred )
1291     cdr car ;
1292
1293 : if-consequent ( ifobj -- conseq )
1294     cdr cdr car ;
1295
1296 : if-alternative ( ifobj -- alt|none )
1297     cdr cdr cdr
1298     nil? if
1299         2drop none
1300     else
1301         car
1302     then ;
1303
1304 : false? ( boolobj -- boolean )
1305     boolean-type istype? if
1306         false boolean-type objeq?
1307     else
1308         2drop false
1309     then
1310 ;
1311
1312 : true? ( boolobj -- bool )
1313     false? invert ;
1314
1315 : lambda? ( obj -- obj bool )
1316     lambda-symbol tagged-list? ;
1317
1318 : lambda-parameters ( obj -- params )
1319     cdr car ;
1320
1321 : lambda-body ( obj -- body )
1322     cdr cdr ;
1323
1324 : begin? ( obj -- obj bool )
1325     begin-symbol tagged-list? ;
1326
1327 : begin-actions ( obj -- actions )
1328     cdr ;
1329
1330 : eval-sequence ( explist env -- finalexp env )
1331     ( Evaluates all bar the final expressions in
1332       an an expression list. The final expression
1333       is returned to allow for tail optimization. )
1334
1335     2swap ( env explist )
1336
1337     \ Abort on empty list
1338     nil? if
1339         2drop none
1340         2swap exit
1341     then
1342
1343     begin
1344         2dup cdr ( env explist nextexplist )
1345         nil? false =
1346     while
1347         -2rot car 2over ( nextexplist env exp env )
1348         eval
1349         2drop \ discard result
1350         2swap ( env nextexplist )
1351     repeat
1352
1353     2drop car 2swap ( finalexp env )
1354 ;
1355
1356 : application? ( obj -- obj bool )
1357     pair-type istype? ;
1358
1359 : operator ( obj -- operator )
1360     car ;
1361
1362 : operands ( obj -- operands )
1363     cdr ;
1364
1365 : nooperands? ( operands -- bool )
1366     nil objeq? ;
1367
1368 : first-operand ( operands -- operand )
1369     car ;
1370
1371 : rest-operands ( operands -- other-operands )
1372     cdr ;
1373
1374 : list-of-vals ( args env -- vals )
1375     2swap
1376
1377     2dup nooperands? if
1378         2swap 2drop
1379     else
1380         2over 2over first-operand 2swap eval
1381         -2rot rest-operands 2swap recurse
1382         cons
1383     then
1384 ;
1385
1386 : procedure-params ( proc -- params )
1387     drop pair-type car ;
1388
1389 : procedure-body ( proc -- body )
1390     drop pair-type cdr car ;
1391
1392 : procedure-env ( proc -- body )
1393     drop pair-type cdr cdr car ;
1394
1395 ( Ensure terminating symbol arg name is handled
1396   specially to allow for variadic procedures. )
1397 : flatten-proc-args ( argvals argnames -- argvals' argnames' )
1398     nil? if
1399         2over nil? false = if
1400             recoverable-exception throw" Too many arguments for compound procedure."
1401         else
1402             2drop
1403         then
1404         exit
1405     then
1406
1407     symbol-type istype? if
1408         nil cons
1409         2swap
1410         nil cons
1411         2swap
1412         exit
1413     then
1414
1415     2over
1416     nil? if
1417         recoverable-exception throw" Too few arguments for compound procedure."
1418     else
1419         cdr
1420     then
1421
1422     2over cdr
1423
1424     recurse ( argvals argnames argvals'' argnames'' )
1425     2rot car 2swap cons  ( argvals argvals'' argnames' )
1426     2rot car 2rot cons ( argnames' argvals' )
1427     2swap
1428 ;
1429
1430 : apply ( proc argvals -- result )
1431         2swap dup case
1432             primitive-proc-type of
1433                 drop execute     
1434             endof
1435
1436             compound-proc-type of
1437                 2dup procedure-body ( argvals proc body )
1438                 -2rot 2dup procedure-params ( body argvals proc argnames )
1439                 -2rot procedure-env ( body argnames argvals procenv )
1440
1441                 -2rot 2swap
1442                 flatten-proc-args
1443                 2swap 2rot
1444
1445                 extend-env ( body env )
1446
1447                 eval-sequence
1448
1449                 R> drop ['] eval goto-deferred  \ Tail call optimization
1450             endof
1451
1452             recoverable-exception throw" Object not applicable."
1453         endcase
1454 ;
1455
1456 ( Simply evaluates the given procedure with expbody as its argument. )
1457 : macro-expand ( proc expbody -- result )
1458     2swap
1459     2dup procedure-body ( expbody proc procbody )
1460     -2rot 2dup procedure-params ( procbody expbody proc argnames )
1461     -2rot procedure-env ( procbody argnames expbody procenv )
1462     
1463     -2rot 2swap
1464     flatten-proc-args
1465     2swap 2rot
1466
1467     extend-env eval-sequence eval
1468 ;
1469
1470 :noname ( obj env -- result )
1471     2swap
1472
1473     self-evaluating? if
1474         2swap 2drop
1475         exit
1476     then
1477
1478     quote? if
1479         quote-body
1480         2swap 2drop
1481         exit
1482     then
1483
1484     quasiquote? if
1485         2swap eval-quasiquote
1486         exit
1487     then
1488
1489     variable? if
1490         2swap lookup-var
1491         exit
1492     then
1493
1494     definition? if
1495         2swap eval-definition
1496         exit
1497     then
1498
1499     assignment? if
1500         2swap eval-assignment
1501         exit
1502     then
1503
1504     macro-definition? if
1505         2swap eval-define-macro
1506         exit
1507     then
1508
1509     if? if
1510         2over 2over
1511         if-predicate
1512         2swap eval 
1513
1514         true? if
1515             if-consequent
1516         else
1517             if-alternative
1518         then
1519
1520         2swap
1521         ['] eval goto-deferred
1522     then
1523
1524     lambda? if
1525         2dup lambda-parameters
1526         2swap lambda-body
1527         2rot make-procedure
1528         exit
1529     then
1530
1531     begin? if
1532         begin-actions 2swap
1533         eval-sequence
1534         ['] eval goto-deferred
1535     then
1536
1537     application? if
1538
1539         2over 2over ( env exp env exp )
1540         operator ( env exp env opname )
1541
1542         2dup lookup-macro nil? false = if
1543              \ Macro function evaluation
1544
1545             ( env exp env opname mproc )
1546             2swap 2drop -2rot 2drop cdr ( env mproc body )
1547
1548             macro-expand
1549
1550             2swap
1551             ['] eval goto-deferred
1552         else
1553            \ Regular function application
1554
1555             2drop ( env exp env opname )
1556
1557             2swap eval ( env exp proc )
1558
1559             -2rot ( proc env exp )
1560             operands 2swap ( proc operands env )
1561             list-of-vals ( proc argvals )
1562
1563             apply
1564             exit
1565         then
1566     then
1567
1568     recoverable-exception throw" Tried to evaluate object with unknown type."
1569 ; is eval
1570
1571 \ }}}
1572
1573 \ ---- Print ---- {{{
1574
1575 : printfixnum ( fixnumobj -- ) drop 0 .R ;
1576
1577 : printbool ( numobj -- )
1578     drop if
1579         ." #t"
1580     else
1581         ." #f"
1582     then
1583 ;
1584
1585 : printchar ( charobj -- )
1586     drop
1587     case
1588         9 of ." #\tab" endof
1589         bl of ." #\space" endof
1590         '\n' of ." #\newline" endof
1591         
1592         dup ." #\" emit
1593     endcase
1594 ;
1595
1596 : (printstring) ( stringobj -- )
1597     nil? if 2drop exit then
1598
1599     2dup car drop dup
1600     case
1601         '\n' of ." \n" drop endof
1602         [char] \ of ." \\" drop endof
1603         [char] " of [char] \ emit [char] " emit drop endof
1604         emit
1605     endcase
1606
1607     cdr recurse
1608 ;
1609 : printstring ( stringobj -- )
1610     [char] " emit
1611     (printstring)
1612     [char] " emit ;
1613
1614 : printsymbol ( symbolobj -- )
1615     nil-type istype? if 2drop exit then
1616
1617     2dup car drop emit
1618     cdr recurse
1619 ;
1620
1621 : printnil ( nilobj -- )
1622     2drop ." ()" ;
1623
1624 : printpair ( pairobj -- )
1625     2dup
1626     car print
1627     cdr
1628     nil-type istype? if 2drop exit then
1629     pair-type istype? if space recurse exit then
1630     ."  . " print
1631 ;
1632
1633 : printprim ( primobj -- )
1634     2drop ." <primitive procedure>" ;
1635
1636 : printcomp ( primobj -- )
1637     2drop ." <compound procedure>" ;
1638
1639 : printnone ( noneobj -- )
1640     2drop ." Unspecified return value" ;
1641
1642 : printport ( port -- )
1643     2drop ." <port>" ;
1644
1645 :noname ( obj -- )
1646     fixnum-type istype? if printfixnum exit then
1647     boolean-type istype? if printbool exit then
1648     character-type istype? if printchar exit then
1649     string-type istype? if printstring exit then
1650     symbol-type istype? if printsymbol exit then
1651     nil-type istype? if printnil exit then
1652     pair-type istype? if ." (" printpair ." )" exit then
1653     primitive-proc-type istype? if printprim exit then
1654     compound-proc-type istype? if printcomp exit then
1655     none-type istype? if printnone exit then
1656
1657     recoverable-exception throw" Tried to print object with unknown type."
1658 ; is print
1659
1660 \ }}}
1661
1662 \ ---- Garbage Collection ---- {{{
1663
1664 variable gc-enabled
1665 false gc-enabled !
1666
1667 variable gc-stack-depth
1668
1669 : enable-gc
1670     depth gc-stack-depth !
1671     true gc-enabled ! ;
1672
1673 : disable-gc
1674     false gc-enabled ! ;
1675
1676 : gc-enabled?
1677     gc-enabled @ ;
1678
1679 : pairlike? ( obj -- obj bool )
1680     pair-type istype? if true exit then
1681     string-type istype? if true exit then
1682     symbol-type istype? if true exit then
1683     compound-proc-type istype? if true exit then
1684
1685     false
1686 ;
1687
1688 : pairlike-marked? ( obj -- obj bool )
1689     over nextfrees + @ 0=
1690 ;
1691
1692 : mark-pairlike ( obj -- obj )
1693         over nextfrees + 0 swap !
1694 ;
1695
1696 : gc-unmark ( -- )
1697     scheme-memsize 0 do
1698         1 nextfrees i + !
1699     loop
1700 ;
1701
1702 : gc-mark-obj ( obj -- )
1703
1704     pairlike? invert if 2drop exit then
1705     pairlike-marked? if 2drop exit then
1706
1707     mark-pairlike
1708
1709     drop pair-type 2dup
1710
1711     car recurse
1712     cdr recurse
1713 ;
1714
1715 : gc-sweep
1716     scheme-memsize nextfree !
1717     0 scheme-memsize 1- do
1718         nextfrees i + @ 0<> if
1719             nextfree @ nextfrees i + !
1720             i nextfree !
1721         then
1722     -1 +loop
1723 ;
1724
1725 \ Following a GC, this gives the amount of free memory
1726 : gc-count-marked
1727     0
1728     scheme-memsize 0 do
1729         nextfrees i + @ 0= if 1+ then
1730     loop
1731 ;
1732
1733 \ Debugging word - helps spot memory that is retained
1734 : gc-zero-unmarked
1735     scheme-memsize 0 do
1736         nextfrees i + @ 0<> if
1737             0 car-cells i + !
1738             0 cdr-cells i + !
1739         then
1740     loop
1741 ;
1742
1743 :noname
1744     \ ." GC! "
1745
1746     gc-unmark
1747
1748     symbol-table obj@ gc-mark-obj
1749     macro-table obj@ gc-mark-obj
1750     global-env obj@ gc-mark-obj
1751
1752     depth gc-stack-depth @ do
1753         PSP0 i + 1 + @
1754         PSP0 i + 2 + @
1755
1756         gc-mark-obj
1757     2 +loop
1758
1759     gc-sweep
1760
1761     \ ." (" gc-count-marked . ." pairs marked as used.)" cr
1762 ; is collect-garbage
1763
1764 \ }}}
1765
1766 \ ---- Loading files ---- {{{
1767
1768 : charlist>cstr ( charlist addr -- n )
1769
1770     dup 2swap ( origaddr addr charlist )
1771
1772     begin 
1773         nil? false =
1774     while
1775         2dup cdr 2swap car 
1776         drop ( origaddr addr charlist char )
1777         -rot 2swap ( origaddr charlist addr char )
1778         over !
1779         1+ -rot ( origaddr nextaddr charlist )
1780     repeat
1781
1782     2drop ( origaddr finaladdr ) 
1783     swap -
1784 ;
1785
1786 : load ( addr n -- finalResult )
1787     open-input-file
1788
1789     empty-parse-str
1790
1791     ok-symbol ( port res )
1792
1793     begin
1794         2over read-port ( port res obj )
1795
1796         2dup EOF character-type objeq? if
1797             2drop 2swap close-port
1798             exit
1799         then
1800
1801         2swap 2drop ( port obj )
1802
1803         global-env obj@ eval ( port res )
1804     again
1805 ;
1806
1807 \ }}}
1808
1809 \ ---- Standard Library ---- {{{
1810
1811     include scheme-primitives.4th
1812
1813     s" scheme-library.scm" load 2drop
1814     
1815 \ }}}
1816
1817 \ ---- REPL ----
1818
1819 ( REPL calls REPL-BODY in a loop until repl-body returns true. )
1820 : repl-body ( -- bool )
1821     cr bold fg green ." > " reset-term
1822
1823     read-console
1824
1825     2dup EOF character-type objeq? if
1826         2drop
1827         bold fg blue ." Moriturus te saluto." reset-term cr
1828         true exit
1829     then
1830
1831     global-env obj@ eval
1832
1833     fg cyan ." ; " print reset-term
1834
1835     false
1836 ;
1837
1838 : repl
1839     cr ." Welcome to scheme.forth.jl!" cr
1840        ." Use Ctrl-D to exit." cr
1841
1842     empty-parse-str
1843
1844     enable-gc
1845
1846     begin
1847         ['] repl-body catch
1848         case
1849             recoverable-exception of false endof
1850             unrecoverable-exception of true endof
1851
1852             throw false
1853         endcase
1854     until
1855 ;
1856
1857 forth definitions
1858
1859 \ vim:fdm=marker