Merge pull request #306 from kanaka/add-predicates
[jackhill/mal.git] / forth / step8_macros.fs
CommitLineData
e82947d0
C
1require reader.fs
2require printer.fs
3require core.fs
4
5core MalEnv. constant repl-env
6
e82947d0
C
799999999 constant TCO-eval
8
9: read read-str ;
10: eval ( env obj )
11 begin
12 \ ." eval-> " dup pr-str safe-type cr
13 mal-eval
14 dup TCO-eval =
15 while
16 drop
17 repeat ;
18: print
19 \ ." Type: " dup mal-type @ type-name safe-type cr
20 pr-str ;
21
22MalDefault extend mal-eval nip ;; drop \ By default, evalutate to yourself
23
24MalKeyword
45c1894b 25 extend eval-invoke { env list kw -- val }
e82947d0
C
26 0 kw env list MalList/start @ cell+ @ eval get
27 ?dup 0= if
28 \ compute not-found value
29 list MalList/count @ 1 > if
30 env list MalList/start @ 2 cells + @ TCO-eval
31 else
32 mal-nil
33 endif
34 endif ;;
35drop
36
37\ eval all but the first item of list
38: eval-rest { env list -- argv argc }
39 list MalList/start @ cell+ { expr-start }
40 list MalList/count @ 1- { argc }
41 argc cells allocate throw { target }
42 argc 0 ?do
43 env expr-start i cells + @ eval
44 target i cells + !
45 loop
46 target argc ;
47
48MalNativeFn
7148ddb6
DM
49 extend eval-invoke { env list this -- list }
50 env list eval-rest ( argv argc )
51 this invoke ;;
52 extend invoke ( argv argc this -- val )
53 MalNativeFn/xt @ execute ;;
e82947d0
C
54drop
55
56SpecialOp
45c1894b 57 extend eval-invoke ( env list this -- list )
e82947d0
C
58 SpecialOp/xt @ execute ;;
59drop
60
61: install-special ( symbol xt )
62 SpecialOp. repl-env env/set ;
63
64: defspecial
65 parse-allot-name MalSymbol.
66 ['] install-special
67 :noname
68 ;
69
70: is-pair? ( obj -- bool )
71 empty? mal-false = ;
72
73defspecial quote ( env list -- form )
74 nip MalList/start @ cell+ @ ;;
75
76s" concat" MalSymbol. constant concat-sym
77s" cons" MalSymbol. constant cons-sym
78
79defer quasiquote
80: quasiquote0 { ast -- form }
81 ast is-pair? 0= if
82 here quote-sym , ast , here>MalList
83 else
84 ast to-list MalList/start @ { ast-start }
85 ast-start @ { ast[0] }
86 ast[0] unquote-sym m= if
87 ast-start cell+ @
88 else
89 ast[0] is-pair? if
90 ast[0] to-list MalList/start @ { ast[0]-start }
91 ast[0]-start @ splice-unquote-sym m= if
92 here
93 concat-sym ,
94 ast[0]-start cell+ @ ,
95 ast to-list MalList/rest quasiquote ,
96 here>MalList
97 false
98 else true endif
99 else true endif
100 if
101 here
102 cons-sym ,
103 ast[0] quasiquote ,
104 ast to-list MalList/rest quasiquote ,
105 here>MalList
106 endif
107 endif
108 endif ;
109' quasiquote0 is quasiquote
110
111defspecial quasiquote ( env list )
112 MalList/start @ cell+ @ ( ast )
113 quasiquote TCO-eval ;;
114
115defspecial def! { env list -- val }
116 list MalList/start @ cell+ { arg0 }
117 arg0 @ ( key )
118 env arg0 cell+ @ eval dup { val } ( key val )
119 env env/set val ;;
120
121defspecial defmacro! { env list -- val }
122 list MalList/start @ cell+ { arg0 }
123 arg0 @ ( key )
124 env arg0 cell+ @ eval { val }
125 true val MalUserFn/is-macro? !
126 val env env/set
127 val ;;
128
129defspecial let* { old-env list -- val }
130 old-env MalEnv. { env }
131 list MalList/start @ cell+ dup { arg0 }
132 @ to-list
133 dup MalList/start @ { bindings-start } ( list )
134 MalList/count @ 0 +do
135 bindings-start i cells + dup @ swap cell+ @ ( sym expr )
136 env swap eval
137 env env/set
138 2 +loop
139 env arg0 cell+ @ TCO-eval
140 \ TODO: dec refcount of env
141 ;;
142
143defspecial do { env list -- val }
144 list MalList/start @ { start }
145 list MalList/count @ dup 1- { last } 1 ?do
146 env start i cells + @
147 i last = if
148 TCO-eval
149 else
150 eval drop
151 endif
152 loop ;;
153
154defspecial if { env list -- val }
155 list MalList/start @ cell+ { arg0 }
156 env arg0 @ eval ( test-val )
157 dup mal-false = if
158 drop -1
159 else
160 mal-nil =
161 endif
162 if
163 \ branch to false
164 list MalList/count @ 3 > if
165 env arg0 cell+ cell+ @ TCO-eval
166 else
167 mal-nil
168 endif
169 else
170 \ branch to true
171 env arg0 cell+ @ TCO-eval
172 endif ;;
173
174s" &" MalSymbol. constant &-sym
175
176: new-user-fn-env { argv argc mal-fn -- env }
177 mal-fn MalUserFn/formal-args @ { f-args-list }
178 mal-fn MalUserFn/env @ MalEnv. { env }
179
180 f-args-list MalList/start @ { f-args }
181 f-args-list MalList/count @ ?dup 0= if else
fe364a97
DM
182 \ pass empty list for last arg, unless overridden below
183 1- cells f-args + @ MalList new env env/set
e82947d0
C
184 endif
185 argc 0 ?do
186 f-args i cells + @
187 dup &-sym m= if
188 drop
7148ddb6
DM
189 argc i - { c }
190 c cells allocate throw { start }
e82947d0 191 argv i cells + start c cells cmove
7148ddb6
DM
192 f-args i 1+ cells + @ ( more-args-symbol )
193 start c MalList. env env/set
e82947d0
C
194 leave
195 endif
196 argv i cells + @
197 env env/set
198 loop
199 env ;
200
201MalUserFn
45c1894b 202 extend eval-invoke { call-env list mal-fn -- list }
e82947d0 203 mal-fn MalUserFn/is-macro? @ if
7148ddb6
DM
204 list MalList/start @ cell+ \ argv
205 list MalList/count @ 1- \ argc
206 mal-fn new-user-fn-env { env }
e82947d0 207 env mal-fn MalUserFn/body @ eval
7148ddb6 208 call-env swap TCO-eval
e82947d0 209 else
7148ddb6
DM
210 call-env list eval-rest
211 mal-fn invoke
e82947d0 212 endif ;;
7148ddb6
DM
213
214 extend invoke ( argv argc mal-fn )
215 dup { mal-fn } new-user-fn-env { env }
216 env mal-fn MalUserFn/body @ TCO-eval ;;
e82947d0
C
217drop
218
219defspecial fn* { env list -- val }
220 list MalList/start @ cell+ { arg0 }
221 MalUserFn new
7148ddb6 222 false over MalUserFn/is-macro? !
e82947d0
C
223 env over MalUserFn/env !
224 arg0 @ to-list over MalUserFn/formal-args !
225 arg0 cell+ @ over MalUserFn/body ! ;;
226
227defspecial macroexpand ( env list[_,form] -- form )
228 MalList/start @ cell+ @ swap over ( form env form )
229 MalList/start @ @ ( form env macro-name-expr )
230 eval { macro-fn } ( form )
231 dup MalList/start @ cell+ swap MalList/count @ 1- macro-fn ( argv argc fn )
232 new-user-fn-env ( env )
233 macro-fn MalUserFn/body @ TCO-eval ;;
234
235MalSymbol
236 extend mal-eval { env sym -- val }
a631063f 237 sym env env/get-addr
e82947d0
C
238 dup 0= if
239 drop
a631063f 240 ." Symbol '" sym pr-str safe-type ." ' not found." cr
e82947d0 241 1 throw
a631063f
C
242 else
243 @
e82947d0
C
244 endif ;;
245drop
246
247: eval-ast { env list -- list }
248 here
249 list MalList/start @ { expr-start }
250 list MalList/count @ 0 ?do
251 env expr-start i cells + @ eval ,
252 loop
253 here>MalList ;
254
255MalList
256 extend mal-eval { env list -- val }
4e258d3a
DM
257 list MalList/count @ 0= if
258 list
259 else
260 env list MalList/start @ @ eval
261 env list rot eval-invoke
262 endif ;;
e82947d0
C
263drop
264
265MalVector
266 extend mal-eval ( env vector -- vector )
267 MalVector/list @ eval-ast
268 MalVector new swap over MalVector/list ! ;;
269drop
270
271MalMap
272 extend mal-eval ( env map -- map )
273 MalMap/list @ eval-ast
274 MalMap new swap over MalMap/list ! ;;
275drop
276
277defcore eval ( argv argc )
278 drop @ repl-env swap eval ;;
279
45c1894b 280: rep ( str-addr str-len -- str-addr str-len )
e82947d0
C
281 read
282 repl-env swap eval
283 print ;
284
285: mk-args-list ( -- )
286 here
287 begin
288 next-arg 2dup 0 0 d<> while
289 MalString. ,
290 repeat
291 2drop here>MalList ;
292
e82947d0
C
293create buff 128 allot
29477777777777 constant stack-leak-detect
295
7148ddb6
DM
296: nop ;
297
298defcore swap! { argv argc -- val }
299 \ argv is (atom fn args...)
300 argv @ { atom }
301 argv cell+ @ { fn }
302 argc 1- { call-argc }
303 call-argc cells allocate throw { call-argv }
304 atom Atom/val call-argv 1 cells cmove
305 argv cell+ cell+ call-argv cell+ call-argc 1- cells cmove
306 call-argv call-argc fn invoke
307 dup TCO-eval = if drop eval endif { new-val }
308 new-val atom Atom/val !
309 new-val ;;
310
45c1894b
C
311s\" (def! load-file (fn* (f) (eval (read-string (str \"(do \" (slurp f) \")\")))))" rep 2drop
312s\" (defmacro! cond (fn* (& xs) (if (> (count xs) 0) (list 'if (first xs) (if (> (count xs) 1) (nth xs 1) (throw \"odd number of forms to cond\")) (cons 'cond (rest (rest xs)))))))" rep 2drop
313s\" (defmacro! or (fn* (& xs) (if (empty? xs) nil (if (= 1 (count xs)) (first xs) `(let* (or_FIXME ~(first xs)) (if or_FIXME or_FIXME (or ~@(rest xs))))))))" rep 2drop
314
e82947d0
C
315: repl ( -- )
316 begin
317 ." user> "
318 stack-leak-detect
319 buff 128 stdin read-line throw
320 while ( num-bytes-read )
321 buff swap ( str-addr str-len )
322 ['] rep
323 \ execute type
324 catch ?dup 0= if safe-type else ." Caught error " . endif
325 cr
326 stack-leak-detect <> if ." --stack leak--" cr endif
327 repeat ;
328
329: main ( -- )
330 mk-args-list { args-list }
331 args-list MalList/count @ 0= if
332 s" *ARGV*" MalSymbol. MalList/Empty repl-env env/set
333 repl
334 else
335 args-list MalList/start @ @ { filename }
336 s" *ARGV*" MalSymbol. args-list MalList/rest repl-env env/set
337
338 repl-env
339 here s" load-file" MalSymbol. , filename , here>MalList
340 eval print
341 endif ;
342
343main
344cr
345bye