mirror of
https://github.com/kanaka/mal.git
synced 2024-11-13 01:43:50 +03:00
261 lines
7.1 KiB
QBasic
261 lines
7.1 KiB
QBasic
REM READ_TOKEN(RF=0, A$, RI) -> T$
|
|
REM READ_TOKEN(RF=1) -> T$
|
|
READ_TOKEN:
|
|
IF RF=1 THEN RF=2:T$="(":RETURN
|
|
IF RF=2 THEN RF=3:T$="do":RETURN
|
|
GOSUB SKIP_SPACES
|
|
REM PRINT "READ_TOKEN: "+STR$(RI)+", "+MID$(A$,RI,1)
|
|
GOSUB READ_CHAR
|
|
IF C$=";" THEN GOSUB SKIP_TO_EOL:GOTO READ_TOKEN
|
|
T$=C$
|
|
IF T$="(" OR T$=")" OR T$="[" OR T$="]" OR T$="{" OR T$="}" OR T$="'" OR T$="`" OR T$="@" THEN RETURN
|
|
GOSUB PEEK_CHAR: REM peek at next character
|
|
IF T$="~" AND C$<>"@" THEN RETURN
|
|
S1=0:S2=0: REM S1: INSTRING?, S2: ESCAPED?
|
|
IF T$=CHR$(34) THEN S1=1
|
|
READ_TOKEN_LOOP:
|
|
GOSUB PEEK_CHAR: REM peek at next character
|
|
IF C$="" THEN RETURN
|
|
IF S1 THEN GOTO READ_TOKEN_CONT
|
|
IF C$=" " OR C$="," OR C$=CHR$(13) OR C$=CHR$(10) THEN RETURN
|
|
IF C$="(" OR C$=")" OR C$="[" OR C$="]" OR C$="{" OR C$="}" THEN RETURN
|
|
READ_TOKEN_CONT:
|
|
GOSUB READ_CHAR
|
|
T$=T$+C$
|
|
IF T$="~@" THEN RETURN
|
|
IF S1=0 OR S2=1 THEN S2=0:GOTO READ_TOKEN_LOOP
|
|
REM S1=1 (INSTRING?) and S2=0 (not ESCAPED?)
|
|
IF C$=CHR$(92) THEN S2=1
|
|
IF C$=CHR$(34) THEN RETURN
|
|
GOTO READ_TOKEN_LOOP
|
|
|
|
|
|
REM READ_CHAR(A$, RI) -> C$
|
|
READ_CHAR:
|
|
RJ=1:GOSUB DO_READ_CHAR
|
|
RETURN
|
|
|
|
REM PEEK_CHAR(A$, RI) -> C$
|
|
PEEK_CHAR:
|
|
RJ=0:GOSUB DO_READ_CHAR
|
|
RETURN
|
|
|
|
REM DO_READ_CHAR(RJ, A$, RI):
|
|
REM - RI is position in A$
|
|
REM - RJ=1 is read, RJ=0 is peek
|
|
DO_READ_CHAR:
|
|
C$=""
|
|
IF RF>0 THEN GOTO READ_FILE_CHAR
|
|
IF RI<=LEN(A$) THEN C$=MID$(A$,RI,1):RI=RI+RJ
|
|
RETURN
|
|
|
|
REM READ_FILE_CHAR(RJ) -> C$
|
|
REM - RJ=1 is read, RJ=0 is peek
|
|
REM - D$ is global used for already read pending character
|
|
REM - EZ is global used for end of file state
|
|
READ_FILE_CHAR:
|
|
IF D$<>"" THEN C$=D$:IF RJ=0 THEN RETURN
|
|
IF D$<>"" AND RJ=1 THEN D$="":RETURN
|
|
D$=""
|
|
IF EZ>2 THEN C$=""
|
|
IF EZ=2 THEN C$=")"
|
|
IF EZ=1 THEN C$=CHR$(10)
|
|
IF EZ>0 THEN EZ=EZ+RJ:RETURN
|
|
#cbm GET#2,C$
|
|
#qbasic C$=INPUT$(1,2)
|
|
#qbasic IF EOF(2) THEN EZ=1:RETURN
|
|
IF RJ=0 THEN D$=C$
|
|
#cbm IF (ST AND 64) THEN EZ=1:RETURN
|
|
#cbm IF (ST AND 255) THEN EZ=1:ER=-1:E$="File read error"+STR$(ST)
|
|
RETURN
|
|
|
|
SKIP_SPACES:
|
|
GOSUB PEEK_CHAR: REM peek at next character
|
|
IF C$=" " OR C$="," OR C$=CHR$(13) OR C$=CHR$(10) THEN GOSUB READ_CHAR:GOTO SKIP_SPACES
|
|
RETURN
|
|
|
|
SKIP_TO_EOL:
|
|
GOSUB READ_CHAR
|
|
IF C$="" OR C$=CHR$(13) OR C$=CHR$(10) THEN RETURN
|
|
GOTO SKIP_TO_EOL
|
|
|
|
|
|
REM READ_FORM(A$, RI, RF) -> R
|
|
SUB READ_FORM
|
|
Q=T:GOSUB PUSH_Q: REM save current value of T
|
|
READ_FORM_RECUR:
|
|
IF ER<>-2 THEN GOTO READ_FORM_RETURN
|
|
GOSUB READ_TOKEN
|
|
REM PRINT "READ_FORM T$: ["+T$+"]"
|
|
IF T$="" THEN R=0:GOSUB INC_REF_R:GOTO READ_FORM_RETURN
|
|
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 B$="quote":GOTO READ_MACRO
|
|
IF T$="`" THEN B$="quasiquote":GOTO READ_MACRO
|
|
IF T$="~" THEN B$="unquote":GOTO READ_MACRO
|
|
IF T$="~@" THEN B$="splice-unquote":GOTO READ_MACRO
|
|
IF T$="^" THEN B$="with-meta":GOTO READ_MACRO
|
|
IF T$="@" THEN B$="deref":GOTO READ_MACRO
|
|
C$=MID$(T$,1,1)
|
|
REM PRINT "C$: ["+C$+"]("+STR$(ASC(C$))+")"
|
|
IF C$>="0" AND C$<="9" THEN GOTO READ_NUMBER
|
|
IF C$="-" THEN GOTO READ_SYMBOL_MAYBE
|
|
|
|
IF C$=CHR$(34) THEN GOTO READ_STRING
|
|
IF C$=":" THEN GOTO READ_KEYWORD
|
|
REM set end character in Q and read the sequence
|
|
IF C$="(" THEN T=6:Q=41:GOTO READ_SEQ_START: REM ")"
|
|
IF C$="[" THEN T=7:Q=93:GOTO READ_SEQ_START: REM "]"
|
|
IF C$="{" THEN T=8:Q=125:GOTO READ_SEQ_START: REM "}"
|
|
IF C$=")" OR C$="]" OR C$="}" THEN R=-1:ER=-1:E$="unexpected "+C$:GOTO READ_FORM_RETURN
|
|
GOTO READ_SYMBOL
|
|
|
|
READ_NIL_BOOL:
|
|
REM PRINT "READ_NIL_BOOL"
|
|
R=T*2
|
|
GOSUB INC_REF_R
|
|
GOTO READ_FORM_RETURN
|
|
READ_NUMBER:
|
|
REM PRINT "READ_NUMBER"
|
|
T=2:L=VAL(T$):GOSUB ALLOC
|
|
GOTO READ_FORM_RETURN
|
|
READ_MACRO:
|
|
REM push macro type
|
|
Q=-1*(T$="^"):GOSUB PUSH_Q
|
|
|
|
REM B$ is set above
|
|
T=5:GOSUB STRING
|
|
REM push string
|
|
GOSUB PUSH_R
|
|
|
|
CALL READ_FORM
|
|
REM push first form
|
|
GOSUB PUSH_R
|
|
IF ER>-2 THEN GOTO READ_MACRO_DONE
|
|
|
|
GOSUB PEEK_Q_2
|
|
IF Q THEN GOTO READ_MACRO_3
|
|
|
|
READ_MACRO_2:
|
|
GOSUB PEEK_Q_1:B=Q
|
|
GOSUB PEEK_Q:A=Q
|
|
GOSUB LIST2
|
|
GOTO READ_MACRO_DONE
|
|
|
|
READ_MACRO_3:
|
|
CALL READ_FORM
|
|
GOSUB PEEK_Q_1:C=Q
|
|
B=R
|
|
GOSUB PEEK_Q:A=Q
|
|
GOSUB LIST3
|
|
AY=C:GOSUB RELEASE
|
|
|
|
READ_MACRO_DONE:
|
|
REM release values, list has ownership
|
|
AY=B:GOSUB RELEASE
|
|
AY=A:GOSUB RELEASE
|
|
|
|
REM pop the stack
|
|
GOSUB POP_Q: REM pop first form
|
|
GOSUB POP_Q: REM pop string
|
|
GOSUB POP_Q: REM pop macro type
|
|
T$="": REM necessary to prevent unexpected EOF errors
|
|
GOTO READ_FORM_RETURN
|
|
|
|
READ_STRING:
|
|
REM PRINT "READ_STRING"
|
|
C=ASC(MID$(T$,LEN(T$),1))
|
|
IF C<>34 THEN R=-1:ER=-1:E$="expected '"+CHR$(34)+"'":GOTO READ_FORM_RETURN
|
|
R$=MID$(T$,2,LEN(T$)-2)
|
|
S1$=CHR$(92)+CHR$(92):S2$=CHR$(127):GOSUB REPLACE: REM protect backslashes
|
|
S1$=CHR$(92)+CHR$(34):S2$=CHR$(34):GOSUB REPLACE: REM unescape quotes
|
|
#cbm S1$=CHR$(92)+"n":S2$=CHR$(13):GOSUB REPLACE: REM unescape newlines
|
|
#qbasic S1$=CHR$(92)+"n":S2$=CHR$(10):GOSUB REPLACE: REM unescape newlines
|
|
S1$=CHR$(127):S2$=CHR$(92):GOSUB REPLACE: REM unescape backslashes
|
|
REM intern string value
|
|
B$=R$:T=4:GOSUB STRING
|
|
GOTO READ_FORM_RETURN
|
|
READ_KEYWORD:
|
|
R$=CHR$(127)+MID$(T$,2,LEN(T$)-1)
|
|
B$=R$:T=4:GOSUB STRING
|
|
GOTO READ_FORM_RETURN
|
|
READ_SYMBOL_MAYBE:
|
|
C$=MID$(T$,2,1)
|
|
IF C$>="0" AND C$<="9" THEN GOTO READ_NUMBER
|
|
READ_SYMBOL:
|
|
REM PRINT "READ_SYMBOL"
|
|
B$=T$:T=5:GOSUB STRING
|
|
GOTO READ_FORM_RETURN
|
|
|
|
READ_SEQ_START:
|
|
SD=SD+1
|
|
|
|
GOSUB PUSH_Q: REM push return character
|
|
|
|
REM setup the stack for the loop, T has type
|
|
GOSUB MAP_LOOP_START
|
|
|
|
READ_SEQ_LOOP:
|
|
|
|
REM TODO: reduce redundancy with READ_TOKEN
|
|
GOSUB SKIP_SPACES
|
|
GOSUB PEEK_CHAR: REM peek at next character
|
|
IF C$="" THEN ER=-1:E$="unexpected EOF":GOTO READ_SEQ_DONE
|
|
IF C$=";" THEN GOSUB SKIP_TO_EOL:GOTO READ_SEQ_LOOP
|
|
Q=3:GOSUB PEEK_Q_Q
|
|
IF C$=CHR$(Q) THEN GOSUB READ_CHAR:GOTO READ_SEQ_DONE
|
|
|
|
CALL READ_FORM
|
|
M=R: REM value (or key for hash-maps)
|
|
|
|
REM if error, release the unattached element
|
|
IF ER<>-2 THEN AY=R:GOSUB RELEASE:GOTO READ_SEQ_DONE
|
|
|
|
REM if this is a hash-map, READ_FORM again
|
|
IF T=8 THEN GOSUB PUSH_R:CALL READ_FORM
|
|
IF T=8 THEN N=R:GOSUB POP_Q:M=Q: REM set key and value
|
|
|
|
REM update the return sequence structure
|
|
REM release N since list takes full ownership
|
|
C=1:GOSUB MAP_LOOP_UPDATE
|
|
|
|
GOTO READ_SEQ_LOOP
|
|
|
|
READ_SEQ_DONE:
|
|
SD=SD-1
|
|
REM cleanup stack and get return value
|
|
GOSUB MAP_LOOP_DONE
|
|
|
|
GOSUB POP_Q: REM pop end character ptr
|
|
GOTO READ_FORM_RETURN
|
|
|
|
READ_FORM_RETURN:
|
|
GOSUB POP_Q:T=Q: REM restore current value of T
|
|
|
|
END SUB
|
|
|
|
|
|
REM READ_STR(A$) -> R
|
|
READ_STR:
|
|
RI=1: REM index into A$
|
|
RF=0: REM not reading from file
|
|
SD=0: REM sequence read depth
|
|
CALL READ_FORM
|
|
RETURN
|
|
|
|
REM READ_FILE(A$) -> R
|
|
READ_FILE:
|
|
RF=1: REM reading from file
|
|
EZ=0: REM file read state (1: EOF)
|
|
SD=0: REM sequence read depth
|
|
D$="": REM pending read/peek character
|
|
#cbm OPEN 2,8,0,A$
|
|
#qbasic IF NOT _FILEEXISTS(A$) THEN ER=-1:E$="File not found":RETURN
|
|
#qbasic OPEN A$ FOR INPUT AS #2
|
|
REM READ_TOKEN adds "(do ... )"
|
|
CALL READ_FORM
|
|
CLOSE 2
|
|
EZ=0
|
|
RETURN
|