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