Bill Wilkinson
This month Bill continues with the creation of the BAIT interpreter (Basic Almost InTerpreter). And he includes some comments from readers.
BAIT: Part 3
For those of you who may have missed Parts 1 and 2, let me give a brief description of this project. BAIT is an acronym for Basic Almost InTerpreter. It is a pseudo-BASIC actually written in Atari BASIC. It is slow. It uses one letter commands (for example, "P" for PRINT). It is simple. And its purpose is simply to give you an inkling of how a BASIC interpreter works. It is not a finished, usable language.
This month we will study Part 3 of this listing. We will publish only those lines which have changed from Parts 1 and 2. However, next month we will present Part 4, the last part, and we'll publish the entire listing.
Before starting on my own comments about and additions to BAIT this month, though, I would like to share some reader comments on Part 1.
First, Howard Fishman of Brooklyn, New York, pointed out that I could eliminate the question mark prompt from the INPUT statement by simply using OPEN #3,12,0,"E:" at the beginning of the program and then replacing INPUT with INPUT#3.
Sigh. How right you are, Howard. The funny thing is that I remember discovering this technique about three years ago on our Apple II version of OSS BASIC. How soon we forget. I will incorporate his suggestion in the finished version of BAIT.
Also, Howard protested my not including a facility to list BAIT programs to disk and retrieve them. Perhaps I might change my mind later, but for now I feel that adding that code is an excellent exercise for the reader.
The second letter was from Donald Biresch of Ottsville, Pennsylvania. His comment was that he wished I wouldn't "spend [my] time ... writing about creating BASIC interpreters (something ... less than 1 percent of the end user market has any interest in)." Is he right or wrong? Wrong, I hope, though I admit I have sometimes regretted starting this project, since it has proven to be a larger program than envisioned.
Still, I believe that the subject interests more than 1 percent of you, even if my readers aren't necessarily typical "end users." In particular, I think the BAIT articles are a good lead-in to a more serious study of a BASIC interpreter.
However, if Donald is correct, I apologize. Let me know how you feel.
New Features Of BAIT
As with the previous parts, I will describe this month's changes by line number or line number range.
1110. We set all variables to zero.
1515 to 1580. These are simply some line number equates for use as the objects of GOTOs or GOSUBs. Note, though, that they help produce readable code.
3060. Just centralizing some error messages.
4200 to 4250. A complete restructuring of the "Execute Next Statement" routines. Note that multiple statements per line are now legal. Also, note that pushing the START button now serves as a program break (the BREAK key still stops BASIC itself).
4610 to 4620. Sometimes when you generalize things, the program gets simpler. Direct and deferred execution are now virtually identical.
4700 to 4730. After executing a direct line, we wipe it out of the program memory.
4910 to 4960. Look at all the wonderful statements we can now use! They are in order here. Thus a statement "A" will cause DO ACCEPT to be called, etc.
8290. More clean up.
8400 to 8410. Ditto. See line 3060.
10190. Now, we exit from the statement "DO" routines only after getting the character which terminates the statement (that is, the colon or return character).
10250 to 10270. Ditto. Just making PRINT's code cleaner.
10400 to 10420. Look how easy BEGIN (same as BASIC's RUN) is! We zero out the variables, set the current line number to zero, say we found an end of line, and let execute-next-line (at 4600) start the program execution.
10500 to 10530. GOTO is almost as simple. Find what line number the user wants and fool execute-next-line into getting the next execution line from there.
10600 to 10650. LET is only a little more complex. It insists on a variable (10610) for a destination (10620), an equal sign (10630), and an expression (10640). Then it simply gives the destination variable the value of the expression.
10700 to 10730. IF is, I think, a little clever. It simply tells the get-next-statement code (4240 and 4250) that the next character is an end of line if the user's expression evaluates to zero. Otherwise, it does nothing, and the next statement (if any) gets executed.
10800 to 10910. ACCEPT and CALL will be implemented next month.
11000 to 11030. END simply forces an end of line character and an illegal next line number value. The direct statement test (line 4620) effectively ends the program.
11100 to 11410. FETCH, NEW, RETURN, and STORE are left for next month.
Well, there you have it. A functional, albeit minimal interpreter. If you have typed it all in properly, you might try the following program as a test of its logic.
1
P"N",:P"N+N",:P"N*N"
2 PN,:PN+N,:PN*N
3 LN=N+1
4 IN<20:G2
5 E
B
2 PN,:PN+N,:PN*N
3 LN=N+1
4 IN<20:G2
5 E
B
And, for those of you who have not followed BAIT up until now, that translates roughly into BASIC as:
1
PRINT"N","N+N","N*N"
2 PRINT N,N+N,N*N
3 LET N=N+1
4 IF N<20 THEN GOTO 2
5 END
RUN
2 PRINT N,N+N,N*N
3 LET N=N+1
4 IF N<20 THEN GOTO 2
5 END
RUN
And that's enough BAIT for this month. If you don't do anything else while waiting for next month's column, you might try writing the code to execute NEW. It will be extremely simple.
BAIT
1110 FOR ALPHA=0 TO 26:VARIABLES(ALPHA)=
0:NEXT ALPHA
1515 DIRECT=4700:BADLINE=8400
1560 DOBEGIN=10400:DOGOTO=10500:DOLET=10
600:DOIF=10700
1570 DOACCEPT=10800:DOCALL=10900:DOEND=1
1000:DOFETCH=11100
1580 DONEW=11200:DORETURN=11300:LET DOST
ORE=11400
3060 GOTO BADLINE
<<< DELETE LINE 3070 >>>
4200 REM EXECUTE A SINGLE STATEMENT
4230 IF PEEK(53279)<>7 THEN GOSUB DOEND
4240 IF C$=":" THEN 4200
4250 IF C>=0 THEN GOTO SYNTAX
4610 CURLINE=CURLINE+l
4620 IF CURLINE>0 AND CURLINE<=MAXLINE T
HEN 4000
<<< DELETE LINE 10280 >>>
4700 REM ===COME HERE ON END OF DIRECT L
INE EXECUTE===
4710 IF LINES(0) THEN BUFFER$(INT(LINES(
0)1000))="*"
4720 LINES(0)=0
4730 GOTO PROMPT
4910 ERR$="BAD STATEMENT NAME"
4920 ON ALPHA GOTO DOACCEPT,DOBEGIN,DOCA
LL,DODISPLAY,DOEND
4930 ON ALPHA-5 GOTO DOFETCH,DOGOTO,ERRO
R,DOIF,ERROR,ERROR
4940 ON ALPHA-11 GOTO DOLET,ERROR,DONEW,
ERROR,DOPRINT
4950 ON ALPHA-16 GOTO ERROR,DORETURN,DOS
TORE
4960 GOTO ERROR
8290 GOTO DIRECT
8400 REM BAD LINE NUMBER
8410 ERR$="BAD LINE NUMBER":GOTO 8200
10190 GOTO GETNC
10250 IF C$=";"THEN GOTO GETNC
10260 IF C$=","THEN PRINT,:GOTO GETNC
10270 PRINT:RETURN
<<< DELETE 4630 >>>
<<< DELETE 4640 >>>
10400 REM ===EXECUTE BEGIN===
10410 FOR ALPHA=0 TO 26:VARIABLES(ALPHA)
=0:NEXT ALPHA
10420 CURLINE=0:C=-1:RETURN
10500 REM ===EXECUTE GOTO===
10510 GOSUB EXEXP
10520 IF LINES(EVAL)=0 THEN ERR$="NO SUCH
LINE":GOTO 8200
10530 CURLINE=EVAL-1:RETURN
10600 REM ===EXECUTE LET===
10610 GOSUB GETNC:IF NOT ALPHA THEN GOTO
SYNTAX
10620 DESTVAR=ALPHA
10630 GOSUB GETNC:IF C$<>"=" THEN GOTO S
YNTAX
10640 GOSUB EXEXP:VARIABLES(DESTVAR)=EVA
L
10650 RETURN
10700 REM ===EXECUTE IF===
10710 GOSUB EXEXP
10720 IF NOT EVAL THEN C=-1:C$=""
10730 RETURN
10800 REM ===EXECUTE ACCEPT===
10900 REM ===EXECUTE CALL===
10910 GOTO ERROR
11000 REM ===EXECUTE END===
11010 PRINT"===END AT LINE";CURLINE;"===
"
11020 C=-1:CURLINE=C:C$=""
11030 RETURN
11100 REM ===EXECUTE FETCH===
11200 REM ===EXECUTE NEW===
11300 REM ===EXECUTE RETURN===
11400 REM EXECUTE STORE===
11410 GOTO ERROR