bbc-basic: Start of step 9: add 'try*' form and 'throw'.
[jackhill/mal.git] / ps / step4_if_fn_do.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 /eval_ast { 2 dict begin
19 /env exch def
20 /ast exch def
21 %(eval_ast: ) print ast ==
22 ast _symbol? { %if symbol
23 env ast env_get
24 }{ ast _sequential? { %elseif list or vector
25 [
26 ast /data get { %forall items
27 env EVAL
28 } forall
29 ] ast _list? { _list_from_array }{ _vector_from_array } ifelse
30 }{ ast _hash_map? { %elseif list or vector
31 <<
32 ast /data get { %forall entries
33 env EVAL
34 } forall
35 >> _hash_map_from_dict
36 }{ % else
37 ast
38 } ifelse } ifelse } ifelse
39 end } def
40
41 /EVAL { 9 dict begin
42 /env exch def
43 /ast exch def
44
45 %(EVAL: ) print ast true _pr_str print (\n) print
46 ast _list? not { %if not a list
47 ast env eval_ast
48 }{ %else apply the list
49 /a0 ast 0 _nth def
50 a0 _nil? { %if ()
51 ast
52 }{ /def! a0 eq { %if def!
53 /a1 ast 1 _nth def
54 /a2 ast 2 _nth def
55 env a1 a2 env EVAL env_set
56 }{ /let* a0 eq { %if let*
57 /a1 ast 1 _nth def
58 /a2 ast 2 _nth def
59 /let_env env null null env_new def
60 0 2 a1 _count 1 sub { %for each pair
61 /idx exch def
62 let_env
63 a1 idx _nth
64 a1 idx 1 add _nth let_env EVAL
65 env_set
66 pop % discard the return value
67 } for
68 a2 let_env EVAL
69 }{ /do a0 eq { %if do
70 /el ast _rest env eval_ast def
71 el el _count 1 sub _nth % return last value
72 }{ /if a0 eq { %if if
73 /a1 ast 1 _nth def
74 /cond a1 env EVAL def
75 cond null eq cond false eq or { % if cond is nil or false
76 ast _count 3 gt { %if false branch with a3
77 ast 3 _nth env
78 EVAL
79 }{ % else false branch with no a3
80 null
81 } ifelse
82 }{ % true branch
83 ast 2 _nth env
84 EVAL
85 } ifelse
86 }{ /fn* a0 eq { %if fn*
87 /a1 ast 1 _nth def
88 /a2 ast 2 _nth def
89 a2 env a1 _mal_function
90 }{
91 /el ast env eval_ast def
92 el _rest el _first % stack: ast function
93 dup _mal_function? { %if user defined function
94 fload % stack: ast new_env
95 EVAL
96 }{ dup _function? { %else if builtin function
97 /data get exec
98 }{ %else (regular procedure/function)
99 (cannot apply native proc!\n) print quit
100 } ifelse } ifelse
101 } ifelse } ifelse } ifelse } ifelse } ifelse } ifelse
102 } ifelse
103 end } def
104
105
106 % print
107 /PRINT {
108 true _pr_str
109 } def
110
111
112 % repl
113 /repl_env null null null env_new def
114
115 /RE { READ repl_env EVAL } def
116 /REP { READ repl_env EVAL PRINT } def
117
118 % core.ps: defined using postscript
119 /_ref { repl_env 3 1 roll env_set pop } def
120 core_ns { _function _ref } forall
121
122 % core.mal: defined using the language itself
123 (\(def! not \(fn* \(a\) \(if a false true\)\)\)) RE pop
124
125 % repl loop
126 { %loop
127 (user> ) _readline
128 not { exit } if % exit if EOF
129
130 { %try
131 REP print (\n) print
132 } stopped {
133 (Error: ) print
134 get_error_data false _pr_str print (\n) print
135 $error /newerror false put
136 $error /errorinfo null put
137 clear
138 cleardictstack
139 } if
140 } bind loop
141
142 (\n) print % final newline before exit for cleanliness
143 quit