8 /_readline
{ print flush
(%stdin) (r) file 99 string readline } def
17 % is_pair?: ast -> is_pair? -> bool
18 % return true if non-empty list, otherwise false
20 dup _list?
{ _count
0 gt
}{ pop false } ifelse
23 % ast -> quasiquote -> new_ast
24 /quasiquote
{ 3 dict begin
26 ast is_pair? not
{ %if not is_pair?
30 a0
/unquote eq
{ %if a0 unquote symbol
32 }{ a0 is_pair?
{ %elseif a0 is_pair?
34 a00
/splice
-unquote eq
{ %if splice-unquote
35 /concat a0
1 _nth ast _rest quasiquote
3 _list
36 }{ %else not splice-unquote
37 /cons a0 quasiquote ast _rest quasiquote
3 _list
39 }{ % else not a0 is_pair?
40 /cons a0 quasiquote ast _rest quasiquote
3 _list
45 /eval_ast
{ 2 dict begin
48 %(eval_ast: ) print ast ==
49 ast _symbol?
{ %if symbol
51 }{ ast _sequential?
{ %elseif list or vector
53 ast
/data get
{ %forall items
56 ] ast _list?
{ _list_from_array
}{ _vector_from_array
} ifelse
57 }{ ast _hash_map?
{ %elseif list or vector
59 ast
/data get
{ %forall entries
62 >> _hash_map_from_dict
65 } ifelse } ifelse } ifelse
75 %(EVAL: ) print ast true _pr_str print (\n) print
76 ast _list? not
{ %if not a list
78 }{ %else apply the list
80 /def
! a0 eq
{ %if def!
83 env a1 a2 env EVAL env_set
84 }{ /let
* a0 eq
{ %if let*
87 /let_env env null null env_new def
88 0 2 a1 _count
1 sub { %for each pair
92 a1 idx
1 add _nth let_env EVAL
94 pop % discard the return value
97 }{ /quote a0 eq
{ %if quote
99 }{ /quasiquote a0 eq
{ %if quasiquote
100 ast
1 _nth quasiquote env EVAL
101 }{ /do a0 eq
{ %if do
102 ast _count
2 gt
{ %if ast has more than 2 elements
103 ast
1 ast _count
2 sub _slice env eval_ast
pop
105 ast ast _count
1 sub _nth
% last ast becomes new ast
107 /loop?
true def
% loop
108 }{ /if a0 eq
{ %if if
110 /cond a1 env EVAL def
111 cond null eq cond
false eq or
{ % if cond is nil or false
112 ast _count
3 gt
{ %if false branch with a3
115 }{ % else false branch with no a3
122 }{ /fn
* a0 eq
{ %if fn*
125 a2 env a1 _mal_function
127 /el ast env eval_ast def
128 el _rest el _first
% stack: ast function
129 dup _mal_function?
{ %if user defined function
130 fload
% stack: ast new_env
132 }{ dup _function?
{ %else if builtin function
134 }{ %else (regular procedure/function)
135 (cannot apply native proc
!\n) print
quit
137 } ifelse } ifelse } ifelse } ifelse } ifelse } ifelse } ifelse
140 loop? not
{ exit } if
152 /repl_env null null null env_new def
154 /RE
{ READ repl_env EVAL
} def
155 /REP
{ READ repl_env EVAL PRINT
} def
157 % core.ps: defined using postscript
158 /_ref
{ _function repl_env
3 1 roll env_set
pop } def
159 core_ns
{ _ref
} forall
160 (eval
) { 0 _nth repl_env EVAL
} _ref
162 % core.mal: defined using the language itself
163 (\
(def
! not \
(fn
* \
(a\
) \
(if a
false true\
)\
)\
)) RE
pop
164 (\
(def
! load
-file \
(fn
* \
(f\
) \
(eval \
(read
-string \
(str
"\(do " \
(slurp f\
) "\)"\
)\
)\
)\
)\
)) RE
pop
166 userdict
/ARGUMENTS known
{ %if command line arguments
167 ARGUMENTS length
0 gt
{ %if more than 0 arguments
169 (\
(load
-file
") exch ("\
)) concatenate concatenate RE
pop
176 not
{ exit } if % exit if EOF
182 get_error_data
false _pr_str print
(\n) print
183 $error
/newerror
false put
184 $error
/errorinfo null put
190 (\n) print
% final newline before exit for cleanliness