blob: 66494cc5bd42dc2fc7f022295d88e0b198a3a2ad (
plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
|
(types.ps) run
(reader.ps) run
% read
/_readline { print flush (%stdin) (r) file 99 string readline } def
/READ {
/str exch def
str read_str
} def
% eval
% is_pair?: ast -> is_pair? -> bool
% return true if non-empty list, otherwise false
/is_pair? {
dup _list? { length 0 gt }{ pop false } ifelse
} def
% ast -> quasiquote -> new_ast
/quasiquote { 3 dict begin
/ast exch def
ast is_pair? not { %if not is_pair?
/quote ast 2 _list
}{
/a0 ast 0 get def
a0 /unquote eq { %if a0 unquote symbol
ast 1 get
}{ a0 is_pair? { %elseif a0 is_pair?
/a00 a0 0 get def
a00 /splice-unquote eq { %if splice-unquote
/concat a0 1 get ast _rest quasiquote 3 _list
}{ %else not splice-unquote
/cons a0 quasiquote ast _rest quasiquote 3 _list
} ifelse
}{ % else not a0 is_pair?
/cons a0 quasiquote ast _rest quasiquote 3 _list
} ifelse } ifelse
} ifelse
end } def
/is_macro_call? { 3 dict begin
/env exch def
/ast exch def
ast _list? {
/a0 ast 0 get def
a0 _symbol? { %if a0 is symbol
env a0 env_find null ne { %if a0 is in env
env a0 env_get _mal_function? { %if user defined function
env a0 env_get /macro? get true eq %if marked as macro
}{ false } ifelse
}{ false } ifelse
}{ false } ifelse
}{ false } ifelse
end } def
/macroexpand { 3 dict begin
/env exch def
/ast exch def
{
ast env is_macro_call? {
/mac env ast 0 get env_get def
/ast ast _rest mac fload EVAL def
}{
exit
} ifelse
} loop
ast
end } def
/eval_ast { 2 dict begin
/env exch def
/ast exch def
%(eval_ast: ) print ast ==
ast _symbol? { %if symbol
env ast env_get
}{ ast _list? { %elseif list
[
ast {
env EVAL
} forall
]
}{ % else
ast
} ifelse } ifelse
end } def
/EVAL { 13 dict begin
{ %loop (TCO)
/env exch def
/ast exch def
/loop? false def
%(EVAL: ) print ast true _pr_str print (\n) print
ast _list? not { %if not a list
ast env eval_ast
}{ %else apply the list
/ast ast env macroexpand def
ast _list? not { %if no longer a list
ast
}{ %else still a list
/a0 ast 0 get def
/def! a0 eq { %if def!
/a1 ast 1 get def
/a2 ast 2 get def
env a1 a2 env EVAL env_set
}{ /let* a0 eq { %if let*
/a1 ast 1 get def
/a2 ast 2 get def
/let_env env [ ] [ ] env_new def
0 2 a1 length 1 sub { %for each pair
/idx exch def
let_env
a1 idx get
a1 idx 1 add get let_env EVAL
env_set
pop % discard the return value
} for
a2 let_env EVAL
}{ /quote a0 eq { %if quote
ast 1 get
}{ /quasiquote a0 eq { %if quasiquote
ast 1 get quasiquote env EVAL
}{ /defmacro! a0 eq { %if defmacro!
/a1 ast 1 get def
/a2 ast 2 get def
a2 env EVAL
dup /macro? true put % set macro flag
env exch a1 exch env_set % def! it
}{ /macroexpand a0 eq { %if defmacro!
ast 1 get env macroexpand
}{ /ps* a0 eq { %if ps*
count /stackcnt exch def
ast 1 get
{
token not { exit } if
exch
} loop
exec
count stackcnt gt { % if new operands on stack
% return an list of new operands
count stackcnt sub array astore
}{
null % return nil
} ifelse
}{ /do a0 eq { %if do
ast length 2 gt { %if ast has more than 2 elements
ast 1 ast length 2 sub getinterval env eval_ast pop
} if
ast ast length 1 sub get % last ast becomes new ast
env
/loop? true def % loop
}{ /try* a0 eq { %if try*
{ %try
countdictstack /dictcnt exch def
count /stackcnt exch def
%(here1:\n) print pstack
ast 1 get env EVAL
%(here2\n) print
} stopped { %catch
%(here3:\n) print pstack
% clean up the dictionary stack
1 1 countdictstack dictcnt sub { %foreach added dict
%(popping dict\n) print
pop end % pop idx and pop dict
%(new ast: ) print ast true _pr_str print (\n) print
} for
% clean up the operand stack
%(op stack cleanup: ) print count stackcnt sub ==
count 1 exch 1 exch stackcnt sub { %foreach added operand
%(op stack: ) print pstack
pop pop % pop idx and operand
%(popped op stack\n) print pstack
} for
% get error data and reset $error dict
/errdata get_error_data def
$error /newerror false put
$error /errorinfo null put
ast length 3 lt { %if no third (catch*) form
errdata throw
} if
%(here4: ) print ast true _pr_str print (\n) print
ast 2 get 0 get (catch*) eq not { %if third form not catch*
(No catch* in throw form) throw
} if
%(here5: ) print ast 2 get 2 get true _pr_str print (\n) print
ast 2 get 2 get
%(here5: ) print ast 2 get 1 get true _pr_str print (\n) print
%(here6: ) print errdata true _pr_str print (\n) print
env [ ast 2 get 1 get ] [ errdata ] env_new
%(here7:\n) print pstack
EVAL
} if
}{ /if a0 eq { %if if
/a1 ast 1 get def
/cond a1 env EVAL def
cond null eq cond false eq or { % if cond is nil or false
ast length 3 gt { %if false branch with a3
ast 3 get env
/loop? true def
}{ % else false branch with no a3
null
} ifelse
}{ % true branch
ast 2 get env
/loop? true def
} ifelse
}{ /fn* a0 eq { %if fn*
/a1 ast 1 get def
/a2 ast 2 get def
<<
/type /_maltype_function % user defined function
/macro? false % macro flag, false by default
/params null % close over parameters
/ast null % close over ast
/env null % close over environment
/data { __self__ fload EVAL }
>>
dup length dict copy % make an actual copy/new instance
dup /params a1 put % insert closed over a1 into position 2
dup /ast a2 put % insert closed over a2 into position 3
dup /env env put % insert closed over env into position 4
dup dup /data get exch 0 exch put % insert self reference
}{
/el ast env eval_ast def
el _rest el _first % stack: ast function
dup _mal_function? { % if user defined function
fload % stack: ast new_env
/loop? true def
}{ %else (regular procedure/function)
exec % apply function to args
} ifelse
} ifelse } ifelse } ifelse } ifelse } ifelse } ifelse } ifelse } ifelse } ifelse } ifelse } ifelse
} ifelse
} ifelse
loop? not { exit } if
} loop % TCO
end } def
% print
/PRINT {
true _pr_str
} def
% repl
/repl_env null [ ] [ ] env_new def
/RE { READ repl_env EVAL } def
/REP { READ repl_env EVAL PRINT } def
/_ref { repl_env 3 1 roll env_set pop } def
types_ns { _ref } forall
(readline) { 0 get _readline not { null } if } _ref
(read-string) { 0 get read_str } _ref
(eval) { 0 get repl_env EVAL } _ref
/slurp { (r) file dup bytesavailable string readstring pop } def
(slurp) { 0 get slurp } _ref
(\(def! not \(fn* \(a\) \(if a false true\)\)\)) RE pop
(\(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\)\)\)\)\)\)\)) RE pop
(\(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\)\)\)\)\)\)\)\)) RE pop
(\(def! load-file \(fn* \(f\) \(eval \(read-string \(str "\(do " \(slurp f\) "\)"\)\)\)\)\)) RE pop
userdict /ARGUMENTS known { %if command line arguments
ARGUMENTS length 0 gt { %if more than 0 arguments
ARGUMENTS {
(\(load-file ") exch ("\)) concatenate concatenate RE pop
} forall
quit
} if
} if
{ % loop
(user> ) _readline
not { exit } if % exit if EOF
{ %try
REP print (\n) print
} stopped {
(Error: ) print
get_error_data false _pr_str print (\n) print
$error /newerror false put
$error /errorinfo null put
clear
cleardictstack
} if
} bind loop
(\n) print % final newline before exit for cleanliness
quit
|