Added draft readflonum.
[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 ( -- fixnum )
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 : readflonum ( -- flonum )
877     \ DRAFT!!!
878     readfixnum drop i->f
879
880     [char] . netchar = if
881         10 i->f
882         begin digit? while
883             nextchar [char] 0 - i->f over f/ f+
884             inc-parse-idx
885         repeat
886     then
887
888     [char] e nextchar = [char] E nextchar = or if
889         readfixnum drop i->f
890         f^
891     then
892
893     flonum-type
894 ;
895
896 : readbool ( -- bool-obj )
897     inc-parse-idx
898     
899     nextchar [char] f = if
900         false
901     else
902         true
903     then
904
905     inc-parse-idx
906
907     boolean-type
908 ;
909
910 : readchar ( -- char-obj )
911     inc-parse-idx
912     inc-parse-idx
913
914     S" newline" str-equiv? if 7 parse-idx +! '\n' character-type exit then
915     S" space" str-equiv? if 5 parse-idx +! bl character-type exit then
916     S" tab" str-equiv? if 3 parse-idx +! 9 character-type exit then
917
918     nextchar character-type
919
920     inc-parse-idx
921 ;
922
923 : readstring ( -- charlist )
924     nextchar [char] " = if
925         inc-parse-idx
926
927         delim? false = if
928             bold fg red
929             ." No delimiter following right double quote. Aborting." cr
930             reset-term abort
931         then
932
933         dec-parse-idx
934
935         0 nil-type exit
936     then
937
938     nextchar [char] \ = if
939         inc-parse-idx
940         nextchar case
941             [char] n of '\n' endof
942             [char] " of [char] " endof
943             [char] \
944         endcase
945     else
946         nextchar
947     then
948     inc-parse-idx character-type
949
950     recurse
951
952     cons
953 ;
954
955 : readsymbol ( -- charlist )
956     delim? if nil exit then
957
958     nextchar inc-parse-idx character-type
959
960     recurse
961
962     cons
963 ;
964
965 : readpair ( -- pairobj )
966     eatspaces
967
968     \ Empty lists
969     nextchar [char] ) = if
970         inc-parse-idx
971
972         delim? false = if
973             bold fg red
974             ." No delimiter following right paren. Aborting." cr
975             reset-term abort
976         then
977
978         dec-parse-idx
979
980         0 nil-type exit
981     then
982
983     \ Read first pair element
984     read
985
986     \ Pairs
987     eatspaces
988     nextchar [char] . = if
989         inc-parse-idx
990
991         delim? false = if
992             bold fg red
993             ." No delimiter following '.'. Aborting." cr
994             reset-term abort
995         then
996
997         eatspaces read
998     else
999         recurse
1000     then
1001
1002     eatspaces
1003
1004     cons
1005 ;
1006
1007 \ Parse a scheme expression
1008 :noname ( -- obj )
1009
1010     eatspaces
1011
1012     fixnum? if
1013         readfixnum
1014         exit
1015     then
1016
1017     boolean? if
1018         readbool
1019         exit
1020     then
1021
1022     character? if
1023         readchar
1024         exit
1025     then
1026
1027     string? if
1028         inc-parse-idx
1029
1030         readstring
1031         drop string-type
1032
1033         nextchar [char] " <> if
1034             bold red ." Missing closing double-quote." reset-term cr
1035             abort
1036         then
1037
1038         inc-parse-idx
1039
1040         exit
1041     then
1042
1043     pair? if
1044         inc-parse-idx
1045
1046         eatspaces
1047
1048         readpair
1049
1050         eatspaces
1051
1052         nextchar [char] ) <> if
1053             bold red ." Missing closing paren." reset-term cr
1054             abort
1055         then
1056
1057         inc-parse-idx
1058
1059         exit
1060     then
1061
1062     nextchar [char] ' = if
1063         inc-parse-idx
1064         quote-symbol recurse nil cons cons exit
1065     then
1066
1067     nextchar [char] ` = if
1068         inc-parse-idx
1069         quasiquote-symbol recurse nil cons cons exit
1070     then
1071
1072     nextchar [char] , = if
1073         inc-parse-idx
1074         nextchar [char] @ = if
1075             inc-parse-idx
1076             unquote-splicing-symbol recurse nil cons cons exit
1077         else
1078             unquote-symbol recurse nil cons cons exit
1079         then
1080     then
1081
1082     eof? if
1083         EOF character-type
1084         inc-parse-idx
1085         exit
1086     then
1087
1088     \ Anything else is parsed as a symbol
1089     readsymbol charlist>symbol
1090
1091     \ Replace Î» with lambda
1092     2dup Î»-symbol objeq? if
1093         2drop lambda-symbol
1094     then
1095     
1096
1097 ; is read
1098
1099 \ }}}
1100
1101 \ ---- Eval ---- {{{
1102
1103 : self-evaluating? ( obj -- obj bool )
1104     boolean-type istype? if true exit then
1105     fixnum-type istype? if true exit then
1106     character-type istype? if true exit then
1107     string-type istype? if true exit then
1108     nil-type istype? if true exit then
1109     none-type istype? if true exit then
1110
1111     false
1112 ;
1113
1114 : tagged-list? ( obj tag-obj -- obj bool )
1115     2over 
1116     pair-type istype? false = if
1117         2drop 2drop false
1118     else
1119         car objeq?
1120     then ;
1121
1122 : quote? ( obj -- obj bool )
1123     quote-symbol tagged-list?  ;
1124
1125 : quote-body ( quote-obj -- quote-body-obj )
1126     cdr car ;
1127
1128 : quasiquote? ( obj -- obj bool )
1129     quasiquote-symbol tagged-list? ;
1130
1131 : unquote? ( obj -- obj bool )
1132     unquote-symbol tagged-list? ;
1133
1134 : unquote-splicing? ( obj -- obj bool )
1135     unquote-splicing-symbol tagged-list? ;
1136
1137 : eval-unquote ( env obj -- res )
1138     cdr ( env args )
1139
1140     nil? if
1141         recoverable-exception throw" no arguments to unquote."
1142     then
1143
1144     2dup cdr
1145     nil? false = if
1146         recoverable-exception throw" too many arguments to unquote."
1147     then
1148
1149     2drop car 2swap eval
1150 ;
1151
1152 ( Create a new list from elements of l1 consed on to l2 )
1153 : join-lists ( l2 l1 -- l3 )
1154     nil? if 2drop exit then
1155
1156     2dup car
1157     -2rot cdr
1158     recurse cons
1159 ;
1160
1161 defer eval-quasiquote-item
1162 : eval-quasiquote-pair ( env obj -- res )
1163     2over 2over ( env obj env obj )
1164
1165     cdr eval-quasiquote-item
1166
1167     -2rot car ( cdritem env objcar )
1168
1169     unquote-splicing? if
1170         eval-unquote ( cdritems caritem )
1171
1172         2swap nil? if
1173             2drop
1174         else
1175             2swap join-lists
1176         then
1177     else
1178         eval-quasiquote-item ( cdritems caritem )
1179         2swap cons
1180     then
1181
1182 ;
1183
1184 :noname ( env obj )
1185     nil? if
1186         2swap 2drop exit
1187     then
1188
1189     unquote? if
1190         eval-unquote exit
1191     then
1192
1193     pair-type istype? if
1194         eval-quasiquote-pair exit
1195     then
1196
1197     2swap 2drop
1198 ; is eval-quasiquote-item
1199
1200 : eval-quasiquote ( obj env -- res )
1201     2swap cdr ( env args )
1202
1203     nil? if
1204         recoverable-exception throw" no arguments to quasiquote."
1205     then
1206
1207     2dup cdr ( env args args-cdr )
1208     nil? false = if
1209         recoverable-exception throw" too many arguments to quasiquote."
1210     then
1211
1212     2drop car ( env arg )
1213
1214     eval-quasiquote-item
1215 ;
1216
1217 : variable? ( obj -- obj bool )
1218     symbol-type istype? ;
1219
1220 : definition? ( obj -- obj bool )
1221     define-symbol tagged-list? ;
1222
1223 : make-lambda ( params body -- lambda-exp )
1224     lambda-symbol -2rot cons cons ;
1225
1226 ( Handles iterative expansion of defines in
1227   terms of nested lambdas. Most Schemes only
1228   handle one iteration of expansion! )
1229 : definition-var-val ( obj -- var val )
1230
1231     cdr 2dup cdr 2swap car ( val var )
1232
1233     begin
1234         symbol-type istype? false =
1235     while
1236         2dup cdr 2swap car ( val formals var' )
1237         -2rot 2swap ( var' formals val )
1238         make-lambda nil cons ( var' val' )
1239         2swap ( val' var' )
1240     repeat
1241
1242     2swap car
1243 ;
1244
1245 : eval-definition ( obj env -- res )
1246     2dup 2rot ( env env obj )
1247     definition-var-val ( env env var val )
1248     2rot eval  ( env var val )
1249
1250     2rot ( var val env )
1251     define-var
1252
1253     ok-symbol
1254 ;
1255
1256 : assignment? ( obj -- obj bool )
1257     set!-symbol tagged-list? ;
1258
1259 : assignment-var ( obj -- var )
1260     cdr car ;
1261     
1262 : assignment-val ( obj -- val )
1263     cdr cdr car ;
1264
1265 : eval-assignment ( obj env -- res )
1266     2swap 
1267     2over 2over ( env obj env obj )
1268     assignment-val 2swap ( env obj valexp env )
1269     eval  ( env obj val )
1270     
1271     2swap assignment-var 2swap ( env var val )
1272
1273     2rot ( var val env )
1274     set-var
1275
1276     ok-symbol
1277 ;
1278
1279 : macro-definition? ( obj -- obj bool )
1280     define-macro-symbol tagged-list? ;
1281
1282 : macro-definition-name ( exp -- mname )
1283     cdr car car ;
1284
1285 : macro-definition-params ( exp -- params )
1286     cdr car cdr ;
1287
1288 : macro-definition-body ( exp -- body )
1289     cdr cdr ;
1290
1291 objvar env
1292 : eval-define-macro ( obj env -- res )
1293     env obj!
1294
1295     2dup macro-definition-name 2swap ( name obj )
1296     2dup macro-definition-params 2swap ( name params obj )
1297     macro-definition-body ( name params body )
1298
1299     env obj@ ( name params body env )
1300
1301     make-macro
1302
1303     ok-symbol
1304 ;
1305 hide env
1306
1307 : if? ( obj -- obj bool )
1308     if-symbol tagged-list? ;
1309
1310 : if-predicate ( ifobj -- pred )
1311     cdr car ;
1312
1313 : if-consequent ( ifobj -- conseq )
1314     cdr cdr car ;
1315
1316 : if-alternative ( ifobj -- alt|none )
1317     cdr cdr cdr
1318     nil? if
1319         2drop none
1320     else
1321         car
1322     then ;
1323
1324 : false? ( boolobj -- boolean )
1325     boolean-type istype? if
1326         false boolean-type objeq?
1327     else
1328         2drop false
1329     then
1330 ;
1331
1332 : true? ( boolobj -- bool )
1333     false? invert ;
1334
1335 : lambda? ( obj -- obj bool )
1336     lambda-symbol tagged-list? ;
1337
1338 : lambda-parameters ( obj -- params )
1339     cdr car ;
1340
1341 : lambda-body ( obj -- body )
1342     cdr cdr ;
1343
1344 : begin? ( obj -- obj bool )
1345     begin-symbol tagged-list? ;
1346
1347 : begin-actions ( obj -- actions )
1348     cdr ;
1349
1350 : eval-sequence ( explist env -- finalexp env )
1351     ( Evaluates all bar the final expressions in
1352       an an expression list. The final expression
1353       is returned to allow for tail optimization. )
1354
1355     2swap ( env explist )
1356
1357     \ Abort on empty list
1358     nil? if
1359         2drop none
1360         2swap exit
1361     then
1362
1363     begin
1364         2dup cdr ( env explist nextexplist )
1365         nil? false =
1366     while
1367         -2rot car 2over ( nextexplist env exp env )
1368         eval
1369         2drop \ discard result
1370         2swap ( env nextexplist )
1371     repeat
1372
1373     2drop car 2swap ( finalexp env )
1374 ;
1375
1376 : application? ( obj -- obj bool )
1377     pair-type istype? ;
1378
1379 : operator ( obj -- operator )
1380     car ;
1381
1382 : operands ( obj -- operands )
1383     cdr ;
1384
1385 : nooperands? ( operands -- bool )
1386     nil objeq? ;
1387
1388 : first-operand ( operands -- operand )
1389     car ;
1390
1391 : rest-operands ( operands -- other-operands )
1392     cdr ;
1393
1394 : list-of-vals ( args env -- vals )
1395     2swap
1396
1397     2dup nooperands? if
1398         2swap 2drop
1399     else
1400         2over 2over first-operand 2swap eval
1401         -2rot rest-operands 2swap recurse
1402         cons
1403     then
1404 ;
1405
1406 : procedure-params ( proc -- params )
1407     drop pair-type car ;
1408
1409 : procedure-body ( proc -- body )
1410     drop pair-type cdr car ;
1411
1412 : procedure-env ( proc -- body )
1413     drop pair-type cdr cdr car ;
1414
1415 ( Ensure terminating symbol arg name is handled
1416   specially to allow for variadic procedures. )
1417 : flatten-proc-args ( argvals argnames -- argvals' argnames' )
1418     nil? if
1419         2over nil? false = if
1420             recoverable-exception throw" Too many arguments for compound procedure."
1421         else
1422             2drop
1423         then
1424         exit
1425     then
1426
1427     symbol-type istype? if
1428         nil cons
1429         2swap
1430         nil cons
1431         2swap
1432         exit
1433     then
1434
1435     2over
1436     nil? if
1437         recoverable-exception throw" Too few arguments for compound procedure."
1438     else
1439         cdr
1440     then
1441
1442     2over cdr
1443
1444     recurse ( argvals argnames argvals'' argnames'' )
1445     2rot car 2swap cons  ( argvals argvals'' argnames' )
1446     2rot car 2rot cons ( argnames' argvals' )
1447     2swap
1448 ;
1449
1450 : apply ( proc argvals -- result )
1451         2swap dup case
1452             primitive-proc-type of
1453                 drop execute     
1454             endof
1455
1456             compound-proc-type of
1457                 2dup procedure-body ( argvals proc body )
1458                 -2rot 2dup procedure-params ( body argvals proc argnames )
1459                 -2rot procedure-env ( body argnames argvals procenv )
1460
1461                 -2rot 2swap
1462                 flatten-proc-args
1463                 2swap 2rot
1464
1465                 extend-env ( body env )
1466
1467                 eval-sequence
1468
1469                 R> drop ['] eval goto-deferred  \ Tail call optimization
1470             endof
1471
1472             recoverable-exception throw" Object not applicable."
1473         endcase
1474 ;
1475
1476 ( Simply evaluates the given procedure with expbody as its argument. )
1477 : macro-expand ( proc expbody -- result )
1478     2swap
1479     2dup procedure-body ( expbody proc procbody )
1480     -2rot 2dup procedure-params ( procbody expbody proc argnames )
1481     -2rot procedure-env ( procbody argnames expbody procenv )
1482     
1483     -2rot 2swap
1484     flatten-proc-args
1485     2swap 2rot
1486
1487     extend-env eval-sequence eval
1488 ;
1489
1490 :noname ( obj env -- result )
1491     2swap
1492
1493     self-evaluating? if
1494         2swap 2drop
1495         exit
1496     then
1497
1498     quote? if
1499         quote-body
1500         2swap 2drop
1501         exit
1502     then
1503
1504     quasiquote? if
1505         2swap eval-quasiquote
1506         exit
1507     then
1508
1509     variable? if
1510         2swap lookup-var
1511         exit
1512     then
1513
1514     definition? if
1515         2swap eval-definition
1516         exit
1517     then
1518
1519     assignment? if
1520         2swap eval-assignment
1521         exit
1522     then
1523
1524     macro-definition? if
1525         2swap eval-define-macro
1526         exit
1527     then
1528
1529     if? if
1530         2over 2over
1531         if-predicate
1532         2swap eval 
1533
1534         true? if
1535             if-consequent
1536         else
1537             if-alternative
1538         then
1539
1540         2swap
1541         ['] eval goto-deferred
1542     then
1543
1544     lambda? if
1545         2dup lambda-parameters
1546         2swap lambda-body
1547         2rot make-procedure
1548         exit
1549     then
1550
1551     begin? if
1552         begin-actions 2swap
1553         eval-sequence
1554         ['] eval goto-deferred
1555     then
1556
1557     application? if
1558
1559         2over 2over ( env exp env exp )
1560         operator ( env exp env opname )
1561
1562         2dup lookup-macro nil? false = if
1563              \ Macro function evaluation
1564
1565             ( env exp env opname mproc )
1566             2swap 2drop -2rot 2drop cdr ( env mproc body )
1567
1568             macro-expand
1569
1570             2swap
1571             ['] eval goto-deferred
1572         else
1573            \ Regular function application
1574
1575             2drop ( env exp env opname )
1576
1577             2swap eval ( env exp proc )
1578
1579             -2rot ( proc env exp )
1580             operands 2swap ( proc operands env )
1581             list-of-vals ( proc argvals )
1582
1583             apply
1584             exit
1585         then
1586     then
1587
1588     recoverable-exception throw" Tried to evaluate object with unknown type."
1589 ; is eval
1590
1591 \ }}}
1592
1593 \ ---- Print ---- {{{
1594
1595 : printfixnum ( fixnumobj -- ) drop 0 .R ;
1596
1597 : printbool ( numobj -- )
1598     drop if
1599         ." #t"
1600     else
1601         ." #f"
1602     then
1603 ;
1604
1605 : printchar ( charobj -- )
1606     drop
1607     case
1608         9 of ." #\tab" endof
1609         bl of ." #\space" endof
1610         '\n' of ." #\newline" endof
1611         
1612         dup ." #\" emit
1613     endcase
1614 ;
1615
1616 : (printstring) ( stringobj -- )
1617     nil? if 2drop exit then
1618
1619     2dup car drop dup
1620     case
1621         '\n' of ." \n" drop endof
1622         [char] \ of ." \\" drop endof
1623         [char] " of [char] \ emit [char] " emit drop endof
1624         emit
1625     endcase
1626
1627     cdr recurse
1628 ;
1629 : printstring ( stringobj -- )
1630     [char] " emit
1631     (printstring)
1632     [char] " emit ;
1633
1634 : printsymbol ( symbolobj -- )
1635     nil-type istype? if 2drop exit then
1636
1637     2dup car drop emit
1638     cdr recurse
1639 ;
1640
1641 : printnil ( nilobj -- )
1642     2drop ." ()" ;
1643
1644 : printpair ( pairobj -- )
1645     2dup
1646     car print
1647     cdr
1648     nil-type istype? if 2drop exit then
1649     pair-type istype? if space recurse exit then
1650     ."  . " print
1651 ;
1652
1653 : printprim ( primobj -- )
1654     2drop ." <primitive procedure>" ;
1655
1656 : printcomp ( primobj -- )
1657     2drop ." <compound procedure>" ;
1658
1659 : printnone ( noneobj -- )
1660     2drop ." Unspecified return value" ;
1661
1662 : printport ( port -- )
1663     2drop ." <port>" ;
1664
1665 :noname ( obj -- )
1666     fixnum-type istype? if printfixnum exit then
1667     boolean-type istype? if printbool exit then
1668     character-type istype? if printchar exit then
1669     string-type istype? if printstring exit then
1670     symbol-type istype? if printsymbol exit then
1671     nil-type istype? if printnil exit then
1672     pair-type istype? if ." (" printpair ." )" exit then
1673     primitive-proc-type istype? if printprim exit then
1674     compound-proc-type istype? if printcomp exit then
1675     none-type istype? if printnone exit then
1676
1677     recoverable-exception throw" Tried to print object with unknown type."
1678 ; is print
1679
1680 \ }}}
1681
1682 \ ---- Garbage Collection ---- {{{
1683
1684 variable gc-enabled
1685 false gc-enabled !
1686
1687 variable gc-stack-depth
1688
1689 : enable-gc
1690     depth gc-stack-depth !
1691     true gc-enabled ! ;
1692
1693 : disable-gc
1694     false gc-enabled ! ;
1695
1696 : gc-enabled?
1697     gc-enabled @ ;
1698
1699 : pairlike? ( obj -- obj bool )
1700     pair-type istype? if true exit then
1701     string-type istype? if true exit then
1702     symbol-type istype? if true exit then
1703     compound-proc-type istype? if true exit then
1704
1705     false
1706 ;
1707
1708 : pairlike-marked? ( obj -- obj bool )
1709     over nextfrees + @ 0=
1710 ;
1711
1712 : mark-pairlike ( obj -- obj )
1713         over nextfrees + 0 swap !
1714 ;
1715
1716 : gc-unmark ( -- )
1717     scheme-memsize 0 do
1718         1 nextfrees i + !
1719     loop
1720 ;
1721
1722 : gc-mark-obj ( obj -- )
1723
1724     pairlike? invert if 2drop exit then
1725     pairlike-marked? if 2drop exit then
1726
1727     mark-pairlike
1728
1729     drop pair-type 2dup
1730
1731     car recurse
1732     cdr recurse
1733 ;
1734
1735 : gc-sweep
1736     scheme-memsize nextfree !
1737     0 scheme-memsize 1- do
1738         nextfrees i + @ 0<> if
1739             nextfree @ nextfrees i + !
1740             i nextfree !
1741         then
1742     -1 +loop
1743 ;
1744
1745 \ Following a GC, this gives the amount of free memory
1746 : gc-count-marked
1747     0
1748     scheme-memsize 0 do
1749         nextfrees i + @ 0= if 1+ then
1750     loop
1751 ;
1752
1753 \ Debugging word - helps spot memory that is retained
1754 : gc-zero-unmarked
1755     scheme-memsize 0 do
1756         nextfrees i + @ 0<> if
1757             0 car-cells i + !
1758             0 cdr-cells i + !
1759         then
1760     loop
1761 ;
1762
1763 :noname
1764     \ ." GC! "
1765
1766     gc-unmark
1767
1768     symbol-table obj@ gc-mark-obj
1769     macro-table obj@ gc-mark-obj
1770     global-env obj@ gc-mark-obj
1771
1772     depth gc-stack-depth @ do
1773         PSP0 i + 1 + @
1774         PSP0 i + 2 + @
1775
1776         gc-mark-obj
1777     2 +loop
1778
1779     gc-sweep
1780
1781     \ ." (" gc-count-marked . ." pairs marked as used.)" cr
1782 ; is collect-garbage
1783
1784 \ }}}
1785
1786 \ ---- Loading files ---- {{{
1787
1788 : charlist>cstr ( charlist addr -- n )
1789
1790     dup 2swap ( origaddr addr charlist )
1791
1792     begin 
1793         nil? false =
1794     while
1795         2dup cdr 2swap car 
1796         drop ( origaddr addr charlist char )
1797         -rot 2swap ( origaddr charlist addr char )
1798         over !
1799         1+ -rot ( origaddr nextaddr charlist )
1800     repeat
1801
1802     2drop ( origaddr finaladdr ) 
1803     swap -
1804 ;
1805
1806 : load ( addr n -- finalResult )
1807     open-input-file
1808
1809     empty-parse-str
1810
1811     ok-symbol ( port res )
1812
1813     begin
1814         2over read-port ( port res obj )
1815
1816         2dup EOF character-type objeq? if
1817             2drop 2swap close-port
1818             exit
1819         then
1820
1821         2swap 2drop ( port obj )
1822
1823         global-env obj@ eval ( port res )
1824     again
1825 ;
1826
1827 \ }}}
1828
1829 \ ---- Standard Library ---- {{{
1830
1831     include scheme-primitives.4th
1832
1833     s" scheme-library.scm" load 2drop
1834     
1835 \ }}}
1836
1837 \ ---- REPL ----
1838
1839 ( REPL calls REPL-BODY in a loop until repl-body returns true. )
1840 : repl-body ( -- bool )
1841     cr bold fg green ." > " reset-term
1842
1843     read-console
1844
1845     2dup EOF character-type objeq? if
1846         2drop
1847         bold fg blue ." Moriturus te saluto." reset-term cr
1848         true exit
1849     then
1850
1851     global-env obj@ eval
1852
1853     fg cyan ." ; " print reset-term
1854
1855     false
1856 ;
1857
1858 : repl
1859     cr ." Welcome to scheme.forth.jl!" cr
1860        ." Use Ctrl-D to exit." cr
1861
1862     empty-parse-str
1863
1864     enable-gc
1865
1866     begin
1867         ['] repl-body catch
1868         case
1869             recoverable-exception of false endof
1870             unrecoverable-exception of true endof
1871
1872             throw false
1873         endcase
1874     until
1875 ;
1876
1877 forth definitions
1878
1879 \ vim:fdm=marker