Merge pull request #250 from dubek/skew
[jackhill/mal.git] / basic / step9_try.in.bas
CommitLineData
5e5ca0d4
JM
1GOTO MAIN
2
d7a6c2d6 3REM $INCLUDE: 'mem.in.bas'
5e5ca0d4 4REM $INCLUDE: 'types.in.bas'
93593012 5REM $INCLUDE: 'readline.in.bas'
5e5ca0d4
JM
6REM $INCLUDE: 'reader.in.bas'
7REM $INCLUDE: 'printer.in.bas'
8REM $INCLUDE: 'env.in.bas'
9REM $INCLUDE: 'core.in.bas'
10
11REM $INCLUDE: 'debug.in.bas'
12
cc9dbd92 13REM READ(A$) -> R
5e5ca0d4
JM
14MAL_READ:
15 GOSUB READ_STR
16 RETURN
17
cc9dbd92 18REM QUASIQUOTE(A) -> R
af621e3a
JM
19SUB QUASIQUOTE
20 REM pair?
4202ef7b
JM
21 GOSUB TYPE_A
22 IF T<6 OR T>7 THEN GOTO QQ_QUOTE
d7a6c2d6 23 IF (Z%(A+1)=0) THEN GOTO QQ_QUOTE
af621e3a
JM
24 GOTO QQ_UNQUOTE
25
26 QQ_QUOTE:
5e5ca0d4 27 REM ['quote, ast]
c756af81
JM
28 B$="quote":T=5:GOSUB STRING
29 B=R:A=A:GOSUB LIST2
30 AY=B:GOSUB RELEASE
5e5ca0d4 31
af621e3a 32 GOTO QQ_DONE
5e5ca0d4
JM
33
34 QQ_UNQUOTE:
d7a6c2d6
JM
35 R=Z%(A+2)
36 IF (Z%(R)AND 31)<>5 THEN GOTO QQ_SPLICE_UNQUOTE
37 IF S$(Z%(R+1))<>"unquote" THEN GOTO QQ_SPLICE_UNQUOTE
5e5ca0d4 38 REM [ast[1]]
d7a6c2d6 39 R=Z%(Z%(A+1)+2)
4202ef7b 40 GOSUB INC_REF_R
5e5ca0d4 41
af621e3a 42 GOTO QQ_DONE
5e5ca0d4
JM
43
44 QQ_SPLICE_UNQUOTE:
93593012 45 GOSUB PUSH_A
5e5ca0d4 46 REM rest of cases call quasiquote on ast[1..]
d7a6c2d6 47 A=Z%(A+1):CALL QUASIQUOTE
037815e0 48 W=R
93593012 49 GOSUB POP_A
cc9dbd92
JM
50
51 REM set A to ast[0] for last two cases
d7a6c2d6 52 A=Z%(A+2)
cc9dbd92 53
af621e3a 54 REM pair?
4202ef7b
JM
55 GOSUB TYPE_A
56 IF T<6 OR T>7 THEN GOTO QQ_DEFAULT
d7a6c2d6 57 IF (Z%(A+1)=0) THEN GOTO QQ_DEFAULT
af621e3a 58
d7a6c2d6
JM
59 B=Z%(A+2)
60 IF (Z%(B)AND 31)<>5 THEN GOTO QQ_DEFAULT
61 IF S$(Z%(B+1))<>"splice-unquote" THEN QQ_DEFAULT
5e5ca0d4
JM
62 REM ['concat, ast[0][1], quasiquote(ast[1..])]
63
d7a6c2d6 64 B=Z%(Z%(A+1)+2)
c756af81 65 B$="concat":T=5:GOSUB STRING:C=R
037815e0 66 A=W:GOSUB LIST3
5e5ca0d4 67 REM release inner quasiquoted since outer list takes ownership
c756af81
JM
68 AY=A:GOSUB RELEASE
69 AY=C:GOSUB RELEASE
af621e3a 70 GOTO QQ_DONE
5e5ca0d4
JM
71
72 QQ_DEFAULT:
73 REM ['cons, quasiquote(ast[0]), quasiquote(ast[1..])]
74
037815e0 75 Q=W:GOSUB PUSH_Q
cc9dbd92 76 REM A set above to ast[0]
af621e3a 77 CALL QUASIQUOTE
c756af81 78 B=R
037815e0 79 GOSUB POP_Q:W=Q
5e5ca0d4 80
c756af81 81 B$="cons":T=5:GOSUB STRING:C=R
037815e0 82 A=W:GOSUB LIST3
5e5ca0d4 83 REM release inner quasiquoted since outer list takes ownership
c756af81
JM
84 AY=A:GOSUB RELEASE
85 AY=B:GOSUB RELEASE
86 AY=C:GOSUB RELEASE
af621e3a
JM
87 QQ_DONE:
88END SUB
5e5ca0d4 89
cc9dbd92 90REM MACROEXPAND(A, E) -> A:
af621e3a 91SUB MACROEXPAND
93593012 92 GOSUB PUSH_A
5e5ca0d4
JM
93
94 MACROEXPAND_LOOP:
95 REM list?
4202ef7b
JM
96 GOSUB TYPE_A
97 IF T<>6 THEN GOTO MACROEXPAND_DONE
5e5ca0d4 98 REM non-empty?
d7a6c2d6
JM
99 IF Z%(A+1)=0 THEN GOTO MACROEXPAND_DONE
100 B=Z%(A+2)
5e5ca0d4 101 REM symbol? in first position
d7a6c2d6 102 IF (Z%(B)AND 31)<>5 THEN GOTO MACROEXPAND_DONE
5e5ca0d4 103 REM defined in environment?
af621e3a 104 K=B:CALL ENV_FIND
cc9dbd92 105 IF R=-1 THEN GOTO MACROEXPAND_DONE
9d59cdb3 106 B=R4
5e5ca0d4 107 REM macro?
d7a6c2d6 108 IF (Z%(B)AND 31)<>11 THEN GOTO MACROEXPAND_DONE
93593012 109
d7a6c2d6 110 F=B:AR=Z%(A+1):CALL APPLY
cc9dbd92 111 A=R
5e5ca0d4 112
93593012 113 GOSUB PEEK_Q:AY=Q
cc9dbd92 114 REM if previous A was not the first A into macroexpand (i.e. an
5e5ca0d4 115 REM intermediate form) then free it
93593012
JM
116 IF A<>AY THEN GOSUB PEND_A_LV
117
cc9dbd92 118 IF ER<>-2 THEN GOTO MACROEXPAND_DONE
5e5ca0d4
JM
119 GOTO MACROEXPAND_LOOP
120
121 MACROEXPAND_DONE:
93593012 122 GOSUB POP_Q: REM pop original A
af621e3a 123END SUB
5e5ca0d4 124
cc9dbd92 125REM EVAL_AST(A, E) -> R
af621e3a 126SUB EVAL_AST
cc9dbd92 127 REM push A and E on the stack
93593012
JM
128 Q=E:GOSUB PUSH_Q
129 GOSUB PUSH_A
5e5ca0d4 130
cc9dbd92 131 IF ER<>-2 THEN GOTO EVAL_AST_RETURN
5e5ca0d4 132
4202ef7b 133 GOSUB TYPE_A
cc9dbd92
JM
134 IF T=5 THEN GOTO EVAL_AST_SYMBOL
135 IF T>=6 AND T<=8 THEN GOTO EVAL_AST_SEQ
5e5ca0d4
JM
136
137 REM scalar: deref to actual value and inc ref cnt
9d59cdb3 138 R=A
4202ef7b 139 GOSUB INC_REF_R
5e5ca0d4
JM
140 GOTO EVAL_AST_RETURN
141
142 EVAL_AST_SYMBOL:
af621e3a
JM
143 K=A:GOTO ENV_GET
144 ENV_GET_RETURN:
5e5ca0d4
JM
145 GOTO EVAL_AST_RETURN
146
147 EVAL_AST_SEQ:
9d59cdb3
JM
148 REM setup the stack for the loop
149 GOSUB MAP_LOOP_START
5e5ca0d4
JM
150
151 EVAL_AST_SEQ_LOOP:
5e5ca0d4 152 REM check if we are done evaluating the source sequence
d7a6c2d6 153 IF Z%(A+1)=0 THEN GOTO EVAL_AST_SEQ_LOOP_DONE
5e5ca0d4 154
01e8850d 155 REM if we are returning to DO, then skip last element
9d59cdb3
JM
156 REM The EVAL_DO call to EVAL_AST must be call #2 for EVAL_AST to
157 REM return early and for TCO to work
158 Q=5:GOSUB PEEK_Q_Q
d7a6c2d6 159 IF Q=2 AND Z%(Z%(A+1)+1)=0 THEN GOTO EVAL_AST_SEQ_LOOP_DONE
01e8850d 160
9d59cdb3
JM
161 REM call EVAL for each entry
162 GOSUB PUSH_A
d7a6c2d6
JM
163 IF T<>8 THEN A=Z%(A+2)
164 IF T=8 THEN A=Z%(A+3)
9d59cdb3
JM
165 Q=T:GOSUB PUSH_Q: REM push/save type
166 CALL EVAL
167 GOSUB POP_Q:T=Q: REM pop/restore type
168 GOSUB POP_A
d7a6c2d6 169 M=R
5e5ca0d4 170
9d59cdb3
JM
171 REM if error, release the unattached element
172 REM TODO: is R=0 correct?
173 IF ER<>-2 THEN AY=R:GOSUB RELEASE:R=0:GOTO EVAL_AST_SEQ_LOOP_DONE
5e5ca0d4 174
9d59cdb3
JM
175 REM for hash-maps, copy the key (inc ref since we are going to
176 REM release it below)
d7a6c2d6 177 IF T=8 THEN N=M:M=Z%(A+2):Z%(M)=Z%(M)+32
5e5ca0d4 178
5e5ca0d4 179
9d59cdb3
JM
180 REM update the return sequence structure
181 REM release N (and M if T=8) since seq takes full ownership
182 C=1:GOSUB MAP_LOOP_UPDATE
5e5ca0d4
JM
183
184 REM process the next sequence entry from source list
d7a6c2d6 185 A=Z%(A+1)
5e5ca0d4
JM
186
187 GOTO EVAL_AST_SEQ_LOOP
188 EVAL_AST_SEQ_LOOP_DONE:
9d59cdb3
JM
189 REM cleanup stack and get return value
190 GOSUB MAP_LOOP_DONE
5e5ca0d4
JM
191 GOTO EVAL_AST_RETURN
192
193 EVAL_AST_RETURN:
cc9dbd92 194 REM pop A and E off the stack
93593012
JM
195 GOSUB POP_A
196 GOSUB POP_Q:E=Q
af621e3a 197END SUB
5e5ca0d4 198
af621e3a
JM
199REM EVAL(A, E) -> R
200SUB EVAL
cc9dbd92 201 LV=LV+1: REM track basic return stack level
5e5ca0d4 202
cc9dbd92 203 REM push A and E on the stack
93593012
JM
204 Q=E:GOSUB PUSH_Q
205 GOSUB PUSH_A
5e5ca0d4 206
01e8850d
JM
207 REM PRINT "EVAL A:"+STR$(A)+",X:"+STR$(X)+",LV:"+STR$(LV)+",FRE:"+STR$(FRE(0))
208
5e5ca0d4
JM
209 EVAL_TCO_RECUR:
210
c7330b3d
JM
211 IF ER<>-2 THEN GOTO EVAL_RETURN
212
c756af81 213 REM AZ=A:B=1:GOSUB PR_STR
cc9dbd92 214 REM PRINT "EVAL: "+R$+" [A:"+STR$(A)+", LV:"+STR$(LV)+"]"
5e5ca0d4 215
5e5ca0d4 216 GOSUB LIST_Q
cc9dbd92 217 IF R THEN GOTO APPLY_LIST
5e5ca0d4
JM
218 EVAL_NOT_LIST:
219 REM ELSE
af621e3a 220 CALL EVAL_AST
5e5ca0d4
JM
221 GOTO EVAL_RETURN
222
223 APPLY_LIST:
af621e3a 224 CALL MACROEXPAND
5e5ca0d4
JM
225
226 GOSUB LIST_Q
cc9dbd92 227 IF R<>1 THEN GOTO EVAL_NOT_LIST
5e5ca0d4
JM
228
229 GOSUB EMPTY_Q
4202ef7b 230 IF R THEN R=A:GOSUB INC_REF_R:GOTO EVAL_RETURN
5e5ca0d4 231
d7a6c2d6 232 A0=Z%(A+2)
5e5ca0d4
JM
233
234 REM get symbol in A$
d7a6c2d6
JM
235 IF (Z%(A0)AND 31)<>5 THEN A$=""
236 IF (Z%(A0)AND 31)=5 THEN A$=S$(Z%(A0+1))
5e5ca0d4
JM
237
238 IF A$="def!" THEN GOTO EVAL_DEF
239 IF A$="let*" THEN GOTO EVAL_LET
240 IF A$="quote" THEN GOTO EVAL_QUOTE
241 IF A$="quasiquote" THEN GOTO EVAL_QUASIQUOTE
242 IF A$="defmacro!" THEN GOTO EVAL_DEFMACRO
243 IF A$="macroexpand" THEN GOTO EVAL_MACROEXPAND
244 IF A$="try*" THEN GOTO EVAL_TRY
245 IF A$="do" THEN GOTO EVAL_DO
246 IF A$="if" THEN GOTO EVAL_IF
247 IF A$="fn*" THEN GOTO EVAL_FN
248 GOTO EVAL_INVOKE
249
250 EVAL_GET_A3:
d7a6c2d6 251 A3=Z%(Z%(Z%(Z%(A+1)+1)+1)+2)
5e5ca0d4 252 EVAL_GET_A2:
d7a6c2d6 253 A2=Z%(Z%(Z%(A+1)+1)+2)
5e5ca0d4 254 EVAL_GET_A1:
d7a6c2d6 255 A1=Z%(Z%(A+1)+2)
5e5ca0d4
JM
256 RETURN
257
258 EVAL_DEF:
259 REM PRINT "def!"
bbab5c5d 260 GOSUB EVAL_GET_A2: REM set A1 and A2
5e5ca0d4 261
93593012 262 Q=A1:GOSUB PUSH_Q
af621e3a 263 A=A2:CALL EVAL: REM eval a2
93593012 264 GOSUB POP_Q:A1=Q
5e5ca0d4 265
cc9dbd92 266 IF ER<>-2 THEN GOTO EVAL_RETURN
5e5ca0d4
JM
267
268 REM set a1 in env to a2
c756af81 269 K=A1:C=R:GOSUB ENV_SET
5e5ca0d4
JM
270 GOTO EVAL_RETURN
271
272 EVAL_LET:
273 REM PRINT "let*"
bbab5c5d 274 GOSUB EVAL_GET_A2: REM set A1 and A2
5e5ca0d4 275
93593012
JM
276 Q=A2:GOSUB PUSH_Q: REM push/save A2
277 Q=E:GOSUB PUSH_Q: REM push env for for later release
5e5ca0d4
JM
278
279 REM create new environment with outer as current environment
c756af81 280 C=E:GOSUB ENV_NEW
cc9dbd92 281 E=R
5e5ca0d4 282 EVAL_LET_LOOP:
d7a6c2d6 283 IF Z%(A1+1)=0 THEN GOTO EVAL_LET_LOOP_DONE
5e5ca0d4 284
93593012 285 Q=A1:GOSUB PUSH_Q: REM push A1
5e5ca0d4 286 REM eval current A1 odd element
d7a6c2d6 287 A=Z%(Z%(A1+1)+2):CALL EVAL
93593012 288 GOSUB POP_Q:A1=Q: REM pop A1
5e5ca0d4 289
c7330b3d
JM
290 IF ER<>-2 THEN GOTO EVAL_LET_LOOP_DONE
291
d7a6c2d6
JM
292 REM set key/value in the environment
293 K=Z%(A1+2):C=R:GOSUB ENV_SET
cc9dbd92 294 AY=R:GOSUB RELEASE: REM release our use, ENV_SET took ownership
5e5ca0d4 295
bbab5c5d 296 REM skip to the next pair of A1 elements
d7a6c2d6 297 A1=Z%(Z%(A1+1)+1)
5e5ca0d4
JM
298 GOTO EVAL_LET_LOOP
299
300 EVAL_LET_LOOP_DONE:
9d59cdb3 301 GOSUB POP_Q:AY=Q: REM pop previous env
5e5ca0d4
JM
302
303 REM release previous environment if not the current EVAL env
93593012 304 GOSUB PEEK_Q_2
9d59cdb3 305 IF AY<>Q THEN GOSUB RELEASE
5e5ca0d4 306
93593012 307 GOSUB POP_Q:A2=Q: REM pop A2
bbab5c5d 308 A=A2:GOTO EVAL_TCO_RECUR: REM TCO loop
5e5ca0d4
JM
309
310 EVAL_DO:
d7a6c2d6 311 A=Z%(A+1): REM rest
93593012 312 GOSUB PUSH_A: REM push/save A
5e5ca0d4 313
9d59cdb3
JM
314 REM this must be EVAL_AST call #2 for EVAL_AST to return early
315 REM and for TCO to work
af621e3a 316 CALL EVAL_AST
5e5ca0d4 317
01e8850d
JM
318 REM cleanup
319 AY=R: REM get eval'd list for release
320
93593012 321 GOSUB POP_A: REM pop/restore original A for LAST
01e8850d
JM
322 GOSUB LAST: REM get last element for return
323 A=R: REM new recur AST
324
325 REM cleanup
326 GOSUB RELEASE: REM release eval'd list
327 AY=A:GOSUB RELEASE: REM release LAST value (not sure why)
328
329 GOTO EVAL_TCO_RECUR: REM TCO loop
5e5ca0d4
JM
330
331 EVAL_QUOTE:
d7a6c2d6 332 R=Z%(Z%(A+1)+2)
4202ef7b 333 GOSUB INC_REF_R
5e5ca0d4
JM
334 GOTO EVAL_RETURN
335
336 EVAL_QUASIQUOTE:
d7a6c2d6 337 R=Z%(Z%(A+1)+2)
af621e3a 338 A=R:CALL QUASIQUOTE
93593012 339 A=R
5e5ca0d4 340 REM add quasiquote result to pending release queue to free when
cc9dbd92 341 REM next lower EVAL level returns (LV)
93593012 342 GOSUB PEND_A_LV
5e5ca0d4 343
93593012 344 GOTO EVAL_TCO_RECUR: REM TCO loop
5e5ca0d4
JM
345
346 EVAL_DEFMACRO:
347 REM PRINT "defmacro!"
bbab5c5d 348 GOSUB EVAL_GET_A2: REM set A1 and A2
5e5ca0d4 349
93593012 350 Q=A1:GOSUB PUSH_Q: REM push A1
af621e3a 351 A=A2:CALL EVAL: REM eval A2
93593012 352 GOSUB POP_Q:A1=Q: REM pop A1
5e5ca0d4
JM
353
354 REM change function to macro
d7a6c2d6 355 Z%(R)=Z%(R)+1
5e5ca0d4 356
bbab5c5d 357 REM set A1 in env to A2
c756af81 358 K=A1:C=R:GOSUB ENV_SET
5e5ca0d4
JM
359 GOTO EVAL_RETURN
360
361 EVAL_MACROEXPAND:
362 REM PRINT "macroexpand"
d7a6c2d6 363 R=Z%(Z%(A+1)+2)
af621e3a
JM
364 A=R:CALL MACROEXPAND
365 R=A
5e5ca0d4
JM
366
367 REM since we are returning it unevaluated, inc the ref cnt
4202ef7b 368 GOSUB INC_REF_R
5e5ca0d4
JM
369 GOTO EVAL_RETURN
370
371 EVAL_TRY:
372 REM PRINT "try*"
bbab5c5d 373 GOSUB EVAL_GET_A1: REM set A1, A2, and A3
5e5ca0d4 374
93593012 375 GOSUB PUSH_A: REM push/save A
af621e3a 376 A=A1:CALL EVAL: REM eval A1
93593012 377 GOSUB POP_A: REM pop/restore A
5e5ca0d4
JM
378
379 REM if there is not error or catch block then return
d7a6c2d6 380 IF ER=-2 OR Z%(A+1)=0 THEN GOTO EVAL_RETURN
5e5ca0d4
JM
381
382 REM create environment for the catch block eval
c756af81 383 C=E:GOSUB ENV_NEW:E=R
5e5ca0d4 384
bbab5c5d
JM
385 GOSUB EVAL_GET_A2: REM set A1 and A2
386 A=A2:GOSUB EVAL_GET_A2: REM set A1 and A2 from catch block
5e5ca0d4 387
cc9dbd92 388 REM create object for ER=-1 type raw string errors
4202ef7b 389 IF ER=-1 THEN B$=E$:T=4:GOSUB STRING:ER=R:GOSUB INC_REF_R
5e5ca0d4
JM
390
391 REM bind the catch symbol to the error object
c756af81 392 K=A1:C=ER:GOSUB ENV_SET
c7330b3d 393 AY=R:GOSUB RELEASE: REM release our use, env took ownership
5e5ca0d4
JM
394
395 REM unset error for catch eval
c756af81 396 ER=-2:E$=""
5e5ca0d4 397
af621e3a 398 A=A2:CALL EVAL
5e5ca0d4
JM
399
400 GOTO EVAL_RETURN
401
402 EVAL_IF:
bbab5c5d 403 GOSUB EVAL_GET_A1: REM set A1
93593012 404 GOSUB PUSH_A: REM push/save A
af621e3a 405 A=A1:CALL EVAL
93593012 406 GOSUB POP_A: REM pop/restore A
d7a6c2d6 407 IF (R=0) OR (R=2) THEN GOTO EVAL_IF_FALSE
5e5ca0d4
JM
408
409 EVAL_IF_TRUE:
cc9dbd92 410 AY=R:GOSUB RELEASE
bbab5c5d
JM
411 GOSUB EVAL_GET_A2: REM set A1 and A2 after EVAL
412 A=A2:GOTO EVAL_TCO_RECUR: REM TCO loop
5e5ca0d4 413 EVAL_IF_FALSE:
cc9dbd92 414 AY=R:GOSUB RELEASE
bbab5c5d 415 REM if no false case (A3), return nil
037815e0 416 GOSUB COUNT
4202ef7b 417 IF R<4 THEN R=0:GOSUB INC_REF_R:GOTO EVAL_RETURN
bbab5c5d
JM
418 GOSUB EVAL_GET_A3: REM set A1 - A3 after EVAL
419 A=A3:GOTO EVAL_TCO_RECUR: REM TCO loop
5e5ca0d4
JM
420
421 EVAL_FN:
bbab5c5d 422 GOSUB EVAL_GET_A2: REM set A1 and A2
4202ef7b 423 T=10:L=A2:M=A1:N=E:GOSUB ALLOC: REM mal function
5e5ca0d4
JM
424 GOTO EVAL_RETURN
425
426 EVAL_INVOKE:
af621e3a 427 CALL EVAL_AST
5e5ca0d4
JM
428
429 REM if error, return f/args for release by caller
cc9dbd92 430 IF ER<>-2 THEN GOTO EVAL_RETURN
5e5ca0d4
JM
431
432 REM push f/args for release after call
93593012 433 GOSUB PUSH_R
5e5ca0d4 434
d7a6c2d6
JM
435 AR=Z%(R+1): REM rest
436 F=Z%(R+2)
5e5ca0d4 437
bbab5c5d 438 REM if metadata, get the actual object
4202ef7b
JM
439 GOSUB TYPE_F
440 IF T=14 THEN F=Z%(F+1):GOSUB TYPE_F
bbab5c5d 441
4202ef7b 442 ON T-8 GOTO EVAL_DO_FUNCTION,EVAL_DO_MAL_FUNCTION,EVAL_DO_MAL_FUNCTION
5e5ca0d4
JM
443
444 REM if error, pop and return f/args for release by caller
93593012 445 GOSUB POP_R
c756af81 446 ER=-1:E$="apply of non-function":GOTO EVAL_RETURN
5e5ca0d4
JM
447
448 EVAL_DO_FUNCTION:
01e8850d 449 REM regular function
d7a6c2d6 450 IF Z%(F+1)<60 THEN GOSUB DO_FUNCTION:GOTO EVAL_DO_FUNCTION_SKIP
01e8850d 451 REM for recur functions (apply, map, swap!), use GOTO
d7a6c2d6 452 IF Z%(F+1)>60 THEN CALL DO_TCO_FUNCTION
af621e3a 453 EVAL_DO_FUNCTION_SKIP:
5e5ca0d4
JM
454
455 REM pop and release f/args
93593012
JM
456 GOSUB POP_Q:AY=Q
457 GOSUB RELEASE
5e5ca0d4
JM
458 GOTO EVAL_RETURN
459
460 EVAL_DO_MAL_FUNCTION:
9d59cdb3 461 Q=E:GOSUB PUSH_Q: REM save the current environment for release
5e5ca0d4 462
d7a6c2d6
JM
463 REM create new environ using env and params stored in function
464 C=Z%(F+3):A=Z%(F+2):B=AR:GOSUB ENV_NEW_BINDS
5e5ca0d4
JM
465
466 REM release previous env if it is not the top one on the
bbab5c5d 467 REM stack (X%(X-2)) because our new env refers to it and
5e5ca0d4 468 REM we no longer need to track it (since we are TCO recurring)
9d59cdb3 469 GOSUB POP_Q:AY=Q
93593012 470 GOSUB PEEK_Q_2
9d59cdb3 471 IF AY<>Q THEN GOSUB RELEASE
5e5ca0d4
JM
472
473 REM claim the AST before releasing the list containing it
d7a6c2d6 474 A=Z%(F+1):Z%(A)=Z%(A)+32
5e5ca0d4 475 REM add AST to pending release queue to free as soon as EVAL
cc9dbd92 476 REM actually returns (LV+1)
93593012 477 LV=LV+1:GOSUB PEND_A_LV:LV=LV-1
5e5ca0d4
JM
478
479 REM pop and release f/args
93593012
JM
480 GOSUB POP_Q:AY=Q
481 GOSUB RELEASE
5e5ca0d4 482
cc9dbd92
JM
483 REM A set above
484 E=R:GOTO EVAL_TCO_RECUR: REM TCO loop
5e5ca0d4
JM
485
486 EVAL_RETURN:
c756af81 487 REM AZ=R: B=1: GOSUB PR_STR
cc9dbd92 488 REM PRINT "EVAL_RETURN R: ["+R$+"] ("+STR$(R)+"), LV:"+STR$(LV)+",ER:"+STR$(ER)
5e5ca0d4
JM
489
490 REM release environment if not the top one on the stack
93593012
JM
491 GOSUB PEEK_Q_1
492 IF E<>Q THEN AY=E:GOSUB RELEASE
5e5ca0d4 493
cc9dbd92 494 LV=LV-1: REM track basic return stack level
5e5ca0d4
JM
495
496 REM release everything we couldn't release earlier
497 GOSUB RELEASE_PEND
498
499 REM trigger GC
c756af81
JM
500 #cbm T=FRE(0)
501 #qbasic T=0
5e5ca0d4 502
cc9dbd92 503 REM pop A and E off the stack
93593012
JM
504 GOSUB POP_A
505 GOSUB POP_Q:E=Q
5e5ca0d4 506
af621e3a 507END SUB
5e5ca0d4 508
cc9dbd92 509REM PRINT(A) -> R$
5e5ca0d4 510MAL_PRINT:
c756af81 511 AZ=A:B=1:GOSUB PR_STR
5e5ca0d4
JM
512 RETURN
513
cc9dbd92 514REM RE(A$) -> R
bbab5c5d 515REM Assume D has repl_env
5e5ca0d4
JM
516REM caller must release result
517RE:
d7a6c2d6 518 R1=-1
5e5ca0d4 519 GOSUB MAL_READ
cc9dbd92 520 R1=R
0e508fa5 521 IF ER<>-2 THEN GOTO RE_DONE
5e5ca0d4 522
af621e3a 523 A=R:E=D:CALL EVAL
5e5ca0d4 524
0e508fa5 525 RE_DONE:
5e5ca0d4 526 REM Release memory from MAL_READ
d7a6c2d6 527 AY=R1:GOSUB RELEASE
5e5ca0d4
JM
528 RETURN: REM caller must release result of EVAL
529
530REM REP(A$) -> R$
bbab5c5d 531REM Assume D has repl_env
af621e3a 532SUB REP
d7a6c2d6 533 R2=-1
5e5ca0d4 534
d7a6c2d6 535 GOSUB RE
cc9dbd92
JM
536 R2=R
537 IF ER<>-2 THEN GOTO REP_DONE
5e5ca0d4 538
cc9dbd92 539 A=R:GOSUB MAL_PRINT
5e5ca0d4
JM
540
541 REP_DONE:
542 REM Release memory from MAL_READ and EVAL
9d59cdb3 543 AY=R2:GOSUB RELEASE
af621e3a 544END SUB
5e5ca0d4
JM
545
546REM MAIN program
547MAIN:
548 GOSUB INIT_MEMORY
549
cc9dbd92 550 LV=0
5e5ca0d4
JM
551
552 REM create repl_env
9d59cdb3 553 C=0:GOSUB ENV_NEW:D=R
5e5ca0d4
JM
554
555 REM core.EXT: defined in Basic
bbab5c5d 556 E=D:GOSUB INIT_CORE_NS: REM set core functions in repl_env
5e5ca0d4 557
bbab5c5d 558 ZT=ZI: REM top of memory after base repl_env
5e5ca0d4
JM
559
560 REM core.mal: defined using the language itself
561 A$="(def! not (fn* (a) (if a false true)))"
cc9dbd92 562 GOSUB RE:AY=R:GOSUB RELEASE
5e5ca0d4 563
0e508fa5 564 A$="(def! load-file (fn* (f) (eval (read-file f))))"
cc9dbd92 565 GOSUB RE:AY=R:GOSUB RELEASE
5e5ca0d4
JM
566
567 A$="(defmacro! cond (fn* (& xs) (if (> (count xs) 0) (list 'if (first xs)"
568 A$=A$+" (if (> (count xs) 1) (nth xs 1) (throw "+CHR$(34)+"odd number of"
569 A$=A$+" forms to cond"+CHR$(34)+")) (cons 'cond (rest (rest xs)))))))"
cc9dbd92 570 GOSUB RE:AY=R:GOSUB RELEASE
5e5ca0d4
JM
571
572 A$="(defmacro! or (fn* (& xs) (if (empty? xs) nil (if (= 1 (count xs)) (first xs)"
573 A$=A$+" `(let* (or_FIXME ~(first xs)) (if or_FIXME or_FIXME (or ~@(rest xs))))))))"
cc9dbd92 574 GOSUB RE:AY=R:GOSUB RELEASE
5e5ca0d4
JM
575
576 REM load the args file
577 A$="(def! -*ARGS*- (load-file "+CHR$(34)+".args.mal"+CHR$(34)+"))"
cc9dbd92 578 GOSUB RE:AY=R:GOSUB RELEASE
5e5ca0d4
JM
579
580 REM set the argument list
581 A$="(def! *ARGV* (rest -*ARGS*-))"
cc9dbd92 582 GOSUB RE:AY=R:GOSUB RELEASE
5e5ca0d4
JM
583
584 REM get the first argument
585 A$="(first -*ARGS*-)"
586 GOSUB RE
587
588 REM if there is an argument, then run it as a program
cc9dbd92 589 IF R<>0 THEN AY=R:GOSUB RELEASE:GOTO RUN_PROG
5e5ca0d4 590 REM no arguments, start REPL loop
cc9dbd92 591 IF R=0 THEN GOTO REPL_LOOP
5e5ca0d4
JM
592
593 RUN_PROG:
594 REM run a single mal program and exit
595 A$="(load-file (first -*ARGS*-))"
596 GOSUB RE
cc9dbd92 597 IF ER<>-2 THEN GOSUB PRINT_ERROR
01e8850d 598 GOTO QUIT
5e5ca0d4
JM
599
600 REPL_LOOP:
601 A$="user> ":GOSUB READLINE: REM call input parser
01975886 602 IF EZ=1 THEN GOTO QUIT
9d59cdb3 603 IF R$="" THEN GOTO REPL_LOOP
5e5ca0d4 604
af621e3a 605 A$=R$:CALL REP: REM call REP
5e5ca0d4 606
cc9dbd92 607 IF ER<>-2 THEN GOSUB PRINT_ERROR:GOTO REPL_LOOP
5e5ca0d4
JM
608 PRINT R$
609 GOTO REPL_LOOP
610
611 QUIT:
9d59cdb3 612 REM GOSUB PR_MEMORY_SUMMARY_SMALL
5e5ca0d4
JM
613 END
614
615 PRINT_ERROR:
616 REM if the error is an object, then print and free it
c756af81
JM
617 IF ER>=0 THEN AZ=ER:B=0:GOSUB PR_STR:E$=R$:AY=ER:GOSUB RELEASE
618 PRINT "Error: "+E$
619 ER=-2:E$=""
5e5ca0d4
JM
620 RETURN
621