Commit | Line | Data |
---|---|---|
24951719 BH |
1 | REM Step 8 of mal in BBC BASIC |
2 | ||
3 | LIBRARY "types" | |
4 | LIBRARY "reader" | |
5 | LIBRARY "printer" | |
6 | LIBRARY "env" | |
7 | LIBRARY "core" | |
8 | ||
9 | PROCtypes_init | |
10 | ||
11 | repl_env% = FNalloc_environment(FNnil) | |
12 | PROCcore_ns : REM This sets the data pointer | |
13 | REPEAT | |
14 | READ sym$, i% | |
15 | IF sym$ <> "" THEN | |
16 | PROCenv_set(repl_env%, FNalloc_symbol(sym$), FNalloc_corefn(i%)) | |
17 | ENDIF | |
18 | UNTIL sym$ = "" | |
19 | ||
20 | REM Initial forms to evaluate | |
21 | RESTORE +0 | |
22 | DATA (def! not (fn* (a) (if a false true))) | |
23 | DATA (def! load-file (fn* (f) (eval (read-string (str "(do " (slurp f) ")"))))) | |
938f8c02 BH |
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)))))))) | |
24951719 BH |
26 | DATA "" |
27 | REPEAT | |
28 | READ form$ | |
29 | IF form$ <> "" THEN val$ = FNrep(form$) | |
30 | UNTIL form$ = "" | |
31 | ||
32 | REPEAT | |
33 | PROCgc_enter | |
34 | sav% = FNgc_save | |
35 | REM Catch all errors apart from "Escape". | |
36 | ON ERROR LOCAL PRINT REPORT$:IF ERR = 17 THEN END ELSE PROCgc_restore(sav%) | |
37 | PRINT "user> "; | |
38 | LINE INPUT "" line$ | |
39 | PRINT FNrep(line$) | |
40 | PROCgc_exit | |
41 | UNTIL FALSE | |
42 | ||
43 | END | |
44 | ||
45 | DEF FNREAD(a$) | |
46 | =FNread_str(a$) | |
47 | ||
48 | DEF FNis_pair(val%) | |
49 | =FNis_list(val%) AND NOT FNis_empty(val%) | |
50 | ||
51 | DEF FNquasiquote(ast%) | |
52 | LOCAL car%, caar% | |
53 | IF NOT FNis_pair(ast%) THEN =FNalloc_list2(FNalloc_symbol("quote"), ast%) | |
54 | car% = FNlist_car(ast%) | |
55 | IF FNis_symbol(car%) THEN | |
56 | IF FNunbox_symbol(car%) = "unquote" THEN =FNlist_nth(ast%, 1) | |
57 | ENDIF | |
58 | IF FNis_pair(car%) THEN | |
59 | caar% = FNlist_car(car%) | |
60 | IF FNis_symbol(caar%) THEN | |
61 | IF FNunbox_symbol(caar%) = "splice-unquote" THEN | |
62 | =FNalloc_list3(FNalloc_symbol("concat"), FNlist_nth(car%, 1), FNquasiquote(FNlist_cdr(ast%))) | |
63 | ENDIF | |
64 | ENDIF | |
65 | ENDIF | |
66 | =FNalloc_list3(FNalloc_symbol("cons"), FNquasiquote(car%), FNquasiquote(FNlist_cdr(ast%))) | |
67 | ||
00164f7e BH |
68 | DEF FNis_macro_call(ast%, env%) |
69 | LOCAL car%, val% | |
70 | IF NOT FNis_list(ast%) THEN =FALSE | |
71 | car% = FNlist_car(ast%) | |
72 | IF NOT FNis_symbol(car%) THEN =FALSE | |
73 | IF FNis_nil(FNenv_find(env%, car%)) THEN =FALSE | |
74 | val% = FNenv_get(env%, car%) | |
75 | =FNis_macro(val%) | |
76 | ||
77 | DEF FNmacroexpand(ast%, env%) | |
78 | LOCAL mac%, macenv%, macast% | |
79 | WHILE FNis_macro_call(ast%, env%) | |
80 | REM PRINT "expanded ";FNpr_str(ast%, TRUE); | |
81 | mac% = FNenv_get(env%, FNlist_car(ast%)) | |
82 | macenv% = FNnew_env(FNfn_env(mac%), FNfn_params(mac%), FNlist_cdr(ast%)) | |
83 | macast% = FNfn_ast(mac%) | |
84 | ast% = FNEVAL(macast%, macenv%) | |
85 | REM PRINT " to ";FNpr_str(ast%, TRUE) | |
86 | ENDWHILE | |
87 | =ast% | |
88 | ||
24951719 BH |
89 | DEF FNEVAL(ast%, env%) |
90 | PROCgc_enter | |
91 | =FNgc_exit(FNEVAL_(ast%, env%)) | |
92 | ||
93 | DEF FNEVAL_(ast%, env%) | |
94 | LOCAL car%, specialform%, val%, bindings% | |
95 | REPEAT | |
96 | PROCgc_keep_only2(ast%, env%) | |
97 | IF NOT FNis_list(ast%) THEN =FNeval_ast(ast%, env%) | |
98 | IF FNis_empty(ast%) THEN =ast% | |
00164f7e BH |
99 | ast% = FNmacroexpand(ast%, env%) |
100 | IF NOT FNis_list(ast%) THEN =FNeval_ast(ast%, env%) | |
24951719 BH |
101 | car% = FNlist_car(ast%) |
102 | specialform% = FALSE | |
103 | IF FNis_symbol(car%) THEN | |
104 | specialform% = TRUE | |
105 | CASE FNunbox_symbol(car%) OF | |
106 | REM Special forms | |
107 | WHEN "def!" | |
108 | val% = FNEVAL(FNlist_nth(ast%, 2), env%) | |
109 | PROCenv_set(env%, FNlist_nth(ast%, 1), val%) | |
110 | =val% | |
111 | WHEN "defmacro!" | |
112 | val% = FNEVAL(FNlist_nth(ast%, 2), env%) | |
113 | IF FNis_fn(val%) THEN PROCmake_macro(val%) | |
114 | PROCenv_set(env%, FNlist_nth(ast%, 1), val%) | |
115 | =val% | |
116 | WHEN "let*" | |
117 | env% = FNalloc_environment(env%) | |
118 | bindings% = FNlist_nth(ast%, 1) | |
119 | WHILE NOT FNis_empty(bindings%) | |
120 | PROCenv_set(env%, FNlist_car(bindings%), FNEVAL(FNlist_nth(bindings%, 1), env%)) | |
121 | bindings% = FNlist_cdr(FNlist_cdr(bindings%)) | |
122 | ENDWHILE | |
123 | ast% = FNlist_nth(ast%, 2) | |
124 | REM Loop round for tail-call optimisation. | |
125 | WHEN "do" | |
126 | REM The guide has us call FNeval_ast on the sub-list that excludes | |
127 | REM the last element of ast%, but that's a bit painful without | |
128 | REM native list slicing, so it's easier to just re-implement the | |
129 | REM bit of FNeval_ast that we need. | |
130 | ast% = FNlist_cdr(ast%) | |
131 | WHILE NOT FNis_empty(FNlist_cdr(ast%)) | |
132 | val% = FNEVAL(FNlist_car(ast%), env%) | |
133 | ast% = FNlist_cdr(ast%) | |
134 | ENDWHILE | |
135 | ast% = FNlist_car(ast%) | |
136 | WHEN "if" | |
137 | IF FNis_truish(FNEVAL(FNlist_nth(ast%, 1), env%)) THEN | |
138 | ast% = FNlist_nth(ast%, 2) | |
139 | ELSE | |
140 | IF FNlist_len(ast%) = 3 THEN =FNnil ELSE ast% = FNlist_nth(ast%, 3) | |
141 | ENDIF | |
142 | REM Loop round for tail-call optimisation. | |
143 | WHEN "fn*" | |
144 | =FNalloc_fn(FNlist_nth(ast%, 2), FNlist_nth(ast%, 1), env%) | |
145 | WHEN "quote" | |
146 | =FNlist_nth(ast%, 1) | |
147 | WHEN "quasiquote" | |
148 | ast% = FNquasiquote(FNlist_nth(ast%, 1)) | |
149 | REM Loop round for tail-call optimisation | |
00164f7e BH |
150 | WHEN "macroexpand" |
151 | =FNmacroexpand(FNlist_nth(ast%, 1), env%) | |
24951719 BH |
152 | OTHERWISE |
153 | specialform% = FALSE | |
154 | ENDCASE | |
155 | ENDIF | |
156 | IF NOT specialform% THEN | |
157 | REM This is the "apply" part. | |
158 | ast% = FNeval_ast(ast%, env%) | |
159 | car% = FNlist_car(ast%) | |
160 | IF FNis_corefn(car%) THEN | |
161 | =FNcore_call(FNunbox_corefn(car%), FNlist_cdr(ast%)) | |
162 | ENDIF | |
163 | IF FNis_fn(car%) THEN | |
164 | env% = FNnew_env(FNfn_env(car%), FNfn_params(car%), FNlist_cdr(ast%)) | |
165 | ast% = FNfn_ast(car%) | |
166 | REM Loop round for tail-call optimisation. | |
167 | ELSE | |
168 | ERROR &40E80918, "Not a function" | |
169 | ENDIF | |
170 | ENDIF | |
171 | UNTIL FALSE | |
172 | ||
173 | DEF FNPRINT(a%) | |
174 | =FNpr_str(a%, TRUE) | |
175 | ||
176 | DEF FNrep(a$) | |
177 | =FNPRINT(FNEVAL(FNREAD(a$), repl_env%)) | |
178 | ||
179 | DEF FNeval_ast(ast%, env%) | |
180 | LOCAL val%, car%, cdr% | |
181 | IF FNis_symbol(ast%) THEN =FNenv_get(env%, ast%) | |
182 | IF FNis_list(ast%) THEN | |
183 | IF FNis_empty(ast%) THEN =ast% | |
184 | car% = FNEVAL(FNlist_car(ast%), env%) | |
185 | cdr% = FNeval_ast(FNlist_cdr(ast%), env%) | |
186 | =FNalloc_pair(car%, cdr%) | |
187 | ENDIF | |
188 | =ast% | |
189 | ||
190 | REM Local Variables: | |
191 | REM indent-tabs-mode: nil | |
192 | REM End: |