Merge pull request #319 from chr15m/refactor-php-web-runner
[jackhill/mal.git] / basic / step4_if_fn_do.in.bas
CommitLineData
241d5d57
JM
1GOTO MAIN
2
d7a6c2d6 3REM $INCLUDE: 'mem.in.bas'
241d5d57 4REM $INCLUDE: 'types.in.bas'
93593012 5REM $INCLUDE: 'readline.in.bas'
241d5d57
JM
6REM $INCLUDE: 'reader.in.bas'
7REM $INCLUDE: 'printer.in.bas'
8REM $INCLUDE: 'env.in.bas'
9REM $INCLUDE: 'core.in.bas'
10
9e8f5211
JM
11REM $INCLUDE: 'debug.in.bas'
12
cc9dbd92 13REM READ(A$) -> R
241d5d57
JM
14MAL_READ:
15 GOSUB READ_STR
16 RETURN
17
cc9dbd92 18REM EVAL_AST(A, E) -> R
af621e3a 19SUB EVAL_AST
cc9dbd92 20 REM push A and E on the stack
93593012
JM
21 Q=E:GOSUB PUSH_Q
22 GOSUB PUSH_A
241d5d57 23
cc9dbd92 24 IF ER<>-2 THEN GOTO EVAL_AST_RETURN
241d5d57 25
4202ef7b 26 GOSUB TYPE_A
cc9dbd92
JM
27 IF T=5 THEN GOTO EVAL_AST_SYMBOL
28 IF T>=6 AND T<=8 THEN GOTO EVAL_AST_SEQ
4b84a23b
JM
29
30 REM scalar: deref to actual value and inc ref cnt
9d59cdb3 31 R=A
4202ef7b 32 GOSUB INC_REF_R
241d5d57
JM
33 GOTO EVAL_AST_RETURN
34
35 EVAL_AST_SYMBOL:
af621e3a
JM
36 K=A:GOTO ENV_GET
37 ENV_GET_RETURN:
241d5d57 38 GOTO EVAL_AST_RETURN
412e7348 39
241d5d57 40 EVAL_AST_SEQ:
9d59cdb3
JM
41 REM setup the stack for the loop
42 GOSUB MAP_LOOP_START
241d5d57
JM
43
44 EVAL_AST_SEQ_LOOP:
4b84a23b 45 REM check if we are done evaluating the source sequence
d7a6c2d6 46 IF Z%(A+1)=0 THEN GOTO EVAL_AST_SEQ_LOOP_DONE
241d5d57 47
9d59cdb3
JM
48 REM call EVAL for each entry
49 GOSUB PUSH_A
d7a6c2d6
JM
50 IF T<>8 THEN A=Z%(A+2)
51 IF T=8 THEN A=Z%(A+3)
9d59cdb3
JM
52 Q=T:GOSUB PUSH_Q: REM push/save type
53 CALL EVAL
54 GOSUB POP_Q:T=Q: REM pop/restore type
55 GOSUB POP_A
d7a6c2d6 56 M=R
4b84a23b 57
9d59cdb3
JM
58 REM if error, release the unattached element
59 REM TODO: is R=0 correct?
60 IF ER<>-2 THEN AY=R:GOSUB RELEASE:R=0:GOTO EVAL_AST_SEQ_LOOP_DONE
241d5d57 61
9d59cdb3
JM
62 REM for hash-maps, copy the key (inc ref since we are going to
63 REM release it below)
d7a6c2d6 64 IF T=8 THEN N=M:M=Z%(A+2):Z%(M)=Z%(M)+32
412e7348 65
241d5d57 66
9d59cdb3
JM
67 REM update the return sequence structure
68 REM release N (and M if T=8) since seq takes full ownership
69 C=1:GOSUB MAP_LOOP_UPDATE
241d5d57 70
4b84a23b 71 REM process the next sequence entry from source list
d7a6c2d6 72 A=Z%(A+1)
241d5d57
JM
73
74 GOTO EVAL_AST_SEQ_LOOP
75 EVAL_AST_SEQ_LOOP_DONE:
9d59cdb3
JM
76 REM cleanup stack and get return value
77 GOSUB MAP_LOOP_DONE
241d5d57
JM
78 GOTO EVAL_AST_RETURN
79
80 EVAL_AST_RETURN:
cc9dbd92 81 REM pop A and E off the stack
93593012
JM
82 GOSUB POP_A
83 GOSUB POP_Q:E=Q
af621e3a 84END SUB
241d5d57 85
af621e3a
JM
86REM EVAL(A, E) -> R
87SUB EVAL
cc9dbd92 88 LV=LV+1: REM track basic return stack level
241d5d57 89
cc9dbd92 90 REM push A and E on the stack
93593012
JM
91 Q=E:GOSUB PUSH_Q
92 GOSUB PUSH_A
241d5d57 93
01e8850d
JM
94 REM PRINT "EVAL A:"+STR$(A)+",X:"+STR$(X)+",LV:"+STR$(LV)+",FRE:"+STR$(FRE(0))
95
241d5d57
JM
96 EVAL_TCO_RECUR:
97
c7330b3d
JM
98 IF ER<>-2 THEN GOTO EVAL_RETURN
99
c756af81 100 REM AZ=A:B=1:GOSUB PR_STR
cc9dbd92 101 REM PRINT "EVAL: "+R$+" [A:"+STR$(A)+", LV:"+STR$(LV)+"]"
241d5d57
JM
102
103 GOSUB LIST_Q
cc9dbd92 104 IF R THEN GOTO APPLY_LIST
241d5d57 105 REM ELSE
af621e3a 106 CALL EVAL_AST
241d5d57
JM
107 GOTO EVAL_RETURN
108
109 APPLY_LIST:
110 GOSUB EMPTY_Q
4202ef7b 111 IF R THEN R=A:GOSUB INC_REF_R:GOTO EVAL_RETURN
241d5d57 112
d7a6c2d6 113 A0=Z%(A+2)
241d5d57
JM
114
115 REM get symbol in A$
d7a6c2d6
JM
116 IF (Z%(A0)AND 31)<>5 THEN A$=""
117 IF (Z%(A0)AND 31)=5 THEN A$=S$(Z%(A0+1))
241d5d57
JM
118
119 IF A$="def!" THEN GOTO EVAL_DEF
120 IF A$="let*" THEN GOTO EVAL_LET
121 IF A$="do" THEN GOTO EVAL_DO
122 IF A$="if" THEN GOTO EVAL_IF
123 IF A$="fn*" THEN GOTO EVAL_FN
124 GOTO EVAL_INVOKE
125
126 EVAL_GET_A3:
d7a6c2d6 127 A3=Z%(Z%(Z%(Z%(A+1)+1)+1)+2)
241d5d57 128 EVAL_GET_A2:
d7a6c2d6 129 A2=Z%(Z%(Z%(A+1)+1)+2)
241d5d57 130 EVAL_GET_A1:
d7a6c2d6 131 A1=Z%(Z%(A+1)+2)
241d5d57
JM
132 RETURN
133
134 EVAL_DEF:
135 REM PRINT "def!"
bbab5c5d 136 GOSUB EVAL_GET_A2: REM set A1 and A2
4b84a23b 137
93593012 138 Q=A1:GOSUB PUSH_Q
af621e3a 139 A=A2:CALL EVAL: REM eval a2
93593012 140 GOSUB POP_Q:A1=Q
4b84a23b 141
cc9dbd92 142 IF ER<>-2 THEN GOTO EVAL_RETURN
70f29a2b 143
241d5d57 144 REM set a1 in env to a2
c756af81 145 K=A1:C=R:GOSUB ENV_SET
241d5d57 146 GOTO EVAL_RETURN
412e7348 147
241d5d57 148 EVAL_LET:
4b84a23b 149 REM PRINT "let*"
bbab5c5d 150 GOSUB EVAL_GET_A2: REM set A1 and A2
70f29a2b 151
93593012 152 Q=A2:GOSUB PUSH_Q: REM push/save A2
241d5d57 153 REM create new environment with outer as current environment
c756af81 154 C=E:GOSUB ENV_NEW
cc9dbd92 155 E=R
241d5d57 156 EVAL_LET_LOOP:
d7a6c2d6 157 IF Z%(A1+1)=0 THEN GOTO EVAL_LET_LOOP_DONE
412e7348 158
93593012 159 Q=A1:GOSUB PUSH_Q: REM push A1
241d5d57 160 REM eval current A1 odd element
d7a6c2d6 161 A=Z%(Z%(A1+1)+2):CALL EVAL
93593012 162 GOSUB POP_Q:A1=Q: REM pop A1
412e7348 163
c7330b3d
JM
164 IF ER<>-2 THEN GOTO EVAL_LET_LOOP_DONE
165
d7a6c2d6
JM
166 REM set key/value in the environment
167 K=Z%(A1+2):C=R:GOSUB ENV_SET
cc9dbd92 168 AY=R:GOSUB RELEASE: REM release our use, ENV_SET took ownership
412e7348 169
bbab5c5d 170 REM skip to the next pair of A1 elements
d7a6c2d6 171 A1=Z%(Z%(A1+1)+1)
241d5d57 172 GOTO EVAL_LET_LOOP
70f29a2b 173
241d5d57 174 EVAL_LET_LOOP_DONE:
93593012 175 GOSUB POP_Q:A2=Q: REM pop A2
af621e3a 176 A=A2:CALL EVAL: REM eval A2 using let_env
241d5d57
JM
177 GOTO EVAL_RETURN
178 EVAL_DO:
d7a6c2d6 179 A=Z%(A+1): REM rest
241d5d57 180
af621e3a 181 CALL EVAL_AST
241d5d57 182
93593012 183 GOSUB PUSH_R: REM push eval'd list
cc9dbd92 184 A=R:GOSUB LAST: REM return the last element
93593012 185 GOSUB POP_Q:AY=Q: REM pop eval'd list
412e7348 186 GOSUB RELEASE: REM release the eval'd list
241d5d57 187 GOTO EVAL_RETURN
412e7348 188
241d5d57 189 EVAL_IF:
bbab5c5d 190 GOSUB EVAL_GET_A1: REM set A1
93593012 191 GOSUB PUSH_A: REM push/save A
af621e3a 192 A=A1:CALL EVAL
93593012 193 GOSUB POP_A: REM pop/restore A
d7a6c2d6 194 IF (R=0) OR (R=2) THEN GOTO EVAL_IF_FALSE
241d5d57
JM
195
196 EVAL_IF_TRUE:
cc9dbd92 197 AY=R:GOSUB RELEASE
bbab5c5d
JM
198 GOSUB EVAL_GET_A2: REM set A1 and A2 after EVAL
199 A=A2:GOTO EVAL_TCO_RECUR: REM TCO loop
241d5d57 200 EVAL_IF_FALSE:
cc9dbd92 201 AY=R:GOSUB RELEASE
bbab5c5d 202 REM if no false case (A3), return nil
037815e0 203 GOSUB COUNT
4202ef7b 204 IF R<4 THEN R=0:GOSUB INC_REF_R:GOTO EVAL_RETURN
bbab5c5d
JM
205 GOSUB EVAL_GET_A3: REM set A1 - A3 after EVAL
206 A=A3:GOTO EVAL_TCO_RECUR: REM TCO loop
412e7348 207
241d5d57 208 EVAL_FN:
bbab5c5d 209 GOSUB EVAL_GET_A2: REM set A1 and A2
4202ef7b 210 T=10:L=A2:M=A1:N=E:GOSUB ALLOC: REM mal function
241d5d57 211 GOTO EVAL_RETURN
412e7348 212
241d5d57 213 EVAL_INVOKE:
af621e3a 214 CALL EVAL_AST
241d5d57 215
412e7348 216 REM if error, return f/args for release by caller
cc9dbd92 217 IF ER<>-2 THEN GOTO EVAL_RETURN
412e7348
JM
218
219 REM push f/args for release after call
93593012 220 GOSUB PUSH_R
412e7348 221
d7a6c2d6
JM
222 AR=Z%(R+1): REM rest
223 F=Z%(R+2)
241d5d57 224
bbab5c5d 225 REM if metadata, get the actual object
4202ef7b
JM
226 GOSUB TYPE_F
227 IF T=14 THEN F=Z%(F+1):GOSUB TYPE_F
bbab5c5d 228
4202ef7b 229 ON T-8 GOTO EVAL_DO_FUNCTION,EVAL_DO_MAL_FUNCTION,EVAL_DO_MAL_FUNCTION
412e7348
JM
230
231 REM if error, pop and return f/args for release by caller
93593012 232 GOSUB POP_R
c756af81 233 ER=-1:E$="apply of non-function":GOTO EVAL_RETURN
412e7348 234
241d5d57 235 EVAL_DO_FUNCTION:
01e8850d 236 REM regular function
d7a6c2d6 237 IF Z%(F+1)<60 THEN GOSUB DO_FUNCTION:GOTO EVAL_DO_FUNCTION_SKIP
01e8850d 238 REM for recur functions (apply, map, swap!), use GOTO
d7a6c2d6 239 IF Z%(F+1)>60 THEN CALL DO_TCO_FUNCTION
af621e3a 240 EVAL_DO_FUNCTION_SKIP:
412e7348
JM
241
242 REM pop and release f/args
93593012
JM
243 GOSUB POP_Q:AY=Q
244 GOSUB RELEASE
241d5d57 245 GOTO EVAL_RETURN
412e7348 246
241d5d57 247 EVAL_DO_MAL_FUNCTION:
9d59cdb3 248 Q=E:GOSUB PUSH_Q: REM save the current environment for release
412e7348 249
d7a6c2d6
JM
250 REM create new environ using env and params stored in function
251 C=Z%(F+3):A=Z%(F+2):B=AR:GOSUB ENV_NEW_BINDS
412e7348
JM
252
253 REM release previous env if it is not the top one on the
bbab5c5d 254 REM stack (X%(X-2)) because our new env refers to it and
412e7348 255 REM we no longer need to track it (since we are TCO recurring)
9d59cdb3 256 GOSUB POP_Q:AY=Q
93593012 257 GOSUB PEEK_Q_2
9d59cdb3 258 IF AY<>Q THEN GOSUB RELEASE
412e7348
JM
259
260 REM claim the AST before releasing the list containing it
d7a6c2d6 261 A=Z%(F+1):Z%(A)=Z%(A)+32
9e8f5211 262 REM add AST to pending release queue to free as soon as EVAL
cc9dbd92 263 REM actually returns (LV+1)
93593012 264 LV=LV+1:GOSUB PEND_A_LV:LV=LV-1
412e7348
JM
265
266 REM pop and release f/args
93593012
JM
267 GOSUB POP_Q:AY=Q
268 GOSUB RELEASE
412e7348 269
cc9dbd92
JM
270 REM A set above
271 E=R:GOTO EVAL_TCO_RECUR: REM TCO loop
241d5d57
JM
272
273 EVAL_RETURN:
c756af81 274 REM AZ=R: B=1: GOSUB PR_STR
cc9dbd92 275 REM PRINT "EVAL_RETURN R: ["+R$+"] ("+STR$(R)+"), LV:"+STR$(LV)+",ER:"+STR$(ER)
9e8f5211 276
412e7348 277 REM release environment if not the top one on the stack
93593012
JM
278 GOSUB PEEK_Q_1
279 IF E<>Q THEN AY=E:GOSUB RELEASE
412e7348 280
cc9dbd92 281 LV=LV-1: REM track basic return stack level
9e8f5211 282
412e7348
JM
283 REM release everything we couldn't release earlier
284 GOSUB RELEASE_PEND
285
241d5d57 286 REM trigger GC
c756af81
JM
287 #cbm T=FRE(0)
288 #qbasic T=0
4b84a23b 289
cc9dbd92 290 REM pop A and E off the stack
93593012
JM
291 GOSUB POP_A
292 GOSUB POP_Q:E=Q
241d5d57 293
af621e3a 294END SUB
241d5d57 295
cc9dbd92 296REM PRINT(A) -> R$
241d5d57 297MAL_PRINT:
c756af81 298 AZ=A:B=1:GOSUB PR_STR
241d5d57
JM
299 RETURN
300
cc9dbd92 301REM RE(A$) -> R
bbab5c5d 302REM Assume D has repl_env
85d70fb7
JM
303REM caller must release result
304RE:
4202ef7b 305 R1=-1
85d70fb7 306 GOSUB MAL_READ
cc9dbd92 307 R1=R
0e508fa5 308 IF ER<>-2 THEN GOTO RE_DONE
85d70fb7 309
af621e3a 310 A=R:E=D:CALL EVAL
85d70fb7 311
0e508fa5 312 RE_DONE:
85d70fb7 313 REM Release memory from MAL_READ
4202ef7b 314 AY=R1:GOSUB RELEASE
85d70fb7
JM
315 RETURN: REM caller must release result of EVAL
316
241d5d57 317REM REP(A$) -> R$
bbab5c5d 318REM Assume D has repl_env
af621e3a 319SUB REP
4202ef7b 320 R2=-1
4b84a23b 321
4202ef7b 322 GOSUB RE
cc9dbd92
JM
323 R2=R
324 IF ER<>-2 THEN GOTO REP_DONE
4b84a23b 325
cc9dbd92 326 A=R:GOSUB MAL_PRINT
4b84a23b
JM
327
328 REP_DONE:
329 REM Release memory from MAL_READ and EVAL
9d59cdb3 330 AY=R2:GOSUB RELEASE
af621e3a 331END SUB
241d5d57
JM
332
333REM MAIN program
334MAIN:
335 GOSUB INIT_MEMORY
336
cc9dbd92 337 LV=0
241d5d57
JM
338
339 REM create repl_env
9d59cdb3 340 C=0:GOSUB ENV_NEW:D=R
241d5d57 341
4b84a23b 342 REM core.EXT: defined in Basic
bbab5c5d 343 E=D:GOSUB INIT_CORE_NS: REM set core functions in repl_env
4b84a23b 344
bbab5c5d 345 ZT=ZI: REM top of memory after base repl_env
4b84a23b
JM
346
347 REM core.mal: defined using the language itself
60ef223c 348 A$="(def! not (fn* (a) (if a false true)))"
cc9dbd92 349 GOSUB RE:AY=R:GOSUB RELEASE
241d5d57 350
85d70fb7 351 REPL_LOOP:
60ef223c 352 A$="user> ":GOSUB READLINE: REM call input parser
01975886 353 IF EZ=1 THEN GOTO QUIT
9d59cdb3 354 IF R$="" THEN GOTO REPL_LOOP
4b84a23b 355
af621e3a 356 A$=R$:CALL REP: REM call REP
4b84a23b 357
cc9dbd92 358 IF ER<>-2 THEN GOSUB PRINT_ERROR:GOTO REPL_LOOP
241d5d57 359 PRINT R$
85d70fb7 360 GOTO REPL_LOOP
241d5d57 361
85d70fb7 362 QUIT:
9d59cdb3 363 REM GOSUB PR_MEMORY_SUMMARY_SMALL
115e430d
JM
364 #cbm END
365 #qbasic SYSTEM
241d5d57 366
85d70fb7 367 PRINT_ERROR:
c756af81
JM
368 PRINT "Error: "+E$
369 ER=-2:E$=""
85d70fb7
JM
370 RETURN
371