Finished full draft of set/def/lookup support.
[scheme.forth.jl.git] / scheme.4th
1 vocabulary scheme
2 scheme definitions
3
4 include term-colours.4th
5 include defer-is.4th
6
7 \ ------ Types ------
8
9 0 constant number-type
10 1 constant boolean-type
11 2 constant character-type
12 3 constant string-type
13 4 constant nil-type
14 5 constant pair-type
15 6 constant symbol-type
16 : istype? ( obj type -- obj bool )
17     over = ;
18
19 \ ------ Cons cell memory ------ {{{
20
21 1000 constant N
22 create car-cells N allot
23 create car-type-cells N allot
24 create cdr-cells N allot
25 create cdr-type-cells N allot
26
27 variable nextfree
28 0 nextfree !
29
30 : cons ( car-obj cdr-obj -- pair-obj )
31     cdr-type-cells nextfree @ + !
32     cdr-cells nextfree @ + !
33     car-type-cells nextfree @ + !
34     car-cells nextfree @ + !
35
36     nextfree @ pair-type
37
38     1 nextfree +!
39 ;
40
41 : car ( pair-obj -- car-obj )
42     drop
43     dup car-cells + @ swap
44     car-type-cells + @
45 ;
46
47 : cdr ( pair-obj -- car-obj )
48     drop
49     dup cdr-cells + @ swap
50     cdr-type-cells + @
51 ;
52
53 : set-car! ( obj pair-obj -- )
54     drop dup
55     rot swap  car-type-cells + !
56     car-cells + !
57 ;
58
59 : set-cdr! ( obj pair-obj -- )
60     drop dup
61     rot swap  cdr-type-cells + !
62     cdr-cells + !
63 ;
64
65 : caar car car ;
66 : cadr cdr car ;
67 : cdar car cdr ;
68 : cddr cdr cdr ;
69
70 : nil 0 nil-type ;
71 : nil? nil-type istype? ;
72
73 : objvar create nil swap , , ;
74
75 : value@ ( objvar -- val ) @ ;
76 : type@ ( objvar -- type ) 1+ @ ;
77 : value! ( newval objvar -- ) ! ;
78 : type! ( newtype objvar -- ) 1+ ! ;
79 : setobj ( newobj objvar -- ) dup rot swap 1+ ! ! ; 
80 : fetchobj ( objvar -- obj ) dup @ swap 1+ @ ; 
81
82 : objeq? ( obj obj -- bool )
83     rot = -rot = and ;
84
85 \ }}}
86
87 \ ---- Pre-defined symbols ---- {{{
88
89 objvar symbol-table
90
91 : (create-symbol) ( addr n -- symbol-obj )
92     dup 0= if
93         2drop nil
94     else
95         2dup drop @ character-type 2swap
96         swap 1+ swap 1-
97         recurse
98
99         cons
100     then
101 ;
102
103 : create-symbol ( -- )
104     bl word
105     count
106
107     (create-symbol)
108     drop symbol-type
109
110     2dup
111
112     symbol-table fetchobj
113     cons
114     symbol-table setobj
115
116     create swap , ,
117     does> dup @ swap 1+ @
118 ;
119
120 create-symbol quote     quote-symbol
121 create-symbol define    define-symbol
122 create-symbol set!      set!-symbol
123 create-symbol ok        ok-symbol
124
125 \ }}}
126
127 \ ---- Environments ---- {{{
128
129 objvar global-env
130
131 : enclosing-env ( env -- env )
132     cdr ;
133
134 : first-frame ( env -- frame )
135     car ;
136
137 : make-frame ( vars vals -- frame )
138     cons ;
139
140 : frame-vars ( frame -- vars )
141     car ;
142
143 : frame-vals ( frame -- vals )
144     cdr ;
145
146 : add-binding ( var val frame -- )
147     2swap 2over frame-vals cons
148     2over set-car!
149     2swap 2over frame-vars cons
150     swap set-cdr!
151 ;
152
153 : extend-env ( vars vals env -- env )
154     >R >R
155     make-frame
156     R> R>
157     cons
158 ;
159
160 objvar vars
161 objvar vals
162
163 : get-vars-vals-frame ( var frame -- bool )
164     2dup frame-vars vars setobj
165     frame-vals vals setobj
166
167     begin
168         vars fetchobj nil objeq? false =
169     while
170         2dup vars fetchobj car objeq? if
171             2drop true
172             exit
173         then
174
175         vars fetchobj cdr vars setobj
176         vals fetchobj cdr vals setobj
177     repeat
178
179     2drop false
180 ;
181
182 : get-vars-vals ( var env -- vars? vals? bool )
183
184     begin
185         2dup nil objeq? false =
186     while
187         2over 2over first-frame
188         lookup-var-frame if
189             2drop 2drop
190             vars fetchobj vals fetchobj true
191             exit
192         then
193
194         enclosing-env
195     repeat
196
197     2drop 2drop
198     false
199 ;
200
201 hide vars
202 hide vals
203
204 : lookup-var ( var env -- val )
205     get-vars-vals if
206         2swap 2drop car
207     else
208         bold fg red ." Tried to read unbound variable." reset-term abort
209     then
210 ;
211
212 : set-var ( var val env -- )
213     >R >R 2swap R> R> ( val var env )
214     get-vars-vals if
215         2swap 2drop ( val vals )
216         set-car!
217     else
218         bold fg red ." Tried to set unbound variable." reset-term abort
219     then
220 ;
221
222 objvar env
223
224 : define-var ( var val env -- )
225     env objset 
226
227     2over env objfetch ( var val var env )
228     get-vars-vals if
229         2swap 2drop ( var val vals )
230         set-car!
231         2drop
232     else
233         env objfetch
234         first-frame ( var val frame )
235         add-binding
236     then
237 ;
238
239 hide env
240
241 \ }}}
242
243 \ ---- Read ---- {{{
244
245 variable parse-idx
246 variable stored-parse-idx
247 create parse-str 161 allot
248 variable parse-str-span
249
250 create parse-idx-stack 10 allot 
251 variable parse-idx-sp
252 parse-idx-stack parse-idx-sp !
253
254 : push-parse-idx
255     parse-idx @ parse-idx-sp @ !
256     1 parse-idx-sp +!
257 ;
258
259 : pop-parse-idx
260     parse-idx-sp @ parse-idx-stack <= abort" Parse index stack underflow."
261
262     1 parse-idx-sp -!
263
264     parse-idx-sp @ @ parse-idx ! ;
265
266
267 : append-newline
268     '\n' parse-str parse-str-span @ + !
269     1 parse-str-span +! ;
270
271 : empty-parse-str
272     0 parse-str-span !
273     0 parse-idx ! ;
274
275 : getline
276     parse-str 160 expect cr
277     span @ parse-str-span !
278     append-newline
279     0 parse-idx ! ;
280
281 : inc-parse-idx
282     1 parse-idx +! ;
283
284 : dec-parse-idx
285     1 parse-idx -! ;
286
287 : charavailable? ( -- bool )
288     parse-str-span @ parse-idx @ > ;
289
290 : nextchar ( -- char )
291     charavailable? false = if getline then
292     parse-str parse-idx @ + @ ;
293
294 : whitespace? ( -- bool )
295     nextchar BL = 
296     nextchar '\n' = or ;
297
298 : eof? ( -- bool )
299     nextchar 4 = ;
300
301 : delim? ( -- bool )
302     whitespace?
303     nextchar [char] ( = or
304     nextchar [char] ) = or
305 ;
306
307 : eatspaces
308     begin
309         whitespace?
310     while
311         inc-parse-idx
312     repeat
313 ;
314
315 : digit? ( -- bool )
316     nextchar [char] 0 >=
317     nextchar [char] 9 <=
318     and ;
319
320 : minus? ( -- bool )
321     nextchar [char] - = ;
322
323 : number? ( -- bool )
324     digit? minus? or false = if
325         false
326         exit
327     then
328
329     push-parse-idx
330     inc-parse-idx
331
332     begin digit? while
333         inc-parse-idx
334     repeat
335
336     delim? if
337         pop-parse-idx
338         true
339     else
340         pop-parse-idx
341         false
342     then
343 ;
344
345 : boolean? ( -- bool )
346     nextchar [char] # <> if false exit then
347
348     push-parse-idx
349     inc-parse-idx
350
351     nextchar [char] t <>
352     nextchar [char] f <>
353     and if pop-parse-idx false exit then
354
355     inc-parse-idx
356     delim? if
357         pop-parse-idx
358         true
359     else
360         pop-parse-idx
361         false
362     then
363 ;
364
365 : str-equiv? ( str -- bool )
366
367     push-parse-idx
368
369     true -rot
370
371     swap dup rot + swap
372
373     do
374         i @ nextchar <> if
375             drop false
376             leave
377         then
378
379         inc-parse-idx
380     loop
381
382     delim? false = if drop false then
383
384     pop-parse-idx
385 ;
386
387 : character? ( -- bool )
388     nextchar [char] # <> if false exit then
389
390     push-parse-idx
391     inc-parse-idx
392
393     nextchar [char] \ <> if pop-parse-idx false exit then
394
395     inc-parse-idx
396
397     S" newline" str-equiv? if pop-parse-idx true exit then
398     S" space" str-equiv? if pop-parse-idx true exit then
399     S" tab" str-equiv? if pop-parse-idx true exit then
400
401     charavailable? false = if pop-parse-idx false exit then
402
403     pop-parse-idx true
404 ;
405
406 : pair? ( -- bool )
407     nextchar [char] ( = ;
408
409 : string? ( -- bool )
410     nextchar [char] " = ;
411
412 : readnum ( -- num-atom )
413     minus? dup if
414         inc-parse-idx
415     then
416
417     0
418
419     begin digit? while
420         10 * nextchar [char] 0 - +
421         inc-parse-idx
422     repeat
423
424     swap if negate then
425
426     number-type
427 ;
428
429 : readbool ( -- bool-atom )
430     inc-parse-idx
431     
432     nextchar [char] f = if
433         false
434     else
435         true
436     then
437
438     inc-parse-idx
439
440     boolean-type
441 ;
442
443 : readchar ( -- char-atom )
444     inc-parse-idx
445     inc-parse-idx
446
447     S" newline" str-equiv? if 7 parse-idx +! '\n' character-type exit then
448     S" space" str-equiv? if 5 parse-idx +! bl character-type exit then
449     S" tab" str-equiv? if 3 parse-idx +! 9 character-type exit then
450
451     nextchar character-type
452
453     inc-parse-idx
454 ;
455
456 : readstring ( -- charlist )
457     nextchar [char] " = if
458         inc-parse-idx
459
460         delim? false = if
461             bold fg red
462             ." No delimiter following right double quote. Aborting." cr
463             reset-term abort
464         then
465
466         dec-parse-idx
467
468         0 nil-type exit
469     then
470
471     nextchar [char] \ = if
472         inc-parse-idx
473         nextchar case
474             [char] n of '\n' endof
475             [char] " of [char] " endof
476             [char] \
477         endcase
478     else
479         nextchar
480     then
481     inc-parse-idx character-type
482
483     recurse
484
485     cons
486 ;
487
488 : readsymbol ( -- charlist )
489     delim? if nil exit then
490
491     nextchar inc-parse-idx character-type
492
493     recurse
494
495     cons
496 ;
497
498 : charlist-equiv ( charlist charlist -- bool )
499
500     2over 2over
501
502     \ One or both nil
503     nil? -rot 2drop
504     if
505         nil? -rot 2drop
506         if
507             2drop 2drop true exit
508         else
509             2drop 2drop false exit
510         then
511     else
512         nil? -rot 2drop
513         if
514             2drop 2drop false exit
515         then
516     then
517
518     2over 2over
519
520     \ Neither nil
521     car drop -rot car drop = if
522             cdr 2swap cdr recurse
523         else
524             2drop 2drop false
525     then
526 ;
527
528 : charlist>symbol ( charlist -- symbol-obj )
529
530     symbol-table fetchobj
531
532     begin
533         nil? false =
534     while
535         2over 2over
536         car drop pair-type
537         charlist-equiv if
538             2swap 2drop
539             car
540             exit
541         else
542             cdr
543         then
544     repeat
545
546     2drop
547     drop symbol-type 2dup
548     symbol-table fetchobj cons
549     symbol-table setobj
550 ;
551
552 defer read
553
554 : readpair ( -- pairobj )
555     eatspaces
556
557     \ Empty lists
558     nextchar [char] ) = if
559         inc-parse-idx
560
561         delim? false = if
562             bold fg red
563             ." No delimiter following right paren. Aborting." cr
564             reset-term abort
565         then
566
567         dec-parse-idx
568
569         0 nil-type exit
570     then
571
572     \ Read first pair element
573     read
574
575     \ Pairs
576     eatspaces
577     nextchar [char] . = if
578         inc-parse-idx
579
580         delim? false = if
581             bold fg red
582             ." No delimiter following '.'. Aborting." cr
583             reset-term abort
584         then
585
586         eatspaces read
587     else
588         recurse
589     then
590
591     eatspaces
592
593     cons
594 ;
595
596 \ Parse a scheme expression
597 :noname ( -- obj )
598
599     eatspaces
600
601     number? if
602         readnum
603         exit
604     then
605
606     boolean? if
607         readbool
608         exit
609     then
610
611     character? if
612         readchar
613         exit
614     then
615
616     string? if
617         inc-parse-idx
618
619         readstring
620         drop string-type
621
622         nextchar [char] " <> if
623             bold red ." Missing closing double-quote." reset-term cr
624             abort
625         then
626
627         inc-parse-idx
628
629         exit
630     then
631
632     pair? if
633         inc-parse-idx
634
635         eatspaces
636
637         readpair
638
639         eatspaces
640
641         nextchar [char] ) <> if
642             bold red ." Missing closing paren." reset-term cr
643             abort
644         then
645
646         inc-parse-idx
647
648         exit
649     then
650
651     nextchar [char] ' = if
652         inc-parse-idx
653         quote-symbol recurse nil cons cons exit
654     then
655
656     eof? if
657         bold fg blue ." Moriturus te saluto." reset-term ."  ok" cr
658         quit
659     then
660
661     \ Anything else is parsed as a symbol
662     readsymbol charlist>symbol
663
664 ; is read
665
666 \ }}}
667
668 \ ---- Eval ---- {{{
669
670 : self-evaluating? ( obj -- obj bool )
671     boolean-type istype? if true exit then
672     number-type istype? if true exit then
673     character-type istype? if true exit then
674     string-type istype? if true exit then
675     nil-type istype? if true exit then
676
677     false
678 ;
679
680 : tagged-list? ( obj tag-obj -- obj bool )
681     2over 
682     pair-type istype? false = if
683         2drop 2drop false
684     else
685         car objeq?
686     then ;
687
688 : quote? ( obj -- obj bool )
689     quote-symbol tagged-list?  ;
690
691 : quote-body ( quote-obj -- quote-body-obj )
692     cadr ;
693
694 : variable? ( obj -- obj bool )
695     symbol-type istype? ;
696
697 : definition? ( obj -- obj bool )
698     define-symbol tagged-list? ;
699
700 : definition-var ( obj -- var )
701     cdr car ;
702
703 : definition-val ( obj -- val )
704     cdr cdr car ;
705
706 : assignment? ( obj -- obj bool )
707     set-symbol tagged-list? ;
708
709 : assignment-var ( obj -- var )
710     cdr car ;
711     
712 : assignment-val ( obj -- val )
713     cdr cdr car ;
714
715 : eval-definition ( obj env -- res )
716     2swap 
717     2over 2over ( env obj env obj )
718     definition-val 2swap ( env obj valexp env )
719     eval  ( env obj val )
720     
721     2swap definition-var 2swap ( env var val )
722
723     >R >R 2swap R> R> 2swap ( var val env )
724     define-var
725
726     ok-symbol
727 ;
728     
729 : eval-assignment ( obj env -- res )
730     2swap 
731     2over 2over ( env obj env obj )
732     assignment-val 2swap ( env obj valexp env )
733     eval  ( env obj val )
734     
735     2swap assignment-var 2swap ( env var val )
736
737     >R >R 2swap R> R> 2swap ( var val env )
738     set-var
739
740     ok-symbol
741 ;
742 : eval ( obj env -- result )
743     2swap
744
745     self-evaluating? if
746         2swap 2drop
747         exit
748     then
749
750     quote? if
751         quote-body
752         2swap 2drop
753         exit
754     then
755
756     variable? if
757         2swap lookup-var
758         exit
759     then
760
761     definition? if
762         2swap eval-definition
763         exit
764     then
765
766     assignment? if
767         2swap eval-assignment
768         exit
769     then
770
771     bold fg red ." Error evaluating expression - unrecognized type. Aborting." reset-term cr
772     abort
773 ;
774
775 \ }}}
776
777 \ ---- Print ---- {{{
778
779 : printnum ( numobj -- ) drop 0 .R ;
780
781 : printbool ( numobj -- )
782     drop if
783         ." #t"
784     else
785         ." #f"
786     then
787 ;
788
789 : printchar ( charobj -- )
790     drop
791     case
792         9 of ." #\tab" endof
793         bl of ." #\space" endof
794         '\n' of ." #\newline" endof
795         
796         dup ." #\" emit
797     endcase
798 ;
799
800 : (printstring) ( stringobj -- )
801     nil-type istype? if 2drop exit then
802
803     2dup car drop dup
804     case
805         '\n' of ." \n" drop endof
806         [char] \ of ." \\" drop endof
807         [char] " of [char] \ emit [char] " emit drop endof
808         emit
809     endcase
810
811     cdr recurse
812 ;
813 : printstring ( stringobj -- )
814     [char] " emit
815     (printstring)
816     [char] " emit ;
817
818 : printsymbol ( symbolobj -- )
819     nil-type istype? if 2drop exit then
820
821     2dup car drop emit
822     cdr recurse
823 ;
824
825 : printnil ( nilobj -- )
826     2drop ." ()" ;
827
828 defer print
829 : printpair ( pairobj -- )
830     2dup
831     car print
832     cdr
833     nil-type istype? if 2drop exit then
834     pair-type istype? if space recurse exit then
835     ."  . " print
836 ;
837
838 :noname ( obj -- )
839     number-type istype? if printnum exit then
840     boolean-type istype? if printbool exit then
841     character-type istype? if printchar exit then
842     string-type istype? if printstring exit then
843     symbol-type istype? if printsymbol exit then
844     nil-type istype? if printnil exit then
845     pair-type istype? if ." (" printpair ." )" exit then
846
847     bold fg red ." Error printing expression - unrecognized type. Aborting" reset-term cr
848     abort
849 ; is print
850
851 \ }}}
852
853 \ ---- REPL ----
854
855 : repl
856     cr ." Welcome to scheme.forth.jl!" cr
857        ." Use Ctrl-D to exit." cr
858
859     empty-parse-str
860
861     begin
862         cr bold fg green ." > " reset-term
863         read
864         global-env fetchobj eval
865         fg cyan ." ; " print reset-term
866     again
867 ;
868
869 forth definitions