1 /runlibfile where
{ pop }{ /runlibfile
{ run
} def
} ifelse %
4 (printer.ps
) runlibfile
9 /_readline
{ print flush
(%stdin) (r) file 1024 string readline } def
18 % is_pair?: ast -> is_pair? -> bool
19 % return true if non-empty list, otherwise false
21 dup _sequential?
{ _count
0 gt
}{ pop false } ifelse
24 % ast -> quasiquote -> new_ast
25 /quasiquote
{ 3 dict begin
27 ast is_pair? not
{ %if not is_pair?
31 a0
/unquote eq
{ %if a0 unquote symbol
33 }{ a0 is_pair?
{ %elseif a0 is_pair?
35 a00
/splice
-unquote eq
{ %if splice-unquote
36 /concat a0
1 _nth ast _rest quasiquote
3 _list
37 }{ %else not splice-unquote
38 /cons a0 quasiquote ast _rest quasiquote
3 _list
40 }{ % else not a0 is_pair?
41 /cons a0 quasiquote ast _rest quasiquote
3 _list
46 /is_macro_call?
{ 3 dict begin
51 a0 _symbol?
{ %if a0 is symbol
52 env a0 env_find null ne
{ %if a0 is in env
53 env a0 env_get _mal_function?
{ %if user defined function
54 env a0 env_get
/macro? get
true eq
%if marked as macro
61 /macroexpand
{ 3 dict begin
65 ast env is_macro_call?
{
66 /mac env ast
0 _nth env_get def
67 /ast ast _rest mac fload EVAL def
75 /eval_ast
{ 2 dict begin
78 %(eval_ast: ) print ast ==
79 ast _symbol?
{ %if symbol
81 }{ ast _sequential?
{ %elseif list or vector
83 ast
/data get
{ %forall items
86 ] ast _list?
{ _list_from_array
}{ _vector_from_array
} ifelse
87 }{ ast _hash_map?
{ %elseif list or vector
89 ast
/data get
{ %forall entries
92 >> _hash_map_from_dict
95 } ifelse } ifelse } ifelse
105 %(EVAL: ) print ast true _pr_str print (\n) print
106 ast _list? not
{ %if not a list
108 }{ %else apply the list
109 /ast ast env macroexpand def
110 ast _list? not
{ %if no longer a list
112 }{ %else still a list
116 }{ /def
! a0 eq
{ %if def!
119 env a1 a2 env EVAL env_set
120 }{ /let
* a0 eq
{ %if let*
123 /let_env env null null env_new def
124 0 2 a1 _count
1 sub { %for each pair
128 a1 idx
1 add _nth let_env EVAL
130 pop % discard the return value
134 /loop?
true def
% loop
135 }{ /quote a0 eq
{ %if quote
137 }{ /quasiquote a0 eq
{ %if quasiquote
138 ast
1 _nth quasiquote
140 /loop?
true def
% loop
141 }{ /defmacro
! a0 eq
{ %if defmacro!
145 dup /macro?
true put
% set macro flag
146 env
exch a1
exch env_set
% def! it
147 }{ /macroexpand a0 eq
{ %if defmacro!
148 ast
1 _nth env macroexpand
149 }{ /ps
* a0 eq
{ %if ps*
150 count /stackcnt
exch def
152 count stackcnt gt
{ % if new operands on stack
153 % return an list of new operands
154 count stackcnt
sub array astore
158 }{ /do a0 eq
{ %if do
159 ast _count
2 gt
{ %if ast has more than 2 elements
160 ast
1 ast _count
2 sub _slice env eval_ast
pop
162 ast ast _count
1 sub _nth
% last ast becomes new ast
164 /loop?
true def
% loop
165 }{ /try
* a0 eq
{ %if try*
166 ast _count
2 gt
{ %if has catch* block
168 countdictstack
/dictcnt
exch def
169 count /stackcnt
exch def
172 % clean up the dictionary stack
173 1 1 countdictstack dictcnt
sub { %foreach added dict
174 %(popping dict\n) print
175 pop end
% pop idx and pop dict
176 %(new ast: ) print ast true _pr_str print (\n) print
178 % clean up the operand stack
179 count 1 exch 1 exch stackcnt
sub { %foreach added operand
180 %(op stack: ) print pstack
181 pop pop % pop idx and operand
182 %(popped op stack\n) print pstack
184 % get error data and reset $error dict
185 /errdata get_error_data def
186 $error
/newerror
false put
187 $error
/errorinfo null put
189 ast _count
3 lt
{ %if no third (catch*) form
192 ast
2 _nth
0 _nth
(catch
*) eq not
{ %if third form not catch*
193 (No catch
* in throw form
) _throw
197 ast
2 _nth
1 _nth
1 _list
202 }{ % else no catch* block
205 }{ /if a0 eq
{ %if if
207 /cond a1 env EVAL def
208 cond null eq cond
false eq or
{ % if cond is nil or false
209 ast _count
3 gt
{ %if false branch with a3
212 }{ % else false branch with no a3
219 }{ /fn
* a0 eq
{ %if fn*
222 a2 env a1 _mal_function
224 /el ast env eval_ast def
225 el _rest el _first
% stack: ast function
226 dup _mal_function?
{ %if user defined function
227 fload
% stack: ast new_env
229 }{ dup _function?
{ %else if builtin function
231 }{ %else (regular procedure/function)
232 (cannot apply native proc
!\n) print
quit
234 } ifelse } ifelse } ifelse } ifelse } ifelse } ifelse } ifelse } ifelse } ifelse } ifelse } ifelse } ifelse
238 loop? not
{ exit } if
250 /repl_env null null null env_new def
252 /RE
{ READ repl_env EVAL
} def
253 /REP
{ READ repl_env EVAL PRINT
} def
255 % core.ps: defined using postscript
256 /_ref
{ repl_env
3 1 roll env_set
pop } def
257 core_ns
{ _function _ref
} forall
258 (eval
) { 0 _nth repl_env EVAL
} _function _ref
259 (*ARGV
*) [ ] _list_from_array _ref
261 % core.mal: defined using the language itself
262 (\
(def
! *host
-language
* "postscript"\
)) RE
pop
263 (\
(def
! not \
(fn
* \
(a\
) \
(if a
false true\
)\
)\
)) RE
pop
264 (\
(def
! load
-file \
(fn
* \
(f\
) \
(eval \
(read
-string \
(str
"\(do " \
(slurp f\
) "\nnil\)"\
)\
)\
)\
)\
)) RE
pop
265 (\
(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
267 userdict
/ARGUMENTS known
{ %if command line arguments
268 ARGUMENTS length
0 gt
{ %if more than 0 arguments
269 (*ARGV
*) ARGUMENTS
1 ARGUMENTS length
1 sub getinterval
270 _list_from_array _ref
272 (\
(load
-file
") exch ("\
)) concatenate concatenate RE
pop
278 (\
(println \
(str
"Mal [" *host
-language
* "]"\
)\
)) RE
pop
281 not
{ exit } if % exit if EOF
287 get_error_data
false _pr_str print
(\n) print
288 $error
/newerror
false put
289 $error
/errorinfo null put
295 (\n) print
% final newline before exit for cleanliness