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