1 REM Step 9 of mal in BBC BASIC
11 repl_env% = FNalloc_environment(FNnil)
12 PROCcore_ns : REM This sets the data pointer
16 PROCenv_set(repl_env%, FNalloc_symbol(sym$), FNalloc_corefn(i%))
20 REM Initial forms to evaluate
22 DATA (def! not (fn* (a) (if a false true)))
23 DATA (def! load-file (fn* (f) (eval (read-string (str "(do " (slurp f) ")")))))
24 DATA (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)))))))
25 DATA (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))))))))
29 IF form$ <> "" THEN val$ = FNrep(form$)
34 IF FNis_empty(argv%) THEN
35 PROCenv_set(repl_env%, FNalloc_symbol("*ARGV*"), FNempty)
37 PROCenv_set(repl_env%, FNalloc_symbol("*ARGV*"), FNrest(argv%))
38 val$ = FNrep("(load-file " + FNpr_str(FNfirst(argv%), TRUE) + ")")
43 REM Catch all errors apart from "Escape".
44 ON ERROR LOCAL PRINT REPORT$:IF ERR = 17 THEN END ELSE PROCgc_restore(sav%)
58 =FNis_seq(val%) AND NOT FNis_empty(val%)
60 DEF FNquasiquote(ast%)
62 IF NOT FNis_pair(ast%) THEN =FNalloc_list2(FNalloc_symbol("quote"), ast%)
64 IF FNis_symbol(car%) THEN
65 IF FNunbox_symbol(car%) = "unquote" THEN =FNnth(ast%, 1)
67 IF FNis_pair(car%) THEN
69 IF FNis_symbol(caar%) THEN
70 IF FNunbox_symbol(caar%) = "splice-unquote" THEN
71 =FNalloc_list3(FNalloc_symbol("concat"), FNnth(car%, 1), FNquasiquote(FNrest(ast%)))
75 =FNalloc_list3(FNalloc_symbol("cons"), FNquasiquote(car%), FNquasiquote(FNrest(ast%)))
77 DEF FNis_macro_call(ast%, env%)
79 IF NOT FNis_list(ast%) THEN =FALSE
81 IF NOT FNis_symbol(car%) THEN =FALSE
82 IF FNis_nil(FNenv_find(env%, car%)) THEN =FALSE
83 val% = FNenv_get(env%, car%)
86 DEF FNmacroexpand(ast%, env%)
87 LOCAL mac%, macenv%, macast%
88 WHILE FNis_macro_call(ast%, env%)
89 REM PRINT "expanded ";FNpr_str(ast%, TRUE);
90 mac% = FNenv_get(env%, FNfirst(ast%))
91 macenv% = FNnew_env(FNfn_env(mac%), FNfn_params(mac%), FNrest(ast%))
92 macast% = FNfn_ast(mac%)
93 ast% = FNEVAL(macast%, macenv%)
94 REM PRINT " to ";FNpr_str(ast%, TRUE)
98 DEF FNtry_catch(ast%, env%)
100 REM If there's no 'catch*' clause then we just evaluate the 'try*'.
101 IF FNcount(ast%) < 3 THEN =FNEVAL(FNnth(ast%, 1), env%)
102 IF FNunbox_symbol(FNfirst(FNnth(ast%, 2))) <> "catch*" THEN
103 ERROR &40E80924, "Invalid 'catch*' clause"
105 ret% = FNtry(FNnth(ast%, 1), env%, is_error%)
106 IF is_error% THEN =FNcatch(FNnth(ast%, 2), env%, ret%)
109 REM Evaluate an expression, returning either the result or an exception
110 REM raised during evaluation. is_error% indicates which it was.
111 DEF FNtry(ast%, env%, RETURN is_error%)
116 ON ERROR LOCAL is_error% = TRUE : =FNgc_restore(trysav%, FNwrap_exception)
117 =FNgc_restore(trysav%, FNEVAL(ast%, env%))
119 REM Return a mal value corresponding to the most-recently thrown exception.
121 IF ERR = &40E80900 THEN =MAL_ERR% : REM Error generated by 'throw'
122 =FNalloc_string(REPORT$) : REM OS error or error generated by mal
124 DEF FNcatch(ast%, env%, err%)
126 binds% = FNalloc_pair(FNnth(ast%, 1), FNempty)
127 exprs% = FNalloc_pair(err%, FNempty)
128 env% = FNnew_env(env%, binds%, exprs%)
129 =FNEVAL(FNnth(ast%, 2), env%)
131 DEF FNEVAL(ast%, env%)
133 =FNgc_exit(FNEVAL_(ast%, env%))
135 DEF FNEVAL_(ast%, env%)
136 LOCAL car%, specialform%, val%, bindings%
138 PROCgc_keep_only2(ast%, env%)
139 IF NOT FNis_list(ast%) THEN =FNeval_ast(ast%, env%)
140 IF FNis_empty(ast%) THEN =ast%
141 ast% = FNmacroexpand(ast%, env%)
142 IF NOT FNis_list(ast%) THEN =FNeval_ast(ast%, env%)
145 IF FNis_symbol(car%) THEN
147 CASE FNunbox_symbol(car%) OF
150 val% = FNEVAL(FNnth(ast%, 2), env%)
151 PROCenv_set(env%, FNnth(ast%, 1), val%)
154 val% = FNEVAL(FNnth(ast%, 2), env%)
155 IF FNis_fn(val%) THEN PROCmake_macro(val%)
156 PROCenv_set(env%, FNnth(ast%, 1), val%)
159 env% = FNalloc_environment(env%)
160 bindings% = FNnth(ast%, 1)
161 WHILE NOT FNis_empty(bindings%)
162 PROCenv_set(env%, FNfirst(bindings%), FNEVAL(FNnth(bindings%, 1), env%))
163 bindings% = FNrest(FNrest(bindings%))
165 ast% = FNnth(ast%, 2)
166 REM Loop round for tail-call optimisation.
168 REM The guide has us call FNeval_ast on the sub-list that excludes
169 REM the last element of ast%, but that's a bit painful without
170 REM native list slicing, so it's easier to just re-implement the
171 REM bit of FNeval_ast that we need.
173 WHILE NOT FNis_empty(FNrest(ast%))
174 val% = FNEVAL(FNfirst(ast%), env%)
179 IF FNis_truish(FNEVAL(FNnth(ast%, 1), env%)) THEN
180 ast% = FNnth(ast%, 2)
182 IF FNcount(ast%) = 3 THEN =FNnil ELSE ast% = FNnth(ast%, 3)
184 REM Loop round for tail-call optimisation.
186 =FNalloc_fn(FNnth(ast%, 2), FNnth(ast%, 1), env%)
190 ast% = FNquasiquote(FNnth(ast%, 1))
191 REM Loop round for tail-call optimisation
193 =FNmacroexpand(FNnth(ast%, 1), env%)
195 =FNtry_catch(ast%, env%)
200 IF NOT specialform% THEN
201 REM This is the "apply" part.
202 ast% = FNeval_ast(ast%, env%)
204 IF FNis_corefn(car%) THEN
205 =FNcore_call(FNunbox_corefn(car%), FNrest(ast%))
207 IF FNis_fn(car%) THEN
208 env% = FNnew_env(FNfn_env(car%), FNfn_params(car%), FNrest(ast%))
209 ast% = FNfn_ast(car%)
210 REM Loop round for tail-call optimisation.
212 ERROR &40E80918, "Not a function"
221 =FNPRINT(FNEVAL(FNREAD(a$), repl_env%))
223 DEF FNeval_ast(ast%, env%)
224 LOCAL val%, car%, cdr%
225 IF FNis_symbol(ast%) THEN =FNenv_get(env%, ast%)
226 IF FNis_seq(ast%) THEN
227 IF FNis_empty(ast%) THEN =ast%
228 car% = FNEVAL(FNfirst(ast%), env%)
229 cdr% = FNeval_ast(FNrest(ast%), env%)
230 IF FNis_vector(ast%) THEN =FNalloc_vector_pair(car%, cdr%)
231 =FNalloc_pair(car%, cdr%)
237 LOCAL argv%, rargv%, cmdptr%, arg$, len%
239 IF !PAGE = &D7C1C7C5 THEN
240 REM Running under Brandy, so ARGC and ARGV$ are usable.
242 FOR i% = ARGC TO 1 STEP -1
243 argv% = FNalloc_pair(FNalloc_string(ARGV$(i%)), argv%)
247 IF (INKEY(-256) AND &F0) = &A0 THEN
249 REM Running under RISC OS
250 REM Vexingly, we can only get the command line that was passed to
251 REM the BASIC interpreter. This means that we need to extract
252 REM the arguments from that. Typically, we will have been started
253 REM with "BASIC -quit <filename> <args>".
256 SYS "OS_GetEnv" TO cmdptr%
258 SYS "OS_GSTrans", cmdptr%, q%, &20000000 + 256 TO cmdptr%, , len%
260 rargv% = FNalloc_pair(FNalloc_string($q%), rargv%)
262 REM Put argv back into the right order.
263 WHILE NOT FNis_empty(rargv%)
264 argv% = FNalloc_pair(FNfirst(rargv%), argv%)
265 rargv% = FNrest(rargv%)
267 IF FNis_empty(argv%) THEN =FNgc_exit(argv%)
268 argv% = FNrest(argv%) : REM skip "BASIC"
269 IF FNis_empty(argv%) THEN =FNgc_exit(argv%)
270 IF FNunbox_string(FNfirst(argv%)) <> "-quit" THEN =FNgc_exit(argv%)
271 argv% = FNrest(argv%) : REM skip "-quit"
272 IF FNis_empty(argv%) THEN =FNgc_exit(argv%)
273 argv% = FNrest(argv%) : REM skip filename
280 REM indent-tabs-mode: nil