def misc_fixups(orig_lines):
text = "\n".join(orig_lines)
- text = re.sub(r"\bTHEN GOTO\b", r"THEN", text)
+ text = re.sub(r"\bTHEN GOTO\b", "THEN", text)
+ text = re.sub(r"\bPRINT \"", "PRINT\"", text)
+ text = re.sub(r"\bIF ", "IF", text)
return text.split("\n")
def finalize(lines, args):
lines.append("%s %s" % (lnum, line))
lnum += 1
+ def update_labels_lines(text, a,b):
+ stext = ""
+ while stext != text:
+ stext = text
+ text = re.sub(r"(THEN) %s\b" % a, r"THEN %s" % b, stext)
+ #text = re.sub(r"(THEN)%s\b" % a, r"THEN%s" % b, stext)
+ text = re.sub(r"(ON [^:]* GOTO [^:\n]*)\b%s\b" % a, r"\g<1>%s" % b, text)
+ text = re.sub(r"(ON [^:]* GOSUB [^:\n]*)\b%s\b" % a, r"\g<2>%s" % b, text)
+ text = re.sub(r"(GOSUB) %s\b" % a, r"\1 %s" % b, text)
+ text = re.sub(r"(GOTO) %s\b" % a, r"\1 %s" % b, text)
+ #text = re.sub(r"(GOTO)%s\b" % a, r"\1%s" % b, text)
+ return text
+
if not args.keep_labels:
src_lines = lines
text = "\n".join(lines)
# search for and replace GOTO/GOSUBs
for label, lnum in labels_lines.items():
- stext = ""
- while stext != text:
- stext = text
- text = re.sub(r"(THEN) %s\b" % label, r"THEN %s" % lnum, stext)
- text = re.sub(r"(ON [^:]* GOTO [^:]*)\b%s\b" % label, r"\g<1>%s" % lnum, text)
- text = re.sub(r"(ON [^:]* GOSUB [^:]*)\b%s\b" % label, r"\g<2>%s" % lnum, text)
- text = re.sub(r"(GOSUB) %s\b" % label, r"\1 %s" % lnum, text)
- text = re.sub(r"(GOTO) %s\b" % label, r"\1 %s" % lnum, text)
+ text = update_labels_lines(text, label, lnum)
lines = text.split("\n")
if args.combine_lines:
+ renumber = {}
src_lines = lines
lines = []
pos = 0
acc_line = ""
+ def renum(line):
+ lnum = len(lines)+1
+ renumber[old_num] = lnum
+ return "%s %s" % (lnum, line)
while pos < len(src_lines):
line = src_lines[pos]
# TODO: handle args.keep_labels and (not args.number_lines)
m = re.match(r"^([0-9]*) (.*)$", line)
- lnum = int(m.group(1))
- rest_line = m.group(2)
+ old_num = int(m.group(1))
+ line = m.group(2)
if acc_line == "":
# Starting a new line
- acc_line = line
- elif lnum in lines_labels:
- # This is a GOTO/GOSUB target line so it must be on
- # a line by itself
+ acc_line = renum(line)
+ elif old_num in lines_labels or re.match(r"^ *FOR\b.*", line):
+ # This is a GOTO/GOSUB target or FOR loop so it must
+ # be on a line by itself
lines.append(acc_line)
- acc_line = line
+ acc_line = renum(line)
elif re.match(r".*\b(?:GOTO|THEN|RETURN)\b.*", acc_line):
+ # GOTO/THEN/RETURN are last thing on the line
lines.append(acc_line)
- acc_line = line
- elif len(acc_line) + 1 + len(rest_line) < 80:
+ acc_line = renum(line)
+ # TODO: not sure why this is 88 rather than 80
+ elif len(acc_line) + 1 + len(line) < 88:
# Continue building up the line
- acc_line = acc_line + ":" + rest_line
+ acc_line = acc_line + ":" + line
# GOTO/IF/RETURN must be the last things on a line so
# start a new line
if re.match(r".*\b(?:GOTO|THEN|RETURN)\b.*", line):
else:
# Too long so start a new line
lines.append(acc_line)
- acc_line = line
+ acc_line = renum(line)
pos += 1
if acc_line != "":
lines.append(acc_line)
+ # Finally renumber GOTO/GOSUBS
+ src_lines = lines
+ text = "\n".join(lines)
+ # search for and replace GOTO/GOSUBs
+ for a in sorted(renumber.keys()):
+ b = renumber[a]
+ text = update_labels_lines(text, a, b)
+ lines = text.split("\n")
+
return lines
R=Z%(AR,1)+1:GOSUB DEREF_R:AB=R
REM Switch on the function number
- IF FF>58 THEN ER=-1:ER$="unknown function"+STR$(FF):RETURN
+ IF FF>59 THEN ER=-1:ER$="unknown function"+STR$(FF):RETURN
ON FF/10+1 GOTO DO_1_9,DO_10_19,DO_20_29,DO_30_39,DO_40_49,DO_50_59
DO_1_9:
DO_40_49:
ON FF-39 GOTO DO_CONS,DO_CONCAT,DO_NTH,DO_FIRST,DO_REST,DO_EMPTY_Q,DO_COUNT,DO_APPLY,DO_MAP,DO_THROW
DO_50_59:
- ON FF-49 GOTO DO_THROW,DO_WITH_META,DO_META,DO_ATOM,DO_ATOM_Q,DO_DEREF,DO_RESET_BANG,DO_SWAP_BANG,DO_EVAL
+ ON FF-49 GOTO DO_THROW,DO_WITH_META,DO_META,DO_ATOM,DO_ATOM_Q,DO_DEREF,DO_RESET_BANG,DO_SWAP_BANG,DO_EVAL,DO_READ_FILE
DO_EQUAL_Q:
A=AA:B=AB:GOSUB EQUAL_Q
A=AA:E=D:GOSUB EVAL
RETURN
+ DO_READ_FILE:
+ A$=S$(Z%(AA,1))
+ GOSUB READ_FILE
+ RETURN
+
INIT_CORE_SET_FUNCTION:
GOSUB NATIVE_FUNCTION
V=R:GOSUB ENV_SET_S
K$="eval":A=58:GOSUB INIT_CORE_SET_FUNCTION
+ K$="read-file":A=59:GOSUB INIT_CORE_SET_FUNCTION
+
RETURN
RETURN
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$(Z1)
- PRINT " ";
- PRINT " used:"+STR$(ZI-1-P2)+", freed:"+STR$(P2);
- PRINT ", post repl_env:"+STR$(ZT)
- PRINT "String values (S$) : "+STR$(S)+" /"+STR$(Z2)
- PRINT "Call stack size (X%) : "+STR$(X+1)+" /"+STR$(Z3)
+ PRINT "Free (FRE) :"+STR$(FRE(0))
+ PRINT "Values (Z%) :"+STR$(ZI-1)+" /"+STR$(Z1)
+ GOSUB CHECK_FREE_LIST: REM get count in P2
+ PRINT " used:"+STR$(ZI-1-P2)+", freed:"+STR$(P2);
+ PRINT ", after repl_env:"+STR$(ZT)
+ PRINT "Strings (S$) :"+STR$(S)+" /"+STR$(Z2)
+ PRINT "Stack (X%) :"+STR$(X+1)+" /"+STR$(Z3)
RETURN
REM REM PR_MEMORY(P1, P2) -> nil
-REM READ_TOKEN(A$, IDX) -> T$
+REM READ_TOKEN(A$, RI, RF) -> T$
READ_TOKEN:
- CUR=IDX
- REM PRINT "READ_TOKEN: "+STR$(CUR)+", "+MID$(A$,CUR,1)
- T$=MID$(A$,CUR,1)
+ RJ=RI
+ IF RF=1 THEN GOSUB READ_FILE_CHUNK
+ REM PRINT "READ_TOKEN: "+STR$(RJ)+", "+MID$(A$,RJ,1)
+ T$=MID$(A$,RJ,1)
IF T$="(" OR T$=")" OR T$="[" OR T$="]" OR T$="{" OR T$="}" THEN RETURN
IF T$="'" OR T$="`" OR T$="@" THEN RETURN
- IF T$="~" AND NOT MID$(A$,CUR+1,1)="@" THEN RETURN
+ IF T$="~" AND NOT MID$(A$,RJ+1,1)="@" THEN RETURN
S1=0:S2=0: REM S1: INSTRING?, S2: ESCAPED?
IF T$=CHR$(34) THEN S1=1
- CUR=CUR+1
+ RJ=RJ+1
READ_TOKEN_LOOP:
- IF CUR>LEN(A$) THEN RETURN
- CH$=MID$(A$,CUR,1)
+ IF RF=1 THEN GOSUB READ_FILE_CHUNK
+ IF RJ>LEN(A$) THEN RETURN
+ CH$=MID$(A$,RJ,1)
IF S2 THEN GOTO READ_TOKEN_CONT
IF S1 THEN GOTO READ_TOKEN_CONT
IF CH$=" " OR CH$="," THEN RETURN
+ IF CH$=" " OR CH$="," OR CH$=CHR$(13) OR CH$=CHR$(10) THEN RETURN
IF CH$="(" OR CH$=")" OR CH$="[" OR CH$="]" OR CH$="{" OR CH$="}" THEN RETURN
READ_TOKEN_CONT:
T$=T$+CH$
IF T$="~@" THEN RETURN
- CUR=CUR+1
+ RJ=RJ+1
IF S1 AND S2 THEN S2=0:GOTO READ_TOKEN_LOOP
IF S1 AND S2=0 AND CH$=CHR$(92) THEN S2=1:GOTO READ_TOKEN_LOOP
IF S1 AND S2=0 AND CH$=CHR$(34) THEN RETURN
GOTO READ_TOKEN_LOOP
+READ_FILE_CHUNK:
+ IF RS=1 THEN RETURN
+ IF RI>1 THEN A$=MID$(A$,RI,LEN(A$)-RI+1):RI=1:RJ=RJ-RI+1
+ READ_FILE_CHUNK_LOOP:
+ IF LEN(A$)>RJ+9 THEN RETURN
+ GET#2,C$:A$=A$+C$
+ IF (ST AND 64) THEN RS=1:A$=A$+CHR$(10)+")":RETURN
+ IF (ST AND 255) THEN RS=1:ER=-1:ER$="File read error "+STR$(ST):RETURN
+ GOTO READ_FILE_CHUNK_LOOP
+
SKIP_SPACES:
- CH$=MID$(A$,IDX,1)
- IF (CH$<>" ") AND (CH$<>",") AND (CH$<>CHR$(13)) AND (CH$<>CHR$(10)) THEN RETURN
- IDX=IDX+1
+ IF RF=1 THEN GOSUB READ_FILE_CHUNK
+ CH$=MID$(A$,RI,1)
+ IF CH$<>" " AND CH$<>"," AND CH$<>CHR$(13) AND CH$<>CHR$(10) THEN RETURN
+ RI=RI+1
GOTO SKIP_SPACES
+SKIP_TO_EOL:
+ IF RF=1 THEN GOSUB READ_FILE_CHUNK
+ CH$=MID$(A$,RI+1,1)
+ RI=RI+1
+ IF CH$="" OR CH$=CHR$(13) OR CH$=CHR$(10) THEN RETURN
+ GOTO SKIP_TO_EOL
+
READ_ATOM:
R=0
RETURN
-REM READ_FORM(A$, IDX) -> R
+REM READ_FORM(A$, RI, RF) -> R
READ_FORM:
IF ER<>-2 THEN RETURN
GOSUB SKIP_SPACES
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:GOSUB SKIP_TO_EOL:GOTO READ_FORM
IF CH$>="0" AND CH$<="9" THEN GOTO READ_NUMBER
IF CH$="-" THEN GOTO READ_SYMBOL_MAYBE
IF CH$="}" THEN T=8:GOTO READ_SEQ_END
GOTO READ_SYMBOL
- READ_TO_EOL:
- CH$=MID$(A$,IDX+1,1)
- IDX=IDX+1
- IF CH$="" OR CH$=CHR$(13) OR CH$=CHR$(10) THEN GOTO READ_FORM
- GOTO READ_TO_EOL
READ_NIL_BOOL:
REM PRINT "READ_NIL_BOOL"
R=T
T=2:L=VAL(T$):GOSUB ALLOC
GOTO READ_FORM_DONE
READ_MACRO:
- IDX=IDX+LEN(T$)
+ RI=RI+LEN(T$)
REM to call READ_FORM recursively, SD needs to be saved, set to
REM 0 for the call and then restored afterwards.
X=X+2:X%(X-1)=(T$="^"):X%(X)=SD: REM push macro type and SD
X=X+1
X%(X)=R
- IDX=IDX+LEN(T$)
+ RI=RI+LEN(T$)
GOTO READ_FORM
READ_SEQ_END:
READ_FORM_DONE:
- IDX=IDX+LEN(T$)
+ RI=RI+LEN(T$)
T8=R: REM save previous value
REM READ_STR(A$) -> R
READ_STR:
- IDX=1
+ RI=1: REM index into A$
+ RF=0: REM not reading from file
SD=0: REM sequence read depth
GOSUB READ_FORM
RETURN
+
+REM READ_FILE(A$) -> R
+READ_FILE:
+ RI=1: REM index into A$
+ RJ=1: REM READ_TOKEN sub-index
+ RF=1: REM reading from file
+ RS=0: REM file read state (1: EOF)
+ SD=0: REM sequence read depth
+ OPEN 2,8,0,A$
+ REM READ_FILE_CHUNK adds terminating ")"
+ A$="(do ":GOSUB READ_FORM
+ CLOSE 2
+ RETURN
R1=0
GOSUB MAL_READ
R1=R
- IF ER<>-2 THEN GOTO REP_DONE
+ IF ER<>-2 THEN GOTO RE_DONE
A=R:E=D:GOSUB EVAL
- REP_DONE:
+ RE_DONE:
REM Release memory from MAL_READ
IF R1<>0 THEN AY=R1:GOSUB RELEASE
RETURN: REM caller must release result of EVAL
R1=0
GOSUB MAL_READ
R1=R
- IF ER<>-2 THEN GOTO REP_DONE
+ IF ER<>-2 THEN GOTO RE_DONE
A=R:E=D:GOSUB EVAL
- REP_DONE:
+ RE_DONE:
REM Release memory from MAL_READ
IF R1<>0 THEN AY=R1:GOSUB RELEASE
RETURN: REM caller must release result of EVAL
R1=0
GOSUB MAL_READ
R1=R
- IF ER<>-2 THEN GOTO REP_DONE
+ IF ER<>-2 THEN GOTO RE_DONE
A=R:E=D:GOSUB EVAL
- REP_DONE:
+ RE_DONE:
REM Release memory from MAL_READ
IF R1<>0 THEN AY=R1:GOSUB RELEASE
RETURN: REM caller must release result of EVAL
A$="(def! not (fn* (a) (if a false true)))"
GOSUB RE:AY=R:GOSUB RELEASE
- A$="(def! load-file (fn* (f) (eval (read-string (str "
- A$=A$+CHR$(34)+"(do "+CHR$(34)+" (slurp f) "+CHR$(34)+")"+CHR$(34)+")))))"
+ A$="(def! load-file (fn* (f) (eval (read-file f))))"
GOSUB RE:AY=R:GOSUB RELEASE
REM load the args file
R1=0
GOSUB MAL_READ
R1=R
- IF ER<>-2 THEN GOTO REP_DONE
+ IF ER<>-2 THEN GOTO RE_DONE
A=R:E=D:GOSUB EVAL
- REP_DONE:
+ RE_DONE:
REM Release memory from MAL_READ
IF R1<>0 THEN AY=R1:GOSUB RELEASE
RETURN: REM caller must release result of EVAL
A$="(def! not (fn* (a) (if a false true)))"
GOSUB RE:AY=R:GOSUB RELEASE
- A$="(def! load-file (fn* (f) (eval (read-string (str "
- A$=A$+CHR$(34)+"(do "+CHR$(34)+" (slurp f) "+CHR$(34)+")"+CHR$(34)+")))))"
+ A$="(def! load-file (fn* (f) (eval (read-file f))))"
GOSUB RE:AY=R:GOSUB RELEASE
REM load the args file
R1=0
GOSUB MAL_READ
R1=R
- IF ER<>-2 THEN GOTO REP_DONE
+ IF ER<>-2 THEN GOTO RE_DONE
A=R:E=D:GOSUB EVAL
- REP_DONE:
+ RE_DONE:
REM Release memory from MAL_READ
IF R1<>0 THEN AY=R1:GOSUB RELEASE
RETURN: REM caller must release result of EVAL
A$="(def! not (fn* (a) (if a false true)))"
GOSUB RE:AY=R:GOSUB RELEASE
- A$="(def! load-file (fn* (f) (eval (read-string (str "
- A$=A$+CHR$(34)+"(do "+CHR$(34)+" (slurp f) "+CHR$(34)+")"+CHR$(34)+")))))"
+ A$="(def! load-file (fn* (f) (eval (read-file f))))"
GOSUB RE:AY=R:GOSUB RELEASE
A$="(defmacro! cond (fn* (& xs) (if (> (count xs) 0) (list 'if (first xs)"
R1=0
GOSUB MAL_READ
R1=R
- IF ER<>-2 THEN GOTO REP_DONE
+ IF ER<>-2 THEN GOTO RE_DONE
A=R:E=D:GOSUB EVAL
- REP_DONE:
+ RE_DONE:
REM Release memory from MAL_READ
IF R1<>0 THEN AY=R1:GOSUB RELEASE
RETURN: REM caller must release result of EVAL
A$="(def! not (fn* (a) (if a false true)))"
GOSUB RE:AY=R:GOSUB RELEASE
- A$="(def! load-file (fn* (f) (eval (read-string (str "
- A$=A$+CHR$(34)+"(do "+CHR$(34)+" (slurp f) "+CHR$(34)+")"+CHR$(34)+")))))"
+ A$="(def! load-file (fn* (f) (eval (read-file f))))"
GOSUB RE:AY=R:GOSUB RELEASE
A$="(defmacro! cond (fn* (& xs) (if (> (count xs) 0) (list 'if (first xs)"
R1=0
GOSUB MAL_READ
R1=R
- IF ER<>-2 THEN GOTO REP_DONE
+ IF ER<>-2 THEN GOTO RE_DONE
A=R:E=D:GOSUB EVAL
- REP_DONE:
+ RE_DONE:
REM Release memory from MAL_READ
IF R1<>0 THEN AY=R1:GOSUB RELEASE
RETURN: REM caller must release result of EVAL
A$="(def! not (fn* (a) (if a false true)))"
GOSUB RE:AY=R:GOSUB RELEASE
- A$="(def! load-file (fn* (f) (eval (read-string (str "
- A$=A$+CHR$(34)+"(do "+CHR$(34)+" (slurp f) "+CHR$(34)+")"+CHR$(34)+")))))"
+ A$="(def! load-file (fn* (f) (eval (read-file f))))"
GOSUB RE:AY=R:GOSUB RELEASE
A$="(defmacro! cond (fn* (& xs) (if (> (count xs) 0) (list 'if (first xs)"
INIT_MEMORY:
T=FRE(0)
- Z1=2048+512: REM Z% (boxed memory) size (4 bytes each)
+ Z1=2048+1024: REM Z% (boxed memory) size (4 bytes each)
Z2=256: REM S$ (string memory) size (3 bytes each)
Z3=256: REM X% (call stack) size (2 bytes each)
Z4=64: REM Y% (release stack) size (4 bytes each)
EX : ENV_NEW_BINDS expressions list
LV : EVAL stack call level/depth
-IDX : reader current string position
+RI : reader current string position
+RJ : READ_TOKEN current character index
Calling arguments/temporaries:
T : common temp, type
V : hash map value
-A0 : EVAL ast elements
-A1 : EVAL ast elements
-A2 : EVAL ast elements
-A3 : EVAL ast elements
B1 : LIST2/LIST3 param
B2 : LIST2/LIST3 param
B3 : LIST3 param
CZ : DO_CONCAT stack position
-CUR : READ_TOKEN current character index
EF : ENV_FIND cur env ptr
P1 : PR_MEMORY, CHECK_FREE_LIST start
P2 : PR_MEMORY, CHECK_FREE_LIST end
Reused/temporaries:
+A0 : EVAL ast elements
+A1 : EVAL ast elements
+A2 : EVAL ast elements
+A3 : EVAL ast elements
+CH$ : READ_TOKEN, SKIP_SPACES, SKIP_TO_EOL current character
I : STRING, REPLACE, SLICE, PR_MEMORY, PR_OBJECT
J : REPLACE
+S1 : READ_TOKEN in a string?
+S2 : READ_TOKEN escaped?
+T$ : READ_* current token string
+T1$ : HASHMAP_GET temp
+T2$ : HASHMAP_GET temp
+T1 : PR_STR, and core DO_KEYS_VALS temp
+T2 :
+T3 :
+T4 :
+T5 :
+T6 :
+T7 : READ_FORM and PR_STR temp
+T8 :
+T9 :
+TA :
+U1 :
+U2 :
+U3 :
+U4 :
+U6 :
Unused:
-G, L, M, N, Q, U, W
+G, Q, U, W
(list form x))
`(->> (->> ~x ~form) ~@more))))))
+nil
self.buf += new_data.replace("\n", "\r\n")
else:
self.buf += new_data
+ self.buf = self.buf.replace("\r\r", "\r")
for prompt in prompts:
regexp = re.compile(prompt)
match = regexp.search(self.buf)