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