1
1
mirror of https://github.com/kanaka/mal.git synced 2024-09-20 01:57:09 +03:00

Basic: variable renaming. Save 2 kbytes.

Also, add variables.txt file with start of documenting meanings of
variables.

List of renamings/savings:

ZZ%   -> S%   : 131 bytes
ZL%   -> X    : 550 bytes
A%    -> A    : 192 bytes
E%    -> E    :  32 bytes
R%    -> R    : 381 bytes
AR%   -> AR   :  30 bytes
AY%   -> AY   :  71 bytes
AZ%   -> AZ   :  33 bytes
B%    -> B    :  47 bytes
AA%   -> AA   :  64 bytes
AB%   -> AB   :  25 bytes
F%    -> F    :  21 bytes
FF%   -> FF   :  14 bytes
ER%   -> ER   :  41 bytes
PR%   -> PR   :   7 bytes
T%    -> T    :  46 bytes
R0-9% -> R0-9 :  31 bytes
T0-9% -> T0-9 :  42 bytes
S1-4% -> S1-4 :  25 bytes
U0-9% -> U0-9 :  44 bytes
ZK%   -> ZK   :  10 bytes
ZI%   -> ZI   :  10 bytes
RC%   -> RC   :  16 bytes
K%/V% -> K/V  :  21 bytes
SD%   -> SD   :  16 bytes
ZS$   -> S$   :  40 bytes
HM%   -> H    :  10 bytes
SZ%   -> SZ   :  39 bytes
LV%   -> LV   :   9 bytes
EO%   -> O    :  18 bytes
C%    -> C    :   4 bytes
P%    -> P    :   4 bytes
This commit is contained in:
Joel Martin 2016-10-14 22:42:56 -05:00
parent 30a3d8286f
commit cc9dbd92e3
18 changed files with 2301 additions and 2253 deletions

View File

@ -1,393 +1,393 @@
REM DO_FUNCTION(F%, AR%)
REM DO_FUNCTION(F, AR)
DO_FUNCTION:
REM Get the function number
FF%=Z%(F%,1)
FF=Z%(F,1)
REM Get argument values
R%=AR%+1:GOSUB DEREF_R:AA%=R%
R%=Z%(AR%,1)+1:GOSUB DEREF_R:AB%=R%
R=AR+1:GOSUB DEREF_R:AA=R
R=Z%(AR,1)+1:GOSUB DEREF_R:AB=R
REM Switch on the function number
IF FF%>=61 THEN ER%=-1:ER$="unknown function"+STR$(FF%):RETURN
IF FF%>=53 THEN DO_53
IF FF%>=39 THEN DO_39
IF FF%>=27 THEN DO_27
IF FF%>=18 THEN DO_18
IF FF%>=11 THEN DO_11
IF FF>=61 THEN ER=-1:ER$="unknown function"+STR$(FF):RETURN
IF FF>=53 THEN DO_53
IF FF>=39 THEN DO_39
IF FF>=27 THEN DO_27
IF FF>=18 THEN DO_18
IF FF>=11 THEN DO_11
ON FF% GOTO DO_EQUAL_Q,DO_THROW,DO_NIL_Q,DO_TRUE_Q,DO_FALSE_Q,DO_STRING_Q,DO_SYMBOL,DO_SYMBOL_Q
ON FF GOTO DO_EQUAL_Q,DO_THROW,DO_NIL_Q,DO_TRUE_Q,DO_FALSE_Q,DO_STRING_Q,DO_SYMBOL,DO_SYMBOL_Q
DO_11:
ON FF%-10 GOTO DO_PR_STR,DO_STR,DO_PRN,DO_PRINTLN,DO_READ_STRING,DO_READLINE,DO_SLURP
ON FF-10 GOTO DO_PR_STR,DO_STR,DO_PRN,DO_PRINTLN,DO_READ_STRING,DO_READLINE,DO_SLURP
DO_18:
ON FF%-17 GOTO DO_LT,DO_LTE,DO_GT,DO_GTE,DO_ADD,DO_SUB,DO_MULT,DO_DIV,DO_TIME_MS
ON FF-17 GOTO DO_LT,DO_LTE,DO_GT,DO_GTE,DO_ADD,DO_SUB,DO_MULT,DO_DIV,DO_TIME_MS
DO_27:
ON FF%-26 GOTO DO_LIST,DO_LIST_Q,DO_VECTOR,DO_VECTOR_Q,DO_HASH_MAP,DO_MAP_Q
ON FF-26 GOTO DO_LIST,DO_LIST_Q,DO_VECTOR,DO_VECTOR_Q,DO_HASH_MAP,DO_MAP_Q
DO_39:
ON FF%-38 GOTO DO_SEQUENTIAL_Q,DO_CONS,DO_CONCAT,DO_NTH,DO_FIRST,DO_REST,DO_EMPTY_Q,DO_COUNT,DO_APPLY,DO_MAP
ON FF-38 GOTO DO_SEQUENTIAL_Q,DO_CONS,DO_CONCAT,DO_NTH,DO_FIRST,DO_REST,DO_EMPTY_Q,DO_COUNT,DO_APPLY,DO_MAP
DO_53:
ON FF%-52 GOTO DO_ATOM,DO_ATOM_Q,DO_DEREF,DO_RESET_BANG,DO_SWAP_BANG,DO_PR_MEMORY,DO_PR_MEMORY_SUMMARY,DO_EVAL
ON FF-52 GOTO DO_ATOM,DO_ATOM_Q,DO_DEREF,DO_RESET_BANG,DO_SWAP_BANG,DO_PR_MEMORY,DO_PR_MEMORY_SUMMARY,DO_EVAL
DO_EQUAL_Q:
A%=AA%:B%=AB%:GOSUB EQUAL_Q
R%=R%+1
A=AA:B=AB:GOSUB EQUAL_Q
R=R+1
RETURN
DO_THROW:
ER%=AA%
Z%(ER%,0)=Z%(ER%,0)+16
R%=0
ER=AA
Z%(ER,0)=Z%(ER,0)+16
R=0
RETURN
DO_NIL_Q:
R%=1
IF AA%=0 THEN R%=2
R=1
IF AA=0 THEN R=2
RETURN
DO_TRUE_Q:
R%=1
IF AA%=2 THEN R%=2
R=1
IF AA=2 THEN R=2
RETURN
DO_FALSE_Q:
R%=1
IF AA%=1 THEN R%=2
R=1
IF AA=1 THEN R=2
RETURN
DO_STRING_Q:
R%=1
IF (Z%(AA%,0)AND15)=4 THEN R%=2
R=1
IF (Z%(AA,0)AND15)=4 THEN R=2
RETURN
DO_SYMBOL:
R%=0
R=0
RETURN
DO_SYMBOL_Q:
R%=1
IF (Z%(AA%,0)AND15)=5 THEN R%=2
R=1
IF (Z%(AA,0)AND15)=5 THEN R=2
RETURN
DO_PR_STR:
AZ%=AR%:PR%=1:SE$=" ":GOSUB PR_STR_SEQ
AS$=R$:T%=4+16:GOSUB STRING
AZ=AR:PR=1:SE$=" ":GOSUB PR_STR_SEQ
AS$=R$:T=4+16:GOSUB STRING
RETURN
DO_STR:
AZ%=AR%:PR%=0:SE$="":GOSUB PR_STR_SEQ
AS$=R$:T%=4+16:GOSUB STRING
AZ=AR:PR=0:SE$="":GOSUB PR_STR_SEQ
AS$=R$:T=4+16:GOSUB STRING
RETURN
DO_PRN:
AZ%=AR%:PR%=1:SE$=" ":GOSUB PR_STR_SEQ
AZ=AR:PR=1:SE$=" ":GOSUB PR_STR_SEQ
PRINT R$
R%=0
R=0
RETURN
DO_PRINTLN:
AZ%=AR%:PR%=0:SE$=" ":GOSUB PR_STR_SEQ
AZ=AR:PR=0:SE$=" ":GOSUB PR_STR_SEQ
PRINT R$
R%=0
R=0
RETURN
DO_READ_STRING:
A$=ZS$(Z%(AA%,1))
A$=S$(Z%(AA,1))
GOSUB READ_STR
RETURN
DO_READLINE:
A$=ZS$(Z%(AA%,1)):GOSUB READLINE
IF EOF=1 THEN EOF=0:R%=0:RETURN
AS$=R$:T%=4:GOSUB STRING
Z%(R%,0)=Z%(R%,0)+16
A$=S$(Z%(AA,1)):GOSUB READLINE
IF EOF=1 THEN EOF=0:R=0:RETURN
AS$=R$:T=4:GOSUB STRING
Z%(R,0)=Z%(R,0)+16
RETURN
DO_SLURP:
R$=""
REM OPEN 1,8,2,ZS$(Z%(AA%,1))+",SEQ,R"
REM OPEN 1,8,2,ZS$(Z%(AA%,1))
OPEN 1,8,0,ZS$(Z%(AA%,1))
REM OPEN 1,8,2,S$(Z%(AA,1))+",SEQ,R"
REM OPEN 1,8,2,S$(Z%(AA,1))
OPEN 1,8,0,S$(Z%(AA,1))
DO_SLURP_LOOP:
A$=""
GET#1,A$
IF ASC(A$)=10 THEN R$=R$+CHR$(13)
IF (ASC(A$)<>10) AND (A$<>"") THEN R$=R$+A$
IF (ST AND 64) THEN GOTO DO_SLURP_DONE
IF (ST AND 255) THEN ER%=-1:ER$="File read error "+STR$(ST):RETURN
IF (ST AND 255) THEN ER=-1:ER$="File read error "+STR$(ST):RETURN
GOTO DO_SLURP_LOOP
DO_SLURP_DONE:
CLOSE 1
AS$=R$:T%=4+16:GOSUB STRING
AS$=R$:T=4+16:GOSUB STRING
RETURN
DO_LT:
R%=1
IF Z%(AA%,1)<Z%(AB%,1) THEN R%=2
R=1
IF Z%(AA,1)<Z%(AB,1) THEN R=2
RETURN
DO_LTE:
R%=1
IF Z%(AA%,1)<=Z%(AB%,1) THEN R%=2
R=1
IF Z%(AA,1)<=Z%(AB,1) THEN R=2
RETURN
DO_GT:
R%=1
IF Z%(AA%,1)>Z%(AB%,1) THEN R%=2
R=1
IF Z%(AA,1)>Z%(AB,1) THEN R=2
RETURN
DO_GTE:
R%=1
IF Z%(AA%,1)>=Z%(AB%,1) THEN R%=2
R=1
IF Z%(AA,1)>=Z%(AB,1) THEN R=2
RETURN
DO_ADD:
SZ%=1:GOSUB ALLOC
Z%(R%,0)=2+16
Z%(R%,1)=Z%(AA%,1)+Z%(AB%,1)
SZ=1:GOSUB ALLOC
Z%(R,0)=2+16
Z%(R,1)=Z%(AA,1)+Z%(AB,1)
RETURN
DO_SUB:
SZ%=1:GOSUB ALLOC
Z%(R%,0)=2+16
Z%(R%,1)=Z%(AA%,1)-Z%(AB%,1)
SZ=1:GOSUB ALLOC
Z%(R,0)=2+16
Z%(R,1)=Z%(AA,1)-Z%(AB,1)
RETURN
DO_MULT:
SZ%=1:GOSUB ALLOC
Z%(R%,0)=2+16
Z%(R%,1)=Z%(AA%,1)*Z%(AB%,1)
SZ=1:GOSUB ALLOC
Z%(R,0)=2+16
Z%(R,1)=Z%(AA,1)*Z%(AB,1)
RETURN
DO_DIV:
SZ%=1:GOSUB ALLOC
Z%(R%,0)=2+16
Z%(R%,1)=Z%(AA%,1)/Z%(AB%,1)
SZ=1:GOSUB ALLOC
Z%(R,0)=2+16
Z%(R,1)=Z%(AA,1)/Z%(AB,1)
RETURN
DO_TIME_MS:
R%=0
R=0
RETURN
DO_LIST:
R%=AR%
Z%(R%,0)=Z%(R%,0)+16
R=AR
Z%(R,0)=Z%(R,0)+16
RETURN
DO_LIST_Q:
A%=AA%:GOSUB LIST_Q
R%=R%+1: REM map to mal false/true
A=AA:GOSUB LIST_Q
R=R+1: REM map to mal false/true
RETURN
DO_VECTOR:
R%=0
R=0
RETURN
DO_VECTOR_Q:
R%=1
IF (Z%(AA%,0)AND15)=7 THEN R%=2
R=1
IF (Z%(AA,0)AND15)=7 THEN R=2
RETURN
DO_HASH_MAP:
R%=0
R=0
RETURN
DO_MAP_Q:
R%=1
IF (Z%(AA%,0)AND15)=8 THEN R%=2
R=1
IF (Z%(AA,0)AND15)=8 THEN R=2
RETURN
DO_SEQUENTIAL_Q:
R%=1
IF (Z%(AA%,0)AND15)=6 OR (Z%(AA%,0)AND15)=7 THEN R%=2
R=1
IF (Z%(AA,0)AND15)=6 OR (Z%(AA,0)AND15)=7 THEN R=2
RETURN
DO_CONS:
A%=AA%:B%=AB%:GOSUB CONS
A=AA:B=AB:GOSUB CONS
RETURN
DO_CONCAT:
REM if empty arguments, return empty list
IF Z%(AR%,1)=0 THEN R%=3:Z%(R%,0)=Z%(R%,0)+16:RETURN
IF Z%(AR,1)=0 THEN R=3:Z%(R,0)=Z%(R,0)+16:RETURN
REM single argument
IF Z%(Z%(AR%,1),1)<>0 THEN GOTO DO_CONCAT_MULT
IF Z%(Z%(AR,1),1)<>0 THEN GOTO DO_CONCAT_MULT
REM if single argument and it's a list, return it
IF (Z%(AA%,0)AND15)=6 THEN R%=AA%:Z%(R%,0)=Z%(R%,0)+16:RETURN
IF (Z%(AA,0)AND15)=6 THEN R=AA:Z%(R,0)=Z%(R,0)+16:RETURN
REM otherwise, copy first element to turn it into a list
B%=AA%+1:GOSUB DEREF_B: REM value to copy
SZ%=2:GOSUB ALLOC
Z%(R%,0)=6+16:Z%(R%,1)=Z%(AA%,1)
Z%(R%+1,0)=14:Z%(R%+1,1)=B%
B=AA+1:GOSUB DEREF_B: REM value to copy
SZ=2:GOSUB ALLOC
Z%(R,0)=6+16:Z%(R,1)=Z%(AA,1)
Z%(R+1,0)=14:Z%(R+1,1)=B
REM inc ref count of trailing list part and of copied value
Z%(Z%(AA%,1),0)=Z%(Z%(AA%,1),0)+16
Z%(B%,0)=Z%(B%,0)+16
Z%(Z%(AA,1),0)=Z%(Z%(AA,1),0)+16
Z%(B,0)=Z%(B,0)+16
RETURN
REM multiple arguments
DO_CONCAT_MULT:
CZ%=ZL%: REM save current stack position
CZ%=X: REM save current stack position
REM push arguments onto the stack
DO_CONCAT_STACK:
R%=AR%+1:GOSUB DEREF_R
ZL%=ZL%+1:ZZ%(ZL%)=R%: REM push sequence
AR%=Z%(AR%,1)
IF Z%(AR%,1)<>0 THEN GOTO DO_CONCAT_STACK
R=AR+1:GOSUB DEREF_R
X=X+1:S%(X)=R: REM push sequence
AR=Z%(AR,1)
IF Z%(AR,1)<>0 THEN GOTO DO_CONCAT_STACK
REM pop last argument as our seq to prepend to
AB%=ZZ%(ZL%):ZL%=ZL%-1
AB=S%(X):X=X-1
REM last arg/seq is not copied so we need to inc ref to it
Z%(AB%,0)=Z%(AB%,0)+16
Z%(AB,0)=Z%(AB,0)+16
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
IF X=CZ% THEN R=AB:RETURN
AA=S%(X):X=X-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)
AY%=Z%(R6%,1):GOSUB RELEASE
AY=Z%(R6,1):GOSUB RELEASE
REM attach new list element before terminator (last actual
REM element to the next sequence
Z%(R6%,1)=AB%
Z%(R6,1)=AB
AB%=R%
AB=R
GOTO DO_CONCAT_LOOP
DO_NTH:
B%=Z%(AB%,1)
A%=AA%:GOSUB COUNT
IF R%<=B% THEN R%=0:ER%=-1:ER$="nth: index out of range":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)
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
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
IF R%<>0 THEN Z%(R%,0)=Z%(R%,0)+16
IF Z%(AA,1)=0 THEN R=0
IF Z%(AA,1)<>0 THEN R=AA+1:GOSUB DEREF_R
IF R<>0 THEN Z%(R,0)=Z%(R,0)+16
RETURN
DO_REST:
IF Z%(AA%,1)=0 THEN R%=AA%
IF Z%(AA%,1)<>0 THEN R%=Z%(AA%,1)
Z%(R%,0)=Z%(R%,0)+16
IF Z%(AA,1)=0 THEN R=AA
IF Z%(AA,1)<>0 THEN R=Z%(AA,1)
Z%(R,0)=Z%(R,0)+16
RETURN
DO_EMPTY_Q:
R%=1
IF Z%(AA%,1)=0 THEN R%=2
R=1
IF Z%(AA,1)=0 THEN R=2
RETURN
DO_COUNT:
A%=AA%:GOSUB COUNT:R4%=R%
SZ%=1:GOSUB ALLOC
Z%(R%,0)=2+16
Z%(R%,1)=R4%
A=AA:GOSUB COUNT:R4=R
SZ=1:GOSUB ALLOC
Z%(R,0)=2+16
Z%(R,1)=R4
RETURN
DO_APPLY:
F%=AA%
AR%=Z%(AR%,1)
A%=AR%:GOSUB COUNT:R4%=R%
F=AA
AR=Z%(AR,1)
A=AR:GOSUB COUNT:R4=R
REM no intermediate args, just call APPLY directly
IF R4%<=1 THEN AR%=Z%(AR%+1,1):GOSUB APPLY:RETURN
IF R4<=1 THEN AR=Z%(AR+1,1):GOSUB APPLY:RETURN
REM prepend intermediate args to final args element
A%=AR%:B%=0:C%=R4%-1:GOSUB SLICE
A=AR:B=0:C=R4-1:GOSUB SLICE
REM release the terminator of new list (we skip over it)
AY%=Z%(R6%,1):GOSUB RELEASE
AY=Z%(R6,1):GOSUB RELEASE
REM attach end of slice to final args element
Z%(R6%,1)=Z%(A%+1,1)
Z%(Z%(A%+1,1),0)=Z%(Z%(A%+1,1),0)+16
Z%(R6,1)=Z%(A+1,1)
Z%(Z%(A+1,1),0)=Z%(Z%(A+1,1),0)+16
ZL%=ZL%+1:ZZ%(ZL%)=R%: REM push/save new args for release
AR%=R%:GOSUB APPLY
AY%=ZZ%(ZL%):ZL%=ZL%-1:GOSUB RELEASE: REM pop/release new args
X=X+1:S%(X)=R: REM push/save new args for release
AR=R:GOSUB APPLY
AY=S%(X):X=X-1:GOSUB RELEASE: REM pop/release new args
RETURN
DO_MAP:
F%=AA%
F=AA
REM first result list element
SZ%=2:GOSUB ALLOC
SZ=2:GOSUB ALLOC
REM push future return val, prior entry, F% and AB%
ZL%=ZL%+4:ZZ%(ZL%-3)=R%:ZZ%(ZL%-2)=0:ZZ%(ZL%-1)=F%:ZZ%(ZL%)=AB%
REM push future return val, prior entry, F and AB
X=X+4:S%(X-3)=R:S%(X-2)=0:S%(X-1)=F:S%(X)=AB
DO_MAP_LOOP:
REM set base values
Z%(R%,0)=6+16:Z%(R%,1)=0
Z%(R%+1,0)=14:Z%(R%+1,1)=0
Z%(R,0)=6+16:Z%(R,1)=0
Z%(R+1,0)=14:Z%(R+1,1)=0
REM set previous to current if not the first element
IF ZZ%(ZL%-2)<>0 THEN Z%(ZZ%(ZL%-2),1)=R%
IF S%(X-2)<>0 THEN Z%(S%(X-2),1)=R
REM update previous reference to current
ZZ%(ZL%-2)=R%
S%(X-2)=R
IF Z%(AB%,1)=0 THEN GOTO DO_MAP_DONE
IF Z%(AB,1)=0 THEN GOTO DO_MAP_DONE
REM create argument list for apply call
SZ%=2:GOSUB ALLOC
Z%(R%,0)=6+16:Z%(R%,1)=0
Z%(R%+1,0)=14:Z%(R%+1,1)=0
AR%=R%: REM save end of list temporarily
SZ%=2:GOSUB ALLOC
Z%(R%,0)=6+16:Z%(R%,1)=AR%
SZ=2:GOSUB ALLOC
Z%(R,0)=6+16:Z%(R,1)=0
Z%(R+1,0)=14:Z%(R+1,1)=0
AR=R: REM save end of list temporarily
SZ=2:GOSUB ALLOC
Z%(R,0)=6+16:Z%(R,1)=AR
REM inc ref cnt of referred argument
A%=Z%(AB%+1,1): Z%(A%,0)=Z%(A%,0)+16
Z%(R%+1,0)=14:Z%(R%+1,1)=A%
A=Z%(AB+1,1): Z%(A,0)=Z%(A,0)+16
Z%(R+1,0)=14:Z%(R+1,1)=A
REM push argument list
ZL%=ZL%+1:ZZ%(ZL%)=R%
X=X+1:S%(X)=R
AR%=R%:GOSUB APPLY
AR=R:GOSUB APPLY
REM pop apply args are release them
AY%=ZZ%(ZL%):ZL%=ZL%-1:GOSUB RELEASE
AY=S%(X):X=X-1:GOSUB RELEASE
REM set the result value
Z%(ZZ%(ZL%-2)+1,1)=R%
Z%(S%(X-2)+1,1)=R
REM restore F%
F%=ZZ%(ZL%-1)
REM restore F
F=S%(X-1)
REM update AB% to next source element
ZZ%(ZL%)=Z%(ZZ%(ZL%),1)
AB%=ZZ%(ZL%)
REM update AB to next source element
S%(X)=Z%(S%(X),1)
AB=S%(X)
REM allocate next element
SZ%=2:GOSUB ALLOC
SZ=2:GOSUB ALLOC
GOTO DO_MAP_LOOP
DO_MAP_DONE:
REM get return val
R%=ZZ%(ZL%-3)
R=S%(X-3)
REM pop everything off stack
ZL%=ZL%-4
X=X-4
RETURN
DO_ATOM:
SZ%=1:GOSUB ALLOC
Z%(AA%,0)=Z%(AA%,0)+16: REM inc ref cnt of contained value
Z%(R%,0)=12+16
Z%(R%,1)=AA%
SZ=1:GOSUB ALLOC
Z%(AA,0)=Z%(AA,0)+16: REM inc ref cnt of contained value
Z%(R,0)=12+16
Z%(R,1)=AA
RETURN
DO_ATOM_Q:
R%=1
IF (Z%(AA%,0)AND15)=12 THEN R%=2
R=1
IF (Z%(AA,0)AND15)=12 THEN R=2
RETURN
DO_DEREF:
R%=Z%(AA%,1):GOSUB DEREF_R
Z%(R%,0)=Z%(R%,0)+16
R=Z%(AA,1):GOSUB DEREF_R
Z%(R,0)=Z%(R,0)+16
RETURN
DO_RESET_BANG:
R%=AB%
R=AB
REM release current value
AY%=Z%(AA%,1):GOSUB RELEASE
AY=Z%(AA,1):GOSUB RELEASE
REM inc ref by 2 for atom ownership and since we are returning it
Z%(R%,0)=Z%(R%,0)+32
Z%(R,0)=Z%(R,0)+32
REM update value
Z%(AA%,1)=R%
Z%(AA,1)=R
RETURN
DO_SWAP_BANG:
F%=AB%
F=AB
REM add atom to front of the args list
A%=Z%(AA%,1):B%=Z%(Z%(AR%,1),1):GOSUB CONS
AR%=R%
A=Z%(AA,1):B=Z%(Z%(AR,1),1):GOSUB CONS
AR=R
REM push args for release after
ZL%=ZL%+1:ZZ%(ZL%)=AR%
X=X+1:S%(X)=AR
REM push atom
ZL%=ZL%+1:ZZ%(ZL%)=AA%
X=X+1:S%(X)=AA
GOSUB APPLY
REM pop atom
AA%=ZZ%(ZL%):ZL%=ZL%-1
AA=S%(X):X=X-1
REM pop and release args
AY%=ZZ%(ZL%):ZL%=ZL%-1:GOSUB RELEASE
AY=S%(X):X=X-1:GOSUB RELEASE
REM use reset to update the value
AB%=R%:GOSUB DO_RESET_BANG
AB=R:GOSUB DO_RESET_BANG
REM but decrease ref cnt of return by 1 (not sure why)
AY%=R%:GOSUB RELEASE
AY=R:GOSUB RELEASE
RETURN
@ -399,72 +399,72 @@ DO_FUNCTION:
RETURN
DO_EVAL:
A%=AA%:E%=RE%:GOSUB EVAL
A=AA:E=RE%:GOSUB EVAL
RETURN
INIT_CORE_SET_FUNCTION:
GOSUB NATIVE_FUNCTION
V%=R%:GOSUB ENV_SET_S
V=R:GOSUB ENV_SET_S
RETURN
REM INIT_CORE_NS(E%)
REM INIT_CORE_NS(E)
INIT_CORE_NS:
REM create the environment mapping
REM must match DO_FUNCTION mappings
K$="=":A%=1:GOSUB INIT_CORE_SET_FUNCTION
K$="throw":A%=2:GOSUB INIT_CORE_SET_FUNCTION
K$="nil?":A%=3:GOSUB INIT_CORE_SET_FUNCTION
K$="true?":A%=4:GOSUB INIT_CORE_SET_FUNCTION
K$="false?":A%=5:GOSUB INIT_CORE_SET_FUNCTION
K$="string?":A%=6:GOSUB INIT_CORE_SET_FUNCTION
K$="symbol":A%=7:GOSUB INIT_CORE_SET_FUNCTION
K$="symbol?":A%=8:GOSUB INIT_CORE_SET_FUNCTION
K$="=":A=1:GOSUB INIT_CORE_SET_FUNCTION
K$="throw":A=2:GOSUB INIT_CORE_SET_FUNCTION
K$="nil?":A=3:GOSUB INIT_CORE_SET_FUNCTION
K$="true?":A=4:GOSUB INIT_CORE_SET_FUNCTION
K$="false?":A=5:GOSUB INIT_CORE_SET_FUNCTION
K$="string?":A=6:GOSUB INIT_CORE_SET_FUNCTION
K$="symbol":A=7:GOSUB INIT_CORE_SET_FUNCTION
K$="symbol?":A=8:GOSUB INIT_CORE_SET_FUNCTION
K$="pr-str":A%=11:GOSUB INIT_CORE_SET_FUNCTION
K$="str":A%=12:GOSUB INIT_CORE_SET_FUNCTION
K$="prn":A%=13:GOSUB INIT_CORE_SET_FUNCTION
K$="println":A%=14:GOSUB INIT_CORE_SET_FUNCTION
K$="read-string":A%=15:GOSUB INIT_CORE_SET_FUNCTION
K$="readline":A%=16:GOSUB INIT_CORE_SET_FUNCTION
K$="slurp":A%=17:GOSUB INIT_CORE_SET_FUNCTION
K$="pr-str":A=11:GOSUB INIT_CORE_SET_FUNCTION
K$="str":A=12:GOSUB INIT_CORE_SET_FUNCTION
K$="prn":A=13:GOSUB INIT_CORE_SET_FUNCTION
K$="println":A=14:GOSUB INIT_CORE_SET_FUNCTION
K$="read-string":A=15:GOSUB INIT_CORE_SET_FUNCTION
K$="readline":A=16:GOSUB INIT_CORE_SET_FUNCTION
K$="slurp":A=17:GOSUB INIT_CORE_SET_FUNCTION
K$="<":A%=18:GOSUB INIT_CORE_SET_FUNCTION
K$="<=":A%=19:GOSUB INIT_CORE_SET_FUNCTION
K$=">":A%=20:GOSUB INIT_CORE_SET_FUNCTION
K$=">=":A%=21:GOSUB INIT_CORE_SET_FUNCTION
K$="+":A%=22:GOSUB INIT_CORE_SET_FUNCTION
K$="-":A%=23:GOSUB INIT_CORE_SET_FUNCTION
K$="*":A%=24:GOSUB INIT_CORE_SET_FUNCTION
K$="/":A%=25:GOSUB INIT_CORE_SET_FUNCTION
K$="time-ms":A%=26:GOSUB INIT_CORE_SET_FUNCTION
K$="<":A=18:GOSUB INIT_CORE_SET_FUNCTION
K$="<=":A=19:GOSUB INIT_CORE_SET_FUNCTION
K$=">":A=20:GOSUB INIT_CORE_SET_FUNCTION
K$=">=":A=21:GOSUB INIT_CORE_SET_FUNCTION
K$="+":A=22:GOSUB INIT_CORE_SET_FUNCTION
K$="-":A=23:GOSUB INIT_CORE_SET_FUNCTION
K$="*":A=24:GOSUB INIT_CORE_SET_FUNCTION
K$="/":A=25:GOSUB INIT_CORE_SET_FUNCTION
K$="time-ms":A=26:GOSUB INIT_CORE_SET_FUNCTION
K$="list":A%=27:GOSUB INIT_CORE_SET_FUNCTION
K$="list?":A%=28:GOSUB INIT_CORE_SET_FUNCTION
K$="vector":A%=29:GOSUB INIT_CORE_SET_FUNCTION
K$="vector?":A%=30:GOSUB INIT_CORE_SET_FUNCTION
K$="hash-map":A%=31:GOSUB INIT_CORE_SET_FUNCTION
K$="map?":A%=32:GOSUB INIT_CORE_SET_FUNCTION
K$="list":A=27:GOSUB INIT_CORE_SET_FUNCTION
K$="list?":A=28:GOSUB INIT_CORE_SET_FUNCTION
K$="vector":A=29:GOSUB INIT_CORE_SET_FUNCTION
K$="vector?":A=30:GOSUB INIT_CORE_SET_FUNCTION
K$="hash-map":A=31:GOSUB INIT_CORE_SET_FUNCTION
K$="map?":A=32:GOSUB INIT_CORE_SET_FUNCTION
K$="sequential?":A%=39:GOSUB INIT_CORE_SET_FUNCTION
K$="cons":A%=40:GOSUB INIT_CORE_SET_FUNCTION
K$="concat":A%=41:GOSUB INIT_CORE_SET_FUNCTION
K$="nth":A%=42:GOSUB INIT_CORE_SET_FUNCTION
K$="first":A%=43:GOSUB INIT_CORE_SET_FUNCTION
K$="rest":A%=44:GOSUB INIT_CORE_SET_FUNCTION
K$="empty?":A%=45:GOSUB INIT_CORE_SET_FUNCTION
K$="count":A%=46:GOSUB INIT_CORE_SET_FUNCTION
K$="apply":A%=47:GOSUB INIT_CORE_SET_FUNCTION
K$="map":A%=48:GOSUB INIT_CORE_SET_FUNCTION
K$="sequential?":A=39:GOSUB INIT_CORE_SET_FUNCTION
K$="cons":A=40:GOSUB INIT_CORE_SET_FUNCTION
K$="concat":A=41:GOSUB INIT_CORE_SET_FUNCTION
K$="nth":A=42:GOSUB INIT_CORE_SET_FUNCTION
K$="first":A=43:GOSUB INIT_CORE_SET_FUNCTION
K$="rest":A=44:GOSUB INIT_CORE_SET_FUNCTION
K$="empty?":A=45:GOSUB INIT_CORE_SET_FUNCTION
K$="count":A=46:GOSUB INIT_CORE_SET_FUNCTION
K$="apply":A=47:GOSUB INIT_CORE_SET_FUNCTION
K$="map":A=48:GOSUB INIT_CORE_SET_FUNCTION
K$="atom":A%=53:GOSUB INIT_CORE_SET_FUNCTION
K$="atom?":A%=54:GOSUB INIT_CORE_SET_FUNCTION
K$="deref":A%=55:GOSUB INIT_CORE_SET_FUNCTION
K$="reset!":A%=56:GOSUB INIT_CORE_SET_FUNCTION
K$="swap!":A%=57:GOSUB INIT_CORE_SET_FUNCTION
K$="atom":A=53:GOSUB INIT_CORE_SET_FUNCTION
K$="atom?":A=54:GOSUB INIT_CORE_SET_FUNCTION
K$="deref":A=55:GOSUB INIT_CORE_SET_FUNCTION
K$="reset!":A=56:GOSUB INIT_CORE_SET_FUNCTION
K$="swap!":A=57:GOSUB INIT_CORE_SET_FUNCTION
K$="pr-memory":A%=58:GOSUB INIT_CORE_SET_FUNCTION
K$="pr-memory-summary":A%=59:GOSUB INIT_CORE_SET_FUNCTION
K$="eval":A%=60:GOSUB INIT_CORE_SET_FUNCTION
K$="pr-memory":A=58:GOSUB INIT_CORE_SET_FUNCTION
K$="pr-memory-summary":A=59:GOSUB INIT_CORE_SET_FUNCTION
K$="eval":A=60:GOSUB INIT_CORE_SET_FUNCTION
RETURN

View File

@ -2,20 +2,20 @@ PR_MEMORY_SUMMARY:
GOSUB CHECK_FREE_LIST: REM get count in P2%
PRINT
PRINT "Free memory (FRE) : "+STR$(FRE(0))
PRINT "Value memory (Z%) : "+STR$(ZI%-1)+" /"+STR$(S1%)
PRINT "Value memory (Z%) : "+STR$(ZI-1)+" /"+STR$(Z1)
PRINT " ";
PRINT " used:"+STR$(ZI%-1-P2%)+", freed:"+STR$(P2%);
PRINT " used:"+STR$(ZI-1-P2%)+", freed:"+STR$(P2%);
PRINT ", post repl_env:"+STR$(ZT%)
PRINT "String values (ZS$) : "+STR$(ZJ%)+" /"+STR$(S2%)
PRINT "Call stack size (ZZ%) : "+STR$(ZL%+1)+" /"+STR$(S3%)
PRINT "String values (S$) : "+STR$(ZJ)+" /"+STR$(Z2)
PRINT "Call stack size (S%) : "+STR$(X+1)+" /"+STR$(Z3)
RETURN
REM REM PR_MEMORY(P1%, P2%) -> nil
REM PR_MEMORY:
REM IF P2%<P1% THEN P2%=ZI%-1
REM IF P2%<P1% THEN P2%=ZI-1
REM PRINT "vvvvvv"
REM PRINT "Z% Value Memory"+STR$(P1%)+"->"+STR$(P2%);
REM PRINT " (ZI%: "+STR$(ZI%)+", ZK%: "+STR$(ZK%)+"):"
REM PRINT " (ZI: "+STR$(ZI)+", ZK: "+STR$(ZK)+"):"
REM IF P2%<P1% THEN PRINT " ---":GOTO PR_MEMORY_AFTER_VALUES
REM I=P1%
REM PR_MEMORY_VALUE_LOOP:
@ -24,8 +24,8 @@ REM PRINT " "+STR$(I);
REM IF (Z%(I,0)AND15)=15 THEN GOTO PR_MEMORY_FREE
REM PRINT ": ref cnt: "+STR$((Z%(I,0)AND-16)/16);
REM PRINT ", type: "+STR$(Z%(I,0)AND15)+", value: "+STR$(Z%(I,1));
REM IF (Z%(I,0)AND15)=4 THEN PRINT " '"+ZS$(Z%(I,1))+"'";
REM IF (Z%(I,0)AND15)=5 THEN PRINT " "+ZS$(Z%(I,1))+"";
REM IF (Z%(I,0)AND15)=4 THEN PRINT " '"+S$(Z%(I,1))+"'";
REM IF (Z%(I,0)AND15)=5 THEN PRINT " "+S$(Z%(I,1))+"";
REM PRINT
REM I=I+1
REM IF (Z%(I-1,0)AND15)<>10 THEN GOTO PR_MEMORY_VALUE_LOOP
@ -35,22 +35,22 @@ REM I=I+1
REM GOTO PR_MEMORY_VALUE_LOOP
REM PR_MEMORY_FREE:
REM PRINT ": FREE size: "+STR$((Z%(I,0)AND-16)/16)+", next: "+STR$(Z%(I,1));
REM IF I=ZK% THEN PRINT " (free list start)";
REM IF I=ZK THEN PRINT " (free list start)";
REM PRINT
REM IF (Z%(I,0)AND-16)=32 THEN I=I+1:PRINT " "+STR$(I)+": ---"
REM I=I+1
REM GOTO PR_MEMORY_VALUE_LOOP
REM PR_MEMORY_AFTER_VALUES:
REM PRINT "ZS% String Memory (ZJ%: "+STR$(ZJ%)+"):"
REM IF ZJ%<=0 THEN PRINT " ---":GOTO PR_MEMORY_SKIP_STRINGS
REM FOR I=0 TO ZJ%-1
REM PRINT " "+STR$(I)+": '"+ZS$(I)+"'"
REM PRINT "ZS% String Memory (ZJ: "+STR$(ZJ)+"):"
REM IF ZJ<=0 THEN PRINT " ---":GOTO PR_MEMORY_SKIP_STRINGS
REM FOR I=0 TO ZJ-1
REM PRINT " "+STR$(I)+": '"+S$(I)+"'"
REM NEXT I
REM PR_MEMORY_SKIP_STRINGS:
REM PRINT "ZZ% Stack Memory (ZL%: "+STR$(ZL%)+"):"
REM IF ZL%<0 THEN PRINT " ---":GOTO PR_MEMORY_SKIP_STACK
REM FOR I=0 TO ZL%
REM PRINT " "+STR$(I)+": "+STR$(ZZ%(I))
REM PRINT "S% Stack Memory (X: "+STR$(X)+"):"
REM IF X<0 THEN PRINT " ---":GOTO PR_MEMORY_SKIP_STACK
REM FOR I=0 TO X
REM PRINT " "+STR$(I)+": "+STR$(S%(I))
REM NEXT I
REM PR_MEMORY_SKIP_STACK:
REM PRINT "^^^^^^"
@ -60,20 +60,20 @@ REM REM PR_OBJECT(P1%) -> nil
REM PR_OBJECT:
REM RD%=0
REM
REM RD%=RD%+1:ZL%=ZL%+1:ZZ%(ZL%)=P1%
REM RD%=RD%+1:X=X+1:S%(X)=P1%
REM
REM PR_OBJ_LOOP:
REM IF RD%=0 THEN RETURN
REM I=ZZ%(ZL%):RD%=RD%-1:ZL%=ZL%-1
REM I=S%(X):RD%=RD%-1:X=X-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 IF P2%=4 THEN PRINT " '"+S$(Z%(I,1))+"'";
REM IF P2%=5 THEN PRINT " "+S$(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 IF Z%(I,1)<>0 THEN RD%=RD%+1:X=X+1:S%(X)=Z%(I,1)
REM IF P2%>=6 AND P2%<=8 THEN RD%=RD%+1:X=X+1:S%(X)=I+1
REM GOTO PR_OBJ_LOOP

View File

@ -1,38 +1,38 @@
REM ENV_NEW(EO%) -> R%
REM ENV_NEW(O) -> R
ENV_NEW:
REM allocate the data hashmap
GOSUB HASHMAP
ET%=R%
ET%=R
REM set the outer and data pointer
SZ%=2:GOSUB ALLOC
Z%(R%,0)=13+16
Z%(R%,1)=ET%
Z%(R%+1,0)=13
Z%(R%+1,1)=EO%
IF EO%<>-1 THEN Z%(EO%,0)=Z%(EO%,0)+16
SZ=2:GOSUB ALLOC
Z%(R,0)=13+16
Z%(R,1)=ET%
Z%(R+1,0)=13
Z%(R+1,1)=O
IF O<>-1 THEN Z%(O,0)=Z%(O,0)+16
RETURN
REM see RELEASE types.in.bas for environment cleanup
REM ENV_NEW_BINDS(EO%, BI%, EX%) -> R%
REM ENV_NEW_BINDS(O, BI%, EX%) -> R
ENV_NEW_BINDS:
GOSUB ENV_NEW
E%=R%
E=R
REM process bindings
ENV_NEW_BINDS_LOOP:
IF Z%(BI%,1)=0 THEN R%=E%:RETURN
IF Z%(BI%,1)=0 THEN R=E:RETURN
REM get/deref the key from BI%
R%=BI%+1:GOSUB DEREF_R
K%=R%
R=BI%+1:GOSUB DEREF_R
K=R
IF ZS$(Z%(K%,1))="&" THEN GOTO EVAL_NEW_BINDS_VARGS
IF S$(Z%(K,1))="&" THEN GOTO EVAL_NEW_BINDS_VARGS
EVAL_NEW_BINDS_1x1:
REM get/deref the key from EX%
R%=EX%+1:GOSUB DEREF_R
V%=R%
R=EX%+1:GOSUB DEREF_R
V=R
REM set the binding in the environment data
GOSUB ENV_SET
REM go to next element of BI% and EX%
@ -43,52 +43,52 @@ ENV_NEW_BINDS:
EVAL_NEW_BINDS_VARGS:
REM get/deref the key from next element of BI%
BI%=Z%(BI%,1)
R%=BI%+1:GOSUB DEREF_R
K%=R%
R=BI%+1:GOSUB DEREF_R
K=R
REM the value is the remaining list in EX%
V%=EX%
V=EX%
REM set the binding in the environment data
GOSUB ENV_SET
R%=E%
R=E
RETURN
REM ENV_SET(E%, K%, V%) -> R%
REM ENV_SET(E, K, V) -> R
ENV_SET:
HM%=Z%(E%,1)
H=Z%(E,1)
GOSUB ASSOC1
Z%(E%,1)=R%
R%=V%
Z%(E,1)=R
R=V
RETURN
REM ENV_SET_S(E%, K$, V%) -> R%
REM ENV_SET_S(E, K$, V) -> R
ENV_SET_S:
HM%=Z%(E%,1)
H=Z%(E,1)
GOSUB ASSOC1_S
Z%(E%,1)=R%
R%=V%
Z%(E,1)=R
R=V
RETURN
REM ENV_FIND(E%, K%) -> R%
REM Returns environment (R%) containing K%. If found, value found is
REM in T4%
REM ENV_FIND(E, K) -> R
REM Returns environment (R) containing K. If found, value found is
REM in T4
ENV_FIND:
EF%=E%
EF%=E
ENV_FIND_LOOP:
HM%=Z%(EF%,1)
REM More efficient to use GET for value (R%) and contains? (T3%)
H=Z%(EF%,1)
REM More efficient to use GET for value (R) and contains? (T3)
GOSUB HASHMAP_GET
REM if we found it, save value in T4% for ENV_GET
IF T3%=1 THEN T4%=R%:GOTO ENV_FIND_DONE
REM if we found it, save value in T4 for ENV_GET
IF T3=1 THEN T4=R:GOTO ENV_FIND_DONE
EF%=Z%(EF%+1,1): REM get outer environment
IF EF%<>-1 THEN GOTO ENV_FIND_LOOP
ENV_FIND_DONE:
R%=EF%
R=EF%
RETURN
REM ENV_GET(E%, K%) -> R%
REM ENV_GET(E, K) -> R
ENV_GET:
GOSUB ENV_FIND
IF R%=-1 THEN R%=0:ER%=-1:ER$="'"+ZS$(Z%(K%,1))+"' not found":RETURN
R%=T4%:GOSUB DEREF_R
Z%(R%,0)=Z%(R%,0)+16
IF R=-1 THEN R=0:ER=-1:ER$="'"+S$(Z%(K,1))+"' not found":RETURN
R=T4:GOSUB DEREF_R
Z%(R,0)=Z%(R,0)+16
RETURN

View File

@ -1,105 +1,105 @@
REM PR_STR(AZ%, PR%) -> R$
REM PR_STR(AZ, PR) -> R$
PR_STR:
RR$=""
PR_STR_RECUR:
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_MAL_FUNCTION,PR_ATOM,PR_ENV,PR_RECUR,PR_FREE
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_MAL_FUNCTION,PR_ATOM,PR_ENV,PR_RECUR,PR_FREE
PR_UNKNOWN:
R$="#<unknown>"
RETURN
PR_RECUR:
AZ%=Z%(AZ%,1)
AZ=Z%(AZ,1)
GOTO PR_STR_RECUR
PR_BOOLEAN:
R$="true"
IF Z%(AZ%,1)=0 THEN R$="false"
IF Z%(AZ,1)=0 THEN R$="false"
RETURN
PR_INTEGER:
T5%=Z%(AZ%,1)
R$=STR$(T5%)
IF T5%<0 THEN RETURN
T5=Z%(AZ,1)
R$=STR$(T5)
IF T5<0 THEN RETURN
REM Remove initial space
R$=RIGHT$(R$, LEN(R$)-1)
RETURN
PR_STRING:
IF PR%=1 THEN PR_STRING_READABLY
R$=ZS$(Z%(AZ%,1))
IF PR=1 THEN PR_STRING_READABLY
R$=S$(Z%(AZ,1))
RETURN
PR_STRING_READABLY:
R$=ZS$(Z%(AZ%,1))
R$=S$(Z%(AZ,1))
S1$=CHR$(92):S2$=CHR$(92)+CHR$(92):GOSUB REPLACE: REM escape backslash
S1$=CHR$(34):S2$=CHR$(92)+CHR$(34):GOSUB REPLACE: REM escape quotes
S1$=CHR$(13):S2$=CHR$(92)+"n":GOSUB REPLACE: REM escape newlines
R$=CHR$(34)+R$+CHR$(34)
RETURN
PR_SYMBOL:
R$=ZS$(Z%(AZ%,1))
R$=S$(Z%(AZ,1))
RETURN
PR_SEQ:
IF T%=6 THEN RR$=RR$+"("
IF T%=7 THEN RR$=RR$+"["
IF T%=8 THEN RR$=RR$+"{"
IF T=6 THEN RR$=RR$+"("
IF T=7 THEN RR$=RR$+"["
IF T=8 THEN RR$=RR$+"{"
REM push the type and where we are in the sequence
ZL%=ZL%+2
ZZ%(ZL%-1)=T%
ZZ%(ZL%)=AZ%
X=X+2
S%(X-1)=T
S%(X)=AZ
PR_SEQ_LOOP:
IF Z%(AZ%,1)=0 THEN PR_SEQ_DONE
AZ%=AZ%+1
IF Z%(AZ,1)=0 THEN PR_SEQ_DONE
AZ=AZ+1
GOSUB PR_STR_RECUR
REM if we just rendered a non-sequence, then append it
IF T%<6 OR T%>8 THEN RR$=RR$+R$
IF T<6 OR T>8 THEN RR$=RR$+R$
REM restore current seq type
T%=ZZ%(ZL%-1)
T=S%(X-1)
REM Go to next list element
AZ%=Z%(ZZ%(ZL%),1)
ZZ%(ZL%)=AZ%
IF Z%(AZ%,1)<>0 THEN RR$=RR$+" "
AZ=Z%(S%(X),1)
S%(X)=AZ
IF Z%(AZ,1)<>0 THEN RR$=RR$+" "
GOTO PR_SEQ_LOOP
PR_SEQ_DONE:
REM get type
T%=ZZ%(ZL%-1)
T=S%(X-1)
REM pop where we are the sequence and type
ZL%=ZL%-2
IF T%=6 THEN RR$=RR$+")"
IF T%=7 THEN RR$=RR$+"]"
IF T%=8 THEN RR$=RR$+"}"
X=X-2
IF T=6 THEN RR$=RR$+")"
IF T=7 THEN RR$=RR$+"]"
IF T=8 THEN RR$=RR$+"}"
R$=RR$
RETURN
PR_FUNCTION:
T1%=Z%(AZ%,1)
R$="#<function"+STR$(T1%)+">"
T1=Z%(AZ,1)
R$="#<function"+STR$(T1)+">"
RETURN
PR_MAL_FUNCTION:
T1%=AZ%
AZ%=Z%(T1%+1,0):GOSUB PR_STR_RECUR
T1=AZ
AZ=Z%(T1+1,0):GOSUB PR_STR_RECUR
T7$="(fn* "+R$
AZ%=Z%(T1%,1):GOSUB PR_STR_RECUR
AZ=Z%(T1,1):GOSUB PR_STR_RECUR
R$=T7$+" "+R$+")"
RETURN
PR_ATOM:
AZ%=Z%(AZ%,1):GOSUB PR_STR_RECUR
AZ=Z%(AZ,1):GOSUB PR_STR_RECUR
R$="(atom "+R$+")"
RETURN
PR_ENV:
R$="#<env"+STR$(AZ%)+", data"+STR$(Z%(AZ%,1))+">"
R$="#<env"+STR$(AZ)+", data"+STR$(Z%(AZ,1))+">"
RETURN
PR_FREE:
R$="#<free memory "+STR$(AZ%)+", next"+STR$(Z%(AZ%,1))+">"
R$="#<free memory "+STR$(AZ)+", next"+STR$(Z%(AZ,1))+">"
RETURN
REM PR_STR_SEQ(AZ%, PR%, SE$) -> R$
REM PR_STR_SEQ(AZ, PR, SE$) -> R$
PR_STR_SEQ:
T9%=AZ%
T9=AZ
R1$=""
PR_STR_SEQ_LOOP:
IF Z%(T9%,1)=0 THEN R$=R1$:RETURN
AZ%=T9%+1:GOSUB PR_STR
IF Z%(T9,1)=0 THEN R$=R1$:RETURN
AZ=T9+1:GOSUB PR_STR
REM goto the next sequence element
T9%=Z%(T9%,1)
IF Z%(T9%,1)=0 THEN R1$=R1$+R$
IF Z%(T9%,1)<>0 THEN R1$=R1$+R$+SE$
T9=Z%(T9,1)
IF Z%(T9,1)=0 THEN R1$=R1$+R$
IF Z%(T9,1)<>0 THEN R1$=R1$+R$+SE$
GOTO PR_STR_SEQ_LOOP

View File

@ -33,20 +33,20 @@ SKIP_SPACES:
READ_ATOM:
R%=0
R=0
RETURN
REM READ_FORM(A$, IDX%) -> R%
REM READ_FORM(A$, IDX%) -> R
READ_FORM:
IF ER%<>-2 THEN RETURN
IF ER<>-2 THEN RETURN
GOSUB SKIP_SPACES
GOSUB READ_TOKEN
IF T$="" AND SD%>0 THEN ER$="unexpected EOF":GOTO READ_FORM_ABORT
IF T$="" AND SD>0 THEN ER$="unexpected EOF":GOTO READ_FORM_ABORT
REM PRINT "READ_FORM T$: ["+T$+"]"
IF T$="" THEN R%=0:GOTO READ_FORM_DONE
IF T$="nil" THEN T%=0:GOTO READ_NIL_BOOL
IF T$="false" THEN T%=1:GOTO READ_NIL_BOOL
IF T$="true" THEN T%=2:GOTO READ_NIL_BOOL
IF T$="" THEN R=0:GOTO READ_FORM_DONE
IF T$="nil" THEN T=0:GOTO READ_NIL_BOOL
IF T$="false" THEN T=1:GOTO READ_NIL_BOOL
IF T$="true" THEN T=2:GOTO READ_NIL_BOOL
IF T$="'" THEN AS$="quote":GOTO READ_MACRO
IF T$="`" THEN AS$="quasiquote":GOTO READ_MACRO
IF T$="~" THEN AS$="unquote":GOTO READ_MACRO
@ -54,17 +54,17 @@ READ_FORM:
IF T$="@" THEN AS$="deref":GOTO READ_MACRO
CH$=MID$(T$,1,1)
REM PRINT "CH$: ["+CH$+"]("+STR$(ASC(CH$))+")"
IF (CH$=";") THEN R%=0:GOTO READ_TO_EOL
IF (CH$=";") THEN R=0:GOTO READ_TO_EOL
IF CH$>="0" AND CH$<="9" THEN GOTO READ_NUMBER
IF CH$="-" THEN GOTO READ_SYMBOL_MAYBE
IF CH$=CHR$(34) THEN GOTO READ_STRING
IF CH$="(" THEN T%=6:GOTO READ_SEQ
IF CH$=")" THEN T%=6:GOTO READ_SEQ_END
IF CH$="[" THEN T%=7:GOTO READ_SEQ
IF CH$="]" THEN T%=7:GOTO READ_SEQ_END
IF CH$="{" THEN T%=8:GOTO READ_SEQ
IF CH$="}" THEN T%=8:GOTO READ_SEQ_END
IF CH$="(" THEN T=6:GOTO READ_SEQ
IF CH$=")" THEN T=6:GOTO READ_SEQ_END
IF CH$="[" THEN T=7:GOTO READ_SEQ
IF CH$="]" THEN T=7:GOTO READ_SEQ_END
IF CH$="{" THEN T=8:GOTO READ_SEQ
IF CH$="}" THEN T=8:GOTO READ_SEQ_END
GOTO READ_SYMBOL
READ_TO_EOL:
@ -74,27 +74,27 @@ READ_FORM:
GOTO READ_TO_EOL
READ_NIL_BOOL:
REM PRINT "READ_NIL_BOOL"
R%=T%
Z%(R%,0)=Z%(R%,0)+16
R=T
Z%(R,0)=Z%(R,0)+16
GOTO READ_FORM_DONE
READ_NUMBER:
REM PRINT "READ_NUMBER"
SZ%=1:GOSUB ALLOC
Z%(R%,0)=2+16
Z%(R%,1)=VAL(T$)
SZ=1:GOSUB ALLOC
Z%(R,0)=2+16
Z%(R,1)=VAL(T$)
GOTO READ_FORM_DONE
READ_MACRO:
IDX%=IDX%+LEN(T$)
T%=5:GOSUB STRING: REM AS$ set above
T=5:GOSUB STRING: REM AS$ set above
REM to call READ_FORM recursively, SD% needs to be saved, set to
REM to call READ_FORM recursively, SD needs to be saved, set to
REM 0 for the call and then restored afterwards.
ZL%=ZL%+2:ZZ%(ZL%-1)=SD%:ZZ%(ZL%)=R%: REM push SD% and symbol
SD%=0:GOSUB READ_FORM:B1%=R%
SD%=ZZ%(ZL%-1):B2%=ZZ%(ZL%):ZL%=ZL%-2: REM pop SD%, pop symbol into B2%
X=X+2:S%(X-1)=SD:S%(X)=R: REM push SD and symbol
SD=0:GOSUB READ_FORM:B1%=R
SD=S%(X-1):B2%=S%(X):X=X-2: REM pop SD, pop symbol into B2%
GOSUB LIST2
AY%=B1%:GOSUB RELEASE: REM release value, list has ownership
AY=B1%:GOSUB RELEASE: REM release value, list has ownership
T$=""
GOTO READ_FORM_DONE
@ -107,102 +107,102 @@ READ_FORM:
S1$=CHR$(92)+"n":S2$=CHR$(13):GOSUB REPLACE: REM unescape newlines
S1$=CHR$(92)+CHR$(92):S2$=CHR$(92):GOSUB REPLACE: REM unescape backslashes
REM intern string value
AS$=R$:T%=4+16:GOSUB STRING
AS$=R$:T=4+16:GOSUB STRING
GOTO READ_FORM_DONE
READ_SYMBOL_MAYBE:
CH$=MID$(T$,2,1)
IF CH$>="0" AND CH$<="9" THEN GOTO READ_NUMBER
READ_SYMBOL:
REM PRINT "READ_SYMBOL"
AS$=T$:T%=5+16:GOSUB STRING
AS$=T$:T=5+16:GOSUB STRING
GOTO READ_FORM_DONE
READ_SEQ:
REM PRINT "READ_SEQ"
SD%=SD%+1: REM increase read sequence depth
SD=SD+1: REM increase read sequence depth
REM allocate first sequence entry and space for value
SZ%=2:GOSUB ALLOC
SZ=2:GOSUB ALLOC
REM set reference value/pointer to new embedded sequence
IF SD%>1 THEN Z%(ZZ%(ZL%)+1,1)=R%
IF SD>1 THEN Z%(S%(X)+1,1)=R
REM set the type (with 1 ref cnt) and next pointer to current end
Z%(R%,0)=T%+16
Z%(R%,1)=0
Z%(R%+1,0)=14
Z%(R%+1,1)=0
Z%(R,0)=T+16
Z%(R,1)=0
Z%(R+1,0)=14
Z%(R+1,1)=0
REM push start ptr on the stack
ZL%=ZL%+1
ZZ%(ZL%)=R%
X=X+1
S%(X)=R
REM push current sequence type
ZL%=ZL%+1
ZZ%(ZL%)=T%
X=X+1
S%(X)=T
REM push previous ptr on the stack
ZL%=ZL%+1
ZZ%(ZL%)=R%
X=X+1
S%(X)=R
IDX%=IDX%+LEN(T$)
GOTO READ_FORM
READ_SEQ_END:
REM PRINT "READ_SEQ_END"
IF SD%=0 THEN ER$="unexpected '"+CH$+"'":GOTO READ_FORM_ABORT
IF ZZ%(ZL%-1)<>T% THEN ER$="sequence mismatch":GOTO READ_FORM_ABORT
SD%=SD%-1: REM decrease read sequence depth
R%=ZZ%(ZL%-2): REM ptr to start of sequence to return
T%=ZZ%(ZL%-1): REM type prior to recur
ZL%=ZL%-3: REM pop previous, type, and start off the stack
IF SD=0 THEN ER$="unexpected '"+CH$+"'":GOTO READ_FORM_ABORT
IF S%(X-1)<>T THEN ER$="sequence mismatch":GOTO READ_FORM_ABORT
SD=SD-1: REM decrease read sequence depth
R=S%(X-2): REM ptr to start of sequence to return
T=S%(X-1): REM type prior to recur
X=X-3: REM pop previous, type, and start off the stack
GOTO READ_FORM_DONE
READ_FORM_DONE:
IDX%=IDX%+LEN(T$)
T8%=R%: REM save previous value
T8=R: REM save previous value
REM check read sequence depth
IF SD%=0 THEN RETURN
IF SD=0 THEN RETURN
REM PRINT "READ_FORM_DONE next list entry"
REM allocate new sequence entry and space for value
SZ%=2:GOSUB ALLOC
SZ=2:GOSUB ALLOC
REM previous element
T7%=ZZ%(ZL%)
T7=S%(X)
REM set previous list element to point to new element
Z%(T7%,1)=R%
Z%(T7,1)=R
REM set the list value pointer
Z%(T7%+1,1)=T8%
Z%(T7+1,1)=T8
REM set type to previous type, with ref count of 1 (from previous)
Z%(R%,0)=ZZ%(ZL%-1)+16
Z%(R%,1)=0: REM current end of sequence
Z%(R%+1,0)=14
Z%(R%+1,1)=0
Z%(R,0)=S%(X-1)+16
Z%(R,1)=0: REM current end of sequence
Z%(R+1,0)=14
Z%(R+1,1)=0
IF T7%=ZZ%(ZL%-2) THEN GOTO READ_FORM_SKIP_FIRST
Z%(T7%,1)=R%
IF T7=S%(X-2) THEN GOTO READ_FORM_SKIP_FIRST
Z%(T7,1)=R
READ_FORM_SKIP_FIRST:
REM update previous pointer to current element
ZZ%(ZL%)=R%
S%(X)=R
GOTO READ_FORM
READ_FORM_ABORT:
ER%=-1
R%=0
ER=-1
R=0
READ_FORM_ABORT_UNWIND:
IF SD%=0 THEN RETURN
ZL%=ZL%-3: REM pop previous, type, and start off the stack
SD%=SD%-1
IF SD%=0 THEN AY%=ZZ%(ZL%+1):GOSUB RELEASE
IF SD=0 THEN RETURN
X=X-3: REM pop previous, type, and start off the stack
SD=SD-1
IF SD=0 THEN AY=S%(X+1):GOSUB RELEASE
GOTO READ_FORM_ABORT_UNWIND
REM READ_STR(A$) -> R%
REM READ_STR(A$) -> R
READ_STR:
IDX%=1
SD%=0: REM sequence read depth
SD=0: REM sequence read depth
GOSUB READ_FORM
RETURN

View File

@ -7,7 +7,7 @@ MAL_READ:
R$=A$
RETURN
REM EVAL(A$, E%) -> R$
REM EVAL(A$, E) -> R$
EVAL:
R$=A$
RETURN
@ -20,8 +20,8 @@ MAL_PRINT:
REM REP(A$) -> R$
REP:
GOSUB MAL_READ
A%=R%:GOSUB EVAL
A%=R%:GOSUB MAL_PRINT
A=R:GOSUB EVAL
A=R:GOSUB MAL_PRINT
RETURN
REM MAIN program

View File

@ -7,35 +7,35 @@ REM $INCLUDE: 'printer.in.bas'
REM $INCLUDE: 'debug.in.bas'
REM READ(A$) -> R%
REM READ(A$) -> R
MAL_READ:
GOSUB READ_STR
RETURN
REM EVAL(A%, E%) -> R%
REM EVAL(A, E) -> R
EVAL:
R%=A%
R=A
RETURN
REM PRINT(A%) -> R$
REM PRINT(A) -> R$
MAL_PRINT:
AZ%=A%:PR%=1:GOSUB PR_STR
AZ=A:PR=1:GOSUB PR_STR
RETURN
REM REP(A$) -> R$
REP:
GOSUB MAL_READ
IF ER%<>-2 THEN GOTO REP_DONE
IF ER<>-2 THEN GOTO REP_DONE
A%=R%:GOSUB EVAL
IF ER%<>-2 THEN GOTO REP_DONE
A=R:GOSUB EVAL
IF ER<>-2 THEN GOTO REP_DONE
A%=R%:GOSUB MAL_PRINT
A=R:GOSUB MAL_PRINT
RT$=R$
REP_DONE:
REM Release memory from EVAL
AY%=R%:GOSUB RELEASE
AY=R:GOSUB RELEASE
R$=RT$
RETURN
@ -43,7 +43,7 @@ REM MAIN program
MAIN:
GOSUB INIT_MEMORY
ZT%=ZI%: REM top of memory after base repl_env
ZT%=ZI: REM top of memory after base repl_env
REPL_LOOP:
A$="user> ":GOSUB READLINE: REM call input parser
@ -51,7 +51,7 @@ MAIN:
A$=R$:GOSUB REP: REM call REP
IF ER%<>-2 THEN GOSUB PRINT_ERROR:GOTO REPL_LOOP
IF ER<>-2 THEN GOSUB PRINT_ERROR:GOTO REPL_LOOP
PRINT R$
GOTO REPL_LOOP
@ -62,6 +62,6 @@ MAIN:
PRINT_ERROR:
PRINT "Error: "+ER$
ER%=-2:ER$=""
ER=-2:ER$=""
RETURN

View File

@ -7,233 +7,233 @@ REM $INCLUDE: 'printer.in.bas'
REM $INCLUDE: 'debug.in.bas'
REM READ(A$) -> R%
REM READ(A$) -> R
MAL_READ:
GOSUB READ_STR
RETURN
REM EVAL_AST(A%, E%) -> R%
REM EVAL_AST(A, E) -> R
EVAL_AST:
LV%=LV%+1
LV=LV+1
REM push A% and E% on the stack
ZL%=ZL%+2:ZZ%(ZL%-1)=E%:ZZ%(ZL%)=A%
REM push A and E on the stack
X=X+2:S%(X-1)=E:S%(X)=A
IF ER%<>-2 THEN GOTO EVAL_AST_RETURN
IF ER<>-2 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
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
R=A:GOSUB DEREF_R
Z%(R,0)=Z%(R,0)+16
GOTO EVAL_AST_RETURN
EVAL_AST_SYMBOL:
HM%=E%:K%=A%:GOSUB HASHMAP_GET
H=E:K=A:GOSUB HASHMAP_GET
GOSUB DEREF_R
IF T3%=0 THEN ER%=-1:ER$="'"+ZS$(Z%(A%,1))+"' not found":GOTO EVAL_AST_RETURN
Z%(R%,0)=Z%(R%,0)+16
IF T3=0 THEN ER=-1:ER$="'"+S$(Z%(A,1))+"' not found":GOTO EVAL_AST_RETURN
Z%(R,0)=Z%(R,0)+16
GOTO EVAL_AST_RETURN
EVAL_AST_SEQ:
REM allocate the first entry
SZ%=2:GOSUB ALLOC
SZ=2:GOSUB ALLOC
REM make space on the stack
ZL%=ZL%+4
X=X+4
REM push type of sequence
ZZ%(ZL%-3)=T%
S%(X-3)=T
REM push sequence index
ZZ%(ZL%-2)=-1
S%(X-2)=-1
REM push future return value (new sequence)
ZZ%(ZL%-1)=R%
S%(X-1)=R
REM push previous new sequence entry
ZZ%(ZL%)=R%
S%(X)=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
Z%(R,0)=S%(X-3)+16
Z%(R,1)=0
REM create value ptr placeholder
Z%(R%+1,0)=14
Z%(R%+1,1)=0
Z%(R+1,0)=14
Z%(R+1,1)=0
REM update index
ZZ%(ZL%-2)=ZZ%(ZL%-2)+1
S%(X-2)=S%(X-2)+1
REM check if we are done evaluating the source sequence
IF Z%(A%,1)=0 THEN GOTO EVAL_AST_SEQ_LOOP_DONE
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
IF (S%(X-3)=8) AND ((S%(X-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
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
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%
Z%(S%(X)+1,1)=R
IF ER%<>-2 THEN GOTO EVAL_AST_SEQ_LOOP_DONE
IF ER<>-2 THEN GOTO EVAL_AST_SEQ_LOOP_DONE
REM allocate the next entry
SZ%=2:GOSUB ALLOC
SZ=2:GOSUB ALLOC
REM update previous sequence entry value to point to new entry
Z%(ZZ%(ZL%),1)=R%
Z%(S%(X),1)=R
REM update previous ptr to current entry
ZZ%(ZL%)=R%
S%(X)=R
REM process the next sequence entry from source list
A%=Z%(A%,1)
A=Z%(A,1)
GOTO EVAL_AST_SEQ_LOOP
EVAL_AST_SEQ_LOOP_DONE:
REM get return value (new seq), index, and seq type
R%=ZZ%(ZL%-1)
R=S%(X-1)
REM pop previous, return, index and type
ZL%=ZL%-4
X=X-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 A and E off the stack
E=S%(X-1):A=S%(X):X=X-2
LV%=LV%-1
LV=LV-1
RETURN
REM EVAL(A%, E%)) -> R%
REM EVAL(A, E)) -> R
EVAL:
LV%=LV%+1: REM track basic return stack level
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%
REM push A and E on the stack
X=X+2:S%(X-1)=E:S%(X)=A
REM AZ%=A%:PR%=1:GOSUB PR_STR
REM PRINT "EVAL: "+R$+" [A%:"+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
GOSUB LIST_Q
IF R% THEN GOTO APPLY_LIST
IF R THEN GOTO APPLY_LIST
REM ELSE
GOSUB EVAL_AST
GOTO EVAL_RETURN
APPLY_LIST:
GOSUB EMPTY_Q
IF R% THEN R%=A%:Z%(R%,0)=Z%(R%,0)+16:GOTO EVAL_RETURN
IF R THEN R=A:Z%(R,0)=Z%(R,0)+16:GOTO EVAL_RETURN
EVAL_INVOKE:
GOSUB EVAL_AST
R3%=R%
R3=R
REM if error, return f/args for release by caller
IF ER%<>-2 THEN GOTO EVAL_RETURN
F%=R%+1
IF ER<>-2 THEN GOTO EVAL_RETURN
F=R+1
AR%=Z%(R%,1): REM rest
R%=F%:GOSUB DEREF_R:F%=R%
IF (Z%(F%,0)AND15)<>9 THEN ER%=-1:ER$="apply of non-function":GOTO EVAL_RETURN
AR=Z%(R,1): REM rest
R=F:GOSUB DEREF_R:F=R
IF (Z%(F,0)AND15)<>9 THEN ER=-1:ER$="apply of non-function":GOTO EVAL_RETURN
GOSUB DO_FUNCTION
AY%=R3%:GOSUB RELEASE
AY=R3:GOSUB RELEASE
GOTO EVAL_RETURN
EVAL_RETURN:
LV%=LV%-1: REM track basic return stack level
LV=LV-1: REM track basic return stack level
REM trigger GC
TA%=FRE(0)
REM pop A% and E% off the stack
E%=ZZ%(ZL%-1):A%=ZZ%(ZL%):ZL%=ZL%-2
REM pop A and E off the stack
E=S%(X-1):A=S%(X):X=X-2
RETURN
REM DO_FUNCTION(F%, AR%)
REM DO_FUNCTION(F, AR)
DO_FUNCTION:
AZ%=F%:GOSUB PR_STR
AZ=F:GOSUB PR_STR
F$=R$
AZ%=AR%:GOSUB PR_STR
AZ=AR:GOSUB PR_STR
AR$=R$
REM Get the function number
FF%=Z%(F%,1)
FF=Z%(F,1)
REM Get argument values
R%=AR%+1:GOSUB DEREF_R:AA%=Z%(R%,1)
R%=Z%(AR%,1)+1:GOSUB DEREF_R:AB%=Z%(R%,1)
R=AR+1:GOSUB DEREF_R:AA=Z%(R,1)
R=Z%(AR,1)+1:GOSUB DEREF_R:AB=Z%(R,1)
REM Allocate the return value
SZ%=1:GOSUB ALLOC
SZ=1:GOSUB ALLOC
REM Switch on the function number
IF FF%=1 THEN GOTO DO_ADD
IF FF%=2 THEN GOTO DO_SUB
IF FF%=3 THEN GOTO DO_MULT
IF FF%=4 THEN GOTO DO_DIV
ER%=-1:ER$="unknown function"+STR$(FF%):RETURN
IF FF=1 THEN GOTO DO_ADD
IF FF=2 THEN GOTO DO_SUB
IF FF=3 THEN GOTO DO_MULT
IF FF=4 THEN GOTO DO_DIV
ER=-1:ER$="unknown function"+STR$(FF):RETURN
DO_ADD:
Z%(R%,0)=2+16
Z%(R%,1)=AA%+AB%
Z%(R,0)=2+16
Z%(R,1)=AA+AB
GOTO DO_FUNCTION_DONE
DO_SUB:
Z%(R%,0)=2+16
Z%(R%,1)=AA%-AB%
Z%(R,0)=2+16
Z%(R,1)=AA-AB
GOTO DO_FUNCTION_DONE
DO_MULT:
Z%(R%,0)=2+16
Z%(R%,1)=AA%*AB%
Z%(R,0)=2+16
Z%(R,1)=AA*AB
GOTO DO_FUNCTION_DONE
DO_DIV:
Z%(R%,0)=2+16
Z%(R%,1)=AA%/AB%
Z%(R,0)=2+16
Z%(R,1)=AA/AB
GOTO DO_FUNCTION_DONE
DO_FUNCTION_DONE:
RETURN
REM PRINT(A%) -> R$
REM PRINT(A) -> R$
MAL_PRINT:
AZ%=A%:PR%=1:GOSUB PR_STR
AZ=A:PR=1:GOSUB PR_STR
RETURN
REM REP(A$) -> R$
REM Assume RE% has repl_env
REP:
R1%=0:R2%=0
R1=0:R2=0
GOSUB MAL_READ
R1%=R%
IF ER%<>-2 THEN GOTO REP_DONE
R1=R
IF ER<>-2 THEN GOTO REP_DONE
A%=R%:E%=RE%:GOSUB EVAL
R2%=R%
IF ER%<>-2 THEN GOTO REP_DONE
A=R:E=RE%:GOSUB EVAL
R2=R
IF ER<>-2 THEN GOTO REP_DONE
A%=R%:GOSUB MAL_PRINT
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
IF R2<>0 THEN AY=R2:GOSUB RELEASE
IF R1<>0 THEN AY=R1:GOSUB RELEASE
R$=RT$
RETURN
@ -241,28 +241,28 @@ REM MAIN program
MAIN:
GOSUB INIT_MEMORY
LV%=0
LV=0
REM create repl_env
GOSUB HASHMAP:RE%=R%
GOSUB HASHMAP:RE%=R
REM + function
A%=1:GOSUB NATIVE_FUNCTION
HM%=RE%:K$="+":V%=R%:GOSUB ASSOC1_S:RE%=R%
A=1:GOSUB NATIVE_FUNCTION
H=RE%:K$="+":V=R:GOSUB ASSOC1_S:RE%=R
REM - function
A%=2:GOSUB NATIVE_FUNCTION
HM%=RE%:K$="-":V%=R%:GOSUB ASSOC1_S:RE%=R%
A=2:GOSUB NATIVE_FUNCTION
H=RE%:K$="-":V=R:GOSUB ASSOC1_S:RE%=R
REM * function
A%=3:GOSUB NATIVE_FUNCTION
HM%=RE%:K$="*":V%=R%:GOSUB ASSOC1_S:RE%=R%
A=3:GOSUB NATIVE_FUNCTION
H=RE%:K$="*":V=R:GOSUB ASSOC1_S:RE%=R
REM / function
A%=4:GOSUB NATIVE_FUNCTION
HM%=RE%:K$="/":V%=R%:GOSUB ASSOC1_S:RE%=R%
A=4:GOSUB NATIVE_FUNCTION
H=RE%:K$="/":V=R:GOSUB ASSOC1_S:RE%=R
ZT%=ZI%: REM top of memory after base repl_env
ZT%=ZI: REM top of memory after base repl_env
REPL_LOOP:
A$="user> ":GOSUB READLINE: REM call input parser
@ -270,7 +270,7 @@ MAIN:
A$=R$:GOSUB REP: REM call REP
IF ER%<>-2 THEN GOSUB PRINT_ERROR:GOTO REPL_LOOP
IF ER<>-2 THEN GOSUB PRINT_ERROR:GOTO REPL_LOOP
PRINT R$
GOTO REPL_LOOP
@ -281,6 +281,6 @@ MAIN:
PRINT_ERROR:
PRINT "Error: "+ER$
ER%=-2:ER$=""
ER=-2:ER$=""
RETURN

View File

@ -8,302 +8,302 @@ REM $INCLUDE: 'env.in.bas'
REM $INCLUDE: 'debug.in.bas'
REM READ(A$) -> R%
REM READ(A$) -> R
MAL_READ:
GOSUB READ_STR
RETURN
REM EVAL_AST(A%, E%) -> R%
REM EVAL_AST(A, E) -> R
EVAL_AST:
LV%=LV%+1
LV=LV+1
REM push A% and E% on the stack
ZL%=ZL%+2:ZZ%(ZL%-1)=E%:ZZ%(ZL%)=A%
REM push A and E on the stack
X=X+2:S%(X-1)=E:S%(X)=A
IF ER%<>-2 THEN GOTO EVAL_AST_RETURN
IF ER<>-2 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
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
R=A:GOSUB DEREF_R
Z%(R,0)=Z%(R,0)+16
GOTO EVAL_AST_RETURN
EVAL_AST_SYMBOL:
K%=A%:GOSUB ENV_GET
K=A:GOSUB ENV_GET
GOTO EVAL_AST_RETURN
EVAL_AST_SEQ:
REM allocate the first entry
SZ%=2:GOSUB ALLOC
SZ=2:GOSUB ALLOC
REM make space on the stack
ZL%=ZL%+4
X=X+4
REM push type of sequence
ZZ%(ZL%-3)=T%
S%(X-3)=T
REM push sequence index
ZZ%(ZL%-2)=-1
S%(X-2)=-1
REM push future return value (new sequence)
ZZ%(ZL%-1)=R%
S%(X-1)=R
REM push previous new sequence entry
ZZ%(ZL%)=R%
S%(X)=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
Z%(R,0)=S%(X-3)+16
Z%(R,1)=0
REM create value ptr placeholder
Z%(R%+1,0)=14
Z%(R%+1,1)=0
Z%(R+1,0)=14
Z%(R+1,1)=0
REM update index
ZZ%(ZL%-2)=ZZ%(ZL%-2)+1
S%(X-2)=S%(X-2)+1
REM check if we are done evaluating the source sequence
IF Z%(A%,1)=0 THEN GOTO EVAL_AST_SEQ_LOOP_DONE
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
IF (S%(X-3)=8) AND ((S%(X-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
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
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%
Z%(S%(X)+1,1)=R
IF ER%<>-2 THEN GOTO EVAL_AST_SEQ_LOOP_DONE
IF ER<>-2 THEN GOTO EVAL_AST_SEQ_LOOP_DONE
REM allocate the next entry
SZ%=2:GOSUB ALLOC
SZ=2:GOSUB ALLOC
REM update previous sequence entry value to point to new entry
Z%(ZZ%(ZL%),1)=R%
Z%(S%(X),1)=R
REM update previous ptr to current entry
ZZ%(ZL%)=R%
S%(X)=R
REM process the next sequence entry from source list
A%=Z%(A%,1)
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%=-2 THEN R%=ZZ%(ZL%-1)
IF ER=-2 THEN R=S%(X-1)
REM otherwise, free the return value and return nil
IF ER%<>-2 THEN R%=0:AY%=ZZ%(ZL%-1):GOSUB RELEASE
IF ER<>-2 THEN R=0:AY=S%(X-1):GOSUB RELEASE
REM pop previous, return, index and type
ZL%=ZL%-4
X=X-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 A and E off the stack
E=S%(X-1):A=S%(X):X=X-2
LV%=LV%-1
LV=LV-1
RETURN
REM EVAL(A%, E%)) -> R%
REM EVAL(A, E)) -> R
EVAL:
LV%=LV%+1: REM track basic return stack level
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%
REM push A and E on the stack
X=X+2:S%(X-1)=E:S%(X)=A
REM AZ%=A%:PR%=1:GOSUB PR_STR
REM PRINT "EVAL: "+R$+" [A%:"+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
GOSUB LIST_Q
IF R% THEN GOTO APPLY_LIST
IF R THEN GOTO APPLY_LIST
REM ELSE
GOSUB EVAL_AST
GOTO EVAL_RETURN
APPLY_LIST:
GOSUB EMPTY_Q
IF R% THEN R%=A%:Z%(R%,0)=Z%(R%,0)+16:GOTO EVAL_RETURN
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%
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 (Z%(A0%,0)AND15)=5 THEN A$=S$(Z%(A0%,1))
IF A$="def!" THEN GOTO EVAL_DEF
IF A$="let*" THEN GOTO EVAL_LET
GOTO EVAL_INVOKE
EVAL_GET_A3:
A3%=Z%(Z%(Z%(A%,1),1),1)+1
R%=A3%:GOSUB DEREF_R:A3%=R%
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%
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%
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%
X=X+1:S%(X)=A1%: REM push A1%
A=A2%:GOSUB EVAL: REM eval a2
A1%=S%(X):X=X-1: REM pop A1%
IF ER%<>-2 THEN GOTO EVAL_RETURN
IF ER<>-2 THEN GOTO EVAL_RETURN
REM set a1 in env to a2
K%=A1%:V%=R%:GOSUB ENV_SET
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%
X=X+1:S%(X)=A2%: REM push/save A2%
REM create new environment with outer as current environment
EO%=E%:GOSUB ENV_NEW
E%=R%
O=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%
X=X+1:S%(X)=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%
A=Z%(A1%,1)+1:GOSUB EVAL
A1%=S%(X):X=X-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
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:
A2%=ZZ%(ZL%):ZL%=ZL%-1: REM pop A2%
A%=A2%:GOSUB EVAL: REM eval a2 using let_env
A2%=S%(X):X=X-1: REM pop A2%
A=A2%:GOSUB EVAL: REM eval a2 using let_env
GOTO EVAL_RETURN
EVAL_INVOKE:
GOSUB EVAL_AST
R3%=R%
R3=R
REM if error, return f/args for release by caller
IF ER%<>-2 THEN GOTO EVAL_RETURN
F%=R%+1
IF ER<>-2 THEN GOTO EVAL_RETURN
F=R+1
AR%=Z%(R%,1): REM rest
R%=F%:GOSUB DEREF_R:F%=R%
IF (Z%(F%,0)AND15)<>9 THEN ER%=-1:ER$="apply of non-function":GOTO EVAL_RETURN
AR=Z%(R,1): REM rest
R=F:GOSUB DEREF_R:F=R
IF (Z%(F,0)AND15)<>9 THEN ER=-1:ER$="apply of non-function":GOTO EVAL_RETURN
GOSUB DO_FUNCTION
AY%=R3%:GOSUB RELEASE
AY=R3:GOSUB RELEASE
GOTO EVAL_RETURN
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 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
IF E<>S%(X-1) THEN AY=E:GOSUB RELEASE
LV%=LV%-1: REM track basic return stack level
LV=LV-1: REM track basic return stack level
REM trigger GC
TA%=FRE(0)
REM pop A% and E% off the stack
E%=ZZ%(ZL%-1):A%=ZZ%(ZL%):ZL%=ZL%-2
REM pop A and E off the stack
E=S%(X-1):A=S%(X):X=X-2
RETURN
REM DO_FUNCTION(F%, AR%)
REM DO_FUNCTION(F, AR)
DO_FUNCTION:
AZ%=F%:GOSUB PR_STR
AZ=F:GOSUB PR_STR
F$=R$
AZ%=AR%:GOSUB PR_STR
AZ=AR:GOSUB PR_STR
AR$=R$
REM Get the function number
FF%=Z%(F%,1)
FF=Z%(F,1)
REM Get argument values
R%=AR%+1:GOSUB DEREF_R:AA%=Z%(R%,1)
R%=Z%(AR%,1)+1:GOSUB DEREF_R:AB%=Z%(R%,1)
R=AR+1:GOSUB DEREF_R:AA=Z%(R,1)
R=Z%(AR,1)+1:GOSUB DEREF_R:AB=Z%(R,1)
REM Allocate the return value
SZ%=1:GOSUB ALLOC
SZ=1:GOSUB ALLOC
REM Switch on the function number
IF FF%=1 THEN GOTO DO_ADD
IF FF%=2 THEN GOTO DO_SUB
IF FF%=3 THEN GOTO DO_MULT
IF FF%=4 THEN GOTO DO_DIV
ER%=-1:ER$="unknown function"+STR$(FF%):RETURN
IF FF=1 THEN GOTO DO_ADD
IF FF=2 THEN GOTO DO_SUB
IF FF=3 THEN GOTO DO_MULT
IF FF=4 THEN GOTO DO_DIV
ER=-1:ER$="unknown function"+STR$(FF):RETURN
DO_ADD:
Z%(R%,0)=2+16
Z%(R%,1)=AA%+AB%
Z%(R,0)=2+16
Z%(R,1)=AA+AB
GOTO DO_FUNCTION_DONE
DO_SUB:
Z%(R%,0)=2+16
Z%(R%,1)=AA%-AB%
Z%(R,0)=2+16
Z%(R,1)=AA-AB
GOTO DO_FUNCTION_DONE
DO_MULT:
Z%(R%,0)=2+16
Z%(R%,1)=AA%*AB%
Z%(R,0)=2+16
Z%(R,1)=AA*AB
GOTO DO_FUNCTION_DONE
DO_DIV:
Z%(R%,0)=2+16
Z%(R%,1)=AA%/AB%
Z%(R,0)=2+16
Z%(R,1)=AA/AB
GOTO DO_FUNCTION_DONE
DO_FUNCTION_DONE:
RETURN
REM PRINT(A%) -> R$
REM PRINT(A) -> R$
MAL_PRINT:
AZ%=A%:PR%=1:GOSUB PR_STR
AZ=A:PR=1:GOSUB PR_STR
RETURN
REM REP(A$) -> R$
REM Assume RE% has repl_env
REP:
R1%=0:R2%=0
R1=0:R2=0
GOSUB MAL_READ
R1%=R%
IF ER%<>-2 THEN GOTO REP_DONE
R1=R
IF ER<>-2 THEN GOTO REP_DONE
A%=R%:E%=RE%:GOSUB EVAL
R2%=R%
IF ER%<>-2 THEN GOTO REP_DONE
A=R:E=RE%:GOSUB EVAL
R2=R
IF ER<>-2 THEN GOTO REP_DONE
A%=R%:GOSUB MAL_PRINT
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
IF R2<>0 THEN AY=R2:GOSUB RELEASE
IF R1<>0 THEN AY=R1:GOSUB RELEASE
R$=RT$
RETURN
@ -311,29 +311,29 @@ REM MAIN program
MAIN:
GOSUB INIT_MEMORY
LV%=0
LV=0
REM create repl_env
EO%=-1:GOSUB ENV_NEW:RE%=R%
O=-1:GOSUB ENV_NEW:RE%=R
E%=RE%
E=RE%
REM + function
A%=1:GOSUB NATIVE_FUNCTION
K$="+":V%=R%:GOSUB ENV_SET_S
A=1:GOSUB NATIVE_FUNCTION
K$="+":V=R:GOSUB ENV_SET_S
REM - function
A%=2:GOSUB NATIVE_FUNCTION
K$="-":V%=R%:GOSUB ENV_SET_S
A=2:GOSUB NATIVE_FUNCTION
K$="-":V=R:GOSUB ENV_SET_S
REM * function
A%=3:GOSUB NATIVE_FUNCTION
K$="*":V%=R%:GOSUB ENV_SET_S
A=3:GOSUB NATIVE_FUNCTION
K$="*":V=R:GOSUB ENV_SET_S
REM / function
A%=4:GOSUB NATIVE_FUNCTION
K$="/":V%=R%:GOSUB ENV_SET_S
A=4:GOSUB NATIVE_FUNCTION
K$="/":V=R:GOSUB ENV_SET_S
ZT%=ZI%: REM top of memory after base repl_env
ZT%=ZI: REM top of memory after base repl_env
REPL_LOOP:
A$="user> ":GOSUB READLINE: REM call input parser
@ -341,7 +341,7 @@ MAIN:
A$=R$:GOSUB REP: REM call REP
IF ER%<>-2 THEN GOSUB PRINT_ERROR:GOTO REPL_LOOP
IF ER<>-2 THEN GOSUB PRINT_ERROR:GOTO REPL_LOOP
PRINT R$
GOTO REPL_LOOP
@ -352,6 +352,6 @@ MAIN:
PRINT_ERROR:
PRINT "Error: "+ER$
ER%=-2:ER$=""
ER=-2:ER$=""
RETURN

View File

@ -9,136 +9,136 @@ REM $INCLUDE: 'core.in.bas'
REM $INCLUDE: 'debug.in.bas'
REM READ(A$) -> R%
REM READ(A$) -> R
MAL_READ:
GOSUB READ_STR
RETURN
REM EVAL_AST(A%, E%) -> R%
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%
REM push A and E on the stack
X=X+2:S%(X-1)=E:S%(X)=A
IF ER%<>-2 THEN GOTO EVAL_AST_RETURN
IF ER<>-2 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
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
R=A:GOSUB DEREF_R
Z%(R,0)=Z%(R,0)+16
GOTO EVAL_AST_RETURN
EVAL_AST_SYMBOL:
K%=A%:GOSUB ENV_GET
K=A:GOSUB ENV_GET
GOTO EVAL_AST_RETURN
EVAL_AST_SEQ:
REM allocate the first entry
SZ%=2:GOSUB ALLOC
SZ=2:GOSUB ALLOC
REM make space on the stack
ZL%=ZL%+4
X=X+4
REM push type of sequence
ZZ%(ZL%-3)=T%
S%(X-3)=T
REM push sequence index
ZZ%(ZL%-2)=-1
S%(X-2)=-1
REM push future return value (new sequence)
ZZ%(ZL%-1)=R%
S%(X-1)=R
REM push previous new sequence entry
ZZ%(ZL%)=R%
S%(X)=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
Z%(R,0)=S%(X-3)+16
Z%(R,1)=0
REM create value ptr placeholder
Z%(R%+1,0)=14
Z%(R%+1,1)=0
Z%(R+1,0)=14
Z%(R+1,1)=0
REM update index
ZZ%(ZL%-2)=ZZ%(ZL%-2)+1
S%(X-2)=S%(X-2)+1
REM check if we are done evaluating the source sequence
IF Z%(A%,1)=0 THEN GOTO EVAL_AST_SEQ_LOOP_DONE
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
IF (S%(X-3)=8) AND ((S%(X-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
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
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%
Z%(S%(X)+1,1)=R
IF ER%<>-2 THEN GOTO EVAL_AST_SEQ_LOOP_DONE
IF ER<>-2 THEN GOTO EVAL_AST_SEQ_LOOP_DONE
REM allocate the next entry
SZ%=2:GOSUB ALLOC
SZ=2:GOSUB ALLOC
REM update previous sequence entry value to point to new entry
Z%(ZZ%(ZL%),1)=R%
Z%(S%(X),1)=R
REM update previous ptr to current entry
ZZ%(ZL%)=R%
S%(X)=R
REM process the next sequence entry from source list
A%=Z%(A%,1)
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%=-2 THEN R%=ZZ%(ZL%-1)
IF ER=-2 THEN R=S%(X-1)
REM otherwise, free the return value and return nil
IF ER%<>-2 THEN R%=0:AY%=ZZ%(ZL%-1):GOSUB RELEASE
IF ER<>-2 THEN R=0:AY=S%(X-1):GOSUB RELEASE
REM pop previous, return, index and type
ZL%=ZL%-4
X=X-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 A and E off the stack
E=S%(X-1):A=S%(X):X=X-2
REM pop EVAL AST return label/address
RN%=ZZ%(ZL%):ZL%=ZL%-1
RN%=S%(X):X=X-1
ON RN% GOTO EVAL_AST_RETURN_1,EVAL_AST_RETURN_2,EVAL_AST_RETURN_3
RETURN
REM EVAL(A%, E%)) -> R%
REM EVAL(A, E)) -> R
EVAL:
LV%=LV%+1: REM track basic return stack level
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%
REM push A and E on the stack
X=X+2:S%(X-1)=E:S%(X)=A
EVAL_TCO_RECUR:
REM AZ%=A%:PR%=1:GOSUB PR_STR
REM PRINT "EVAL: "+R$+" [A%:"+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
GOSUB LIST_Q
IF R% THEN GOTO APPLY_LIST
IF R THEN GOTO APPLY_LIST
REM ELSE
REM push EVAL_AST return label/address
ZL%=ZL%+1:ZZ%(ZL%)=1
X=X+1:S%(X)=1
GOTO EVAL_AST
EVAL_AST_RETURN_1:
@ -146,14 +146,14 @@ EVAL:
APPLY_LIST:
GOSUB EMPTY_Q
IF R% THEN R%=A%:Z%(R%,0)=Z%(R%,0)+16:GOTO EVAL_RETURN
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%
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 (Z%(A0%,0)AND15)=5 THEN A$=S$(Z%(A0%,1))
IF A$="def!" THEN GOTO EVAL_DEF
IF A$="let*" THEN GOTO EVAL_LET
@ -163,159 +163,159 @@ EVAL:
GOTO EVAL_INVOKE
EVAL_GET_A3:
A3%=Z%(Z%(Z%(A%,1),1),1)+1
R%=A3%:GOSUB DEREF_R:A3%=R%
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%
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%
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%
X=X+1:S%(X)=A1%: REM push A1%
A=A2%:GOSUB EVAL: REM eval a2
A1%=S%(X):X=X-1: REM pop A1%
IF ER%<>-2 THEN GOTO EVAL_RETURN
IF ER<>-2 THEN GOTO EVAL_RETURN
REM set a1 in env to a2
K%=A1%:V%=R%:GOSUB ENV_SET
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%
X=X+1:S%(X)=A2%: REM push/save A2%
REM create new environment with outer as current environment
EO%=E%:GOSUB ENV_NEW
E%=R%
O=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%
X=X+1:S%(X)=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%
A=Z%(A1%,1)+1:GOSUB EVAL
A1%=S%(X):X=X-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
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:
A2%=ZZ%(ZL%):ZL%=ZL%-1: REM pop A2%
A%=A2%:GOSUB EVAL: REM eval a2 using let_env
A2%=S%(X):X=X-1: REM pop A2%
A=A2%:GOSUB EVAL: REM eval a2 using let_env
GOTO EVAL_RETURN
EVAL_DO:
A%=Z%(A%,1): REM rest
A=Z%(A,1): REM rest
REM push EVAL_AST return label/address
ZL%=ZL%+1:ZZ%(ZL%)=2
X=X+1:S%(X)=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
X=X+1:S%(X)=R: REM push eval'd list
A=R:GOSUB LAST: REM return the last element
AY=S%(X):X=X-1: REM pop eval'd list
GOSUB RELEASE: REM release the eval'd list
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
REM push A
X=X+1:S%(X)=A
A=A1%:GOSUB EVAL
REM pop A
A=S%(X):X=X-1
IF (R=0) OR (R=1) THEN GOTO EVAL_IF_FALSE
EVAL_IF_TRUE:
AY%=R%:GOSUB RELEASE
AY=R:GOSUB RELEASE
GOSUB EVAL_GET_A2: REM set a1% and a2% after EVAL
A%=A2%:GOTO EVAL_TCO_RECUR: REM TCO loop
A=A2%:GOTO EVAL_TCO_RECUR: REM TCO loop
EVAL_IF_FALSE:
AY%=R%:GOSUB RELEASE
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
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
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
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
X=X+1:S%(X)=3
GOTO EVAL_AST
EVAL_AST_RETURN_3:
REM if error, return f/args for release by caller
IF ER%<>-2 THEN GOTO EVAL_RETURN
IF ER<>-2 THEN GOTO EVAL_RETURN
REM push f/args for release after call
ZL%=ZL%+1:ZZ%(ZL%)=R%
X=X+1:S%(X)=R
F%=R%+1
F=R+1
AR%=Z%(R%,1): REM rest
R%=F%:GOSUB DEREF_R:F%=R%
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
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
R=S%(X):X=X-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
AY=S%(X):X=X-1:GOSUB RELEASE
GOTO EVAL_RETURN
EVAL_DO_MAL_FUNCTION:
E4%=E%: REM save the current environment for release
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
O=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 stack (S%(X-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
IF E4%<>S%(X-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
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 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
AY=S%(X):X=X-1:GOSUB RELEASE
REM A% set above
E%=R%:GOTO EVAL_TCO_RECUR: REM TCO loop
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 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
IF E<>S%(X-1) THEN AY=E:GOSUB RELEASE
LV%=LV%-1: REM track basic return stack level
LV=LV-1: REM track basic return stack level
REM release everything we couldn't release earlier
GOSUB RELEASE_PEND
@ -323,51 +323,51 @@ EVAL:
REM trigger GC
TA%=FRE(0)
REM pop A% and E% off the stack
E%=ZZ%(ZL%-1):A%=ZZ%(ZL%):ZL%=ZL%-2
REM pop A and E off the stack
E=S%(X-1):A=S%(X):X=X-2
RETURN
REM PRINT(A%) -> R$
REM PRINT(A) -> R$
MAL_PRINT:
AZ%=A%:PR%=1:GOSUB PR_STR
AZ=A:PR=1:GOSUB PR_STR
RETURN
REM RE(A$) -> R%
REM RE(A$) -> R
REM Assume RE% has repl_env
REM caller must release result
RE:
R1%=0
R1=0
GOSUB MAL_READ
R1%=R%
IF ER%<>-2 THEN GOTO REP_DONE
R1=R
IF ER<>-2 THEN GOTO REP_DONE
A%=R%:E%=RE%:GOSUB EVAL
A=R:E=RE%:GOSUB EVAL
REP_DONE:
REM Release memory from MAL_READ
IF R1%<>0 THEN AY%=R1%:GOSUB RELEASE
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
R1=0:R2=0
GOSUB MAL_READ
R1%=R%
IF ER%<>-2 THEN GOTO REP_DONE
R1=R
IF ER<>-2 THEN GOTO REP_DONE
A%=R%:E%=RE%:GOSUB EVAL
R2%=R%
IF ER%<>-2 THEN GOTO REP_DONE
A=R:E=RE%:GOSUB EVAL
R2=R
IF ER<>-2 THEN GOTO REP_DONE
A%=R%:GOSUB MAL_PRINT
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
IF R2<>0 THEN AY=R2:GOSUB RELEASE
IF R1<>0 THEN AY=R1:GOSUB RELEASE
R$=RT$
RETURN
@ -375,19 +375,19 @@ REM MAIN program
MAIN:
GOSUB INIT_MEMORY
LV%=0
LV=0
REM create repl_env
EO%=-1:GOSUB ENV_NEW:RE%=R%
O=-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
E=RE%:GOSUB INIT_CORE_NS: REM set core functions in repl_env
ZT%=ZI%: REM top of memory after base 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
GOSUB RE:AY=R:GOSUB RELEASE
REPL_LOOP:
A$="user> ":GOSUB READLINE: REM call input parser
@ -395,7 +395,7 @@ MAIN:
A$=R$:GOSUB REP: REM call REP
IF ER%<>-2 THEN GOSUB PRINT_ERROR:GOTO REPL_LOOP
IF ER<>-2 THEN GOSUB PRINT_ERROR:GOTO REPL_LOOP
PRINT R$
GOTO REPL_LOOP
@ -406,6 +406,6 @@ MAIN:
PRINT_ERROR:
PRINT "Error: "+ER$
ER%=-2:ER$=""
ER=-2:ER$=""
RETURN

View File

@ -9,136 +9,136 @@ REM $INCLUDE: 'core.in.bas'
REM $INCLUDE: 'debug.in.bas'
REM READ(A$) -> R%
REM READ(A$) -> R
MAL_READ:
GOSUB READ_STR
RETURN
REM EVAL_AST(A%, E%) -> R%
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%
REM push A and E on the stack
X=X+2:S%(X-1)=E:S%(X)=A
IF ER%<>-2 THEN GOTO EVAL_AST_RETURN
IF ER<>-2 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
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
R=A:GOSUB DEREF_R
Z%(R,0)=Z%(R,0)+16
GOTO EVAL_AST_RETURN
EVAL_AST_SYMBOL:
K%=A%:GOSUB ENV_GET
K=A:GOSUB ENV_GET
GOTO EVAL_AST_RETURN
EVAL_AST_SEQ:
REM allocate the first entry
SZ%=2:GOSUB ALLOC
SZ=2:GOSUB ALLOC
REM make space on the stack
ZL%=ZL%+4
X=X+4
REM push type of sequence
ZZ%(ZL%-3)=T%
S%(X-3)=T
REM push sequence index
ZZ%(ZL%-2)=-1
S%(X-2)=-1
REM push future return value (new sequence)
ZZ%(ZL%-1)=R%
S%(X-1)=R
REM push previous new sequence entry
ZZ%(ZL%)=R%
S%(X)=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
Z%(R,0)=S%(X-3)+16
Z%(R,1)=0
REM create value ptr placeholder
Z%(R%+1,0)=14
Z%(R%+1,1)=0
Z%(R+1,0)=14
Z%(R+1,1)=0
REM update index
ZZ%(ZL%-2)=ZZ%(ZL%-2)+1
S%(X-2)=S%(X-2)+1
REM check if we are done evaluating the source sequence
IF Z%(A%,1)=0 THEN GOTO EVAL_AST_SEQ_LOOP_DONE
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
IF (S%(X-3)=8) AND ((S%(X-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
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
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%
Z%(S%(X)+1,1)=R
IF ER%<>-2 THEN GOTO EVAL_AST_SEQ_LOOP_DONE
IF ER<>-2 THEN GOTO EVAL_AST_SEQ_LOOP_DONE
REM allocate the next entry
SZ%=2:GOSUB ALLOC
SZ=2:GOSUB ALLOC
REM update previous sequence entry value to point to new entry
Z%(ZZ%(ZL%),1)=R%
Z%(S%(X),1)=R
REM update previous ptr to current entry
ZZ%(ZL%)=R%
S%(X)=R
REM process the next sequence entry from source list
A%=Z%(A%,1)
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%=-2 THEN R%=ZZ%(ZL%-1)
IF ER=-2 THEN R=S%(X-1)
REM otherwise, free the return value and return nil
IF ER%<>-2 THEN R%=0:AY%=ZZ%(ZL%-1):GOSUB RELEASE
IF ER<>-2 THEN R=0:AY=S%(X-1):GOSUB RELEASE
REM pop previous, return, index and type
ZL%=ZL%-4
X=X-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 A and E off the stack
E=S%(X-1):A=S%(X):X=X-2
REM pop EVAL AST return label/address
RN%=ZZ%(ZL%):ZL%=ZL%-1
RN%=S%(X):X=X-1
ON RN% GOTO EVAL_AST_RETURN_1,EVAL_AST_RETURN_2,EVAL_AST_RETURN_3
RETURN
REM EVAL(A%, E%)) -> R%
REM EVAL(A, E)) -> R
EVAL:
LV%=LV%+1: REM track basic return stack level
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%
REM push A and E on the stack
X=X+2:S%(X-1)=E:S%(X)=A
EVAL_TCO_RECUR:
REM AZ%=A%:PR%=1:GOSUB PR_STR
REM PRINT "EVAL: "+R$+" [A%:"+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
GOSUB LIST_Q
IF R% THEN GOTO APPLY_LIST
IF R THEN GOTO APPLY_LIST
REM ELSE
REM push EVAL_AST return label/address
ZL%=ZL%+1:ZZ%(ZL%)=1
X=X+1:S%(X)=1
GOTO EVAL_AST
EVAL_AST_RETURN_1:
@ -146,14 +146,14 @@ EVAL:
APPLY_LIST:
GOSUB EMPTY_Q
IF R% THEN R%=A%:Z%(R%,0)=Z%(R%,0)+16:GOTO EVAL_RETURN
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%
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 (Z%(A0%,0)AND15)=5 THEN A$=S$(Z%(A0%,1))
IF A$="def!" THEN GOTO EVAL_DEF
IF A$="let*" THEN GOTO EVAL_LET
@ -163,168 +163,168 @@ EVAL:
GOTO EVAL_INVOKE
EVAL_GET_A3:
A3%=Z%(Z%(Z%(A%,1),1),1)+1
R%=A3%:GOSUB DEREF_R:A3%=R%
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%
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%
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%
X=X+1:S%(X)=A1%: REM push A1%
A=A2%:GOSUB EVAL: REM eval a2
A1%=S%(X):X=X-1: REM pop A1%
IF ER%<>-2 THEN GOTO EVAL_RETURN
IF ER<>-2 THEN GOTO EVAL_RETURN
REM set a1 in env to a2
K%=A1%:V%=R%:GOSUB ENV_SET
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
X=X+1:S%(X)=A2%: REM push/save A2%
X=X+1:S%(X)=E: REM push env for for later release
REM create new environment with outer as current environment
EO%=E%:GOSUB ENV_NEW
E%=R%
O=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%
X=X+1:S%(X)=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%
A=Z%(A1%,1)+1:GOSUB EVAL
A1%=S%(X):X=X-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
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
E4%=S%(X):X=X-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
IF E4%<>S%(X-2) THEN AY=E4%:GOSUB RELEASE
A2%=ZZ%(ZL%):ZL%=ZL%-1: REM pop A2%
A%=A2%:GOTO EVAL_TCO_RECUR: REM TCO loop
A2%=S%(X):X=X-1: REM pop A2%
A=A2%:GOTO EVAL_TCO_RECUR: REM TCO loop
EVAL_DO:
A%=Z%(A%,1): REM rest
A=Z%(A,1): REM rest
REM TODO: TCO
REM push EVAL_AST return label/address
ZL%=ZL%+1:ZZ%(ZL%)=2
X=X+1:S%(X)=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
X=X+1:S%(X)=R: REM push eval'd list
A=R:GOSUB LAST: REM return the last element
AY=S%(X):X=X-1: REM pop eval'd list
GOSUB RELEASE: REM release the eval'd list
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
REM push A
X=X+1:S%(X)=A
A=A1%:GOSUB EVAL
REM pop A
A=S%(X):X=X-1
IF (R=0) OR (R=1) THEN GOTO EVAL_IF_FALSE
EVAL_IF_TRUE:
AY%=R%:GOSUB RELEASE
AY=R:GOSUB RELEASE
GOSUB EVAL_GET_A2: REM set a1% and a2% after EVAL
A%=A2%:GOTO EVAL_TCO_RECUR: REM TCO loop
A=A2%:GOTO EVAL_TCO_RECUR: REM TCO loop
EVAL_IF_FALSE:
AY%=R%:GOSUB RELEASE
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
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
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
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
X=X+1:S%(X)=3
GOTO EVAL_AST
EVAL_AST_RETURN_3:
REM if error, return f/args for release by caller
IF ER%<>-2 THEN GOTO EVAL_RETURN
IF ER<>-2 THEN GOTO EVAL_RETURN
REM push f/args for release after call
ZL%=ZL%+1:ZZ%(ZL%)=R%
X=X+1:S%(X)=R
F%=R%+1
F=R+1
AR%=Z%(R%,1): REM rest
R%=F%:GOSUB DEREF_R:F%=R%
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
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
R=S%(X):X=X-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
AY=S%(X):X=X-1:GOSUB RELEASE
GOTO EVAL_RETURN
EVAL_DO_MAL_FUNCTION:
E4%=E%: REM save the current environment for release
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
O=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 stack (S%(X-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
IF E4%<>S%(X-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
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 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
AY=S%(X):X=X-1:GOSUB RELEASE
REM A% set above
E%=R%:GOTO EVAL_TCO_RECUR: REM TCO loop
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 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
IF E<>S%(X-1) THEN AY=E:GOSUB RELEASE
LV%=LV%-1: REM track basic return stack level
LV=LV-1: REM track basic return stack level
REM release everything we couldn't release earlier
GOSUB RELEASE_PEND
@ -332,51 +332,51 @@ EVAL:
REM trigger GC
TA%=FRE(0)
REM pop A% and E% off the stack
E%=ZZ%(ZL%-1):A%=ZZ%(ZL%):ZL%=ZL%-2
REM pop A and E off the stack
E=S%(X-1):A=S%(X):X=X-2
RETURN
REM PRINT(A%) -> R$
REM PRINT(A) -> R$
MAL_PRINT:
AZ%=A%:PR%=1:GOSUB PR_STR
AZ=A:PR=1:GOSUB PR_STR
RETURN
REM RE(A$) -> R%
REM RE(A$) -> R
REM Assume RE% has repl_env
REM caller must release result
RE:
R1%=0
R1=0
GOSUB MAL_READ
R1%=R%
IF ER%<>-2 THEN GOTO REP_DONE
R1=R
IF ER<>-2 THEN GOTO REP_DONE
A%=R%:E%=RE%:GOSUB EVAL
A=R:E=RE%:GOSUB EVAL
REP_DONE:
REM Release memory from MAL_READ
IF R1%<>0 THEN AY%=R1%:GOSUB RELEASE
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
R1=0:R2=0
GOSUB MAL_READ
R1%=R%
IF ER%<>-2 THEN GOTO REP_DONE
R1=R
IF ER<>-2 THEN GOTO REP_DONE
A%=R%:E%=RE%:GOSUB EVAL
R2%=R%
IF ER%<>-2 THEN GOTO REP_DONE
A=R:E=RE%:GOSUB EVAL
R2=R
IF ER<>-2 THEN GOTO REP_DONE
A%=R%:GOSUB MAL_PRINT
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
IF R2<>0 THEN AY=R2:GOSUB RELEASE
IF R1<>0 THEN AY=R1:GOSUB RELEASE
R$=RT$
RETURN
@ -384,19 +384,19 @@ REM MAIN program
MAIN:
GOSUB INIT_MEMORY
LV%=0
LV=0
REM create repl_env
EO%=-1:GOSUB ENV_NEW:RE%=R%
O=-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
E=RE%:GOSUB INIT_CORE_NS: REM set core functions in repl_env
ZT%=ZI%: REM top of memory after base 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
GOSUB RE:AY=R:GOSUB RELEASE
REPL_LOOP:
A$="user> ":GOSUB READLINE: REM call input parser
@ -404,7 +404,7 @@ MAIN:
A$=R$:GOSUB REP: REM call REP
IF ER%<>-2 THEN GOSUB PRINT_ERROR:GOTO REPL_LOOP
IF ER<>-2 THEN GOSUB PRINT_ERROR:GOTO REPL_LOOP
PRINT R$
GOTO REPL_LOOP
@ -415,6 +415,6 @@ MAIN:
PRINT_ERROR:
PRINT "Error: "+ER$
ER%=-2:ER$=""
ER=-2:ER$=""
RETURN

View File

@ -9,136 +9,136 @@ REM $INCLUDE: 'core.in.bas'
REM $INCLUDE: 'debug.in.bas'
REM READ(A$) -> R%
REM READ(A$) -> R
MAL_READ:
GOSUB READ_STR
RETURN
REM EVAL_AST(A%, E%) -> R%
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%
REM push A and E on the stack
X=X+2:S%(X-1)=E:S%(X)=A
IF ER%<>-2 THEN GOTO EVAL_AST_RETURN
IF ER<>-2 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
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
R=A:GOSUB DEREF_R
Z%(R,0)=Z%(R,0)+16
GOTO EVAL_AST_RETURN
EVAL_AST_SYMBOL:
K%=A%:GOSUB ENV_GET
K=A:GOSUB ENV_GET
GOTO EVAL_AST_RETURN
EVAL_AST_SEQ:
REM allocate the first entry
SZ%=2:GOSUB ALLOC
SZ=2:GOSUB ALLOC
REM make space on the stack
ZL%=ZL%+4
X=X+4
REM push type of sequence
ZZ%(ZL%-3)=T%
S%(X-3)=T
REM push sequence index
ZZ%(ZL%-2)=-1
S%(X-2)=-1
REM push future return value (new sequence)
ZZ%(ZL%-1)=R%
S%(X-1)=R
REM push previous new sequence entry
ZZ%(ZL%)=R%
S%(X)=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
Z%(R,0)=S%(X-3)+16
Z%(R,1)=0
REM create value ptr placeholder
Z%(R%+1,0)=14
Z%(R%+1,1)=0
Z%(R+1,0)=14
Z%(R+1,1)=0
REM update index
ZZ%(ZL%-2)=ZZ%(ZL%-2)+1
S%(X-2)=S%(X-2)+1
REM check if we are done evaluating the source sequence
IF Z%(A%,1)=0 THEN GOTO EVAL_AST_SEQ_LOOP_DONE
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
IF (S%(X-3)=8) AND ((S%(X-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
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
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%
Z%(S%(X)+1,1)=R
IF ER%<>-2 THEN GOTO EVAL_AST_SEQ_LOOP_DONE
IF ER<>-2 THEN GOTO EVAL_AST_SEQ_LOOP_DONE
REM allocate the next entry
SZ%=2:GOSUB ALLOC
SZ=2:GOSUB ALLOC
REM update previous sequence entry value to point to new entry
Z%(ZZ%(ZL%),1)=R%
Z%(S%(X),1)=R
REM update previous ptr to current entry
ZZ%(ZL%)=R%
S%(X)=R
REM process the next sequence entry from source list
A%=Z%(A%,1)
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%=-2 THEN R%=ZZ%(ZL%-1)
IF ER=-2 THEN R=S%(X-1)
REM otherwise, free the return value and return nil
IF ER%<>-2 THEN R%=0:AY%=ZZ%(ZL%-1):GOSUB RELEASE
IF ER<>-2 THEN R=0:AY=S%(X-1):GOSUB RELEASE
REM pop previous, return, index and type
ZL%=ZL%-4
X=X-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 A and E off the stack
E=S%(X-1):A=S%(X):X=X-2
REM pop EVAL AST return label/address
RN%=ZZ%(ZL%):ZL%=ZL%-1
RN%=S%(X):X=X-1
ON RN% GOTO EVAL_AST_RETURN_1,EVAL_AST_RETURN_2,EVAL_AST_RETURN_3
RETURN
REM EVAL(A%, E%)) -> R%
REM EVAL(A, E)) -> R
EVAL:
LV%=LV%+1: REM track basic return stack level
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%
REM push A and E on the stack
X=X+2:S%(X-1)=E:S%(X)=A
EVAL_TCO_RECUR:
REM AZ%=A%:PR%=1:GOSUB PR_STR
REM PRINT "EVAL: "+R$+" [A%:"+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
GOSUB LIST_Q
IF R% THEN GOTO APPLY_LIST
IF R THEN GOTO APPLY_LIST
REM ELSE
REM push EVAL_AST return label/address
ZL%=ZL%+1:ZZ%(ZL%)=1
X=X+1:S%(X)=1
GOTO EVAL_AST
EVAL_AST_RETURN_1:
@ -146,14 +146,14 @@ EVAL:
APPLY_LIST:
GOSUB EMPTY_Q
IF R% THEN R%=A%:Z%(R%,0)=Z%(R%,0)+16:GOTO EVAL_RETURN
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%
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 (Z%(A0%,0)AND15)=5 THEN A$=S$(Z%(A0%,1))
IF A$="def!" THEN GOTO EVAL_DEF
IF A$="let*" THEN GOTO EVAL_LET
@ -163,168 +163,168 @@ EVAL:
GOTO EVAL_INVOKE
EVAL_GET_A3:
A3%=Z%(Z%(Z%(A%,1),1),1)+1
R%=A3%:GOSUB DEREF_R:A3%=R%
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%
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%
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%
X=X+1:S%(X)=A1%: REM push A1%
A=A2%:GOSUB EVAL: REM eval a2
A1%=S%(X):X=X-1: REM pop A1%
IF ER%<>-2 THEN GOTO EVAL_RETURN
IF ER<>-2 THEN GOTO EVAL_RETURN
REM set a1 in env to a2
K%=A1%:V%=R%:GOSUB ENV_SET
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
X=X+1:S%(X)=A2%: REM push/save A2%
X=X+1:S%(X)=E: REM push env for for later release
REM create new environment with outer as current environment
EO%=E%:GOSUB ENV_NEW
E%=R%
O=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%
X=X+1:S%(X)=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%
A=Z%(A1%,1)+1:GOSUB EVAL
A1%=S%(X):X=X-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
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
E4%=S%(X):X=X-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
IF E4%<>S%(X-2) THEN AY=E4%:GOSUB RELEASE
A2%=ZZ%(ZL%):ZL%=ZL%-1: REM pop A2%
A%=A2%:GOTO EVAL_TCO_RECUR: REM TCO loop
A2%=S%(X):X=X-1: REM pop A2%
A=A2%:GOTO EVAL_TCO_RECUR: REM TCO loop
EVAL_DO:
A%=Z%(A%,1): REM rest
A=Z%(A,1): REM rest
REM TODO: TCO
REM push EVAL_AST return label/address
ZL%=ZL%+1:ZZ%(ZL%)=2
X=X+1:S%(X)=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
X=X+1:S%(X)=R: REM push eval'd list
A=R:GOSUB LAST: REM return the last element
AY=S%(X):X=X-1: REM pop eval'd list
GOSUB RELEASE: REM release the eval'd list
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
REM push A
X=X+1:S%(X)=A
A=A1%:GOSUB EVAL
REM pop A
A=S%(X):X=X-1
IF (R=0) OR (R=1) THEN GOTO EVAL_IF_FALSE
EVAL_IF_TRUE:
AY%=R%:GOSUB RELEASE
AY=R:GOSUB RELEASE
GOSUB EVAL_GET_A2: REM set a1% and a2% after EVAL
A%=A2%:GOTO EVAL_TCO_RECUR: REM TCO loop
A=A2%:GOTO EVAL_TCO_RECUR: REM TCO loop
EVAL_IF_FALSE:
AY%=R%:GOSUB RELEASE
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
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
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
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
X=X+1:S%(X)=3
GOTO EVAL_AST
EVAL_AST_RETURN_3:
REM if error, return f/args for release by caller
IF ER%<>-2 THEN GOTO EVAL_RETURN
IF ER<>-2 THEN GOTO EVAL_RETURN
REM push f/args for release after call
ZL%=ZL%+1:ZZ%(ZL%)=R%
X=X+1:S%(X)=R
F%=R%+1
F=R+1
AR%=Z%(R%,1): REM rest
R%=F%:GOSUB DEREF_R:F%=R%
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
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
R=S%(X):X=X-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
AY=S%(X):X=X-1:GOSUB RELEASE
GOTO EVAL_RETURN
EVAL_DO_MAL_FUNCTION:
E4%=E%: REM save the current environment for release
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
O=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 stack (S%(X-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
IF E4%<>S%(X-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
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 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
AY=S%(X):X=X-1:GOSUB RELEASE
REM A% set above
E%=R%:GOTO EVAL_TCO_RECUR: REM TCO loop
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 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
IF E<>S%(X-1) THEN AY=E:GOSUB RELEASE
LV%=LV%-1: REM track basic return stack level
LV=LV-1: REM track basic return stack level
REM release everything we couldn't release earlier
GOSUB RELEASE_PEND
@ -332,51 +332,51 @@ EVAL:
REM trigger GC
TA%=FRE(0)
REM pop A% and E% off the stack
E%=ZZ%(ZL%-1):A%=ZZ%(ZL%):ZL%=ZL%-2
REM pop A and E off the stack
E=S%(X-1):A=S%(X):X=X-2
RETURN
REM PRINT(A%) -> R$
REM PRINT(A) -> R$
MAL_PRINT:
AZ%=A%:PR%=1:GOSUB PR_STR
AZ=A:PR=1:GOSUB PR_STR
RETURN
REM RE(A$) -> R%
REM RE(A$) -> R
REM Assume RE% has repl_env
REM caller must release result
RE:
R1%=0
R1=0
GOSUB MAL_READ
R1%=R%
IF ER%<>-2 THEN GOTO REP_DONE
R1=R
IF ER<>-2 THEN GOTO REP_DONE
A%=R%:E%=RE%:GOSUB EVAL
A=R:E=RE%:GOSUB EVAL
REP_DONE:
REM Release memory from MAL_READ
IF R1%<>0 THEN AY%=R1%:GOSUB RELEASE
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
R1=0:R2=0
GOSUB MAL_READ
R1%=R%
IF ER%<>-2 THEN GOTO REP_DONE
R1=R
IF ER<>-2 THEN GOTO REP_DONE
A%=R%:E%=RE%:GOSUB EVAL
R2%=R%
IF ER%<>-2 THEN GOTO REP_DONE
A=R:E=RE%:GOSUB EVAL
R2=R
IF ER<>-2 THEN GOTO REP_DONE
A%=R%:GOSUB MAL_PRINT
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
IF R2<>0 THEN AY=R2:GOSUB RELEASE
IF R1<>0 THEN AY=R1:GOSUB RELEASE
R$=RT$
RETURN
@ -384,46 +384,46 @@ REM MAIN program
MAIN:
GOSUB INIT_MEMORY
LV%=0
LV=0
REM create repl_env
EO%=-1:GOSUB ENV_NEW:RE%=R%
O=-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
E=RE%:GOSUB INIT_CORE_NS: REM set core functions in repl_env
ZT%=ZI%: REM top of memory after base 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
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
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
GOSUB RE:AY=R:GOSUB RELEASE
REM set the argument list
A$="(def! *ARGV* (rest -*ARGS*-))"
GOSUB RE:AY%=R%:GOSUB RELEASE
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
IF R<>0 THEN AY=R:GOSUB RELEASE:GOTO RUN_PROG
REM no arguments, start REPL loop
IF R%=0 THEN GOTO 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%<>-2 THEN GOSUB PRINT_ERROR
IF ER<>-2 THEN GOSUB PRINT_ERROR
END
REPL_LOOP:
@ -432,7 +432,7 @@ MAIN:
A$=R$:GOSUB REP: REM call REP
IF ER%<>-2 THEN GOSUB PRINT_ERROR:GOTO REPL_LOOP
IF ER<>-2 THEN GOSUB PRINT_ERROR:GOTO REPL_LOOP
PRINT R$
GOTO REPL_LOOP
@ -443,6 +443,6 @@ MAIN:
PRINT_ERROR:
PRINT "Error: "+ER$
ER%=-2:ER$=""
ER=-2:ER$=""
RETURN

View File

@ -11,207 +11,207 @@ REM $INCLUDE: 'core.in.bas'
REM $INCLUDE: 'debug.in.bas'
REM READ(A$) -> R%
REM READ(A$) -> R
MAL_READ:
GOSUB READ_STR
RETURN
REM PAIR_Q(B%) -> R%
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
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%
REM QUASIQUOTE(A) -> R
QUASIQUOTE:
B%=A%:GOSUB PAIR_Q
IF R%=1 THEN GOTO QQ_UNQUOTE
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
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
R=A+1:GOSUB DEREF_R
IF (Z%(R,0)AND15)<>5 THEN GOTO QQ_SPLICE_UNQUOTE
IF S$(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
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 push A on the stack
X=X+1:S%(X)=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
A=Z%(A,1):GOSUB QUASIQUOTE:T6=R
REM pop A off the stack
A=S%(X):X=X-1
REM set A% to ast[0] for last two cases
A%=A%+1:GOSUB DEREF_A
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
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 S$(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
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
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
REM push T6 on the stack
X=X+1:S%(X)=T6
REM A set above to ast[0]
GOSUB QUASIQUOTE:B2%=R
REM pop T6 off the stack
T6=S%(X):X=X-1
AS$="cons":T%=5:GOSUB STRING:B3%=R%
B1%=T6%:GOSUB LIST3
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
AY=B1%:GOSUB RELEASE
AY=B2%:GOSUB RELEASE
RETURN
REM EVAL_AST(A%, E%) -> R%
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%
REM push A and E on the stack
X=X+2:S%(X-1)=E:S%(X)=A
IF ER%<>-2 THEN GOTO EVAL_AST_RETURN
IF ER<>-2 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
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
R=A:GOSUB DEREF_R
Z%(R,0)=Z%(R,0)+16
GOTO EVAL_AST_RETURN
EVAL_AST_SYMBOL:
K%=A%:GOSUB ENV_GET
K=A:GOSUB ENV_GET
GOTO EVAL_AST_RETURN
EVAL_AST_SEQ:
REM allocate the first entry
SZ%=2:GOSUB ALLOC
SZ=2:GOSUB ALLOC
REM make space on the stack
ZL%=ZL%+4
X=X+4
REM push type of sequence
ZZ%(ZL%-3)=T%
S%(X-3)=T
REM push sequence index
ZZ%(ZL%-2)=-1
S%(X-2)=-1
REM push future return value (new sequence)
ZZ%(ZL%-1)=R%
S%(X-1)=R
REM push previous new sequence entry
ZZ%(ZL%)=R%
S%(X)=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
Z%(R,0)=S%(X-3)+16
Z%(R,1)=0
REM create value ptr placeholder
Z%(R%+1,0)=14
Z%(R%+1,1)=0
Z%(R+1,0)=14
Z%(R+1,1)=0
REM update index
ZZ%(ZL%-2)=ZZ%(ZL%-2)+1
S%(X-2)=S%(X-2)+1
REM check if we are done evaluating the source sequence
IF Z%(A%,1)=0 THEN GOTO EVAL_AST_SEQ_LOOP_DONE
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
IF (S%(X-3)=8) AND ((S%(X-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
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
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%
Z%(S%(X)+1,1)=R
IF ER%<>-2 THEN GOTO EVAL_AST_SEQ_LOOP_DONE
IF ER<>-2 THEN GOTO EVAL_AST_SEQ_LOOP_DONE
REM allocate the next entry
SZ%=2:GOSUB ALLOC
SZ=2:GOSUB ALLOC
REM update previous sequence entry value to point to new entry
Z%(ZZ%(ZL%),1)=R%
Z%(S%(X),1)=R
REM update previous ptr to current entry
ZZ%(ZL%)=R%
S%(X)=R
REM process the next sequence entry from source list
A%=Z%(A%,1)
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%=-2 THEN R%=ZZ%(ZL%-1)
IF ER=-2 THEN R=S%(X-1)
REM otherwise, free the return value and return nil
IF ER%<>-2 THEN R%=0:AY%=ZZ%(ZL%-1):GOSUB RELEASE
IF ER<>-2 THEN R=0:AY=S%(X-1):GOSUB RELEASE
REM pop previous, return, index and type
ZL%=ZL%-4
X=X-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 A and E off the stack
E=S%(X-1):A=S%(X):X=X-2
REM pop EVAL AST return label/address
RN%=ZZ%(ZL%):ZL%=ZL%-1
RN%=S%(X):X=X-1
ON RN% GOTO EVAL_AST_RETURN_1,EVAL_AST_RETURN_2,EVAL_AST_RETURN_3
RETURN
REM EVAL(A%, E%)) -> R%
REM EVAL(A, E)) -> R
EVAL:
LV%=LV%+1: REM track basic return stack level
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%
REM push A and E on the stack
X=X+2:S%(X-1)=E:S%(X)=A
EVAL_TCO_RECUR:
REM AZ%=A%:PR%=1:GOSUB PR_STR
REM PRINT "EVAL: "+R$+" [A%:"+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
GOSUB LIST_Q
IF R% THEN GOTO APPLY_LIST
IF R THEN GOTO APPLY_LIST
REM ELSE
REM push EVAL_AST return label/address
ZL%=ZL%+1:ZZ%(ZL%)=1
X=X+1:S%(X)=1
GOTO EVAL_AST
EVAL_AST_RETURN_1:
@ -219,14 +219,14 @@ EVAL:
APPLY_LIST:
GOSUB EMPTY_Q
IF R% THEN R%=A%:Z%(R%,0)=Z%(R%,0)+16:GOTO EVAL_RETURN
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%
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 (Z%(A0%,0)AND15)=5 THEN A$=S$(Z%(A0%,1))
IF A$="def!" THEN GOTO EVAL_DEF
IF A$="let*" THEN GOTO EVAL_LET
@ -238,182 +238,182 @@ EVAL:
GOTO EVAL_INVOKE
EVAL_GET_A3:
A3%=Z%(Z%(Z%(A%,1),1),1)+1
R%=A3%:GOSUB DEREF_R:A3%=R%
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%
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%
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%
X=X+1:S%(X)=A1%: REM push A1%
A=A2%:GOSUB EVAL: REM eval a2
A1%=S%(X):X=X-1: REM pop A1%
IF ER%<>-2 THEN GOTO EVAL_RETURN
IF ER<>-2 THEN GOTO EVAL_RETURN
REM set a1 in env to a2
K%=A1%:V%=R%:GOSUB ENV_SET
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
X=X+1:S%(X)=A2%: REM push/save A2%
X=X+1:S%(X)=E: REM push env for for later release
REM create new environment with outer as current environment
EO%=E%:GOSUB ENV_NEW
E%=R%
O=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%
X=X+1:S%(X)=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%
A=Z%(A1%,1)+1:GOSUB EVAL
A1%=S%(X):X=X-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
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
E4%=S%(X):X=X-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
IF E4%<>S%(X-2) THEN AY=E4%:GOSUB RELEASE
A2%=ZZ%(ZL%):ZL%=ZL%-1: REM pop A2%
A%=A2%:GOTO EVAL_TCO_RECUR: REM TCO loop
A2%=S%(X):X=X-1: REM pop A2%
A=A2%:GOTO EVAL_TCO_RECUR: REM TCO loop
EVAL_DO:
A%=Z%(A%,1): REM rest
A=Z%(A,1): REM rest
REM TODO: TCO
REM push EVAL_AST return label/address
ZL%=ZL%+1:ZZ%(ZL%)=2
X=X+1:S%(X)=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
X=X+1:S%(X)=R: REM push eval'd list
A=R:GOSUB LAST: REM return the last element
AY=S%(X):X=X-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
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
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%
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
A=R:GOTO EVAL_TCO_RECUR: REM TCO loop
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
REM push A
X=X+1:S%(X)=A
A=A1%:GOSUB EVAL
REM pop A
A=S%(X):X=X-1
IF (R=0) OR (R=1) THEN GOTO EVAL_IF_FALSE
EVAL_IF_TRUE:
AY%=R%:GOSUB RELEASE
AY=R:GOSUB RELEASE
GOSUB EVAL_GET_A2: REM set a1% and a2% after EVAL
A%=A2%:GOTO EVAL_TCO_RECUR: REM TCO loop
A=A2%:GOTO EVAL_TCO_RECUR: REM TCO loop
EVAL_IF_FALSE:
AY%=R%:GOSUB RELEASE
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
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
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
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
X=X+1:S%(X)=3
GOTO EVAL_AST
EVAL_AST_RETURN_3:
REM if error, return f/args for release by caller
IF ER%<>-2 THEN GOTO EVAL_RETURN
IF ER<>-2 THEN GOTO EVAL_RETURN
REM push f/args for release after call
ZL%=ZL%+1:ZZ%(ZL%)=R%
X=X+1:S%(X)=R
F%=R%+1
F=R+1
AR%=Z%(R%,1): REM rest
R%=F%:GOSUB DEREF_R:F%=R%
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
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
R=S%(X):X=X-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
AY=S%(X):X=X-1:GOSUB RELEASE
GOTO EVAL_RETURN
EVAL_DO_MAL_FUNCTION:
E4%=E%: REM save the current environment for release
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
O=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 stack (S%(X-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
IF E4%<>S%(X-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
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 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
AY=S%(X):X=X-1:GOSUB RELEASE
REM A% set above
E%=R%:GOTO EVAL_TCO_RECUR: REM TCO loop
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 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
IF E<>S%(X-1) THEN AY=E:GOSUB RELEASE
LV%=LV%-1: REM track basic return stack level
LV=LV-1: REM track basic return stack level
REM release everything we couldn't release earlier
GOSUB RELEASE_PEND
@ -421,51 +421,51 @@ EVAL:
REM trigger GC
TA%=FRE(0)
REM pop A% and E% off the stack
E%=ZZ%(ZL%-1):A%=ZZ%(ZL%):ZL%=ZL%-2
REM pop A and E off the stack
E=S%(X-1):A=S%(X):X=X-2
RETURN
REM PRINT(A%) -> R$
REM PRINT(A) -> R$
MAL_PRINT:
AZ%=A%:PR%=1:GOSUB PR_STR
AZ=A:PR=1:GOSUB PR_STR
RETURN
REM RE(A$) -> R%
REM RE(A$) -> R
REM Assume RE% has repl_env
REM caller must release result
RE:
R1%=0
R1=0
GOSUB MAL_READ
R1%=R%
IF ER%<>-2 THEN GOTO REP_DONE
R1=R
IF ER<>-2 THEN GOTO REP_DONE
A%=R%:E%=RE%:GOSUB EVAL
A=R:E=RE%:GOSUB EVAL
REP_DONE:
REM Release memory from MAL_READ
IF R1%<>0 THEN AY%=R1%:GOSUB RELEASE
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
R1=0:R2=0
GOSUB MAL_READ
R1%=R%
IF ER%<>-2 THEN GOTO REP_DONE
R1=R
IF ER<>-2 THEN GOTO REP_DONE
A%=R%:E%=RE%:GOSUB EVAL
R2%=R%
IF ER%<>-2 THEN GOTO REP_DONE
A=R:E=RE%:GOSUB EVAL
R2=R
IF ER<>-2 THEN GOTO REP_DONE
A%=R%:GOSUB MAL_PRINT
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
IF R2<>0 THEN AY=R2:GOSUB RELEASE
IF R1<>0 THEN AY=R1:GOSUB RELEASE
R$=RT$
RETURN
@ -473,46 +473,46 @@ REM MAIN program
MAIN:
GOSUB INIT_MEMORY
LV%=0
LV=0
REM create repl_env
EO%=-1:GOSUB ENV_NEW:RE%=R%
O=-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
E=RE%:GOSUB INIT_CORE_NS: REM set core functions in repl_env
ZT%=ZI%: REM top of memory after base 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
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
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
GOSUB RE:AY=R:GOSUB RELEASE
REM set the argument list
A$="(def! *ARGV* (rest -*ARGS*-))"
GOSUB RE:AY%=R%:GOSUB RELEASE
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
IF R<>0 THEN AY=R:GOSUB RELEASE:GOTO RUN_PROG
REM no arguments, start REPL loop
IF R%=0 THEN GOTO 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%<>-2 THEN GOSUB PRINT_ERROR
IF ER<>-2 THEN GOSUB PRINT_ERROR
END
REPL_LOOP:
@ -521,7 +521,7 @@ MAIN:
A$=R$:GOSUB REP: REM call REP
IF ER%<>-2 THEN GOSUB PRINT_ERROR:GOTO REPL_LOOP
IF ER<>-2 THEN GOSUB PRINT_ERROR:GOTO REPL_LOOP
PRINT R$
GOTO REPL_LOOP
@ -532,6 +532,6 @@ MAIN:
PRINT_ERROR:
PRINT "Error: "+ER$
ER%=-2:ER$=""
ER=-2:ER$=""
RETURN

View File

@ -11,243 +11,243 @@ REM $INCLUDE: 'core.in.bas'
REM $INCLUDE: 'debug.in.bas'
REM READ(A$) -> R%
REM READ(A$) -> R
MAL_READ:
GOSUB READ_STR
RETURN
REM PAIR_Q(B%) -> R%
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
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%
REM QUASIQUOTE(A) -> R
QUASIQUOTE:
B%=A%:GOSUB PAIR_Q
IF R%=1 THEN GOTO QQ_UNQUOTE
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
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
R=A+1:GOSUB DEREF_R
IF (Z%(R,0)AND15)<>5 THEN GOTO QQ_SPLICE_UNQUOTE
IF S$(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
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 push A on the stack
X=X+1:S%(X)=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
A=Z%(A,1):GOSUB QUASIQUOTE:T6=R
REM pop A off the stack
A=S%(X):X=X-1
REM set A% to ast[0] for last two cases
A%=A%+1:GOSUB DEREF_A
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
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 S$(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
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
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
REM push T6 on the stack
X=X+1:S%(X)=T6
REM A set above to ast[0]
GOSUB QUASIQUOTE:B2%=R
REM pop T6 off the stack
T6=S%(X):X=X-1
AS$="cons":T%=5:GOSUB STRING:B3%=R%
B1%=T6%:GOSUB LIST3
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
AY=B1%:GOSUB RELEASE
AY=B2%:GOSUB RELEASE
RETURN
REM MACROEXPAND(A%, E%) -> A%:
REM MACROEXPAND(A, E) -> A:
MACROEXPAND:
REM push original A%
ZL%=ZL%+1:ZZ%(ZL%)=A%
REM push original A
X=X+1:S%(X)=A
MACROEXPAND_LOOP:
REM list?
IF (Z%(A%,0)AND15)<>6 THEN GOTO MACROEXPAND_DONE
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
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
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
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
IF (Z%(B,0)AND15)<>11 THEN GOTO MACROEXPAND_DONE
REM apply
F%=B%:AR%=Z%(A%,1):GOSUB APPLY
A%=R%
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
AY=S%(X)
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 A<>AY THEN ZM%=ZM%+1:ZR%(ZM%,0)=A:ZR%(ZM%,1)=LV
IF ER%<>-2 THEN GOTO MACROEXPAND_DONE
IF ER<>-2 THEN GOTO MACROEXPAND_DONE
GOTO MACROEXPAND_LOOP
MACROEXPAND_DONE:
ZL%=ZL%-1: REM pop original A%
X=X-1: REM pop original A
RETURN
REM EVAL_AST(A%, E%) -> R%
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%
REM push A and E on the stack
X=X+2:S%(X-1)=E:S%(X)=A
IF ER%<>-2 THEN GOTO EVAL_AST_RETURN
IF ER<>-2 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
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
R=A:GOSUB DEREF_R
Z%(R,0)=Z%(R,0)+16
GOTO EVAL_AST_RETURN
EVAL_AST_SYMBOL:
K%=A%:GOSUB ENV_GET
K=A:GOSUB ENV_GET
GOTO EVAL_AST_RETURN
EVAL_AST_SEQ:
REM allocate the first entry
SZ%=2:GOSUB ALLOC
SZ=2:GOSUB ALLOC
REM make space on the stack
ZL%=ZL%+4
X=X+4
REM push type of sequence
ZZ%(ZL%-3)=T%
S%(X-3)=T
REM push sequence index
ZZ%(ZL%-2)=-1
S%(X-2)=-1
REM push future return value (new sequence)
ZZ%(ZL%-1)=R%
S%(X-1)=R
REM push previous new sequence entry
ZZ%(ZL%)=R%
S%(X)=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
Z%(R,0)=S%(X-3)+16
Z%(R,1)=0
REM create value ptr placeholder
Z%(R%+1,0)=14
Z%(R%+1,1)=0
Z%(R+1,0)=14
Z%(R+1,1)=0
REM update index
ZZ%(ZL%-2)=ZZ%(ZL%-2)+1
S%(X-2)=S%(X-2)+1
REM check if we are done evaluating the source sequence
IF Z%(A%,1)=0 THEN GOTO EVAL_AST_SEQ_LOOP_DONE
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
IF (S%(X-3)=8) AND ((S%(X-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
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
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%
Z%(S%(X)+1,1)=R
IF ER%<>-2 THEN GOTO EVAL_AST_SEQ_LOOP_DONE
IF ER<>-2 THEN GOTO EVAL_AST_SEQ_LOOP_DONE
REM allocate the next entry
SZ%=2:GOSUB ALLOC
SZ=2:GOSUB ALLOC
REM update previous sequence entry value to point to new entry
Z%(ZZ%(ZL%),1)=R%
Z%(S%(X),1)=R
REM update previous ptr to current entry
ZZ%(ZL%)=R%
S%(X)=R
REM process the next sequence entry from source list
A%=Z%(A%,1)
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%=-2 THEN R%=ZZ%(ZL%-1)
IF ER=-2 THEN R=S%(X-1)
REM otherwise, free the return value and return nil
IF ER%<>-2 THEN R%=0:AY%=ZZ%(ZL%-1):GOSUB RELEASE
IF ER<>-2 THEN R=0:AY=S%(X-1):GOSUB RELEASE
REM pop previous, return, index and type
ZL%=ZL%-4
X=X-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 A and E off the stack
E=S%(X-1):A=S%(X):X=X-2
REM pop EVAL AST return label/address
RN%=ZZ%(ZL%):ZL%=ZL%-1
RN%=S%(X):X=X-1
ON RN% GOTO EVAL_AST_RETURN_1,EVAL_AST_RETURN_2,EVAL_AST_RETURN_3
RETURN
REM EVAL(A%, E%)) -> R%
REM EVAL(A, E)) -> R
EVAL:
LV%=LV%+1: REM track basic return stack level
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%
REM push A and E on the stack
X=X+2:S%(X-1)=E:S%(X)=A
EVAL_TCO_RECUR:
REM AZ%=A%:PR%=1:GOSUB PR_STR
REM PRINT "EVAL: "+R$+" [A%:"+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
GOSUB LIST_Q
IF R% THEN GOTO APPLY_LIST
IF R THEN GOTO APPLY_LIST
EVAL_NOT_LIST:
REM ELSE
REM push EVAL_AST return label/address
ZL%=ZL%+1:ZZ%(ZL%)=1
X=X+1:S%(X)=1
GOTO EVAL_AST
EVAL_AST_RETURN_1:
@ -257,17 +257,17 @@ EVAL:
GOSUB MACROEXPAND
GOSUB LIST_Q
IF R%<>1 THEN GOTO EVAL_NOT_LIST
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
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%
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 (Z%(A0%,0)AND15)=5 THEN A$=S$(Z%(A0%,1))
IF A$="def!" THEN GOTO EVAL_DEF
IF A$="let*" THEN GOTO EVAL_LET
@ -281,206 +281,206 @@ EVAL:
GOTO EVAL_INVOKE
EVAL_GET_A3:
A3%=Z%(Z%(Z%(A%,1),1),1)+1
R%=A3%:GOSUB DEREF_R:A3%=R%
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%
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%
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%
X=X+1:S%(X)=A1%: REM push A1%
A=A2%:GOSUB EVAL: REM eval a2
A1%=S%(X):X=X-1: REM pop A1%
IF ER%<>-2 THEN GOTO EVAL_RETURN
IF ER<>-2 THEN GOTO EVAL_RETURN
REM set a1 in env to a2
K%=A1%:V%=R%:GOSUB ENV_SET
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
X=X+1:S%(X)=A2%: REM push/save A2%
X=X+1:S%(X)=E: REM push env for for later release
REM create new environment with outer as current environment
EO%=E%:GOSUB ENV_NEW
E%=R%
O=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%
X=X+1:S%(X)=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%
A=Z%(A1%,1)+1:GOSUB EVAL
A1%=S%(X):X=X-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
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
E4%=S%(X):X=X-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
IF E4%<>S%(X-2) THEN AY=E4%:GOSUB RELEASE
A2%=ZZ%(ZL%):ZL%=ZL%-1: REM pop A2%
A%=A2%:GOTO EVAL_TCO_RECUR: REM TCO loop
A2%=S%(X):X=X-1: REM pop A2%
A=A2%:GOTO EVAL_TCO_RECUR: REM TCO loop
EVAL_DO:
A%=Z%(A%,1): REM rest
A=Z%(A,1): REM rest
REM TODO: TCO
REM push EVAL_AST return label/address
ZL%=ZL%+1:ZZ%(ZL%)=2
X=X+1:S%(X)=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
X=X+1:S%(X)=R: REM push eval'd list
A=R:GOSUB LAST: REM return the last element
AY=S%(X):X=X-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
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
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%
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
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%
X=X+1:S%(X)=A1%: REM push A1%
A=A2%:GOSUB EVAL: REM eval a2
A1%=S%(X):X=X-1: REM pop A1%
REM change function to macro
Z%(R%,0)=Z%(R%,0)+1
Z%(R,0)=Z%(R,0)+1
REM set a1 in env to a2
K%=A1%:V%=R%:GOSUB ENV_SET
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%
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
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
REM push A
X=X+1:S%(X)=A
A=A1%:GOSUB EVAL
REM pop A
A=S%(X):X=X-1
IF (R=0) OR (R=1) THEN GOTO EVAL_IF_FALSE
EVAL_IF_TRUE:
AY%=R%:GOSUB RELEASE
AY=R:GOSUB RELEASE
GOSUB EVAL_GET_A2: REM set a1% and a2% after EVAL
A%=A2%:GOTO EVAL_TCO_RECUR: REM TCO loop
A=A2%:GOTO EVAL_TCO_RECUR: REM TCO loop
EVAL_IF_FALSE:
AY%=R%:GOSUB RELEASE
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
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
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
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
X=X+1:S%(X)=3
GOTO EVAL_AST
EVAL_AST_RETURN_3:
REM if error, return f/args for release by caller
IF ER%<>-2 THEN GOTO EVAL_RETURN
IF ER<>-2 THEN GOTO EVAL_RETURN
REM push f/args for release after call
ZL%=ZL%+1:ZZ%(ZL%)=R%
X=X+1:S%(X)=R
F%=R%+1
F=R+1
AR%=Z%(R%,1): REM rest
R%=F%:GOSUB DEREF_R:F%=R%
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
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
R=S%(X):X=X-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
AY=S%(X):X=X-1:GOSUB RELEASE
GOTO EVAL_RETURN
EVAL_DO_MAL_FUNCTION:
E4%=E%: REM save the current environment for release
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
O=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 stack (S%(X-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
IF E4%<>S%(X-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
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 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
AY=S%(X):X=X-1:GOSUB RELEASE
REM A% set above
E%=R%:GOTO EVAL_TCO_RECUR: REM TCO loop
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 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
IF E<>S%(X-1) THEN AY=E:GOSUB RELEASE
LV%=LV%-1: REM track basic return stack level
LV=LV-1: REM track basic return stack level
REM release everything we couldn't release earlier
GOSUB RELEASE_PEND
@ -488,51 +488,51 @@ EVAL:
REM trigger GC
TA%=FRE(0)
REM pop A% and E% off the stack
E%=ZZ%(ZL%-1):A%=ZZ%(ZL%):ZL%=ZL%-2
REM pop A and E off the stack
E=S%(X-1):A=S%(X):X=X-2
RETURN
REM PRINT(A%) -> R$
REM PRINT(A) -> R$
MAL_PRINT:
AZ%=A%:PR%=1:GOSUB PR_STR
AZ=A:PR=1:GOSUB PR_STR
RETURN
REM RE(A$) -> R%
REM RE(A$) -> R
REM Assume RE% has repl_env
REM caller must release result
RE:
R1%=0
R1=0
GOSUB MAL_READ
R1%=R%
IF ER%<>-2 THEN GOTO REP_DONE
R1=R
IF ER<>-2 THEN GOTO REP_DONE
A%=R%:E%=RE%:GOSUB EVAL
A=R:E=RE%:GOSUB EVAL
REP_DONE:
REM Release memory from MAL_READ
IF R1%<>0 THEN AY%=R1%:GOSUB RELEASE
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
R1=0:R2=0
GOSUB MAL_READ
R1%=R%
IF ER%<>-2 THEN GOTO REP_DONE
R1=R
IF ER<>-2 THEN GOTO REP_DONE
A%=R%:E%=RE%:GOSUB EVAL
R2%=R%
IF ER%<>-2 THEN GOTO REP_DONE
A=R:E=RE%:GOSUB EVAL
R2=R
IF ER<>-2 THEN GOTO REP_DONE
A%=R%:GOSUB MAL_PRINT
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
IF R2<>0 THEN AY=R2:GOSUB RELEASE
IF R1<>0 THEN AY=R1:GOSUB RELEASE
R$=RT$
RETURN
@ -540,55 +540,55 @@ REM MAIN program
MAIN:
GOSUB INIT_MEMORY
LV%=0
LV=0
REM create repl_env
EO%=-1:GOSUB ENV_NEW:RE%=R%
O=-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
E=RE%:GOSUB INIT_CORE_NS: REM set core functions in repl_env
ZT%=ZI%: REM top of memory after base 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
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
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
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
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
GOSUB RE:AY=R:GOSUB RELEASE
REM set the argument list
A$="(def! *ARGV* (rest -*ARGS*-))"
GOSUB RE:AY%=R%:GOSUB RELEASE
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
IF R<>0 THEN AY=R:GOSUB RELEASE:GOTO RUN_PROG
REM no arguments, start REPL loop
IF R%=0 THEN GOTO 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%<>-2 THEN GOSUB PRINT_ERROR
IF ER<>-2 THEN GOSUB PRINT_ERROR
END
REPL_LOOP:
@ -597,7 +597,7 @@ MAIN:
A$=R$:GOSUB REP: REM call REP
IF ER%<>-2 THEN GOSUB PRINT_ERROR:GOTO REPL_LOOP
IF ER<>-2 THEN GOSUB PRINT_ERROR:GOTO REPL_LOOP
PRINT R$
GOTO REPL_LOOP
@ -608,6 +608,6 @@ MAIN:
PRINT_ERROR:
PRINT "Error: "+ER$
ER%=-2:ER$=""
ER=-2:ER$=""
RETURN

View File

@ -11,243 +11,243 @@ REM $INCLUDE: 'core.in.bas'
REM $INCLUDE: 'debug.in.bas'
REM READ(A$) -> R%
REM READ(A$) -> R
MAL_READ:
GOSUB READ_STR
RETURN
REM PAIR_Q(B%) -> R%
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
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%
REM QUASIQUOTE(A) -> R
QUASIQUOTE:
B%=A%:GOSUB PAIR_Q
IF R%=1 THEN GOTO QQ_UNQUOTE
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
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
R=A+1:GOSUB DEREF_R
IF (Z%(R,0)AND15)<>5 THEN GOTO QQ_SPLICE_UNQUOTE
IF S$(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
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 push A on the stack
X=X+1:S%(X)=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
A=Z%(A,1):GOSUB QUASIQUOTE:T6=R
REM pop A off the stack
A=S%(X):X=X-1
REM set A% to ast[0] for last two cases
A%=A%+1:GOSUB DEREF_A
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
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 S$(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
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
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
REM push T6 on the stack
X=X+1:S%(X)=T6
REM A set above to ast[0]
GOSUB QUASIQUOTE:B2%=R
REM pop T6 off the stack
T6=S%(X):X=X-1
AS$="cons":T%=5:GOSUB STRING:B3%=R%
B1%=T6%:GOSUB LIST3
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
AY=B1%:GOSUB RELEASE
AY=B2%:GOSUB RELEASE
RETURN
REM MACROEXPAND(A%, E%) -> A%:
REM MACROEXPAND(A, E) -> A:
MACROEXPAND:
REM push original A%
ZL%=ZL%+1:ZZ%(ZL%)=A%
REM push original A
X=X+1:S%(X)=A
MACROEXPAND_LOOP:
REM list?
IF (Z%(A%,0)AND15)<>6 THEN GOTO MACROEXPAND_DONE
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
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
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
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
IF (Z%(B,0)AND15)<>11 THEN GOTO MACROEXPAND_DONE
REM apply
F%=B%:AR%=Z%(A%,1):GOSUB APPLY
A%=R%
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
AY=S%(X)
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 A<>AY THEN ZM%=ZM%+1:ZR%(ZM%,0)=A:ZR%(ZM%,1)=LV
IF ER%<>-2 THEN GOTO MACROEXPAND_DONE
IF ER<>-2 THEN GOTO MACROEXPAND_DONE
GOTO MACROEXPAND_LOOP
MACROEXPAND_DONE:
ZL%=ZL%-1: REM pop original A%
X=X-1: REM pop original A
RETURN
REM EVAL_AST(A%, E%) -> R%
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%
REM push A and E on the stack
X=X+2:S%(X-1)=E:S%(X)=A
IF ER%<>-2 THEN GOTO EVAL_AST_RETURN
IF ER<>-2 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
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
R=A:GOSUB DEREF_R
Z%(R,0)=Z%(R,0)+16
GOTO EVAL_AST_RETURN
EVAL_AST_SYMBOL:
K%=A%:GOSUB ENV_GET
K=A:GOSUB ENV_GET
GOTO EVAL_AST_RETURN
EVAL_AST_SEQ:
REM allocate the first entry
SZ%=2:GOSUB ALLOC
SZ=2:GOSUB ALLOC
REM make space on the stack
ZL%=ZL%+4
X=X+4
REM push type of sequence
ZZ%(ZL%-3)=T%
S%(X-3)=T
REM push sequence index
ZZ%(ZL%-2)=-1
S%(X-2)=-1
REM push future return value (new sequence)
ZZ%(ZL%-1)=R%
S%(X-1)=R
REM push previous new sequence entry
ZZ%(ZL%)=R%
S%(X)=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
Z%(R,0)=S%(X-3)+16
Z%(R,1)=0
REM create value ptr placeholder
Z%(R%+1,0)=14
Z%(R%+1,1)=0
Z%(R+1,0)=14
Z%(R+1,1)=0
REM update index
ZZ%(ZL%-2)=ZZ%(ZL%-2)+1
S%(X-2)=S%(X-2)+1
REM check if we are done evaluating the source sequence
IF Z%(A%,1)=0 THEN GOTO EVAL_AST_SEQ_LOOP_DONE
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
IF (S%(X-3)=8) AND ((S%(X-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
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
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%
Z%(S%(X)+1,1)=R
IF ER%<>-2 THEN GOTO EVAL_AST_SEQ_LOOP_DONE
IF ER<>-2 THEN GOTO EVAL_AST_SEQ_LOOP_DONE
REM allocate the next entry
SZ%=2:GOSUB ALLOC
SZ=2:GOSUB ALLOC
REM update previous sequence entry value to point to new entry
Z%(ZZ%(ZL%),1)=R%
Z%(S%(X),1)=R
REM update previous ptr to current entry
ZZ%(ZL%)=R%
S%(X)=R
REM process the next sequence entry from source list
A%=Z%(A%,1)
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%=-2 THEN R%=ZZ%(ZL%-1)
IF ER=-2 THEN R=S%(X-1)
REM otherwise, free the return value and return nil
IF ER%<>-2 THEN R%=0:AY%=ZZ%(ZL%-1):GOSUB RELEASE
IF ER<>-2 THEN R=0:AY=S%(X-1):GOSUB RELEASE
REM pop previous, return, index and type
ZL%=ZL%-4
X=X-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 A and E off the stack
E=S%(X-1):A=S%(X):X=X-2
REM pop EVAL AST return label/address
RN%=ZZ%(ZL%):ZL%=ZL%-1
RN%=S%(X):X=X-1
ON RN% GOTO EVAL_AST_RETURN_1,EVAL_AST_RETURN_2,EVAL_AST_RETURN_3
RETURN
REM EVAL(A%, E%)) -> R%
REM EVAL(A, E)) -> R
EVAL:
LV%=LV%+1: REM track basic return stack level
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%
REM push A and E on the stack
X=X+2:S%(X-1)=E:S%(X)=A
EVAL_TCO_RECUR:
REM AZ%=A%:PR%=1:GOSUB PR_STR
REM PRINT "EVAL: "+R$+" [A%:"+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
GOSUB LIST_Q
IF R% THEN GOTO APPLY_LIST
IF R THEN GOTO APPLY_LIST
EVAL_NOT_LIST:
REM ELSE
REM push EVAL_AST return label/address
ZL%=ZL%+1:ZZ%(ZL%)=1
X=X+1:S%(X)=1
GOTO EVAL_AST
EVAL_AST_RETURN_1:
@ -257,17 +257,17 @@ EVAL:
GOSUB MACROEXPAND
GOSUB LIST_Q
IF R%<>1 THEN GOTO EVAL_NOT_LIST
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
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%
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 (Z%(A0%,0)AND15)=5 THEN A$=S$(Z%(A0%,1))
IF A$="def!" THEN GOTO EVAL_DEF
IF A$="let*" THEN GOTO EVAL_LET
@ -282,237 +282,237 @@ EVAL:
GOTO EVAL_INVOKE
EVAL_GET_A3:
A3%=Z%(Z%(Z%(A%,1),1),1)+1
R%=A3%:GOSUB DEREF_R:A3%=R%
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%
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%
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%
X=X+1:S%(X)=A1%: REM push A1%
A=A2%:GOSUB EVAL: REM eval a2
A1%=S%(X):X=X-1: REM pop A1%
IF ER%<>-2 THEN GOTO EVAL_RETURN
IF ER<>-2 THEN GOTO EVAL_RETURN
REM set a1 in env to a2
K%=A1%:V%=R%:GOSUB ENV_SET
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
X=X+1:S%(X)=A2%: REM push/save A2%
X=X+1:S%(X)=E: REM push env for for later release
REM create new environment with outer as current environment
EO%=E%:GOSUB ENV_NEW
E%=R%
O=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%
X=X+1:S%(X)=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%
A=Z%(A1%,1)+1:GOSUB EVAL
A1%=S%(X):X=X-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
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
E4%=S%(X):X=X-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
IF E4%<>S%(X-2) THEN AY=E4%:GOSUB RELEASE
A2%=ZZ%(ZL%):ZL%=ZL%-1: REM pop A2%
A%=A2%:GOTO EVAL_TCO_RECUR: REM TCO loop
A2%=S%(X):X=X-1: REM pop A2%
A=A2%:GOTO EVAL_TCO_RECUR: REM TCO loop
EVAL_DO:
A%=Z%(A%,1): REM rest
A=Z%(A,1): REM rest
REM TODO: TCO
REM push EVAL_AST return label/address
ZL%=ZL%+1:ZZ%(ZL%)=2
X=X+1:S%(X)=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
X=X+1:S%(X)=R: REM push eval'd list
A=R:GOSUB LAST: REM return the last element
AY=S%(X):X=X-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
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
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%
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
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%
X=X+1:S%(X)=A1%: REM push A1%
A=A2%:GOSUB EVAL: REM eval a2
A1%=S%(X):X=X-1: REM pop A1%
REM change function to macro
Z%(R%,0)=Z%(R%,0)+1
Z%(R,0)=Z%(R,0)+1
REM set a1 in env to a2
K%=A1%:V%=R%:GOSUB ENV_SET
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%
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
Z%(R,0)=Z%(R,0)+16
GOTO EVAL_RETURN
EVAL_TRY:
REM PRINT "try*"
GOSUB EVAL_GET_A1: REM set a1%, a2%, and a3%
ZL%=ZL%+1:ZZ%(ZL%)=A%: REM push/save A%
A%=A1%:GOSUB EVAL: REM eval a1
A%=ZZ%(ZL%):ZL%=ZL%-1: REM pop/restore A%
X=X+1:S%(X)=A: REM push/save A
A=A1%:GOSUB EVAL: REM eval a1
A=S%(X):X=X-1: REM pop/restore A
REM if there is not error or catch block then return
IF ER%=-2 OR Z%(A%,1)=0 THEN GOTO EVAL_RETURN
IF ER=-2 OR Z%(A,1)=0 THEN GOTO EVAL_RETURN
REM create environment for the catch block eval
EO%=E%:GOSUB ENV_NEW:E%=R%
O=E:GOSUB ENV_NEW:E=R
GOSUB EVAL_GET_A2: REM set a1% and a2%
A%=A2%:GOSUB EVAL_GET_A2: REM set a1% and a2% from catch block
A=A2%:GOSUB EVAL_GET_A2: REM set a1% and a2% from catch block
REM create object for ER%=-1 type raw string errors
IF ER%=-1 THEN AS$=ER$:T%=4:GOSUB STRING:ER%=R%:Z%(R%,0)=Z%(R%,0)+16
REM create object for ER=-1 type raw string errors
IF ER=-1 THEN AS$=ER$:T=4:GOSUB STRING:ER=R:Z%(R,0)=Z%(R,0)+16
REM bind the catch symbol to the error object
K%=A1%:V%=ER%:GOSUB ENV_SET
AY%=R%:GOSUB RELEASE: REM release out use, env took ownership
K=A1%:V=ER:GOSUB ENV_SET
AY=R:GOSUB RELEASE: REM release out use, env took ownership
REM unset error for catch eval
ER%=-2:ER$=""
ER=-2:ER$=""
A%=A2%:GOSUB EVAL
A=A2%:GOSUB EVAL
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
REM push A
X=X+1:S%(X)=A
A=A1%:GOSUB EVAL
REM pop A
A=S%(X):X=X-1
IF (R=0) OR (R=1) THEN GOTO EVAL_IF_FALSE
EVAL_IF_TRUE:
AY%=R%:GOSUB RELEASE
AY=R:GOSUB RELEASE
GOSUB EVAL_GET_A2: REM set a1% and a2% after EVAL
A%=A2%:GOTO EVAL_TCO_RECUR: REM TCO loop
A=A2%:GOTO EVAL_TCO_RECUR: REM TCO loop
EVAL_IF_FALSE:
AY%=R%:GOSUB RELEASE
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
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
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
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
X=X+1:S%(X)=3
GOTO EVAL_AST
EVAL_AST_RETURN_3:
REM if error, return f/args for release by caller
IF ER%<>-2 THEN GOTO EVAL_RETURN
IF ER<>-2 THEN GOTO EVAL_RETURN
REM push f/args for release after call
ZL%=ZL%+1:ZZ%(ZL%)=R%
X=X+1:S%(X)=R
F%=R%+1
F=R+1
AR%=Z%(R%,1): REM rest
R%=F%:GOSUB DEREF_R:F%=R%
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
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
R=S%(X):X=X-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
AY=S%(X):X=X-1:GOSUB RELEASE
GOTO EVAL_RETURN
EVAL_DO_MAL_FUNCTION:
E4%=E%: REM save the current environment for release
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
O=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 stack (S%(X-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
IF E4%<>S%(X-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
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 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
AY=S%(X):X=X-1:GOSUB RELEASE
REM A% set above
E%=R%:GOTO EVAL_TCO_RECUR: REM TCO loop
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 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
IF E<>S%(X-1) THEN AY=E:GOSUB RELEASE
LV%=LV%-1: REM track basic return stack level
LV=LV-1: REM track basic return stack level
REM release everything we couldn't release earlier
GOSUB RELEASE_PEND
@ -520,51 +520,51 @@ EVAL:
REM trigger GC
TA%=FRE(0)
REM pop A% and E% off the stack
E%=ZZ%(ZL%-1):A%=ZZ%(ZL%):ZL%=ZL%-2
REM pop A and E off the stack
E=S%(X-1):A=S%(X):X=X-2
RETURN
REM PRINT(A%) -> R$
REM PRINT(A) -> R$
MAL_PRINT:
AZ%=A%:PR%=1:GOSUB PR_STR
AZ=A:PR=1:GOSUB PR_STR
RETURN
REM RE(A$) -> R%
REM RE(A$) -> R
REM Assume RE% has repl_env
REM caller must release result
RE:
R1%=0
R1=0
GOSUB MAL_READ
R1%=R%
IF ER%<>-2 THEN GOTO REP_DONE
R1=R
IF ER<>-2 THEN GOTO REP_DONE
A%=R%:E%=RE%:GOSUB EVAL
A=R:E=RE%:GOSUB EVAL
REP_DONE:
REM Release memory from MAL_READ
IF R1%<>0 THEN AY%=R1%:GOSUB RELEASE
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
R1=0:R2=0
GOSUB MAL_READ
R1%=R%
IF ER%<>-2 THEN GOTO REP_DONE
R1=R
IF ER<>-2 THEN GOTO REP_DONE
A%=R%:E%=RE%:GOSUB EVAL
R2%=R%
IF ER%<>-2 THEN GOTO REP_DONE
A=R:E=RE%:GOSUB EVAL
R2=R
IF ER<>-2 THEN GOTO REP_DONE
A%=R%:GOSUB MAL_PRINT
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
IF R2<>0 THEN AY=R2:GOSUB RELEASE
IF R1<>0 THEN AY=R1:GOSUB RELEASE
R$=RT$
RETURN
@ -572,55 +572,55 @@ REM MAIN program
MAIN:
GOSUB INIT_MEMORY
LV%=0
LV=0
REM create repl_env
EO%=-1:GOSUB ENV_NEW:RE%=R%
O=-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
E=RE%:GOSUB INIT_CORE_NS: REM set core functions in repl_env
ZT%=ZI%: REM top of memory after base 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
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
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
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
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
GOSUB RE:AY=R:GOSUB RELEASE
REM set the argument list
A$="(def! *ARGV* (rest -*ARGS*-))"
GOSUB RE:AY%=R%:GOSUB RELEASE
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
IF R<>0 THEN AY=R:GOSUB RELEASE:GOTO RUN_PROG
REM no arguments, start REPL loop
IF R%=0 THEN GOTO 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%<>-2 THEN GOSUB PRINT_ERROR
IF ER<>-2 THEN GOSUB PRINT_ERROR
END
REPL_LOOP:
@ -629,7 +629,7 @@ MAIN:
A$=R$:GOSUB REP: REM call REP
IF ER%<>-2 THEN GOSUB PRINT_ERROR:GOTO REPL_LOOP
IF ER<>-2 THEN GOSUB PRINT_ERROR:GOTO REPL_LOOP
PRINT R$
GOTO REPL_LOOP
@ -640,8 +640,8 @@ MAIN:
PRINT_ERROR:
REM if the error is an object, then print and free it
IF ER%>=0 THEN AZ%=ER%:PR%=0:GOSUB PR_STR:ER$=R$:AY%=ER%:GOSUB RELEASE
IF ER>=0 THEN AZ=ER:PR=0:GOSUB PR_STR:ER$=R$:AY=ER:GOSUB RELEASE
PRINT "Error: "+ER$
ER%=-2:ER$=""
ER=-2:ER$=""
RETURN

View File

@ -11,243 +11,243 @@ REM $INCLUDE: 'core.in.bas'
REM $INCLUDE: 'debug.in.bas'
REM READ(A$) -> R%
REM READ(A$) -> R
MAL_READ:
GOSUB READ_STR
RETURN
REM PAIR_Q(B%) -> R%
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
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%
REM QUASIQUOTE(A) -> R
QUASIQUOTE:
B%=A%:GOSUB PAIR_Q
IF R%=1 THEN GOTO QQ_UNQUOTE
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
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
R=A+1:GOSUB DEREF_R
IF (Z%(R,0)AND15)<>5 THEN GOTO QQ_SPLICE_UNQUOTE
IF S$(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
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 push A on the stack
X=X+1:S%(X)=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
A=Z%(A,1):GOSUB QUASIQUOTE:T6=R
REM pop A off the stack
A=S%(X):X=X-1
REM set A% to ast[0] for last two cases
A%=A%+1:GOSUB DEREF_A
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
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 S$(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
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
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
REM push T6 on the stack
X=X+1:S%(X)=T6
REM A set above to ast[0]
GOSUB QUASIQUOTE:B2%=R
REM pop T6 off the stack
T6=S%(X):X=X-1
AS$="cons":T%=5:GOSUB STRING:B3%=R%
B1%=T6%:GOSUB LIST3
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
AY=B1%:GOSUB RELEASE
AY=B2%:GOSUB RELEASE
RETURN
REM MACROEXPAND(A%, E%) -> A%:
REM MACROEXPAND(A, E) -> A:
MACROEXPAND:
REM push original A%
ZL%=ZL%+1:ZZ%(ZL%)=A%
REM push original A
X=X+1:S%(X)=A
MACROEXPAND_LOOP:
REM list?
IF (Z%(A%,0)AND15)<>6 THEN GOTO MACROEXPAND_DONE
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
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
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
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
IF (Z%(B,0)AND15)<>11 THEN GOTO MACROEXPAND_DONE
REM apply
F%=B%:AR%=Z%(A%,1):GOSUB APPLY
A%=R%
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
AY=S%(X)
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 A<>AY THEN ZM%=ZM%+1:ZR%(ZM%,0)=A:ZR%(ZM%,1)=LV
IF ER%<>-2 THEN GOTO MACROEXPAND_DONE
IF ER<>-2 THEN GOTO MACROEXPAND_DONE
GOTO MACROEXPAND_LOOP
MACROEXPAND_DONE:
ZL%=ZL%-1: REM pop original A%
X=X-1: REM pop original A
RETURN
REM EVAL_AST(A%, E%) -> R%
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%
REM push A and E on the stack
X=X+2:S%(X-1)=E:S%(X)=A
IF ER%<>-2 THEN GOTO EVAL_AST_RETURN
IF ER<>-2 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
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
R=A:GOSUB DEREF_R
Z%(R,0)=Z%(R,0)+16
GOTO EVAL_AST_RETURN
EVAL_AST_SYMBOL:
K%=A%:GOSUB ENV_GET
K=A:GOSUB ENV_GET
GOTO EVAL_AST_RETURN
EVAL_AST_SEQ:
REM allocate the first entry
SZ%=2:GOSUB ALLOC
SZ=2:GOSUB ALLOC
REM make space on the stack
ZL%=ZL%+4
X=X+4
REM push type of sequence
ZZ%(ZL%-3)=T%
S%(X-3)=T
REM push sequence index
ZZ%(ZL%-2)=-1
S%(X-2)=-1
REM push future return value (new sequence)
ZZ%(ZL%-1)=R%
S%(X-1)=R
REM push previous new sequence entry
ZZ%(ZL%)=R%
S%(X)=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
Z%(R,0)=S%(X-3)+16
Z%(R,1)=0
REM create value ptr placeholder
Z%(R%+1,0)=14
Z%(R%+1,1)=0
Z%(R+1,0)=14
Z%(R+1,1)=0
REM update index
ZZ%(ZL%-2)=ZZ%(ZL%-2)+1
S%(X-2)=S%(X-2)+1
REM check if we are done evaluating the source sequence
IF Z%(A%,1)=0 THEN GOTO EVAL_AST_SEQ_LOOP_DONE
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
IF (S%(X-3)=8) AND ((S%(X-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
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
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%
Z%(S%(X)+1,1)=R
IF ER%<>-2 THEN GOTO EVAL_AST_SEQ_LOOP_DONE
IF ER<>-2 THEN GOTO EVAL_AST_SEQ_LOOP_DONE
REM allocate the next entry
SZ%=2:GOSUB ALLOC
SZ=2:GOSUB ALLOC
REM update previous sequence entry value to point to new entry
Z%(ZZ%(ZL%),1)=R%
Z%(S%(X),1)=R
REM update previous ptr to current entry
ZZ%(ZL%)=R%
S%(X)=R
REM process the next sequence entry from source list
A%=Z%(A%,1)
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%=-2 THEN R%=ZZ%(ZL%-1)
IF ER=-2 THEN R=S%(X-1)
REM otherwise, free the return value and return nil
IF ER%<>-2 THEN R%=0:AY%=ZZ%(ZL%-1):GOSUB RELEASE
IF ER<>-2 THEN R=0:AY=S%(X-1):GOSUB RELEASE
REM pop previous, return, index and type
ZL%=ZL%-4
X=X-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 A and E off the stack
E=S%(X-1):A=S%(X):X=X-2
REM pop EVAL AST return label/address
RN%=ZZ%(ZL%):ZL%=ZL%-1
RN%=S%(X):X=X-1
ON RN% GOTO EVAL_AST_RETURN_1,EVAL_AST_RETURN_2,EVAL_AST_RETURN_3
RETURN
REM EVAL(A%, E%)) -> R%
REM EVAL(A, E)) -> R
EVAL:
LV%=LV%+1: REM track basic return stack level
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%
REM push A and E on the stack
X=X+2:S%(X-1)=E:S%(X)=A
EVAL_TCO_RECUR:
REM AZ%=A%:PR%=1:GOSUB PR_STR
REM PRINT "EVAL: "+R$+" [A%:"+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
GOSUB LIST_Q
IF R% THEN GOTO APPLY_LIST
IF R THEN GOTO APPLY_LIST
EVAL_NOT_LIST:
REM ELSE
REM push EVAL_AST return label/address
ZL%=ZL%+1:ZZ%(ZL%)=1
X=X+1:S%(X)=1
GOTO EVAL_AST
EVAL_AST_RETURN_1:
@ -257,17 +257,17 @@ EVAL:
GOSUB MACROEXPAND
GOSUB LIST_Q
IF R%<>1 THEN GOTO EVAL_NOT_LIST
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
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%
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 (Z%(A0%,0)AND15)=5 THEN A$=S$(Z%(A0%,1))
IF A$="def!" THEN GOTO EVAL_DEF
IF A$="let*" THEN GOTO EVAL_LET
@ -282,237 +282,237 @@ EVAL:
GOTO EVAL_INVOKE
EVAL_GET_A3:
A3%=Z%(Z%(Z%(A%,1),1),1)+1
R%=A3%:GOSUB DEREF_R:A3%=R%
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%
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%
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%
X=X+1:S%(X)=A1%: REM push A1%
A=A2%:GOSUB EVAL: REM eval a2
A1%=S%(X):X=X-1: REM pop A1%
IF ER%<>-2 THEN GOTO EVAL_RETURN
IF ER<>-2 THEN GOTO EVAL_RETURN
REM set a1 in env to a2
K%=A1%:V%=R%:GOSUB ENV_SET
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
X=X+1:S%(X)=A2%: REM push/save A2%
X=X+1:S%(X)=E: REM push env for for later release
REM create new environment with outer as current environment
EO%=E%:GOSUB ENV_NEW
E%=R%
O=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%
X=X+1:S%(X)=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%
A=Z%(A1%,1)+1:GOSUB EVAL
A1%=S%(X):X=X-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
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
E4%=S%(X):X=X-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
IF E4%<>S%(X-2) THEN AY=E4%:GOSUB RELEASE
A2%=ZZ%(ZL%):ZL%=ZL%-1: REM pop A2%
A%=A2%:GOTO EVAL_TCO_RECUR: REM TCO loop
A2%=S%(X):X=X-1: REM pop A2%
A=A2%:GOTO EVAL_TCO_RECUR: REM TCO loop
EVAL_DO:
A%=Z%(A%,1): REM rest
A=Z%(A,1): REM rest
REM TODO: TCO
REM push EVAL_AST return label/address
ZL%=ZL%+1:ZZ%(ZL%)=2
X=X+1:S%(X)=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
X=X+1:S%(X)=R: REM push eval'd list
A=R:GOSUB LAST: REM return the last element
AY=S%(X):X=X-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
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
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%
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
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%
X=X+1:S%(X)=A1%: REM push A1%
A=A2%:GOSUB EVAL: REM eval a2
A1%=S%(X):X=X-1: REM pop A1%
REM change function to macro
Z%(R%,0)=Z%(R%,0)+1
Z%(R,0)=Z%(R,0)+1
REM set a1 in env to a2
K%=A1%:V%=R%:GOSUB ENV_SET
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%
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
Z%(R,0)=Z%(R,0)+16
GOTO EVAL_RETURN
EVAL_TRY:
REM PRINT "try*"
GOSUB EVAL_GET_A1: REM set a1%, a2%, and a3%
ZL%=ZL%+1:ZZ%(ZL%)=A%: REM push/save A%
A%=A1%:GOSUB EVAL: REM eval a1
A%=ZZ%(ZL%):ZL%=ZL%-1: REM pop/restore A%
X=X+1:S%(X)=A: REM push/save A
A=A1%:GOSUB EVAL: REM eval a1
A=S%(X):X=X-1: REM pop/restore A
REM if there is not error or catch block then return
IF ER%=-2 OR Z%(A%,1)=0 THEN GOTO EVAL_RETURN
IF ER=-2 OR Z%(A,1)=0 THEN GOTO EVAL_RETURN
REM create environment for the catch block eval
EO%=E%:GOSUB ENV_NEW:E%=R%
O=E:GOSUB ENV_NEW:E=R
GOSUB EVAL_GET_A2: REM set a1% and a2%
A%=A2%:GOSUB EVAL_GET_A2: REM set a1% and a2% from catch block
A=A2%:GOSUB EVAL_GET_A2: REM set a1% and a2% from catch block
REM create object for ER%=-1 type raw string errors
IF ER%=-1 THEN AS$=ER$:T%=4:GOSUB STRING:ER%=R%:Z%(R%,0)=Z%(R%,0)+16
REM create object for ER=-1 type raw string errors
IF ER=-1 THEN AS$=ER$:T=4:GOSUB STRING:ER=R:Z%(R,0)=Z%(R,0)+16
REM bind the catch symbol to the error object
K%=A1%:V%=ER%:GOSUB ENV_SET
AY%=R%:GOSUB RELEASE: REM release out use, env took ownership
K=A1%:V=ER:GOSUB ENV_SET
AY=R:GOSUB RELEASE: REM release out use, env took ownership
REM unset error for catch eval
ER%=-2:ER$=""
ER=-2:ER$=""
A%=A2%:GOSUB EVAL
A=A2%:GOSUB EVAL
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
REM push A
X=X+1:S%(X)=A
A=A1%:GOSUB EVAL
REM pop A
A=S%(X):X=X-1
IF (R=0) OR (R=1) THEN GOTO EVAL_IF_FALSE
EVAL_IF_TRUE:
AY%=R%:GOSUB RELEASE
AY=R:GOSUB RELEASE
GOSUB EVAL_GET_A2: REM set a1% and a2% after EVAL
A%=A2%:GOTO EVAL_TCO_RECUR: REM TCO loop
A=A2%:GOTO EVAL_TCO_RECUR: REM TCO loop
EVAL_IF_FALSE:
AY%=R%:GOSUB RELEASE
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
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
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
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
X=X+1:S%(X)=3
GOTO EVAL_AST
EVAL_AST_RETURN_3:
REM if error, return f/args for release by caller
IF ER%<>-2 THEN GOTO EVAL_RETURN
IF ER<>-2 THEN GOTO EVAL_RETURN
REM push f/args for release after call
ZL%=ZL%+1:ZZ%(ZL%)=R%
X=X+1:S%(X)=R
F%=R%+1
F=R+1
AR%=Z%(R%,1): REM rest
R%=F%:GOSUB DEREF_R:F%=R%
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
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
R=S%(X):X=X-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
AY=S%(X):X=X-1:GOSUB RELEASE
GOTO EVAL_RETURN
EVAL_DO_MAL_FUNCTION:
E4%=E%: REM save the current environment for release
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
O=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 stack (S%(X-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
IF E4%<>S%(X-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
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 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
AY=S%(X):X=X-1:GOSUB RELEASE
REM A% set above
E%=R%:GOTO EVAL_TCO_RECUR: REM TCO loop
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 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
IF E<>S%(X-1) THEN AY=E:GOSUB RELEASE
LV%=LV%-1: REM track basic return stack level
LV=LV-1: REM track basic return stack level
REM release everything we couldn't release earlier
GOSUB RELEASE_PEND
@ -520,51 +520,51 @@ EVAL:
REM trigger GC
TA%=FRE(0)
REM pop A% and E% off the stack
E%=ZZ%(ZL%-1):A%=ZZ%(ZL%):ZL%=ZL%-2
REM pop A and E off the stack
E=S%(X-1):A=S%(X):X=X-2
RETURN
REM PRINT(A%) -> R$
REM PRINT(A) -> R$
MAL_PRINT:
AZ%=A%:PR%=1:GOSUB PR_STR
AZ=A:PR=1:GOSUB PR_STR
RETURN
REM RE(A$) -> R%
REM RE(A$) -> R
REM Assume RE% has repl_env
REM caller must release result
RE:
R1%=0
R1=0
GOSUB MAL_READ
R1%=R%
IF ER%<>-2 THEN GOTO REP_DONE
R1=R
IF ER<>-2 THEN GOTO REP_DONE
A%=R%:E%=RE%:GOSUB EVAL
A=R:E=RE%:GOSUB EVAL
REP_DONE:
REM Release memory from MAL_READ
IF R1%<>0 THEN AY%=R1%:GOSUB RELEASE
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
R1=0:R2=0
GOSUB MAL_READ
R1%=R%
IF ER%<>-2 THEN GOTO REP_DONE
R1=R
IF ER<>-2 THEN GOTO REP_DONE
A%=R%:E%=RE%:GOSUB EVAL
R2%=R%
IF ER%<>-2 THEN GOTO REP_DONE
A=R:E=RE%:GOSUB EVAL
R2=R
IF ER<>-2 THEN GOTO REP_DONE
A%=R%:GOSUB MAL_PRINT
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
IF R2<>0 THEN AY=R2:GOSUB RELEASE
IF R1<>0 THEN AY=R1:GOSUB RELEASE
R$=RT$
RETURN
@ -572,65 +572,65 @@ REM MAIN program
MAIN:
GOSUB INIT_MEMORY
LV%=0
LV=0
REM create repl_env
EO%=-1:GOSUB ENV_NEW:RE%=R%
O=-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
E=RE%:GOSUB INIT_CORE_NS: REM set core functions in repl_env
ZT%=ZI%: REM top of memory after base repl_env
ZT%=ZI: REM top of memory after base repl_env
REM core.mal: defined using the language itself
A$="(def! *host-language* "+CHR$(34)+"C64 Basic"+CHR$(34)+")"
GOSUB RE:AY%=R%:GOSUB RELEASE
GOSUB RE:AY=R:GOSUB RELEASE
A$="(def! not (fn* (a) (if a false true)))"
GOSUB RE:AY%=R%:GOSUB RELEASE
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
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
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
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
GOSUB RE:AY=R:GOSUB RELEASE
REM set the argument list
A$="(def! *ARGV* (rest -*ARGS*-))"
GOSUB RE:AY%=R%:GOSUB RELEASE
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
IF R<>0 THEN AY=R:GOSUB RELEASE:GOTO RUN_PROG
REM no arguments, start REPL loop
IF R%=0 THEN GOTO REPL
IF R=0 THEN GOTO REPL
RUN_PROG:
REM run a single mal program and exit
A$="(load-file (first -*ARGS*-))"
GOSUB RE
IF ER%<>-2 THEN GOSUB PRINT_ERROR
IF ER<>-2 THEN GOSUB PRINT_ERROR
END
REPL:
REM print the REPL startup header
A$="(println (str "+CHR$(34)+"Mal ["+CHR$(34)+" *host-language* "
A$=A$+CHR$(34)+"]"+CHR$(34)+"))"
GOSUB RE:AY%=R%:GOSUB RELEASE
GOSUB RE:AY=R:GOSUB RELEASE
REPL_LOOP:
A$="user> ":GOSUB READLINE: REM call input parser
@ -638,7 +638,7 @@ MAIN:
A$=R$:GOSUB REP: REM call REP
IF ER%<>-2 THEN GOSUB PRINT_ERROR:GOTO REPL_LOOP
IF ER<>-2 THEN GOSUB PRINT_ERROR:GOTO REPL_LOOP
PRINT R$
GOTO REPL_LOOP
@ -649,8 +649,8 @@ MAIN:
PRINT_ERROR:
REM if the error is an object, then print and free it
IF ER%>=0 THEN AZ%=ER%:PR%=0:GOSUB PR_STR:ER$=R$:AY%=ER%:GOSUB RELEASE
IF ER>=0 THEN AZ=ER:PR=0:GOSUB PR_STR:ER$=R$:AY=ER:GOSUB RELEASE
PRINT "Error: "+ER$
ER%=-2:ER$=""
ER=-2:ER$=""
RETURN

View File

@ -3,8 +3,8 @@ REM nil 0 -> (unused)
REM boolean 1 -> 0: false, 1: true
REM integer 2 -> int value
REM float 3 -> ???
REM string/kw 4 -> ZS$ index
REM symbol 5 -> ZS$ index
REM string/kw 4 -> S$ index
REM symbol 5 -> S$ index
REM list next/val 6 -> next Z% index (0 for last)
REM followed by value (unless empty)
REM vector next/val 7 -> next Z% index (0 for last)
@ -23,22 +23,22 @@ REM reference/ptr 14 -> Z% index / or 0
REM next free ptr 15 -> Z% index / or 0
INIT_MEMORY:
T%=FRE(0)
T=FRE(0)
S1%=2048+512: REM Z% (boxed memory) size (4 bytes each)
S2%=256: REM ZS% (string memory) size (3 bytes each)
S3%=256: REM ZZ% (call stack) size (2 bytes each)
S4%=64: REM ZR% (release stack) size (4 bytes each)
Z1=2048+512: REM Z% (boxed memory) size (4 bytes each)
Z2=256: REM S$ (string memory) size (3 bytes each)
Z3=256: REM S% (call stack) size (2 bytes each)
Z4=64: REM ZR% (release stack) size (4 bytes each)
REM global error state
REM -2 : no error
REM -1 : string error in ER$
REM >=0 : pointer to error object
ER%=-2
ER=-2
ER$=""
REM boxed element memory
DIM Z%(S1%,1): REM TYPE ARRAY
DIM Z%(Z1,1): REM TYPE ARRAY
REM Predefine nil, false, true, and an empty list
Z%(0,0)=0:Z%(0,1)=0
@ -48,188 +48,188 @@ INIT_MEMORY:
Z%(4,0)=0:Z%(4,1)=0
REM start of unused memory
ZI%=5
ZI=5
REM start of free list
ZK%=5
ZK=5
REM string memory storage
ZJ%=0:DIM ZS$(S2%)
ZJ=0:DIM S$(Z2)
REM call/logic stack
ZL%=-1:DIM ZZ%(S3%): REM stack of Z% indexes
X=-1:DIM S%(Z3): REM stack of Z% indexes
REM pending release stack
ZM%=-1:DIM ZR%(S4%,1): REM stack of Z% indexes
ZM%=-1:DIM ZR%(Z4,1): REM stack of Z% indexes
REM PRINT "Lisp data memory: "+STR$(T%-FRE(0))
REM PRINT "Lisp data memory: "+STR$(T-FRE(0))
REM PRINT "Interpreter working memory: "+STR$(FRE(0))
RETURN
REM memory functions
REM ALLOC(SZ%) -> R%
REM ALLOC(SZ) -> R
ALLOC:
REM PRINT "ALLOC SZ%: "+STR$(SZ%)+", ZK%: "+STR$(ZK%)
U3%=ZK%
U4%=ZK%
REM PRINT "ALLOC SZ: "+STR$(SZ)+", ZK: "+STR$(ZK)
U3=ZK
U4=ZK
ALLOC_LOOP:
IF U4%=ZI% THEN GOTO ALLOC_UNUSED
IF U4=ZI THEN GOTO ALLOC_UNUSED
REM TODO sanity check that type is 15
IF ((Z%(U4%,0)AND-16)/16)=SZ% THEN GOTO ALLOC_MIDDLE
REM PRINT "ALLOC search: U3%: "+STR$(U3%)+", U4%: "+STR$(U4%)
U3%=U4%: REM previous set to current
U4%=Z%(U4%,1): REM current set to next
IF ((Z%(U4,0)AND-16)/16)=SZ THEN GOTO ALLOC_MIDDLE
REM PRINT "ALLOC search: U3: "+STR$(U3)+", U4: "+STR$(U4)
U3=U4: REM previous set to current
U4=Z%(U4,1): REM current set to next
GOTO ALLOC_LOOP
ALLOC_MIDDLE:
REM PRINT "ALLOC_MIDDLE: U3%: "+STR$(U3%)+", U4%: "+STR$(U4%)
R%=U4%
REM set free pointer (ZK%) to next free
IF U4%=ZK% THEN ZK%=Z%(U4%,1)
REM PRINT "ALLOC_MIDDLE: U3: "+STR$(U3)+", U4: "+STR$(U4)
R=U4
REM set free pointer (ZK) to next free
IF U4=ZK THEN ZK=Z%(U4,1)
REM set previous free to next free
IF U4%<>ZK% THEN Z%(U3%,1)=Z%(U4%,1)
IF U4<>ZK THEN Z%(U3,1)=Z%(U4,1)
RETURN
ALLOC_UNUSED:
REM PRINT "ALLOC_UNUSED ZI%: "+STR$(ZI%)+", U3%: "+STR$(U3%)+", U4%: "+STR$(U4%)
R%=U4%
ZI%=ZI%+SZ%
IF U3%=U4% THEN ZK%=ZI%
REM PRINT "ALLOC_UNUSED ZI: "+STR$(ZI)+", U3: "+STR$(U3)+", U4: "+STR$(U4)
R=U4
ZI=ZI+SZ
IF U3=U4 THEN ZK=ZI
REM set previous free to new memory top
IF U3%<>U4% THEN Z%(U3%,1)=ZI%
IF U3<>U4 THEN Z%(U3,1)=ZI
RETURN
REM FREE(AY%, SZ%) -> nil
REM FREE(AY, SZ) -> nil
FREE:
REM assumes reference count cleanup already (see RELEASE)
Z%(AY%,0)=(SZ%*16)+15: REM set type(15) and size
Z%(AY%,1)=ZK%
ZK%=AY%
IF SZ%>=2 THEN Z%(AY%+1,0)=0:Z%(AY%+1,1)=0
IF SZ%>=3 THEN Z%(AY%+2,0)=0:Z%(AY%+2,1)=0
Z%(AY,0)=(SZ*16)+15: REM set type(15) and size
Z%(AY,1)=ZK
ZK=AY
IF SZ>=2 THEN Z%(AY+1,0)=0:Z%(AY+1,1)=0
IF SZ>=3 THEN Z%(AY+2,0)=0:Z%(AY+2,1)=0
RETURN
REM RELEASE(AY%) -> nil
REM R% should not be affected by this call
REM RELEASE(AY) -> nil
REM R should not be affected by this call
RELEASE:
RC%=0
RC=0
GOTO RELEASE_ONE
RELEASE_TOP:
IF RC%=0 THEN RETURN
IF RC=0 THEN RETURN
REM pop next object to release, decrease remaining count
AY%=ZZ%(ZL%):ZL%=ZL%-1
RC%=RC%-1
AY=S%(X):X=X-1
RC=RC-1
RELEASE_ONE:
REM nil, false, true
IF AY%<3 THEN GOTO RELEASE_TOP
IF AY<3 THEN GOTO RELEASE_TOP
U6%=Z%(AY%,0)AND15: REM type
U6=Z%(AY,0)AND15: REM type
REM AZ%=AY%: PR%=1: GOSUB PR_STR
REM PRINT "RELEASE AY%:"+STR$(AY%)+"["+R$+"] (byte0:"+STR$(Z%(AY%,0))+")"
REM AZ=AY: PR=1: GOSUB PR_STR
REM PRINT "RELEASE AY:"+STR$(AY)+"["+R$+"] (byte0:"+STR$(Z%(AY,0))+")"
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
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
Z%(AY%,0)=Z%(AY%,0)-16
Z%(AY,0)=Z%(AY,0)-16
REM our reference count is not 0, so don't release
IF Z%(AY%,0)>=16 GOTO RELEASE_TOP
IF Z%(AY,0)>=16 GOTO RELEASE_TOP
REM switch on type
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%=15 THEN ER%=-1:ER$="RELEASE of already freed: "+STR$(AY%):RETURN
ER%=-1:ER$="RELEASE not defined for type "+STR$(U6%):RETURN
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=15 THEN ER=-1:ER$="RELEASE of already freed: "+STR$(AY):RETURN
ER=-1:ER$="RELEASE not defined for type "+STR$(U6):RETURN
RELEASE_SIMPLE:
REM simple type (no recursing), just call FREE on it
SZ%=1:GOSUB FREE
SZ=1:GOSUB FREE
GOTO RELEASE_TOP
RELEASE_SIMPLE_2:
REM free the current element and continue
SZ%=2:GOSUB FREE
SZ=2:GOSUB FREE
GOTO RELEASE_TOP
RELEASE_SEQ:
IF Z%(AY%,1)=0 THEN GOTO RELEASE_SIMPLE_2
IF Z%(AY%+1,0)<>14 THEN ER%=-1:ER$="invalid list value"+STR$(AY%+1):RETURN
IF Z%(AY,1)=0 THEN GOTO RELEASE_SIMPLE_2
IF Z%(AY+1,0)<>14 THEN ER=-1:ER$="invalid list value"+STR$(AY+1):RETURN
REM add value and next element to stack
RC%=RC%+2:ZL%=ZL%+2:ZZ%(ZL%-1)=Z%(AY%+1,1):ZZ%(ZL%)=Z%(AY%,1)
RC=RC+2:X=X+2:S%(X-1)=Z%(AY+1,1):S%(X)=Z%(AY,1)
GOTO RELEASE_SIMPLE_2
RELEASE_ATOM:
REM add contained/referred value
RC%=RC%+1:ZL%=ZL%+1:ZZ%(ZL%)=Z%(AY%,1)
RC=RC+1:X=X+1:S%(X)=Z%(AY,1)
REM free the atom itself
GOTO RELEASE_SIMPLE
RELEASE_MAL_FUNCTION:
REM add ast, params and environment to stack
RC%=RC%+3:ZL%=ZL%+3
ZZ%(ZL%-2)=Z%(AY%,1):ZZ%(ZL%-1)=Z%(AY%+1,0):ZZ%(ZL%)=Z%(AY%+1,1)
RC=RC+3:X=X+3
S%(X-2)=Z%(AY,1):S%(X-1)=Z%(AY+1,0):S%(X)=Z%(AY+1,1)
REM free the current 2 element mal_function and continue
SZ%=2:GOSUB FREE
SZ=2:GOSUB FREE
GOTO RELEASE_TOP
RELEASE_ENV:
REM add the hashmap data to the stack
RC%=RC%+1:ZL%=ZL%+1:ZZ%(ZL%)=Z%(AY%,1)
RC=RC+1:X=X+1:S%(X)=Z%(AY,1)
REM if no outer set
IF Z%(AY%+1,1)=-1 THEN GOTO RELEASE_ENV_FREE
IF Z%(AY+1,1)=-1 THEN GOTO RELEASE_ENV_FREE
REM add outer environment to the stack
RC%=RC%+1:ZL%=ZL%+1:ZZ%(ZL%)=Z%(AY%+1,1)
RC=RC+1:X=X+1:S%(X)=Z%(AY+1,1)
RELEASE_ENV_FREE:
REM free the current 2 element environment and continue
SZ%=2:GOSUB FREE
SZ=2:GOSUB FREE
GOTO RELEASE_TOP
RELEASE_REFERENCE:
IF Z%(AY%,1)=0 THEN GOTO RELEASE_SIMPLE
IF Z%(AY,1)=0 THEN GOTO RELEASE_SIMPLE
REM add the referred element to the stack
RC%=RC%+1:ZL%=ZL%+1:ZZ%(ZL%)=Z%(AY%,1)
RC=RC+1:X=X+1:S%(X)=Z%(AY,1)
REM free the current element and continue
SZ%=1:GOSUB FREE
SZ=1:GOSUB FREE
GOTO RELEASE_TOP
REM RELEASE_PEND(LV%) -> nil
REM RELEASE_PEND(LV) -> nil
RELEASE_PEND:
IF ZM%<0 THEN RETURN
IF ZR%(ZM%,1)<=LV% THEN RETURN
IF ZR%(ZM%,1)<=LV THEN RETURN
REM PRINT "RELEASE_PEND releasing:"+STR$(ZR%(ZM%,0))
AY%=ZR%(ZM%,0):GOSUB RELEASE
AY=ZR%(ZM%,0):GOSUB RELEASE
ZM%=ZM%-1
GOTO RELEASE_PEND
REM DEREF_R(R%) -> R%
REM DEREF_R(R) -> R
DEREF_R:
IF (Z%(R%,0)AND15)=14 THEN R%=Z%(R%,1):GOTO DEREF_R
IF (Z%(R,0)AND15)=14 THEN R=Z%(R,1):GOTO DEREF_R
RETURN
REM DEREF_A(A%) -> A%
REM DEREF_A(A) -> A
DEREF_A:
IF (Z%(A%,0)AND15)=14 THEN A%=Z%(A%,1):GOTO DEREF_A
IF (Z%(A,0)AND15)=14 THEN A=Z%(A,1):GOTO DEREF_A
RETURN
REM DEREF_B(B%) -> B%
REM DEREF_B(B) -> B
DEREF_B:
IF (Z%(B%,0)AND15)=14 THEN B%=Z%(B%,1):GOTO DEREF_B
IF (Z%(B,0)AND15)=14 THEN B=Z%(B,1):GOTO DEREF_B
RETURN
CHECK_FREE_LIST:
REM start and accumulator
P1%=ZK%
P1%=ZK
P2%=0
CHECK_FREE_LIST_LOOP:
IF P1%>=ZI% THEN GOTO CHECK_FREE_LIST_DONE
IF P1%>=ZI THEN GOTO CHECK_FREE_LIST_DONE
IF (Z%(P1%,0)AND15)<>15 THEN P2%=-1:GOTO CHECK_FREE_LIST_DONE
P2%=P2%+(Z%(P1%,0)AND-16)/16
P1%=Z%(P1%,1)
@ -241,66 +241,66 @@ CHECK_FREE_LIST:
REM general functions
REM EQUAL_Q(A%, B%) -> R%
REM EQUAL_Q(A, B) -> R
EQUAL_Q:
GOSUB DEREF_A
GOSUB DEREF_B
R%=0
U1%=(Z%(A%,0)AND15)
U2%=(Z%(B%,0)AND15)
IF NOT ((U1%=U2%) OR ((U1%=6 OR U1%=7) AND (U2%=6 OR U2%=7))) THEN RETURN
IF U1%=6 THEN GOTO EQUAL_Q_SEQ
IF U1%=7 THEN GOTO EQUAL_Q_SEQ
IF U1%=8 THEN GOTO EQUAL_Q_HM
R=0
U1=(Z%(A,0)AND15)
U2=(Z%(B,0)AND15)
IF NOT ((U1=U2) OR ((U1=6 OR U1=7) AND (U2=6 OR U2=7))) THEN RETURN
IF U1=6 THEN GOTO EQUAL_Q_SEQ
IF U1=7 THEN GOTO EQUAL_Q_SEQ
IF U1=8 THEN GOTO EQUAL_Q_HM
IF Z%(A%,1)=Z%(B%,1) THEN R%=1
IF Z%(A,1)=Z%(B,1) THEN R=1
RETURN
EQUAL_Q_SEQ:
IF (Z%(A%,1)=0) AND (Z%(B%,1)=0) THEN R%=1:RETURN
IF (Z%(A%,1)=0) OR (Z%(B%,1)=0) THEN R%=0:RETURN
IF (Z%(A,1)=0) AND (Z%(B,1)=0) THEN R=1:RETURN
IF (Z%(A,1)=0) OR (Z%(B,1)=0) THEN R=0:RETURN
REM push A% and B%
ZL%=ZL%+2:ZZ%(ZL%-1)=A%:ZZ%(ZL%)=B%
REM push A and B
X=X+2:S%(X-1)=A:S%(X)=B
REM compare the elements
A%=Z%(A%+1,1):B%=Z%(B%+1,1):GOSUB EQUAL_Q
REM pop A% and B%
A%=ZZ%(ZL%-1):B%=ZZ%(ZL%):ZL%=ZL%-2
IF R%=0 THEN RETURN
A=Z%(A+1,1):B=Z%(B+1,1):GOSUB EQUAL_Q
REM pop A and B
A=S%(X-1):B=S%(X):X=X-2
IF R=0 THEN RETURN
REM next elements of the sequences
A%=Z%(A%,1):B%=Z%(B%,1):GOTO EQUAL_Q_SEQ
A=Z%(A,1):B=Z%(B,1):GOTO EQUAL_Q_SEQ
EQUAL_Q_HM:
R%=0
R=0
RETURN
REM string functions
REM STRING_(AS$) -> R%
REM STRING_(AS$) -> R
REM intern string (returns string index, not Z% index)
STRING_:
IF ZJ%=0 THEN GOTO STRING_NOT_FOUND
IF ZJ=0 THEN GOTO STRING_NOT_FOUND
REM search for matching string in ZS$
FOR I=0 TO ZJ%-1
IF AS$=ZS$(I) THEN R%=I:RETURN
REM search for matching string in S$
FOR I=0 TO ZJ-1
IF AS$=S$(I) THEN R=I:RETURN
NEXT I
STRING_NOT_FOUND:
ZS$(ZJ%)=AS$
R%=ZJ%
ZJ%=ZJ%+1
S$(ZJ)=AS$
R=ZJ
ZJ=ZJ+1
RETURN
REM STRING(AS$, T%) -> R%
REM STRING(AS$, T) -> R
REM intern string and allocate reference (return Z% index)
STRING:
GOSUB STRING_
TS%=R%
SZ%=1:GOSUB ALLOC
Z%(R%,0)=T%
Z%(R%,1)=TS%
TS%=R
SZ=1:GOSUB ALLOC
Z%(R,0)=T
Z%(R,1)=TS%
RETURN
REM REPLACE(R$, S1$, S2$) -> R$
@ -319,211 +319,211 @@ REPLACE:
REM list functions
REM LIST_Q(A%) -> R%
REM LIST_Q(A) -> R
LIST_Q:
R%=0
IF (Z%(A%,0)AND15)=6 THEN R%=1
R=0
IF (Z%(A,0)AND15)=6 THEN R=1
RETURN
REM EMPTY_Q(A%) -> R%
REM EMPTY_Q(A) -> R
EMPTY_Q:
R%=0
IF Z%(A%,1)=0 THEN R%=1
R=0
IF Z%(A,1)=0 THEN R=1
RETURN
REM COUNT(A%) -> R%
REM COUNT(A) -> R
COUNT:
R%=-1
R=-1
DO_COUNT_LOOP:
R%=R%+1
IF Z%(A%,1)<>0 THEN A%=Z%(A%,1):GOTO DO_COUNT_LOOP
R=R+1
IF Z%(A,1)<>0 THEN A=Z%(A,1):GOTO DO_COUNT_LOOP
RETURN
REM LAST(A%) -> R%
REM LAST(A) -> R
LAST:
REM TODO check that actually a list/vector
IF Z%(A%,1)=0 THEN R%=0:RETURN: REM empty seq, return nil
T6%=0
IF Z%(A,1)=0 THEN R=0:RETURN: REM empty seq, return nil
T6=0
LAST_LOOP:
IF Z%(A%,1)=0 THEN GOTO LAST_DONE: REM end, return previous value
T6%=A%: REM current becomes previous entry
A%=Z%(A%,1): REM next entry
IF Z%(A,1)=0 THEN GOTO LAST_DONE: REM end, return previous value
T6=A: REM current becomes previous entry
A=Z%(A,1): REM next entry
GOTO LAST_LOOP
LAST_DONE:
R%=T6%+1:GOSUB DEREF_R
Z%(R%,0)=Z%(R%,0)+16
R=T6+1:GOSUB DEREF_R
Z%(R,0)=Z%(R,0)+16
RETURN
REM CONS(A%,B%) -> R%
REM CONS(A,B) -> R
CONS:
SZ%=2:GOSUB ALLOC
Z%(R%,0)=6+16
Z%(R%,1)=B%
Z%(R%+1,0)=14
Z%(R%+1,1)=A%
SZ=2:GOSUB ALLOC
Z%(R,0)=6+16
Z%(R,1)=B
Z%(R+1,0)=14
Z%(R+1,1)=A
REM inc ref cnt of item we are including
Z%(A%,0)=Z%(A%,0)+16
Z%(A,0)=Z%(A,0)+16
REM inc ref cnt of list we are prepending
Z%(B%,0)=Z%(B%,0)+16
Z%(B,0)=Z%(B,0)+16
RETURN
REM SLICE(A%,B%,C%) -> R%
REM make copy of sequence A% from index B% to C%
REM returns R6% as reference to last element of slice
REM returns A% as next element following slice (of original)
REM SLICE(A,B,C) -> R
REM make copy of sequence A from index B to C
REM returns R6 as reference to last element of slice
REM returns A as next element following slice (of original)
SLICE:
I=0
R5%=-1: REM temporary for return as R%
R6%=0: REM previous list element
R5=-1: REM temporary for return as R
R6=0: REM previous list element
SLICE_LOOP:
REM always allocate at least one list element
SZ%=2:GOSUB ALLOC
Z%(R%,0)=6+16:Z%(R%,1)=0:Z%(R%+1,0)=14:Z%(R%+1,1)=0
IF R5%=-1 THEN R5%=R%
IF R5%<>-1 THEN Z%(R6%,1)=R%
REM advance A% to position B%
SZ=2:GOSUB ALLOC
Z%(R,0)=6+16:Z%(R,1)=0:Z%(R+1,0)=14:Z%(R+1,1)=0
IF R5=-1 THEN R5=R
IF R5<>-1 THEN Z%(R6,1)=R
REM advance A to position B
SLICE_FIND_B:
IF I<B% AND Z%(A%,1)<>0 THEN A%=Z%(A%,1):I=I+1:GOTO SLICE_FIND_B
REM if current position is C%, then return
IF C%<>-1 AND I>=C% THEN R%=R5%:RETURN
REM if we reached end of A%, then return
IF Z%(A%,1)=0 THEN R%=R5%:RETURN
R6%=R%: REM save previous list element
IF I<B AND Z%(A,1)<>0 THEN A=Z%(A,1):I=I+1:GOTO SLICE_FIND_B
REM if current position is C, then return
IF C<>-1 AND I>=C THEN R=R5:RETURN
REM if we reached end of A, then return
IF Z%(A,1)=0 THEN R=R5:RETURN
R6=R: REM save previous list element
REM copy value and inc ref cnt
Z%(R6%+1,1)=Z%(A%+1,1)
R%=A%+1:GOSUB DEREF_R:Z%(R%,0)=Z%(R%,0)+16
REM advance to next element of A%
A%=Z%(A%,1)
Z%(R6+1,1)=Z%(A+1,1)
R=A+1:GOSUB DEREF_R:Z%(R,0)=Z%(R,0)+16
REM advance to next element of A
A=Z%(A,1)
I=I+1
GOTO SLICE_LOOP
REM LIST2(B2%,B1%) -> R%
REM LIST2(B2%,B1%) -> R
LIST2:
REM terminator
SZ%=2:GOSUB ALLOC:TB%=R%
Z%(R%,0)=6+16:Z%(R%,1)=0:Z%(R%+1,0)=0:Z%(R%+1,1)=0
SZ=2:GOSUB ALLOC:TB%=R
Z%(R,0)=6+16:Z%(R,1)=0:Z%(R+1,0)=0:Z%(R+1,1)=0
REM second element is B1%
SZ%=2:GOSUB ALLOC:TC%=R%
Z%(R%,0)=6+16:Z%(R%,1)=TB%:Z%(R%+1,0)=14:Z%(R%+1,1)=B1%
SZ=2:GOSUB ALLOC:TC%=R
Z%(R,0)=6+16:Z%(R,1)=TB%:Z%(R+1,0)=14:Z%(R+1,1)=B1%
Z%(B1%,0)=Z%(B1%,0)+16
REM first element is B2%
SZ%=2:GOSUB ALLOC
Z%(R%,0)=6+16:Z%(R%,1)=TC%:Z%(R%+1,0)=14:Z%(R%+1,1)=B2%
SZ=2:GOSUB ALLOC
Z%(R,0)=6+16:Z%(R,1)=TC%:Z%(R+1,0)=14:Z%(R+1,1)=B2%
Z%(B2%,0)=Z%(B2%,0)+16
RETURN
REM LIST3(B3%,B2%,B1%) -> R%
REM LIST3(B3%,B2%,B1%) -> R
LIST3:
GOSUB LIST2:TC%=R%
GOSUB LIST2:TC%=R
REM first element is B3%
SZ%=2:GOSUB ALLOC
Z%(R%,0)=6+16:Z%(R%,1)=TC%:Z%(R%+1,0)=14:Z%(R%+1,1)=B3%
SZ=2:GOSUB ALLOC
Z%(R,0)=6+16:Z%(R,1)=TC%:Z%(R+1,0)=14:Z%(R+1,1)=B3%
Z%(B3%,0)=Z%(B3%,0)+16
RETURN
REM hashmap functions
REM HASHMAP() -> R%
REM HASHMAP() -> R
HASHMAP:
SZ%=2:GOSUB ALLOC
Z%(R%,0)=8+16
Z%(R%,1)=0
Z%(R%+1,0)=14
Z%(R%+1,1)=0
SZ=2:GOSUB ALLOC
Z%(R,0)=8+16
Z%(R,1)=0
Z%(R+1,0)=14
Z%(R+1,1)=0
RETURN
REM ASSOC1(HM%, K%, V%) -> R%
REM ASSOC1(H, K, V) -> R
ASSOC1:
REM deref to actual key and value
R%=K%:GOSUB DEREF_R:K%=R%
R%=V%:GOSUB DEREF_R:V%=R%
R=K:GOSUB DEREF_R:K=R
R=V:GOSUB DEREF_R:V=R
REM inc ref count of key and value
Z%(K%,0)=Z%(K%,0)+16
Z%(V%,0)=Z%(V%,0)+16
SZ%=4:GOSUB ALLOC
Z%(K,0)=Z%(K,0)+16
Z%(V,0)=Z%(V,0)+16
SZ=4:GOSUB ALLOC
REM key ptr
Z%(R%,0)=8+16
Z%(R%,1)=R%+2: REM point to next element (value)
Z%(R%+1,0)=14
Z%(R%+1,1)=K%
Z%(R,0)=8+16
Z%(R,1)=R+2: REM point to next element (value)
Z%(R+1,0)=14
Z%(R+1,1)=K
REM value ptr
Z%(R%+2,0)=8+16
Z%(R%+2,1)=HM%: REM hashmap to assoc onto
Z%(R%+3,0)=14
Z%(R%+3,1)=V%
Z%(R+2,0)=8+16
Z%(R+2,1)=H: REM hashmap to assoc onto
Z%(R+3,0)=14
Z%(R+3,1)=V
RETURN
REM ASSOC1(HM%, K$, V%) -> R%
REM ASSOC1(H, K$, V) -> R
ASSOC1_S:
REM add the key string, then call ASSOC1
SZ%=1:GOSUB ALLOC
K%=R%
ZS$(ZJ%)=K$
Z%(R%,0)=4: REM key ref cnt will be inc'd by ASSOC1
Z%(R%,1)=ZJ%
ZJ%=ZJ%+1
SZ=1:GOSUB ALLOC
K=R
S$(ZJ)=K$
Z%(R,0)=4: REM key ref cnt will be inc'd by ASSOC1
Z%(R,1)=ZJ
ZJ=ZJ+1
GOSUB ASSOC1
RETURN
REM HASHMAP_GET(HM%, K%) -> R%
REM HASHMAP_GET(H, K) -> R
HASHMAP_GET:
H2%=HM%
T1$=ZS$(Z%(K%,1)): REM search key string
T3%=0: REM whether found or not (for HASHMAP_CONTAINS)
R%=0
H2%=H
T1$=S$(Z%(K,1)): REM search key string
T3=0: REM whether found or not (for HASHMAP_CONTAINS)
R=0
HASHMAP_GET_LOOP:
REM no matching key found
IF Z%(H2%,1)=0 THEN R%=0:RETURN
IF Z%(H2%,1)=0 THEN R=0:RETURN
REM follow value ptrs
T2%=H2%+1
T2=H2%+1
HASHMAP_GET_DEREF:
IF Z%(T2%,0)=14 THEN T2%=Z%(T2%,1):GOTO HASHMAP_GET_DEREF
IF Z%(T2,0)=14 THEN T2=Z%(T2,1):GOTO HASHMAP_GET_DEREF
REM get key string
T2$=ZS$(Z%(T2%,1))
T2$=S$(Z%(T2,1))
REM if they are equal, we found it
IF T1$=T2$ THEN T3%=1:R%=Z%(H2%,1)+1:RETURN
IF T1$=T2$ THEN T3=1:R=Z%(H2%,1)+1:RETURN
REM skip to next key
H2%=Z%(Z%(H2%,1),1)
GOTO HASHMAP_GET_LOOP
REM HASHMAP_CONTAINS(HM%, K%) -> R%
REM HASHMAP_CONTAINS(H, K) -> R
HASHMAP_CONTAINS:
GOSUB HASHMAP_GET
R%=T3%
R=T3
RETURN
REM NATIVE_FUNCTION(A%) -> R%
REM NATIVE_FUNCTION(A) -> R
NATIVE_FUNCTION:
SZ%=1:GOSUB ALLOC
Z%(R%,0)=9+16
Z%(R%,1)=A%
SZ=1:GOSUB ALLOC
Z%(R,0)=9+16
Z%(R,1)=A
RETURN
REM NATIVE_FUNCTION(A%, P%, E%) -> R%
REM MAL_FUNCTION(A, P, E) -> R
MAL_FUNCTION:
SZ%=2:GOSUB ALLOC
Z%(A%,0)=Z%(A%,0)+16
Z%(P%,0)=Z%(P%,0)+16
Z%(E%,0)=Z%(E%,0)+16
SZ=2:GOSUB ALLOC
Z%(A,0)=Z%(A,0)+16
Z%(P,0)=Z%(P,0)+16
Z%(E,0)=Z%(E,0)+16
Z%(R%,0)=10+16
Z%(R%,1)=A%
Z%(R%+1,0)=P%
Z%(R%+1,1)=E%
Z%(R,0)=10+16
Z%(R,1)=A
Z%(R+1,0)=P
Z%(R+1,1)=E
RETURN
REM APPLY(F%, AR%) -> R%
REM restores E%
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
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
@ -531,17 +531,17 @@ APPLY:
RETURN
DO_APPLY_MAL_FUNCTION:
ZL%=ZL%+1:ZZ%(ZL%)=E%: REM save the current environment
X=X+1:S%(X)=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
O=Z%(F+1,1):BI%=Z%(F+1,0):EX%=AR:GOSUB ENV_NEW_BINDS
A%=Z%(F%,1):E%=R%:GOSUB EVAL
A=Z%(F,1):E=R:GOSUB EVAL
AY%=E%:GOSUB RELEASE: REM release the new environment
AY=E:GOSUB RELEASE: REM release the new environment
E%=ZZ%(ZL%):ZL%=ZL%-1: REM pop/restore the saved environment
E=S%(X):X=X-1: REM pop/restore the saved environment
RETURN

48
basic/variables.txt Normal file
View File

@ -0,0 +1,48 @@
Global Unique:
Z% : boxed memory values
ZI : start of unused memory (index into Z%)
ZK : start of free list (index into Z%)
S$ : string memory storage
ZJ : next free index in S$
S% : logic/call stack (Z% indexes)
X : top element of S% stack
ZR% : pending release stack (index into Z%, eval level)
ZM% : top element of ZR% stack
RE% : root repl environment
ER : error type (-2: none, -1: string, >=0: object)
ER$ : error string (ER=-1)
LV : EVAL stack call level/depth
Calling arguments/temporaries:
A : common call arguments (especially EVAL, EVAL_AST)
B : common call arguments
C : common call arguments
E : environment (EVAL, EVAL_AST)
F : function
H : hash map
K : hash map key (Z% index)
K$ : hash map key string
O : outer environment
P : MAL_FUNCTION
R : common return value
T : common temp, type
V : hash map value
SZ : size argument to ALLOC
Reused/temporaries:
I : STRING, REPLACE, SLICE, PR_MEMORY, PR_OBJECT
J : REPLACE
Unused:
D, G, L, M, N, Q, U, W, Y