12 % is_pair?: ast -> is_pair? -> bool
13 % return true if non-empty list, otherwise false
15 dup _list?
{ length
0 gt
}{ pop false } ifelse
18 % ast -> quasiquote -> new_ast
19 /quasiquote
{ 3 dict begin
21 ast is_pair? not
{ %if not is_pair?
25 a0
/unquote eq
{ %if a0 unquote symbol
27 }{ a0 is_pair?
{ %elseif a0 is_pair?
29 a00
/splice
-unquote eq
{ %if splice-unquote
30 /concat a0
1 get ast _rest quasiquote
3 _list
31 }{ %else not splice-unquote
32 /cons a0 quasiquote ast _rest quasiquote
3 _list
34 }{ % else not a0 is_pair?
35 /cons a0 quasiquote ast _rest quasiquote
3 _list
40 /is_macro_call?
{ 3 dict begin
45 a0 _symbol?
{ %if a0 is symbol
46 env a0 env_find null ne
{ %if a0 is in env
47 env a0 env_get _mal_function?
{ %if user defined function
48 env a0 env_get
/macro? get
true eq
%if marked as macro
55 /macroexpand
{ 3 dict begin
59 ast env is_macro_call?
{
60 /mac env ast
0 get env_get def
61 /ast ast _rest mac fload EVAL def
69 /eval_ast
{ 2 dict begin
72 %(eval_ast: ) print ast ==
73 ast _symbol?
{ %if symbol
75 }{ ast _list?
{ %elseif list
93 %(EVAL: ) print ast true _pr_str print (\n) print
94 ast _list? not
{ %if not a list
96 }{ %else apply the list
97 /ast ast env macroexpand def
98 ast _list? not
{ %if no longer a list
100 }{ %else still a list
102 /def
! a0 eq
{ %if def!
105 env a1 a2 env EVAL env_set
106 }{ /let
* a0 eq
{ %if let*
109 /let_env env
[ ] [ ] env_new def
110 0 2 a1 length
1 sub { %for each pair
114 a1 idx
1 add get let_env EVAL
116 pop % discard the return value
119 }{ /quote a0 eq
{ %if quote
121 }{ /quasiquote a0 eq
{ %if quasiquote
122 ast
1 get quasiquote env EVAL
123 }{ /defmacro
! a0 eq
{ %if defmacro!
127 dup /macro?
true put
% set macro flag
128 env
exch a1
exch env_set
% def! it
129 }{ /macroexpand a0 eq
{ %if defmacro!
130 ast
1 get env macroexpand
131 }{ /ps
* a0 eq
{ %if ps*
132 count /stackcnt
exch def
135 token not
{ exit } if
139 count stackcnt gt
{ % if new operands on stack
140 % return an list of new operands
141 count stackcnt
sub array astore
145 }{ /do a0 eq
{ %if do
146 ast length
2 gt
{ %if ast has more than 2 elements
147 ast
1 ast length
2 sub getinterval env eval_ast
pop
149 ast ast length
1 sub get
% last ast becomes new ast
151 /loop?
true def
% loop
152 }{ /if a0 eq
{ %if if
154 /cond a1 env EVAL def
155 cond null eq cond
false eq or
{ % if cond is nil or false
156 ast length
3 gt
{ %if false branch with a3
159 }{ % else false branch with no a3
166 }{ /fn
* a0 eq
{ %if fn*
170 /type
/_maltype_function
% user defined function
171 /macro?
false % macro flag, false by default
172 /params null
% close over parameters
173 /ast null
% close over ast
174 /env null
% close over environment
176 dup length dict
copy % make an actual copy/new instance
177 dup /params a1 put
% insert closed over a1 into position 2
178 dup /ast a2 put
% insert closed over a2 into position 3
179 dup /env env put
% insert closed over env into position 4
181 /el ast env eval_ast def
182 el _first _mal_function?
{ % if user defined function
183 el _rest el _first fload
% stack: ast new_env
185 }{ %else (regular procedure/function)
186 el _rest el _first
exec % apply function to args
188 } ifelse } ifelse } ifelse } ifelse } ifelse } ifelse } ifelse } ifelse } ifelse } ifelse
192 loop? not
{ exit } if
204 /repl_env null
[ ] [ ] env_new def
206 /RE
{ READ repl_env EVAL
} def
207 /REP
{ READ repl_env EVAL PRINT
} def
208 /_ref
{ repl_env
3 1 roll env_set
pop } def
210 types_ns
{ _ref
} forall
212 (read
-string
) { 0 get read_str
} _ref
213 (eval
) { 0 get repl_env EVAL
} _ref
214 /slurp
{ (r
) file
dup bytesavailable string readstring
pop } def
215 (slurp
) { 0 get slurp
} _ref
216 (pstack
) { (vvv
\n) print pstack
(^^^
\n) print
} _ref
217 (p1
) { 1 index
true _pr_str print
(\n) print
} _ref
219 (\
(def
! not \
(fn
* \
(a\
) \
(if a
false true\
)\
)\
)) RE
pop
220 (\
(def
! load
-file \
(fn
* \
(f\
) \
(eval \
(read
-string \
(str
"\(do " \
(slurp f\
) "\)"\
)\
)\
)\
)\
)) RE
pop
222 /stdin
(%stdin) (r) file def
224 userdict
/ARGUMENTS known
{ %if command line arguments
225 ARGUMENTS length
0 gt
{ %if more than 0 arguments
227 (\
(load
-file
") exch ("\
)) concatenate concatenate RE
pop
235 stdin
99 string readline
237 not
{ exit } if % exit if EOF
239 %(\ngot line: ) print dup print (\n) print flush
245 get_error_data
false _pr_str print
(\n) print
246 $error
/newerror
false put
247 $error
/errorinfo null put
252 (\n) print
% final newline before exit for cleanliness