1
1
mirror of https://github.com/kanaka/mal.git synced 2024-10-26 14:22:25 +03:00

Basic: step8 basics. Fix def!, let*, concat, scalars.

- Move apply logic in swap! to APPLY function in types and use that
  for macroexpand
- Abort def! if error before updating the environment
- let* wasn't properly saving A2% for the final eval. Also, the
  environment release check should be against the top-level EVAL env,
  not the root repl env.
- (concat (list) ...) was broken so fix it to ignore empty lists that
  aren't in the trailing position.
- nil, false and true in the reader were always being returned as
  references (with an ref cnt) but we have the assumption that
  references (14) are not ref cnt'd and are always part of a compound
  type so fix the reader to just return the interned addresses.
This commit is contained in:
Joel Martin 2016-10-06 22:22:57 -05:00
parent 60ef223c3c
commit 70f29a2b3c
14 changed files with 773 additions and 126 deletions

View File

@ -15,13 +15,10 @@ STEP3_DEPS = $(STEP1_DEPS) env.in.bas
STEP4_DEPS = $(STEP3_DEPS) core.in.bas
step0_repl.bas: $(STEP0_DEPS)
step1_read_print.bas: $(STEP1_DEPS)
step2_eval.bas: $(STEP1_DEPS)
step1_read_print.bas step2_eval.bas: $(STEP1_DEPS)
step3_env.bas: $(STEP3_DEPS)
step4_if_fn_do.bas: $(STEP4_DEPS)
step5_tco.bas: $(STEP4_DEPS)
step6_file.bas: $(STEP4_DEPS)
step7_quote.bas: $(STEP4_DEPS)
step4_if_fn_do.bas step5_tco.bas step6_file.bas step7_quote.bas: $(STEP4_DEPS)
step8_macros.bas: $(STEP4_DEPS)
tests/%.bas: tests/%.in.bas
./basicpp.py $(BASICPP_OPTS) $< > $@
@ -31,10 +28,10 @@ tests/%.prg: tests/%.bas
petcat -text -w2 -o $@ $<.tmp
rm $<.tmp
mal.prg: step7_quote.prg
mal.prg: step8_macros.prg
cp $< $@
SOURCES_LISP = env.in.bas core.in.bas step7_quote.in.bas
SOURCES_LISP = env.in.bas core.in.bas step8_macros.in.bas
SOURCES = readline.in.bas types.in.bas reader.in.bas printer.in.bas $(SOURCES_LISP)
.PHONY: stats

View File

@ -153,6 +153,7 @@ DO_FUNCTION:
Z%(R%,1)=Z%(AA%,1)/Z%(AB%,1)
RETURN
DO_TIME_MS:
R%=0
RETURN
DO_LIST:
@ -202,6 +203,7 @@ DO_FUNCTION:
DO_CONCAT_LOOP:
IF ZL%=CZ% THEN R%=AB%:RETURN
AA%=ZZ%(ZL%):ZL%=ZL%-1: REM pop off next seq to prepend
IF Z%(AA%,1)=0 THEN GOTO DO_CONCAT_LOOP: REM skip empty seqs
A%=AA%:B%=0:C%=-1:GOSUB SLICE
REM release the terminator of new list (we skip over it)
@ -213,7 +215,18 @@ DO_FUNCTION:
AB%=R%
GOTO DO_CONCAT_LOOP
DO_NTH:
RETURN
B%=Z%(AB%,1)
A%=AA%:GOSUB COUNT
IF R%<=B% THEN R%=0:ER%=1:ER$="nth: index out of range":RETURN
DO_NTH_LOOP:
IF B%=0 THEN GOTO DO_NTH_DONE
B%=B%-1
AA%=Z%(AA%,1)
GOTO DO_NTH_LOOP
DO_NTH_DONE:
R%=Z%(AA%+1,1)
Z%(R%,0)=Z%(R%,0)+16
RETURN
DO_FIRST:
IF Z%(AA%,1)=0 THEN R%=0
IF Z%(AA%,1)<>0 THEN R%=AA%+1:GOSUB DEREF_R
@ -268,53 +281,24 @@ DO_FUNCTION:
REM push args for release after
ZL%=ZL%+1:ZZ%(ZL%)=AR%
REM TODO: break this out into APPLY
IF (Z%(F%,0)AND15)=9 THEN GOTO DO_SWAP_FUNCTION
IF (Z%(F%,0)AND15)=10 THEN GOTO DO_SWAP_MAL_FUNCTION
REM push atom
ZL%=ZL%+1:ZZ%(ZL%)=AA%
DO_SWAP_FUNCTION:
REM push atom
ZL%=ZL%+1:ZZ%(ZL%)=AA%
GOSUB APPLY
GOSUB DO_FUNCTION
REM pop atom
AA%=ZZ%(ZL%):ZL%=ZL%-1
REM pop atom
AA%=ZZ%(ZL%):ZL%=ZL%-1
REM pop and release args
AY%=ZZ%(ZL%):ZL%=ZL%-1:GOSUB RELEASE
REM pop and release args
AY%=ZZ%(ZL%):ZL%=ZL%-1:GOSUB RELEASE
GOTO DO_SWAP_DONE
REM use reset to update the value
AB%=R%:GOSUB DO_RESET_BANG
DO_SWAP_MAL_FUNCTION:
REM push current environment for later release
ZL%=ZL%+1:ZZ%(ZL%)=E%
REM but decrease ref cnt of return by 1 (not sure why)
AY%=R%:GOSUB RELEASE
REM create new environ using env stored with function
EO%=Z%(F%+1,1):BI%=Z%(F%+1,0):EX%=AR%:GOSUB ENV_NEW_BINDS
REM push atom
ZL%=ZL%+1:ZZ%(ZL%)=AA%
A%=Z%(F%,1):E%=R%:GOSUB EVAL
REM pop atom
AA%=ZZ%(ZL%):ZL%=ZL%-1
REM pop and release args
AY%=ZZ%(ZL%):ZL%=ZL%-1:GOSUB RELEASE
REM pop and release previous env
AY%=ZZ%(ZL%):ZL%=ZL%-1:GOSUB RELEASE
GOTO DO_SWAP_DONE
DO_SWAP_DONE:
REM use reset to update the value
AB%=R%:GOSUB DO_RESET_BANG
REM but decrease ref cnt of return by 1 (not sure why)
AY%=R%:GOSUB RELEASE
RETURN
RETURN
DO_PR_MEMORY:
P1%=ZT%:P2%=-1:GOSUB PR_MEMORY

View File

@ -55,25 +55,25 @@ REM NEXT I
REM PR_MEMORY_SKIP_STACK:
REM PRINT "^^^^^^"
REM RETURN
REM PR_OBJECT(P1%) -> nil
PR_OBJECT:
RC%=0
RC%=RC%+1:ZL%=ZL%+1:ZZ%(ZL%)=P1%
PR_OBJ_LOOP:
IF RC%=0 THEN RETURN
I=ZZ%(ZL%):RC%=RC%-1:ZL%=ZL%-1
P2%=Z%(I,0)AND15
PRINT " "+STR$(I);
PRINT ": ref cnt: "+STR$((Z%(I,0)AND-16)/16);
PRINT ", type: "+STR$(P2%)+", value: "+STR$(Z%(I,1));
IF P2%=4 THEN PRINT " '"+ZS$(Z%(I,1))+"'";
IF P2%=5 THEN PRINT " "+ZS$(Z%(I,1))+"";
PRINT
IF P2%<=5 OR P2%=9 THEN GOTO PR_OBJ_LOOP
IF Z%(I,1)<>0 THEN RC%=RC%+1:ZL%=ZL%+1:ZZ%(ZL%)=Z%(I,1)
IF P2%>=6 AND P2%<=8 THEN RC%=RC%+1:ZL%=ZL%+1:ZZ%(ZL%)=I+1
GOTO PR_OBJ_LOOP
REM
REM REM PR_OBJECT(P1%) -> nil
REM PR_OBJECT:
REM RD%=0
REM
REM RD%=RD%+1:ZL%=ZL%+1:ZZ%(ZL%)=P1%
REM
REM PR_OBJ_LOOP:
REM IF RD%=0 THEN RETURN
REM I=ZZ%(ZL%):RD%=RD%-1:ZL%=ZL%-1
REM
REM P2%=Z%(I,0)AND15
REM PRINT " "+STR$(I);
REM PRINT ": ref cnt: "+STR$((Z%(I,0)AND-16)/16);
REM PRINT ", type: "+STR$(P2%)+", value: "+STR$(Z%(I,1));
REM IF P2%=4 THEN PRINT " '"+ZS$(Z%(I,1))+"'";
REM IF P2%=5 THEN PRINT " "+ZS$(Z%(I,1))+"";
REM PRINT
REM IF P2%<=5 OR P2%=9 THEN GOTO PR_OBJ_LOOP
REM IF Z%(I,1)<>0 THEN RD%=RD%+1:ZL%=ZL%+1:ZZ%(ZL%)=Z%(I,1)
REM IF P2%>=6 AND P2%<=8 THEN RD%=RD%+1:ZL%=ZL%+1:ZZ%(ZL%)=I+1
REM GOTO PR_OBJ_LOOP

View File

@ -69,6 +69,8 @@ ENV_SET_S:
RETURN
REM ENV_FIND(E%, K%) -> R%
REM Returns environment (R%) containing K%. If found, value found is
REM in T4%
ENV_FIND:
EF%=E%
ENV_FIND_LOOP:

View File

@ -5,7 +5,7 @@ PR_STR:
T%=Z%(AZ%,0)AND15
REM PRINT "AZ%: "+STR$(AZ%)+", T%: "+STR$(T%)+", V%: "+STR$(Z%(AZ%,1))
IF T%=0 THEN R$="nil":RETURN
ON T% GOTO PR_BOOLEAN,PR_INTEGER,PR_UNKNOWN,PR_STRING,PR_SYMBOL,PR_SEQ,PR_SEQ,PR_SEQ,PR_FUNCTION,PR_MAL_FUNCTION,PR_UNKNOWN,PR_ATOM,PR_ENV,PR_RECUR,PR_FREE
ON T% GOTO PR_BOOLEAN,PR_INTEGER,PR_UNKNOWN,PR_STRING,PR_SYMBOL,PR_SEQ,PR_SEQ,PR_SEQ,PR_FUNCTION,PR_MAL_FUNCTION,PR_MAL_FUNCTION,PR_ATOM,PR_ENV,PR_RECUR,PR_FREE
PR_UNKNOWN:
R$="#<unknown>"

View File

@ -74,9 +74,8 @@ READ_FORM:
GOTO READ_TO_EOL
READ_NIL_BOOL:
REM PRINT "READ_NIL_BOOL"
SZ%=1:GOSUB ALLOC
Z%(R%,0)=14+16
Z%(R%,1)=T%
R%=T%
Z%(R%,0)=Z%(R%,0)+16
GOTO READ_FORM_DONE
READ_NUMBER:
REM PRINT "READ_NUMBER"

View File

@ -124,8 +124,8 @@ EVAL:
REM push A% and E% on the stack
ZL%=ZL%+2:ZZ%(ZL%-1)=E%:ZZ%(ZL%)=A%
REM AZ%=A%: GOSUB PR_STR
REM PRINT "EVAL: "+R$+"("+STR$(A%)+"), LV%:"+STR$(LV%)
REM AZ%=A%:PR%=1:GOSUB PR_STR
REM PRINT "EVAL: "+R$+" [A%:"+STR$(A%)+", LV%:"+STR$(LV%)+"]"
GOSUB DEREF_A
@ -169,6 +169,8 @@ EVAL:
A%=A2%:GOSUB EVAL: REM eval a2
A1%=ZZ%(ZL%):ZL%=ZL%-1: REM pop A1%
IF ER%<>0 THEN GOTO EVAL_RETURN
REM set a1 in env to a2
K%=A1%:V%=R%:GOSUB ENV_SET
GOTO EVAL_RETURN
@ -176,18 +178,18 @@ EVAL:
EVAL_LET:
REM PRINT "let*"
GOSUB EVAL_GET_A2: REM set a1% and a2%
ZL%=ZL%+1:ZZ%(ZL%)=A2%: REM push/save A2%
REM create new environment with outer as current environment
EO%=E%:GOSUB ENV_NEW
E%=R%
EVAL_LET_LOOP:
IF Z%(A1%,1)=0 THEN GOTO EVAL_LET_LOOP_DONE
REM push A1%
ZL%=ZL%+1:ZZ%(ZL%)=A1%
ZL%=ZL%+1:ZZ%(ZL%)=A1%: REM push A1%
REM eval current A1 odd element
A%=Z%(A1%,1)+1:GOSUB EVAL
REM pop A1%
A1%=ZZ%(ZL%):ZL%=ZL%-1
A1%=ZZ%(ZL%):ZL%=ZL%-1: REM pop A1%
REM set environment: even A1% key to odd A1% eval'd above
K%=A1%+1:V%=R%:GOSUB ENV_SET
@ -196,7 +198,9 @@ EVAL:
REM skip to the next pair of A1% elements
A1%=Z%(Z%(A1%,1),1)
GOTO EVAL_LET_LOOP
EVAL_LET_LOOP_DONE:
A2%=ZZ%(ZL%):ZL%=ZL%-1: REM pop A2%
A%=A2%:GOSUB EVAL: REM eval a2 using let_env
GOTO EVAL_RETURN
EVAL_INVOKE:

View File

@ -129,8 +129,8 @@ EVAL:
EVAL_TCO_RECUR:
REM AZ%=A%: GOSUB PR_STR
REM PRINT "EVAL: "+R$+"("+STR$(A%)+"), LV%:"+STR$(LV%)
REM AZ%=A%:PR%=1:GOSUB PR_STR
REM PRINT "EVAL: "+R$+" [A%:"+STR$(A%)+", LV%:"+STR$(LV%)+"]"
GOSUB DEREF_A
@ -181,6 +181,8 @@ EVAL:
A%=A2%:GOSUB EVAL: REM eval a2
A1%=ZZ%(ZL%):ZL%=ZL%-1: REM pop A1%
IF ER%<>0 THEN GOTO EVAL_RETURN
REM set a1 in env to a2
K%=A1%:V%=R%:GOSUB ENV_SET
GOTO EVAL_RETURN
@ -188,18 +190,18 @@ EVAL:
EVAL_LET:
REM PRINT "let*"
GOSUB EVAL_GET_A2: REM set a1% and a2%
ZL%=ZL%+1:ZZ%(ZL%)=A2%: REM push/save A2%
REM create new environment with outer as current environment
EO%=E%:GOSUB ENV_NEW
E%=R%
EVAL_LET_LOOP:
IF Z%(A1%,1)=0 THEN GOTO EVAL_LET_LOOP_DONE
REM push A1%
ZL%=ZL%+1:ZZ%(ZL%)=A1%
ZL%=ZL%+1:ZZ%(ZL%)=A1%: REM push A1%
REM eval current A1 odd element
A%=Z%(A1%,1)+1:GOSUB EVAL
REM pop A1%
A1%=ZZ%(ZL%):ZL%=ZL%-1
A1%=ZZ%(ZL%):ZL%=ZL%-1: REM pop A1%
REM set environment: even A1% key to odd A1% eval'd above
K%=A1%+1:V%=R%:GOSUB ENV_SET
@ -208,7 +210,9 @@ EVAL:
REM skip to the next pair of A1% elements
A1%=Z%(Z%(A1%,1),1)
GOTO EVAL_LET_LOOP
EVAL_LET_LOOP_DONE:
A2%=ZZ%(ZL%):ZL%=ZL%-1: REM pop A2%
A%=A2%:GOSUB EVAL: REM eval a2 using let_env
GOTO EVAL_RETURN
EVAL_DO:

View File

@ -129,8 +129,8 @@ EVAL:
EVAL_TCO_RECUR:
REM AZ%=A%: GOSUB PR_STR
REM PRINT "EVAL: "+R$+"("+STR$(A%)+"), LV%:"+STR$(LV%)
REM AZ%=A%:PR%=1:GOSUB PR_STR
REM PRINT "EVAL: "+R$+" [A%:"+STR$(A%)+", LV%:"+STR$(LV%)+"]"
GOSUB DEREF_A
@ -181,6 +181,8 @@ EVAL:
A%=A2%:GOSUB EVAL: REM eval a2
A1%=ZZ%(ZL%):ZL%=ZL%-1: REM pop A1%
IF ER%<>0 THEN GOTO EVAL_RETURN
REM set a1 in env to a2
K%=A1%:V%=R%:GOSUB ENV_SET
GOTO EVAL_RETURN
@ -189,7 +191,8 @@ EVAL:
REM PRINT "let*"
GOSUB EVAL_GET_A2: REM set a1% and a2%
E4%=E%: REM save the current environment for release
ZL%=ZL%+1:ZZ%(ZL%)=A2%: REM push/save A2%
ZL%=ZL%+1:ZZ%(ZL%)=E%: REM push env for for later release
REM create new environment with outer as current environment
EO%=E%:GOSUB ENV_NEW
@ -197,12 +200,10 @@ EVAL:
EVAL_LET_LOOP:
IF Z%(A1%,1)=0 THEN GOTO EVAL_LET_LOOP_DONE
REM push A1%
ZL%=ZL%+1:ZZ%(ZL%)=A1%
ZL%=ZL%+1:ZZ%(ZL%)=A1%: REM push A1%
REM eval current A1 odd element
A%=Z%(A1%,1)+1:GOSUB EVAL
REM pop A1%
A1%=ZZ%(ZL%):ZL%=ZL%-1
A1%=ZZ%(ZL%):ZL%=ZL%-1: REM pop A1%
REM set environment: even A1% key to odd A1% eval'd above
K%=A1%+1:V%=R%:GOSUB ENV_SET
@ -211,12 +212,14 @@ EVAL:
REM skip to the next pair of A1% elements
A1%=Z%(Z%(A1%,1),1)
GOTO EVAL_LET_LOOP
EVAL_LET_LOOP_DONE:
REM release previous env (if not root repl_env) because our
REM new env refers to it and we no longer need to track it
REM (since we are TCO recurring)
IF E4%<>RE% THEN AY%=E4%:GOSUB RELEASE
EVAL_LET_LOOP_DONE:
E4%=ZZ%(ZL%):ZL%=ZL%-1: REM pop previous env
REM release previous environment if not the current EVAL env
IF E4%<>ZZ%(ZL%-2) THEN AY%=E4%:GOSUB RELEASE
A2%=ZZ%(ZL%):ZL%=ZL%-1: REM pop A2%
A%=A2%:GOTO EVAL_TCO_RECUR: REM TCO loop
EVAL_DO:

View File

@ -129,8 +129,8 @@ EVAL:
EVAL_TCO_RECUR:
REM AZ%=A%: GOSUB PR_STR
REM PRINT "EVAL: "+R$+"("+STR$(A%)+"), LV%:"+STR$(LV%)
REM AZ%=A%:PR%=1:GOSUB PR_STR
REM PRINT "EVAL: "+R$+" [A%:"+STR$(A%)+", LV%:"+STR$(LV%)+"]"
GOSUB DEREF_A
@ -181,6 +181,8 @@ EVAL:
A%=A2%:GOSUB EVAL: REM eval a2
A1%=ZZ%(ZL%):ZL%=ZL%-1: REM pop A1%
IF ER%<>0 THEN GOTO EVAL_RETURN
REM set a1 in env to a2
K%=A1%:V%=R%:GOSUB ENV_SET
GOTO EVAL_RETURN
@ -189,7 +191,8 @@ EVAL:
REM PRINT "let*"
GOSUB EVAL_GET_A2: REM set a1% and a2%
E4%=E%: REM save the current environment for release
ZL%=ZL%+1:ZZ%(ZL%)=A2%: REM push/save A2%
ZL%=ZL%+1:ZZ%(ZL%)=E%: REM push env for for later release
REM create new environment with outer as current environment
EO%=E%:GOSUB ENV_NEW
@ -197,12 +200,10 @@ EVAL:
EVAL_LET_LOOP:
IF Z%(A1%,1)=0 THEN GOTO EVAL_LET_LOOP_DONE
REM push A1%
ZL%=ZL%+1:ZZ%(ZL%)=A1%
ZL%=ZL%+1:ZZ%(ZL%)=A1%: REM push A1%
REM eval current A1 odd element
A%=Z%(A1%,1)+1:GOSUB EVAL
REM pop A1%
A1%=ZZ%(ZL%):ZL%=ZL%-1
A1%=ZZ%(ZL%):ZL%=ZL%-1: REM pop A1%
REM set environment: even A1% key to odd A1% eval'd above
K%=A1%+1:V%=R%:GOSUB ENV_SET
@ -211,12 +212,14 @@ EVAL:
REM skip to the next pair of A1% elements
A1%=Z%(Z%(A1%,1),1)
GOTO EVAL_LET_LOOP
EVAL_LET_LOOP_DONE:
REM release previous env (if not root repl_env) because our
REM new env refers to it and we no longer need to track it
REM (since we are TCO recurring)
IF E4%<>RE% THEN AY%=E4%:GOSUB RELEASE
EVAL_LET_LOOP_DONE:
E4%=ZZ%(ZL%):ZL%=ZL%-1: REM pop previous env
REM release previous environment if not the current EVAL env
IF E4%<>ZZ%(ZL%-2) THEN AY%=E4%:GOSUB RELEASE
A2%=ZZ%(ZL%):ZL%=ZL%-1: REM pop A2%
A%=A2%:GOTO EVAL_TCO_RECUR: REM TCO loop
EVAL_DO:

View File

@ -202,8 +202,8 @@ EVAL:
EVAL_TCO_RECUR:
REM AZ%=A%: GOSUB PR_STR
REM PRINT "EVAL: "+R$+"("+STR$(A%)+"), LV%:"+STR$(LV%)
REM AZ%=A%:PR%=1:GOSUB PR_STR
REM PRINT "EVAL: "+R$+" [A%:"+STR$(A%)+", LV%:"+STR$(LV%)+"]"
GOSUB DEREF_A
@ -256,6 +256,8 @@ EVAL:
A%=A2%:GOSUB EVAL: REM eval a2
A1%=ZZ%(ZL%):ZL%=ZL%-1: REM pop A1%
IF ER%<>0 THEN GOTO EVAL_RETURN
REM set a1 in env to a2
K%=A1%:V%=R%:GOSUB ENV_SET
GOTO EVAL_RETURN
@ -264,7 +266,8 @@ EVAL:
REM PRINT "let*"
GOSUB EVAL_GET_A2: REM set a1% and a2%
E4%=E%: REM save the current environment for release
ZL%=ZL%+1:ZZ%(ZL%)=A2%: REM push/save A2%
ZL%=ZL%+1:ZZ%(ZL%)=E%: REM push env for for later release
REM create new environment with outer as current environment
EO%=E%:GOSUB ENV_NEW
@ -272,12 +275,10 @@ EVAL:
EVAL_LET_LOOP:
IF Z%(A1%,1)=0 THEN GOTO EVAL_LET_LOOP_DONE
REM push A1%
ZL%=ZL%+1:ZZ%(ZL%)=A1%
ZL%=ZL%+1:ZZ%(ZL%)=A1%: REM push A1%
REM eval current A1 odd element
A%=Z%(A1%,1)+1:GOSUB EVAL
REM pop A1%
A1%=ZZ%(ZL%):ZL%=ZL%-1
A1%=ZZ%(ZL%):ZL%=ZL%-1: REM pop A1%
REM set environment: even A1% key to odd A1% eval'd above
K%=A1%+1:V%=R%:GOSUB ENV_SET
@ -286,12 +287,14 @@ EVAL:
REM skip to the next pair of A1% elements
A1%=Z%(Z%(A1%,1),1)
GOTO EVAL_LET_LOOP
EVAL_LET_LOOP_DONE:
REM release previous env (if not root repl_env) because our
REM new env refers to it and we no longer need to track it
REM (since we are TCO recurring)
IF E4%<>RE% THEN AY%=E4%:GOSUB RELEASE
EVAL_LET_LOOP_DONE:
E4%=ZZ%(ZL%):ZL%=ZL%-1: REM pop previous env
REM release previous environment if not the current EVAL env
IF E4%<>ZZ%(ZL%-2) THEN AY%=E4%:GOSUB RELEASE
A2%=ZZ%(ZL%):ZL%=ZL%-1: REM pop A2%
A%=A2%:GOTO EVAL_TCO_RECUR: REM TCO loop
EVAL_DO:

613
basic/step8_macros.in.bas Executable file
View File

@ -0,0 +1,613 @@
REM POKE 1, PEEK(1)AND248: REM enable all ROM areas as RAM
REM POKE 55,0: POKE 56,192: CLR: REM move BASIC end from $A000 to $C000
GOTO MAIN
REM $INCLUDE: 'readline.in.bas'
REM $INCLUDE: 'types.in.bas'
REM $INCLUDE: 'reader.in.bas'
REM $INCLUDE: 'printer.in.bas'
REM $INCLUDE: 'env.in.bas'
REM $INCLUDE: 'core.in.bas'
REM $INCLUDE: 'debug.in.bas'
REM READ(A$) -> R%
MAL_READ:
GOSUB READ_STR
RETURN
REM PAIR_Q(B%) -> R%
PAIR_Q:
R%=0
IF (Z%(B%,0)AND15)<>6 AND (Z%(B%,0)AND15)<>7 THEN RETURN
IF (Z%(B%,1)=0) THEN RETURN
R%=1
RETURN
REM QUASIQUOTE(A%) -> R%
QUASIQUOTE:
B%=A%:GOSUB PAIR_Q
IF R%=1 THEN GOTO QQ_UNQUOTE
REM ['quote, ast]
AS$="quote":T%=5:GOSUB STRING
B2%=R%:B1%=A%:GOSUB LIST2
RETURN
QQ_UNQUOTE:
R%=A%+1:GOSUB DEREF_R
IF (Z%(R%,0)AND15)<>5 THEN GOTO QQ_SPLICE_UNQUOTE
IF ZS$(Z%(R%,1))<>"unquote" THEN GOTO QQ_SPLICE_UNQUOTE
REM [ast[1]]
R%=Z%(A%,1)+1:GOSUB DEREF_R
Z%(R%,0)=Z%(R%,0)+16
RETURN
QQ_SPLICE_UNQUOTE:
REM push A% on the stack
ZL%=ZL%+1:ZZ%(ZL%)=A%
REM rest of cases call quasiquote on ast[1..]
A%=Z%(A%,1):GOSUB QUASIQUOTE:T6%=R%
REM pop A% off the stack
A%=ZZ%(ZL%):ZL%=ZL%-1
REM set A% to ast[0] for last two cases
A%=A%+1:GOSUB DEREF_A
B%=A%:GOSUB PAIR_Q
IF R%=0 THEN GOTO QQ_DEFAULT
B%=A%+1:GOSUB DEREF_B
IF (Z%(B%,0)AND15)<>5 THEN GOTO QQ_DEFAULT
IF ZS$(Z%(B%,1))<>"splice-unquote" THEN QQ_DEFAULT
REM ['concat, ast[0][1], quasiquote(ast[1..])]
B%=Z%(A%,1)+1:GOSUB DEREF_B:B2%=B%
AS$="concat":T%=5:GOSUB STRING:B3%=R%
B1%=T6%:GOSUB LIST3
REM release inner quasiquoted since outer list takes ownership
AY%=B1%:GOSUB RELEASE
RETURN
QQ_DEFAULT:
REM ['cons, quasiquote(ast[0]), quasiquote(ast[1..])]
REM push T6% on the stack
ZL%=ZL%+1:ZZ%(ZL%)=T6%
REM A% set above to ast[0]
GOSUB QUASIQUOTE:B2%=R%
REM pop T6% off the stack
T6%=ZZ%(ZL%):ZL%=ZL%-1
AS$="cons":T%=5:GOSUB STRING:B3%=R%
B1%=T6%:GOSUB LIST3
REM release inner quasiquoted since outer list takes ownership
AY%=B1%:GOSUB RELEASE
AY%=B2%:GOSUB RELEASE
RETURN
REM MACROEXPAND(A%, E%) -> A%:
MACROEXPAND:
REM push original A%
ZL%=ZL%+1:ZZ%(ZL%)=A%
MACROEXPAND_LOOP:
REM list?
IF (Z%(A%,0)AND15)<>6 THEN GOTO MACROEXPAND_DONE
REM non-empty?
IF Z%(A%,1)=0 THEN GOTO MACROEXPAND_DONE
B%=A%+1:GOSUB DEREF_B
REM symbol? in first position
IF (Z%(B%,0)AND15)<>5 THEN GOTO MACROEXPAND_DONE
REM defined in environment?
K%=B%:GOSUB ENV_FIND
IF R%=-1 THEN GOTO MACROEXPAND_DONE
B%=T4%:GOSUB DEREF_B
REM macro?
IF (Z%(B%,0)AND15)<>11 THEN GOTO MACROEXPAND_DONE
REM apply
F%=B%:AR%=Z%(A%,1):GOSUB APPLY
A%=R%
AY%=ZZ%(ZL%)
REM if previous A% was not the first A% into macroexpand (i.e. an
REM intermediate form) then free it
IF A%<>AY% THEN ZM%=ZM%+1:ZR%(ZM%,0)=A%:ZR%(ZM%,1)=LV%
IF ER%<>0 THEN GOTO MACROEXPAND_DONE
GOTO MACROEXPAND_LOOP
MACROEXPAND_DONE:
ZL%=ZL%-1: REM pop original A%
RETURN
REM EVAL_AST(A%, E%) -> R%
REM called using GOTO to avoid basic return address stack usage
REM top of stack should have return label index
EVAL_AST:
REM push A% and E% on the stack
ZL%=ZL%+2:ZZ%(ZL%-1)=E%:ZZ%(ZL%)=A%
IF ER%<>0 THEN GOTO EVAL_AST_RETURN
GOSUB DEREF_A
T%=Z%(A%,0)AND15
IF T%=5 THEN GOTO EVAL_AST_SYMBOL
IF T%>=6 AND T%<=8 THEN GOTO EVAL_AST_SEQ
REM scalar: deref to actual value and inc ref cnt
R%=A%:GOSUB DEREF_R
Z%(R%,0)=Z%(R%,0)+16
GOTO EVAL_AST_RETURN
EVAL_AST_SYMBOL:
K%=A%:GOSUB ENV_GET
GOTO EVAL_AST_RETURN
EVAL_AST_SEQ:
REM allocate the first entry
SZ%=2:GOSUB ALLOC
REM make space on the stack
ZL%=ZL%+4
REM push type of sequence
ZZ%(ZL%-3)=T%
REM push sequence index
ZZ%(ZL%-2)=-1
REM push future return value (new sequence)
ZZ%(ZL%-1)=R%
REM push previous new sequence entry
ZZ%(ZL%)=R%
EVAL_AST_SEQ_LOOP:
REM set new sequence entry type (with 1 ref cnt)
Z%(R%,0)=ZZ%(ZL%-3)+16
Z%(R%,1)=0
REM create value ptr placeholder
Z%(R%+1,0)=14
Z%(R%+1,1)=0
REM update index
ZZ%(ZL%-2)=ZZ%(ZL%-2)+1
REM check if we are done evaluating the source sequence
IF Z%(A%,1)=0 THEN GOTO EVAL_AST_SEQ_LOOP_DONE
REM if hashmap, skip eval of even entries (keys)
IF (ZZ%(ZL%-3)=8) AND ((ZZ%(ZL%-2) AND 1)=0) THEN GOTO EVAL_AST_DO_REF
GOTO EVAL_AST_DO_EVAL
EVAL_AST_DO_REF:
R%=A%+1:GOSUB DEREF_R: REM deref to target of referred entry
Z%(R%,0)=Z%(R%,0)+16: REM inc ref cnt of referred value
GOTO EVAL_AST_ADD_VALUE
EVAL_AST_DO_EVAL:
REM call EVAL for each entry
A%=A%+1:GOSUB EVAL
A%=A%-1
GOSUB DEREF_R: REM deref to target of evaluated entry
EVAL_AST_ADD_VALUE:
REM update previous value pointer to evaluated entry
Z%(ZZ%(ZL%)+1,1)=R%
IF ER%<>0 THEN GOTO EVAL_AST_SEQ_LOOP_DONE
REM allocate the next entry
SZ%=2:GOSUB ALLOC
REM update previous sequence entry value to point to new entry
Z%(ZZ%(ZL%),1)=R%
REM update previous ptr to current entry
ZZ%(ZL%)=R%
REM process the next sequence entry from source list
A%=Z%(A%,1)
GOTO EVAL_AST_SEQ_LOOP
EVAL_AST_SEQ_LOOP_DONE:
REM if no error, get return value (new seq)
IF ER%=0 THEN R%=ZZ%(ZL%-1)
REM otherwise, free the return value and return nil
IF ER%<>0 THEN R%=0:AY%=ZZ%(ZL%-1):GOSUB RELEASE
REM pop previous, return, index and type
ZL%=ZL%-4
GOTO EVAL_AST_RETURN
EVAL_AST_RETURN:
REM pop A% and E% off the stack
E%=ZZ%(ZL%-1):A%=ZZ%(ZL%):ZL%=ZL%-2
REM pop EVAL AST return label/address
RN%=ZZ%(ZL%):ZL%=ZL%-1
ON RN% GOTO EVAL_AST_RETURN_1,EVAL_AST_RETURN_2,EVAL_AST_RETURN_3
RETURN
REM EVAL(A%, E%)) -> R%
EVAL:
LV%=LV%+1: REM track basic return stack level
REM push A% and E% on the stack
ZL%=ZL%+2:ZZ%(ZL%-1)=E%:ZZ%(ZL%)=A%
EVAL_TCO_RECUR:
REM AZ%=A%:PR%=1:GOSUB PR_STR
REM PRINT "EVAL: "+R$+" [A%:"+STR$(A%)+", LV%:"+STR$(LV%)+"]"
GOSUB DEREF_A
GOSUB LIST_Q
IF R% THEN GOTO APPLY_LIST
EVAL_NOT_LIST:
REM ELSE
REM push EVAL_AST return label/address
ZL%=ZL%+1:ZZ%(ZL%)=1
GOTO EVAL_AST
EVAL_AST_RETURN_1:
GOTO EVAL_RETURN
APPLY_LIST:
GOSUB MACROEXPAND
GOSUB LIST_Q
IF R%<>1 THEN GOTO EVAL_NOT_LIST
GOSUB EMPTY_Q
IF R% THEN R%=A%:Z%(R%,0)=Z%(R%,0)+16:GOTO EVAL_RETURN
A0%=A%+1
R%=A0%:GOSUB DEREF_R:A0%=R%
REM get symbol in A$
IF (Z%(A0%,0)AND15)<>5 THEN A$=""
IF (Z%(A0%,0)AND15)=5 THEN A$=ZS$(Z%(A0%,1))
IF A$="def!" THEN GOTO EVAL_DEF
IF A$="let*" THEN GOTO EVAL_LET
IF A$="quote" THEN GOTO EVAL_QUOTE
IF A$="quasiquote" THEN GOTO EVAL_QUASIQUOTE
IF A$="defmacro!" THEN GOTO EVAL_DEFMACRO
IF A$="macroexpand" THEN GOTO EVAL_MACROEXPAND
IF A$="do" THEN GOTO EVAL_DO
IF A$="if" THEN GOTO EVAL_IF
IF A$="fn*" THEN GOTO EVAL_FN
GOTO EVAL_INVOKE
EVAL_GET_A3:
A3%=Z%(Z%(Z%(A%,1),1),1)+1
R%=A3%:GOSUB DEREF_R:A3%=R%
EVAL_GET_A2:
A2%=Z%(Z%(A%,1),1)+1
R%=A2%:GOSUB DEREF_R:A2%=R%
EVAL_GET_A1:
A1%=Z%(A%,1)+1
R%=A1%:GOSUB DEREF_R:A1%=R%
RETURN
EVAL_DEF:
REM PRINT "def!"
GOSUB EVAL_GET_A2: REM set a1% and a2%
ZL%=ZL%+1:ZZ%(ZL%)=A1%: REM push A1%
A%=A2%:GOSUB EVAL: REM eval a2
A1%=ZZ%(ZL%):ZL%=ZL%-1: REM pop A1%
IF ER%<>0 THEN GOTO EVAL_RETURN
REM set a1 in env to a2
K%=A1%:V%=R%:GOSUB ENV_SET
GOTO EVAL_RETURN
EVAL_LET:
REM PRINT "let*"
GOSUB EVAL_GET_A2: REM set a1% and a2%
ZL%=ZL%+1:ZZ%(ZL%)=A2%: REM push/save A2%
ZL%=ZL%+1:ZZ%(ZL%)=E%: REM push env for for later release
REM create new environment with outer as current environment
EO%=E%:GOSUB ENV_NEW
E%=R%
EVAL_LET_LOOP:
IF Z%(A1%,1)=0 THEN GOTO EVAL_LET_LOOP_DONE
ZL%=ZL%+1:ZZ%(ZL%)=A1%: REM push A1%
REM eval current A1 odd element
A%=Z%(A1%,1)+1:GOSUB EVAL
A1%=ZZ%(ZL%):ZL%=ZL%-1: REM pop A1%
REM set environment: even A1% key to odd A1% eval'd above
K%=A1%+1:V%=R%:GOSUB ENV_SET
AY%=R%:GOSUB RELEASE: REM release our use, ENV_SET took ownership
REM skip to the next pair of A1% elements
A1%=Z%(Z%(A1%,1),1)
GOTO EVAL_LET_LOOP
EVAL_LET_LOOP_DONE:
E4%=ZZ%(ZL%):ZL%=ZL%-1: REM pop previous env
REM release previous environment if not the current EVAL env
IF E4%<>ZZ%(ZL%-2) THEN AY%=E4%:GOSUB RELEASE
A2%=ZZ%(ZL%):ZL%=ZL%-1: REM pop A2%
A%=A2%:GOTO EVAL_TCO_RECUR: REM TCO loop
EVAL_DO:
A%=Z%(A%,1): REM rest
REM TODO: TCO
REM push EVAL_AST return label/address
ZL%=ZL%+1:ZZ%(ZL%)=2
GOTO EVAL_AST
EVAL_AST_RETURN_2:
ZL%=ZL%+1:ZZ%(ZL%)=R%: REM push eval'd list
A%=R%:GOSUB LAST: REM return the last element
AY%=ZZ%(ZL%):ZL%=ZL%-1: REM pop eval'd list
GOSUB RELEASE: REM release the eval'd list
GOTO EVAL_RETURN
EVAL_QUOTE:
R%=Z%(A%,1)+1:GOSUB DEREF_R
Z%(R%,0)=Z%(R%,0)+16
GOTO EVAL_RETURN
EVAL_QUASIQUOTE:
R%=Z%(A%,1)+1:GOSUB DEREF_R
A%=R%:GOSUB QUASIQUOTE
REM add quasiquote result to pending release queue to free when
REM next lower EVAL level returns (LV%)
ZM%=ZM%+1:ZR%(ZM%,0)=R%:ZR%(ZM%,1)=LV%
A%=R%:GOTO EVAL_TCO_RECUR: REM TCO loop
EVAL_DEFMACRO:
REM PRINT "defmacro!"
GOSUB EVAL_GET_A2: REM set a1% and a2%
ZL%=ZL%+1:ZZ%(ZL%)=A1%: REM push A1%
A%=A2%:GOSUB EVAL: REM eval a2
A1%=ZZ%(ZL%):ZL%=ZL%-1: REM pop A1%
REM change function to macro
Z%(R%,0)=Z%(R%,0)+1
REM set a1 in env to a2
K%=A1%:V%=R%:GOSUB ENV_SET
GOTO EVAL_RETURN
EVAL_MACROEXPAND:
REM PRINT "macroexpand"
R%=Z%(A%,1)+1:GOSUB DEREF_R
A%=R%:GOSUB MACROEXPAND:R%=A%
REM since we are returning it unevaluated, inc the ref cnt
Z%(R%,0)=Z%(R%,0)+16
GOTO EVAL_RETURN
EVAL_IF:
GOSUB EVAL_GET_A1: REM set a1%
REM push A%
ZL%=ZL%+1:ZZ%(ZL%)=A%
A%=A1%:GOSUB EVAL
REM pop A%
A%=ZZ%(ZL%):ZL%=ZL%-1
IF (R%=0) OR (R%=1) THEN GOTO EVAL_IF_FALSE
EVAL_IF_TRUE:
AY%=R%:GOSUB RELEASE
GOSUB EVAL_GET_A2: REM set a1% and a2% after EVAL
A%=A2%:GOTO EVAL_TCO_RECUR: REM TCO loop
EVAL_IF_FALSE:
AY%=R%:GOSUB RELEASE
REM if no false case (A3%), return nil
IF Z%(Z%(Z%(A%,1),1),1)=0 THEN R%=0:GOTO EVAL_RETURN
GOSUB EVAL_GET_A3: REM set a1% - a3% after EVAL
A%=A3%:GOTO EVAL_TCO_RECUR: REM TCO loop
EVAL_FN:
GOSUB EVAL_GET_A2: REM set a1% and a2%
A%=A2%:P%=A1%:GOSUB MAL_FUNCTION
GOTO EVAL_RETURN
EVAL_INVOKE:
REM push EVAL_AST return label/address
ZL%=ZL%+1:ZZ%(ZL%)=3
GOTO EVAL_AST
EVAL_AST_RETURN_3:
REM if error, return f/args for release by caller
IF ER%<>0 THEN GOTO EVAL_RETURN
REM push f/args for release after call
ZL%=ZL%+1:ZZ%(ZL%)=R%
F%=R%+1
AR%=Z%(R%,1): REM rest
R%=F%:GOSUB DEREF_R:F%=R%
IF (Z%(F%,0)AND15)=9 THEN GOTO EVAL_DO_FUNCTION
IF (Z%(F%,0)AND15)=10 THEN GOTO EVAL_DO_MAL_FUNCTION
REM if error, pop and return f/args for release by caller
R%=ZZ%(ZL%):ZL%=ZL%-1
ER%=1:ER$="apply of non-function":GOTO EVAL_RETURN
EVAL_DO_FUNCTION:
GOSUB DO_FUNCTION
REM pop and release f/args
AY%=ZZ%(ZL%):ZL%=ZL%-1:GOSUB RELEASE
GOTO EVAL_RETURN
EVAL_DO_MAL_FUNCTION:
E4%=E%: REM save the current environment for release
REM create new environ using env stored with function
EO%=Z%(F%+1,1):BI%=Z%(F%+1,0):EX%=AR%:GOSUB ENV_NEW_BINDS
REM release previous env if it is not the top one on the
REM stack (ZZ%(ZL%-2)) because our new env refers to it and
REM we no longer need to track it (since we are TCO recurring)
IF E4%<>ZZ%(ZL%-2) THEN AY%=E4%:GOSUB RELEASE
REM claim the AST before releasing the list containing it
A%=Z%(F%,1):Z%(A%,0)=Z%(A%,0)+16
REM add AST to pending release queue to free as soon as EVAL
REM actually returns (LV%+1)
ZM%=ZM%+1:ZR%(ZM%,0)=A%:ZR%(ZM%,1)=LV%+1
REM pop and release f/args
AY%=ZZ%(ZL%):ZL%=ZL%-1:GOSUB RELEASE
REM A% set above
E%=R%:GOTO EVAL_TCO_RECUR: REM TCO loop
EVAL_RETURN:
REM AZ%=R%: PR%=1: GOSUB PR_STR
REM PRINT "EVAL_RETURN R%: ["+R$+"] ("+STR$(R%)+"), LV%:"+STR$(LV%)+",ER%:"+STR$(ER%)
REM release environment if not the top one on the stack
IF E%<>ZZ%(ZL%-1) THEN AY%=E%:GOSUB RELEASE
LV%=LV%-1: REM track basic return stack level
REM release everything we couldn't release earlier
GOSUB RELEASE_PEND
REM trigger GC
TA%=FRE(0)
REM pop A% and E% off the stack
E%=ZZ%(ZL%-1):A%=ZZ%(ZL%):ZL%=ZL%-2
RETURN
REM PRINT(A%) -> R$
MAL_PRINT:
AZ%=A%:PR%=1:GOSUB PR_STR
RETURN
REM RE(A$) -> R%
REM Assume RE% has repl_env
REM caller must release result
RE:
R1%=0
GOSUB MAL_READ
R1%=R%
IF ER%<>0 THEN GOTO REP_DONE
A%=R%:E%=RE%:GOSUB EVAL
REP_DONE:
REM Release memory from MAL_READ
IF R1%<>0 THEN AY%=R1%:GOSUB RELEASE
RETURN: REM caller must release result of EVAL
REM REP(A$) -> R$
REM Assume RE% has repl_env
REP:
R1%=0:R2%=0
GOSUB MAL_READ
R1%=R%
IF ER%<>0 THEN GOTO REP_DONE
A%=R%:E%=RE%:GOSUB EVAL
R2%=R%
IF ER%<>0 THEN GOTO REP_DONE
A%=R%:GOSUB MAL_PRINT
RT$=R$
REP_DONE:
REM Release memory from MAL_READ and EVAL
IF R2%<>0 THEN AY%=R2%:GOSUB RELEASE
IF R1%<>0 THEN AY%=R1%:GOSUB RELEASE
R$=RT$
RETURN
REM MAIN program
MAIN:
GOSUB INIT_MEMORY
LV%=0
REM create repl_env
EO%=-1:GOSUB ENV_NEW:RE%=R%
REM core.EXT: defined in Basic
E%=RE%:GOSUB INIT_CORE_NS: REM set core functions in repl_env
ZT%=ZI%: REM top of memory after base repl_env
REM core.mal: defined using the language itself
A$="(def! not (fn* (a) (if a false true)))"
GOSUB RE:AY%=R%:GOSUB RELEASE
A$="(def! load-file (fn* (f) (eval (read-string (str "
A$=A$+CHR$(34)+"(do "+CHR$(34)+" (slurp f) "+CHR$(34)+")"+CHR$(34)+")))))"
GOSUB RE:AY%=R%:GOSUB RELEASE
A$="(defmacro! cond (fn* (& xs) (if (> (count xs) 0) (list 'if (first xs)"
A$=A$+" (if (> (count xs) 1) (nth xs 1) (throw "+CHR$(34)+"odd number of"
A$=A$+" forms to cond"+CHR$(34)+")) (cons 'cond (rest (rest xs)))))))"
GOSUB RE:AY%=R%:GOSUB RELEASE
A$="(defmacro! or (fn* (& xs) (if (empty? xs) nil (if (= 1 (count xs)) (first xs)"
A$=A$+" `(let* (or_FIXME ~(first xs)) (if or_FIXME or_FIXME (or ~@(rest xs))))))))"
GOSUB RE:AY%=R%:GOSUB RELEASE
REM load the args file
A$="(def! -*ARGS*- (load-file "+CHR$(34)+".args.mal"+CHR$(34)+"))"
GOSUB RE:AY%=R%:GOSUB RELEASE
REM set the argument list
A$="(def! *ARGV* (rest -*ARGS*-))"
GOSUB RE:AY%=R%:GOSUB RELEASE
REM get the first argument
A$="(first -*ARGS*-)"
GOSUB RE
REM if there is an argument, then run it as a program
IF R%<>0 THEN AY%=R%:GOSUB RELEASE:GOTO RUN_PROG
REM no arguments, start REPL loop
IF R%=0 THEN GOTO REPL_LOOP
RUN_PROG:
REM run a single mal program and exit
A$="(load-file (first -*ARGS*-))"
GOSUB RE
IF ER%<>0 THEN GOSUB PRINT_ERROR
END
REPL_LOOP:
A$="user> ":GOSUB READLINE: REM call input parser
IF EOF=1 THEN GOTO QUIT
A$=R$:GOSUB REP: REM call REP
IF ER%<>0 THEN GOSUB PRINT_ERROR:GOTO REPL_LOOP
PRINT R$
GOTO REPL_LOOP
QUIT:
REM P1%=ZT%: P2%=-1: GOSUB PR_MEMORY
GOSUB PR_MEMORY_SUMMARY
END
PRINT_ERROR:
PRINT "Error: "+ER$
ER%=0:ER$=""
RETURN

View File

@ -14,6 +14,8 @@ REM followed by key or value (alternating)
REM function 9 -> function index
REM mal function 10 -> body AST Z% index
REM followed by param and env Z% index
REM macro (same as 10) 11 -> body AST Z% index
REM followed by param and env Z% index
REM atom 12 -> Z% index
REM environment 13 -> data/hashmap Z% index
REM followed by 13 and outer Z% index (-1 for none)
@ -130,6 +132,7 @@ RELEASE:
REM sanity check not already freed
IF (U6%)=15 THEN ER%=1:ER$="Free of free memory: "+STR$(AY%):RETURN
IF U6%=14 THEN GOTO RELEASE_REFERENCE
IF Z%(AY%,0)<15 THEN ER%=1:ER$="Free of freed object: "+STR$(AY%):RETURN
REM decrease reference count by one
@ -142,9 +145,9 @@ RELEASE:
IF (U6%<=5) OR (U6%=9) THEN GOTO RELEASE_SIMPLE
IF (U6%>=6) AND (U6%<=8) THEN GOTO RELEASE_SEQ
IF U6%=10 THEN GOTO RELEASE_MAL_FUNCTION
IF U6%=11 THEN GOTO RELEASE_MAL_FUNCTION
IF U6%=12 THEN GOTO RELEASE_ATOM
IF U6%=13 THEN GOTO RELEASE_ENV
IF U6%=14 THEN GOTO RELEASE_REFERENCE
IF U6%=15 THEN ER%=1:ER$="RELEASE of already freed: "+STR$(AY%):RETURN
ER%=1:ER$="RELEASE not defined for type "+STR$(U6%):RETURN
@ -509,3 +512,31 @@ MAL_FUNCTION:
Z%(R%+1,0)=P%
Z%(R%+1,1)=E%
RETURN
REM APPLY(F%, AR%) -> R%
REM restores E%
APPLY:
IF (Z%(F%,0)AND15)=9 THEN GOTO DO_APPLY_FUNCTION
IF (Z%(F%,0)AND15)=10 THEN GOTO DO_APPLY_MAL_FUNCTION
IF (Z%(F%,0)AND15)=11 THEN GOTO DO_APPLY_MAL_FUNCTION
DO_APPLY_FUNCTION:
GOSUB DO_FUNCTION
RETURN
DO_APPLY_MAL_FUNCTION:
ZL%=ZL%+1:ZZ%(ZL%)=E%: REM save the current environment
REM create new environ using env and params stored in the
REM function and bind the params to the apply arguments
EO%=Z%(F%+1,1):BI%=Z%(F%+1,0):EX%=AR%:GOSUB ENV_NEW_BINDS
A%=Z%(F%,1):E%=R%:GOSUB EVAL
AY%=E%:GOSUB RELEASE: REM release the new environment
E%=ZZ%(ZL%):ZL%=ZL%-1: REM pop/restore the saved environment
RETURN

View File

@ -32,7 +32,11 @@ MYNUM
;; Check env lookup non-fatal error
(abc 1 2 3)
; .*\'abc\' not found.*
;; Check that error aborts def!
(def! w 123)
(def! w (abc))
w
;=>123
;; Testing let*
(let* (z 9) z)