forth: Add step 6, clean up comment parsing
[jackhill/mal.git] / forth / step5_tco.fs
CommitLineData
d44f31c2
C
1require reader.fs
2require printer.fs
3require core.fs
4
5core MalEnv. constant repl-env
6
7\ Fully evalutate any Mal object:
8def-protocol-method mal-eval ( env ast -- val )
9
10\ Invoke an object, given whole env and unevaluated argument forms:
11def-protocol-method invoke ( argv argc mal-fn -- ... )
12
1399999999 constant TCO-eval
14
15: read read-str ;
16: eval ( env obj )
17 begin
18 mal-eval
19 dup TCO-eval =
20 while
21 drop
22 repeat ;
23: print
24 \ ." Type: " dup mal-type @ type-name safe-type cr
25 pr-str ;
26
27MalDefault extend mal-eval nip ;; drop \ By default, evalutate to yourself
28
29MalKeyword
30 extend invoke { env list kw -- val }
31 0 kw env list MalList/start @ cell+ @ eval get
32 ?dup 0= if
33 \ compute not-found value
34 list MalList/count @ 1 > if
35 env list MalList/start @ 2 cells + @ TCO-eval
36 else
37 mal-nil
38 endif
39 endif ;;
40drop
41
42\ eval all but the first item of list, storing in temporary memory
43\ that should be freed with free-eval-rest when done.
44: eval-rest { env list -- mem-token argv argc }
45 \ Pass args on dictionary stack (!)
46 \ TODO: consider allocate and free of a real MalList instead
47 \ Normal list, evaluate and invoke
48 here { val-start }
49 list MalList/start @ cell+ { expr-start }
50 list MalList/count @ 1- dup { argc } 0 ?do
51 env expr-start i cells + @ eval ,
52 loop
53 val-start val-start argc ;
54
55: free-eval-rest ( mem-token/val-start -- )
56 here - allot ;
57
58MalNativeFn
59 extend invoke ( env list this -- list )
60 MalNativeFn/xt @ { xt }
61 eval-rest ( mem-token argv argc )
62 xt execute ( mem-token return-val )
63 swap free-eval-rest ;;
64drop
65
66SpecialOp
67 extend invoke ( env list this -- list )
68 SpecialOp/xt @ execute ;;
69drop
70
71: install-special ( symbol xt )
72 SpecialOp. repl-env env/set ;
73
74: defspecial
75 parse-allot-name MalSymbol.
76 ['] install-special
77 :noname
78 ;
79
80defspecial quote ( env list -- form )
81 nip MalList/start @ cell+ @ ;;
82
83defspecial def! { env list -- val }
84 list MalList/start @ cell+ { arg0 }
85 arg0 @ ( key )
86 env arg0 cell+ @ eval dup { val } ( key val )
87 env env/set
88 val ;;
89
90defspecial let* { old-env list -- val }
91 old-env MalEnv. { env }
92 list MalList/start @ cell+ dup { arg0 }
93 @ to-list
94 dup MalList/start @ { bindings-start } ( list )
95 MalList/count @ 0 +do
96 bindings-start i cells + dup @ swap cell+ @ ( sym expr )
97 env swap eval
98 env env/set
99 2 +loop
100 env arg0 cell+ @ TCO-eval
101 \ TODO: dec refcount of env
102 ;;
103
104defspecial do { env list -- val }
105 list MalList/start @ { start }
106 list MalList/count @ dup 1- { last } 1 ?do
107 env start i cells + @
108 i last = if
109 TCO-eval
110 else
111 eval drop
112 endif
113 loop ;;
114
115defspecial if { env list -- val }
116 list MalList/start @ cell+ { arg0 }
117 env arg0 @ eval ( test-val )
118 dup mal-false = if
119 drop -1
120 else
121 mal-nil =
122 endif
123 if
124 \ branch to false
125 list MalList/count @ 3 > if
126 env arg0 cell+ cell+ @ TCO-eval
127 else
128 mal-nil
129 endif
130 else
131 \ branch to true
132 env arg0 cell+ @ TCO-eval
133 endif ;;
134
135s" &" MalSymbol. constant &-sym
136
137MalUserFn
138 extend invoke { call-env list mal-fn -- list }
139 call-env list eval-rest { mem-token argv argc }
140
141 mal-fn MalUserFn/formal-args @ { f-args-list }
142 mal-fn MalUserFn/env @ MalEnv. { env }
143
144 f-args-list MalList/start @ { f-args }
145 f-args-list MalList/count @ ?dup 0= if else
146 \ pass nil for last arg, unless overridden below
147 1- cells f-args + @ mal-nil env env/set
148 endif
149 argc 0 ?do
150 f-args i cells + @
151 dup &-sym m= if
152 drop
153 f-args i 1+ cells + @ ( more-args-symbol )
154 MalList new ( sym more-args )
155 argc i - dup { c } over MalList/count !
156 c cells allocate throw dup { start } over MalList/start !
157 argv i cells + start c cells cmove
158 env env/set
159 leave
160 endif
161 argv i cells + @
162 env env/set
163 loop
164
165 env mal-fn MalUserFn/body @ TCO-eval
166
167 mem-token free-eval-rest ;;
168
169defspecial fn* { env list -- val }
170 list MalList/start @ cell+ { arg0 }
171 MalUserFn new
172 env over MalUserFn/env !
173 arg0 @ to-list over MalUserFn/formal-args !
174 arg0 cell+ @ over MalUserFn/body ! ;;
175
176MalSymbol
177 extend mal-eval { env sym -- val }
178 0 sym env get
179 dup 0= if
180 drop
181 ." Symbol '"
182 sym as-native safe-type
183 ." ' not found." cr
184 1 throw
185 endif ;;
186drop
187
188: eval-ast { env list -- list }
189 here
190 list MalList/start @ { expr-start }
191 list MalList/count @ 0 ?do
192 env expr-start i cells + @ eval ,
193 loop
194 here>MalList ;
195
196MalList
197 extend mal-eval { env list -- val }
198 env list MalList/start @ @ eval
199 env list rot invoke ;;
200drop
201
202MalVector
203 extend mal-eval ( env vector -- vector )
204 MalVector/list @ eval-ast
205 MalVector new swap over MalVector/list ! ;;
206drop
207
208MalMap
209 extend mal-eval ( env map -- map )
210 MalMap/list @ eval-ast
211 MalMap new swap over MalMap/list ! ;;
212drop
213
214: rep ( str -- val )
215 read
216 repl-env swap eval
217 print ;
218
219create buff 128 allot
22077777777777 constant stack-leak-detect
221
222: read-lines
223 begin
224 ." user> "
225 stack-leak-detect
226 buff 128 stdin read-line throw
227 while
228 buff swap
229 ['] rep
230 \ execute safe-type
231 catch ?dup 0= if safe-type else ." Caught error " . endif
232 cr
233 stack-leak-detect <> if ." --stack leak--" cr endif
234 repeat ;
235
236read-lines
237cr
238bye