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