NAME
Language::Basic - Perl Module to interpret BASIC
SYNOPSIS
use Language::Basic;
my $Program = new Language::Basic::Program;
$Program->input("program.bas"); # Read lines from a file
$Program->parse; # Parse the Program
$Program->implement; # Run the Program
$Program->output_perl; # output Program as a Perl program
$Program->line("20 PRINT X"); # add one line to existing Program
Featured scripts:
- basic.pl
-
Runs BASIC programs from the command line.
- termbasic.pl
-
Term::Readline program. Input one line of BASIC at a time, then run the program.
- basic2pl.pl
-
Outputs a Perl program that does the same thing as the input BASIC program.
DESCRIPTION
This module lets you run any BASIC programs you may have lying around, or may inspire you to write new ones!
The aspects of the language that are supported are described below. Note that I was pretty much aiming for Applesoft BASIC (tm) ca. 1985, not some modern BASIC with real subroutines.
Class Language::Basic::Program
This class handles a whole program. A Program is just a bunch of Lines, each of which has one or more Statements on it. Running the program involves moving through the lines, usually in numerical order, and implementing each line.
Methods:
- Current_Program
-
Returns the program currently being parsed/implemented/whatever
- Set_Current_Program
-
Sets arg0 to be the Current Program
- input
-
This method reads in a program from a file. It doesn't do any parsing, except for taking the line number out of the line.
- line
-
This method takes a line of BASIC (arg1, already chomped), forms a new LB::Line with it, and adds it to the Program (arg0). It doesn't do any parsing, except for taking the line number out of the line.
- parse
-
This method parses the program, which just involves looping over the lines in the program and parsing each line.
- implement
-
This method actually runs the program. That is, it starts on the first line, and implements lines one at a time, following line numbers in numerical order unless a GOTO, NEXT, etc. sends it somewhere else. It stops when it hits an END statement or "falls off" the end of the program.
- start
-
This method erases program stack and moves line pointer to beginning of program
It should be called any time we start going through the program. (Either implement or output_perl.)
- line_number
-
What line are we currently on?
- push_stack
-
"Call a subroutine", i.e. push the current line number onto the Program's calling stack
- add_data
-
Add a piece of data to the Program's data storage, to be accessed later. (i.e. parsing a DATA statement)
- get_data
-
Get a piece of data that was stored earlier. (i.e. implementing a READ statement)
- set_next_line
-
Which line number do we move to after doing the current line?
- set_after_next_line
-
Just like set_next_line, except go the line *after* that line. E.g., RETURN from a GOSUB, you want to return to the GOSUB line but start execution after that line. Same with FOR.
- need_sub
-
Tells the Program that it needs to use the sub named arg0 (whose definition is in arg1). This is used for outputting a Perl translation of a BASIC program, so that you only write "sub mid_str {...}" if MID$ is used in the BASIC program.
Class Language::Basic::Line
This class handles one line of a BASIC program, which has one or more Statements on it.
Methods:
- get_next
-
Returns the next line number in the Program
- set_next
-
Sets the next line number in the Program to be arg1.
- parse
-
This method breaks the line up into tokens (and removes whitespace, except in strings), then parses the first statement on the line. (Eventually, it will parse all statements on the line, but colons aren't currently supported.)
- lex
-
This method breaks the line up into tokens. Currently that means it separates by whitespace (which is discarded) and equals signs. (The = split isn't necessary but it's convenient.) Anything in double quotation marks is its own token, and no splitting is done within it.
- implement
-
This method actually executes the line. That is, it starts with the first statement on the line, and performs it. If there's another statement to implement on this line (e.g., an IF/THEN, or multiple statements separated by colons (when that's supported)) then LB::Statement::implement returns that Statement, which in turn is implemented.
BASIC LANGUAGE REFERENCE
This is a (hopefully current) description of what Language::Basic supports. For each command, I give an example use of that command, and possible a comment or two about it.
Also see the Syntax file included in the distribution, which describes the exact syntax for each statement, expressions, variable names, etc.
Commands
- DATA
-
DATA 1,2,"HI". These will be read sequentially by READ statements. Note that currently all string constants must be quoted.
- DEF
-
DEF FNA(X)= INT(X + .5).
- DIM
-
DIM A(20), B(10,10). Arrays default to size 10 (or actually 11 since they start at zero.)
- END
-
END.
- FOR
-
FOR I = 1 TO 10 STEP 3. STEP defaults to 1 if not given, and may be negative. (For loops are always implemented at least once.)
- GOTO
-
GOTO 30. Note that GOTO 30+(X*3) is also supported.
- GOSUB
-
GOSUB 10+X. Gosub is just like GOTO, except that when the program gets to a RETURN statement, it will come back to the statement just after the GOSUB.
- IF
-
IF X > Y THEN 30 ELSE X = X + 1. ELSE is not required. In a THEN or ELSE, a lone number means GOTO that number (also known as an implied GOTO). AND/OR/NOT still aren't supported for conditional expressions.
- INPUT
-
INPUT A$, B$. Also allowed is INPUT "FOO"; BAR. This prints "FOO?" instead of just "?" as the input prompt.
- LET
-
LET X=4. The word "LET" isn't required; i.e. X=4 is just like LET X=4.
- NEXT
-
NEXT I. Increment I by STEP, test against its limit, go back to the FOR statement if it's not over its limit.
- ON
-
ON X-3 GOSUB 10,20. This is equivalent to: IF X-3 = 1 THEN GOSUB 10 IF X-3 = 2 THEN GOSUB 20 ON ... GOTO is also allowed.
-
PRINT FOO; BAR$, 6*BLAH. semicolon means no space (or one space after printing numbers!), comma is like a 14-character tab (or \n past column 56). Print \n after the last expression unless there's a semicolon after it.
- READ
-
READ A, B(I), C$. Reads data from DATA statements into variables
- REM
-
REM WHATEVER. Anything after the REM is ignored.
Intrinsic functions
The following functions are currently supported:
Numeric Functions: INT (like Perl's int), RND (rand), ASC (ord), LEN (length), VAL (turn a string into a number; in Perl you just + 0 :))
RND just calls Perl's rand; you can't seed it or anything.
String functions: CHR$, MID$
Overall Coding Issues
Hopefully your code doesn't have many bugs, because there isn't much error checking.
Everything except string constants is converted to upper case, so 'a' and 'A' are the same variable. (But note that the string "Yes" <> "YES", at least for now.)
Spaces are (currently) required around various pieces of the program, like THEN, ELSE, GOTO. That is, GOTO20 won't work. This may or may not change in the future.
When you use basic.pl (&LB::Program::input), the lines in the input file must be in numerical order. When using termbasic.pl (&LB::Program::line), this rule doesn't apply.
BUGS
This is an alpha release and likely contains many bugs; these are merely the known ones.
If you use multiple Language::Basic::Program objects in a Perl program, functions and variables can leak over from one to another.
It is possible to get some Perl warnings; for example, if you input a string into a numerical variable and then do something with it.
PRINT and so forth all go to the select-ed output handle; there really ought to be a way to set for a Program the output handle.
There needs to be better and consistent error handling, and a more extensive test suite.
AUTHOR
Amir Karger (akarger@cpan.org)
David Glasser gave ideas and feedback, hunted down bugs, and sent in a major patch to help the LB guts.
COPYRIGHT
Copyright (c) Amir Karger 2000
LICENSE
This program is free software; you can redistribute it and/or modify it under the same terms as Perl itself.
HISTORY
BASIC stands for Beginner's All-purpose Symbolic Instruction Code. Since it was considered pretty hot stuff in the early 80's, it's the first language that I and a lot of folks my age learned, so it holds a special place in my heart. Which is the only reason I spent so many hours writing an interpreter for it long after it was superseded by real interpreted languages that had subroutines and didn't rely quite so much on GOTO.
I originally wrote this interpreter in C, as the final project for my first C programming class in college. Its name at that point was COMPLEX, which stood for "C-Oriented Major Project which did not use LEX".
When I learned Perl, I felt like its string handling capabilities would be much better for an interpreter, so eventually I ported and expanded it. (Incidentally, I was right. I had surpassed the original program's functionality in less than a week, and I was able to run wumpus in 2.)
A big goal for the Perl port is to support enough of the language that I can run wumpus, another legacy from my childhood. The interpreter's name could be changed from COMPLEX to "Perl Eclectic Retro interpreter which did not use Parse::LEX", or PERPLEX, but I settled for Language::Basic instead.
SEE ALSO
perl(1), wump(6)
2 POD Errors
The following errors were encountered while parsing the POD:
- Around line 750:
You forgot a '=back' before '=head2'
- Around line 761:
=back without =over