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