Basic: fix step6 arg test. Gensym. Misc cleanup.
[jackhill/mal.git] / basic / step5_tco.in.bas
CommitLineData
412e7348
JM
1GOTO MAIN
2
3REM $INCLUDE: 'readline.in.bas'
4REM $INCLUDE: 'types.in.bas'
5REM $INCLUDE: 'reader.in.bas'
6REM $INCLUDE: 'printer.in.bas'
7REM $INCLUDE: 'env.in.bas'
8REM $INCLUDE: 'core.in.bas'
9
9e8f5211
JM
10REM $INCLUDE: 'debug.in.bas'
11
cc9dbd92 12REM READ(A$) -> R
412e7348
JM
13MAL_READ:
14 GOSUB READ_STR
15 RETURN
16
cc9dbd92 17REM EVAL_AST(A, E) -> R
af621e3a 18SUB EVAL_AST
cc9dbd92 19 REM push A and E on the stack
bbab5c5d 20 X=X+2:X%(X-1)=E:X%(X)=A
412e7348 21
cc9dbd92 22 IF ER<>-2 THEN GOTO EVAL_AST_RETURN
412e7348
JM
23
24 GOSUB DEREF_A
25
bbab5c5d 26 T=Z%(A,0)AND31
cc9dbd92
JM
27 IF T=5 THEN GOTO EVAL_AST_SYMBOL
28 IF T>=6 AND T<=8 THEN GOTO EVAL_AST_SEQ
412e7348
JM
29
30 REM scalar: deref to actual value and inc ref cnt
cc9dbd92 31 R=A:GOSUB DEREF_R
bbab5c5d 32 Z%(R,0)=Z%(R,0)+32
412e7348
JM
33 GOTO EVAL_AST_RETURN
34
35 EVAL_AST_SYMBOL:
af621e3a
JM
36 K=A:GOTO ENV_GET
37 ENV_GET_RETURN:
412e7348
JM
38 GOTO EVAL_AST_RETURN
39
40 EVAL_AST_SEQ:
a742287e
JM
41 REM allocate the first entry (T already set above)
42 L=0:N=0:GOSUB ALLOC
412e7348
JM
43
44 REM make space on the stack
cc9dbd92 45 X=X+4
412e7348 46 REM push type of sequence
bbab5c5d 47 X%(X-3)=T
412e7348 48 REM push sequence index
bbab5c5d 49 X%(X-2)=-1
412e7348 50 REM push future return value (new sequence)
bbab5c5d 51 X%(X-1)=R
412e7348 52 REM push previous new sequence entry
bbab5c5d 53 X%(X)=R
412e7348
JM
54
55 EVAL_AST_SEQ_LOOP:
412e7348 56 REM update index
bbab5c5d 57 X%(X-2)=X%(X-2)+1
412e7348
JM
58
59 REM check if we are done evaluating the source sequence
cc9dbd92 60 IF Z%(A,1)=0 THEN GOTO EVAL_AST_SEQ_LOOP_DONE
412e7348 61
01e8850d
JM
62 REM if we are returning to DO, then skip last element
63 IF X%(X-6)=2 AND Z%(Z%(A,1),1)=0 THEN GOTO EVAL_AST_SEQ_LOOP_DONE
64
412e7348 65 REM if hashmap, skip eval of even entries (keys)
c7330b3d 66 IF (X%(X-3)=8) AND ((X%(X-2)AND1)=0) THEN GOTO EVAL_AST_DO_REF
412e7348
JM
67 GOTO EVAL_AST_DO_EVAL
68
69 EVAL_AST_DO_REF:
cc9dbd92 70 R=A+1:GOSUB DEREF_R: REM deref to target of referred entry
bbab5c5d 71 Z%(R,0)=Z%(R,0)+32: REM inc ref cnt of referred value
412e7348
JM
72 GOTO EVAL_AST_ADD_VALUE
73
74 EVAL_AST_DO_EVAL:
75 REM call EVAL for each entry
af621e3a 76 A=A+1:CALL EVAL
cc9dbd92 77 A=A-1
412e7348
JM
78 GOSUB DEREF_R: REM deref to target of evaluated entry
79
80 EVAL_AST_ADD_VALUE:
81
82 REM update previous value pointer to evaluated entry
bbab5c5d 83 Z%(X%(X)+1,1)=R
412e7348 84
cc9dbd92 85 IF ER<>-2 THEN GOTO EVAL_AST_SEQ_LOOP_DONE
412e7348
JM
86
87 REM allocate the next entry
a742287e 88 REM same new sequence entry type
bbab5c5d 89 T=X%(X-3):L=0:N=0:GOSUB ALLOC
412e7348
JM
90
91 REM update previous sequence entry value to point to new entry
bbab5c5d 92 Z%(X%(X),1)=R
412e7348 93 REM update previous ptr to current entry
bbab5c5d 94 X%(X)=R
412e7348
JM
95
96 REM process the next sequence entry from source list
cc9dbd92 97 A=Z%(A,1)
412e7348
JM
98
99 GOTO EVAL_AST_SEQ_LOOP
100 EVAL_AST_SEQ_LOOP_DONE:
9e8f5211 101 REM if no error, get return value (new seq)
bbab5c5d 102 IF ER=-2 THEN R=X%(X-1)
9e8f5211 103 REM otherwise, free the return value and return nil
bbab5c5d 104 IF ER<>-2 THEN R=0:AY=X%(X-1):GOSUB RELEASE
412e7348
JM
105
106 REM pop previous, return, index and type
cc9dbd92 107 X=X-4
412e7348
JM
108 GOTO EVAL_AST_RETURN
109
110 EVAL_AST_RETURN:
cc9dbd92 111 REM pop A and E off the stack
bbab5c5d 112 E=X%(X-1):A=X%(X):X=X-2
af621e3a 113END SUB
412e7348 114
af621e3a
JM
115REM EVAL(A, E) -> R
116SUB EVAL
cc9dbd92 117 LV=LV+1: REM track basic return stack level
412e7348 118
cc9dbd92 119 REM push A and E on the stack
bbab5c5d 120 X=X+2:X%(X-1)=E:X%(X)=A
412e7348 121
01e8850d
JM
122 REM PRINT "EVAL A:"+STR$(A)+",X:"+STR$(X)+",LV:"+STR$(LV)+",FRE:"+STR$(FRE(0))
123
412e7348
JM
124 EVAL_TCO_RECUR:
125
c7330b3d
JM
126 IF ER<>-2 THEN GOTO EVAL_RETURN
127
cc9dbd92
JM
128 REM AZ=A:PR=1:GOSUB PR_STR
129 REM PRINT "EVAL: "+R$+" [A:"+STR$(A)+", LV:"+STR$(LV)+"]"
412e7348
JM
130
131 GOSUB DEREF_A
132
133 GOSUB LIST_Q
cc9dbd92 134 IF R THEN GOTO APPLY_LIST
412e7348 135 REM ELSE
af621e3a 136 CALL EVAL_AST
412e7348
JM
137 GOTO EVAL_RETURN
138
139 APPLY_LIST:
140 GOSUB EMPTY_Q
bbab5c5d 141 IF R THEN R=A:Z%(R,0)=Z%(R,0)+32:GOTO EVAL_RETURN
412e7348 142
bbab5c5d
JM
143 A0=A+1
144 R=A0:GOSUB DEREF_R:A0=R
412e7348
JM
145
146 REM get symbol in A$
bbab5c5d
JM
147 IF (Z%(A0,0)AND31)<>5 THEN A$=""
148 IF (Z%(A0,0)AND31)=5 THEN A$=S$(Z%(A0,1))
412e7348
JM
149
150 IF A$="def!" THEN GOTO EVAL_DEF
151 IF A$="let*" THEN GOTO EVAL_LET
152 IF A$="do" THEN GOTO EVAL_DO
153 IF A$="if" THEN GOTO EVAL_IF
154 IF A$="fn*" THEN GOTO EVAL_FN
155 GOTO EVAL_INVOKE
156
157 EVAL_GET_A3:
bbab5c5d
JM
158 A3=Z%(Z%(Z%(A,1),1),1)+1
159 R=A3:GOSUB DEREF_R:A3=R
412e7348 160 EVAL_GET_A2:
bbab5c5d
JM
161 A2=Z%(Z%(A,1),1)+1
162 R=A2:GOSUB DEREF_R:A2=R
412e7348 163 EVAL_GET_A1:
bbab5c5d
JM
164 A1=Z%(A,1)+1
165 R=A1:GOSUB DEREF_R:A1=R
412e7348
JM
166 RETURN
167
168 EVAL_DEF:
169 REM PRINT "def!"
bbab5c5d 170 GOSUB EVAL_GET_A2: REM set A1 and A2
412e7348 171
bbab5c5d 172 X=X+1:X%(X)=A1: REM push A1
af621e3a 173 A=A2:CALL EVAL: REM eval a2
bbab5c5d 174 A1=X%(X):X=X-1: REM pop A1
412e7348 175
cc9dbd92 176 IF ER<>-2 THEN GOTO EVAL_RETURN
70f29a2b 177
412e7348 178 REM set a1 in env to a2
bbab5c5d 179 K=A1:V=R:GOSUB ENV_SET
412e7348
JM
180 GOTO EVAL_RETURN
181
182 EVAL_LET:
183 REM PRINT "let*"
bbab5c5d 184 GOSUB EVAL_GET_A2: REM set A1 and A2
412e7348 185
bbab5c5d
JM
186 X=X+1:X%(X)=A2: REM push/save A2
187 X=X+1:X%(X)=E: REM push env for for later release
412e7348
JM
188
189 REM create new environment with outer as current environment
cc9dbd92
JM
190 O=E:GOSUB ENV_NEW
191 E=R
412e7348 192 EVAL_LET_LOOP:
bbab5c5d 193 IF Z%(A1,1)=0 THEN GOTO EVAL_LET_LOOP_DONE
412e7348 194
bbab5c5d 195 X=X+1:X%(X)=A1: REM push A1
412e7348 196 REM eval current A1 odd element
af621e3a 197 A=Z%(A1,1)+1:CALL EVAL
bbab5c5d 198 A1=X%(X):X=X-1: REM pop A1
412e7348 199
c7330b3d
JM
200 IF ER<>-2 THEN GOTO EVAL_LET_LOOP_DONE
201
bbab5c5d
JM
202 REM set environment: even A1 key to odd A1 eval'd above
203 K=A1+1:V=R:GOSUB ENV_SET
cc9dbd92 204 AY=R:GOSUB RELEASE: REM release our use, ENV_SET took ownership
412e7348 205
bbab5c5d
JM
206 REM skip to the next pair of A1 elements
207 A1=Z%(Z%(A1,1),1)
412e7348 208 GOTO EVAL_LET_LOOP
70f29a2b 209
412e7348 210 EVAL_LET_LOOP_DONE:
bbab5c5d 211 E4=X%(X):X=X-1: REM pop previous env
70f29a2b
JM
212
213 REM release previous environment if not the current EVAL env
bbab5c5d 214 IF E4<>X%(X-2) THEN AY=E4:GOSUB RELEASE
412e7348 215
bbab5c5d
JM
216 A2=X%(X):X=X-1: REM pop A2
217 A=A2:GOTO EVAL_TCO_RECUR: REM TCO loop
412e7348
JM
218
219 EVAL_DO:
cc9dbd92 220 A=Z%(A,1): REM rest
01e8850d 221 X=X+1:X%(X)=A: REM push/save A
412e7348 222
af621e3a 223 CALL EVAL_AST
412e7348 224
01e8850d
JM
225 REM cleanup
226 AY=R: REM get eval'd list for release
227
228 A=X%(X):X=X-1: REM pop/restore original A for LAST
229 GOSUB LAST: REM get last element for return
230 A=R: REM new recur AST
231
232 REM cleanup
233 GOSUB RELEASE: REM release eval'd list
234 AY=A:GOSUB RELEASE: REM release LAST value (not sure why)
235
236 GOTO EVAL_TCO_RECUR: REM TCO loop
412e7348
JM
237
238 EVAL_IF:
bbab5c5d 239 GOSUB EVAL_GET_A1: REM set A1
cc9dbd92 240 REM push A
bbab5c5d 241 X=X+1:X%(X)=A
af621e3a 242 A=A1:CALL EVAL
cc9dbd92 243 REM pop A
bbab5c5d 244 A=X%(X):X=X-1
cc9dbd92 245 IF (R=0) OR (R=1) THEN GOTO EVAL_IF_FALSE
412e7348
JM
246
247 EVAL_IF_TRUE:
cc9dbd92 248 AY=R:GOSUB RELEASE
bbab5c5d
JM
249 GOSUB EVAL_GET_A2: REM set A1 and A2 after EVAL
250 A=A2:GOTO EVAL_TCO_RECUR: REM TCO loop
412e7348 251 EVAL_IF_FALSE:
cc9dbd92 252 AY=R:GOSUB RELEASE
bbab5c5d 253 REM if no false case (A3), return nil
c7330b3d
JM
254 B=A:GOSUB COUNT
255 IF R<4 THEN R=0:GOTO EVAL_RETURN
bbab5c5d
JM
256 GOSUB EVAL_GET_A3: REM set A1 - A3 after EVAL
257 A=A3:GOTO EVAL_TCO_RECUR: REM TCO loop
412e7348
JM
258
259 EVAL_FN:
bbab5c5d
JM
260 GOSUB EVAL_GET_A2: REM set A1 and A2
261 A=A2:P=A1:GOSUB MAL_FUNCTION
412e7348
JM
262 GOTO EVAL_RETURN
263
264 EVAL_INVOKE:
af621e3a 265 CALL EVAL_AST
412e7348
JM
266
267 REM if error, return f/args for release by caller
cc9dbd92 268 IF ER<>-2 THEN GOTO EVAL_RETURN
412e7348
JM
269
270 REM push f/args for release after call
bbab5c5d 271 X=X+1:X%(X)=R
412e7348 272
cc9dbd92 273 F=R+1
412e7348 274
cc9dbd92
JM
275 AR=Z%(R,1): REM rest
276 R=F:GOSUB DEREF_R:F=R
412e7348 277
bbab5c5d
JM
278 REM if metadata, get the actual object
279 IF (Z%(F,0)AND31)>=16 THEN F=Z%(F,1)
280
281 IF (Z%(F,0)AND31)=9 THEN GOTO EVAL_DO_FUNCTION
282 IF (Z%(F,0)AND31)=10 THEN GOTO EVAL_DO_MAL_FUNCTION
412e7348
JM
283
284 REM if error, pop and return f/args for release by caller
bbab5c5d 285 R=X%(X):X=X-1
cc9dbd92 286 ER=-1:ER$="apply of non-function":GOTO EVAL_RETURN
412e7348
JM
287
288 EVAL_DO_FUNCTION:
01e8850d 289 REM regular function
af621e3a 290 IF Z%(F,1)<60 THEN GOSUB DO_FUNCTION:GOTO EVAL_DO_FUNCTION_SKIP
01e8850d 291 REM for recur functions (apply, map, swap!), use GOTO
af621e3a
JM
292 IF Z%(F,1)>60 THEN CALL DO_TCO_FUNCTION
293 EVAL_DO_FUNCTION_SKIP:
412e7348
JM
294
295 REM pop and release f/args
bbab5c5d 296 AY=X%(X):X=X-1:GOSUB RELEASE
412e7348
JM
297 GOTO EVAL_RETURN
298
299 EVAL_DO_MAL_FUNCTION:
bbab5c5d 300 E4=E: REM save the current environment for release
412e7348
JM
301
302 REM create new environ using env stored with function
bbab5c5d 303 O=Z%(F+1,1):BI=Z%(F+1,0):EX=AR:GOSUB ENV_NEW_BINDS
412e7348
JM
304
305 REM release previous env if it is not the top one on the
bbab5c5d 306 REM stack (X%(X-2)) because our new env refers to it and
412e7348 307 REM we no longer need to track it (since we are TCO recurring)
bbab5c5d 308 IF E4<>X%(X-2) THEN AY=E4:GOSUB RELEASE
412e7348
JM
309
310 REM claim the AST before releasing the list containing it
bbab5c5d 311 A=Z%(F,1):Z%(A,0)=Z%(A,0)+32
9e8f5211 312 REM add AST to pending release queue to free as soon as EVAL
cc9dbd92 313 REM actually returns (LV+1)
bbab5c5d 314 Y=Y+1:Y%(Y,0)=A:Y%(Y,1)=LV+1
412e7348
JM
315
316 REM pop and release f/args
bbab5c5d 317 AY=X%(X):X=X-1:GOSUB RELEASE
412e7348 318
cc9dbd92
JM
319 REM A set above
320 E=R:GOTO EVAL_TCO_RECUR: REM TCO loop
412e7348
JM
321
322 EVAL_RETURN:
cc9dbd92
JM
323 REM AZ=R: PR=1: GOSUB PR_STR
324 REM PRINT "EVAL_RETURN R: ["+R$+"] ("+STR$(R)+"), LV:"+STR$(LV)+",ER:"+STR$(ER)
9e8f5211 325
412e7348 326 REM release environment if not the top one on the stack
bbab5c5d 327 IF E<>X%(X-1) THEN AY=E:GOSUB RELEASE
412e7348 328
cc9dbd92 329 LV=LV-1: REM track basic return stack level
9e8f5211 330
412e7348
JM
331 REM release everything we couldn't release earlier
332 GOSUB RELEASE_PEND
333
412e7348 334 REM trigger GC
bbab5c5d 335 TA=FRE(0)
412e7348 336
cc9dbd92 337 REM pop A and E off the stack
bbab5c5d 338 E=X%(X-1):A=X%(X):X=X-2
412e7348 339
af621e3a 340END SUB
412e7348 341
cc9dbd92 342REM PRINT(A) -> R$
412e7348 343MAL_PRINT:
cc9dbd92 344 AZ=A:PR=1:GOSUB PR_STR
412e7348
JM
345 RETURN
346
cc9dbd92 347REM RE(A$) -> R
bbab5c5d 348REM Assume D has repl_env
85d70fb7
JM
349REM caller must release result
350RE:
cc9dbd92 351 R1=0
85d70fb7 352 GOSUB MAL_READ
cc9dbd92 353 R1=R
0e508fa5 354 IF ER<>-2 THEN GOTO RE_DONE
85d70fb7 355
af621e3a 356 A=R:E=D:CALL EVAL
85d70fb7 357
0e508fa5 358 RE_DONE:
85d70fb7 359 REM Release memory from MAL_READ
cc9dbd92 360 IF R1<>0 THEN AY=R1:GOSUB RELEASE
85d70fb7
JM
361 RETURN: REM caller must release result of EVAL
362
412e7348 363REM REP(A$) -> R$
bbab5c5d 364REM Assume D has repl_env
af621e3a 365SUB REP
cc9dbd92 366 R1=0:R2=0
412e7348 367 GOSUB MAL_READ
cc9dbd92
JM
368 R1=R
369 IF ER<>-2 THEN GOTO REP_DONE
412e7348 370
af621e3a 371 A=R:E=D:CALL EVAL
cc9dbd92
JM
372 R2=R
373 IF ER<>-2 THEN GOTO REP_DONE
412e7348 374
cc9dbd92 375 A=R:GOSUB MAL_PRINT
412e7348
JM
376 RT$=R$
377
378 REP_DONE:
379 REM Release memory from MAL_READ and EVAL
cc9dbd92
JM
380 IF R2<>0 THEN AY=R2:GOSUB RELEASE
381 IF R1<>0 THEN AY=R1:GOSUB RELEASE
412e7348 382 R$=RT$
af621e3a 383END SUB
412e7348
JM
384
385REM MAIN program
386MAIN:
387 GOSUB INIT_MEMORY
388
cc9dbd92 389 LV=0
412e7348
JM
390
391 REM create repl_env
bbab5c5d 392 O=-1:GOSUB ENV_NEW:D=R
412e7348
JM
393
394 REM core.EXT: defined in Basic
bbab5c5d 395 E=D:GOSUB INIT_CORE_NS: REM set core functions in repl_env
412e7348 396
bbab5c5d 397 ZT=ZI: REM top of memory after base repl_env
412e7348
JM
398
399 REM core.mal: defined using the language itself
60ef223c 400 A$="(def! not (fn* (a) (if a false true)))"
cc9dbd92 401 GOSUB RE:AY=R:GOSUB RELEASE
412e7348 402
85d70fb7 403 REPL_LOOP:
60ef223c 404 A$="user> ":GOSUB READLINE: REM call input parser
85d70fb7 405 IF EOF=1 THEN GOTO QUIT
412e7348 406
af621e3a 407 A$=R$:CALL REP: REM call REP
412e7348 408
cc9dbd92 409 IF ER<>-2 THEN GOSUB PRINT_ERROR:GOTO REPL_LOOP
412e7348 410 PRINT R$
85d70fb7 411 GOTO REPL_LOOP
412e7348 412
85d70fb7 413 QUIT:
4e7d8a1b 414 REM GOSUB PR_MEMORY_SUMMARY
412e7348
JM
415 END
416
85d70fb7 417 PRINT_ERROR:
bf8d1f7d 418 PRINT "Error: "+ER$
cc9dbd92 419 ER=-2:ER$=""
85d70fb7
JM
420 RETURN
421