A program consists of a series of lines. Each line consists of a line number (which may be an expression containing variables), and one or more statements separated by the ':' character. Valid programs are encoded in ASCII or UTF-8, with Unix or Windows style line separators.
Execution begins at the lowest non-negative numbered line. If there are multiple lines with equal line numbers: the earlier line in the program file is always chosen. Execution always proceeds from the current value of the line number of the line just executed; so control flow can be achieved by a line setting variables which are referenced by its line number expression.
Variable names consist of a letter, followed by zero or more letters, numbers, or underscores (_), and ended by a '%' character. Each variable is an unbounded array (in this implementation implemented as a map). If referenced without an index: an implicit index of 0 is used. An explicit index is given after the variable name and surrounded in parentheses. for example: a% is the same as a%(0). All values are either 32 or 64 bit integers, depending on the platform.
There are four types of integer expressions:
- Integer constants: non-negative numbers such as 1, 25, etc. Negative numbers can must be created using the subtraction operator
- Variables: described above
- Operators: "+", "*", "-", and "/". No operator precedence is applied, and every use of an operator must be surrounded by parentheses. Division is always rounded towards -2147483648 or -9223372036854775808 depending on platform. In subexpressions: rounding is performed immediately; not once the entire expression is complete.
- Random numbers: rnd$(xyz%) evaluates to a random number between 0 and xyz%
There are 5 types of statements: REM, LET, INPUT, PRINT, and END
- REM is used to hold source code comments, or to block other lines from executing. ':' characters following a REM are counted as part of the comment, so a REM is always the last statement on a line.
- LET is used to set variables. Syntax example: LET xyz%(3) = (xyz%(2) + 1). Because line numbers may depend on variables: LET statements can also be used for control flow.
- INPUT reads items from stdin to variables. It has 3 forms:
- `INPUT xyz%` reads an ASCII integer and parses it, sets xyz% to the result
- `INPUT chr$xyz%` reads a UTF-8 character and sets xyz% to its unicode codepoint.
- `INPUT byte$xyz%` reads a byte and sets xyz% to its value.
- PRINT writes items to stdout. The argument can be a string surrounded by double quote marks, or a variable optionally prefixed with chr$ or byte$. PRINT wites a newline character after writing its argument, which can be prevented by adding a ';' character to the end of the statement.
- End immediately stops execution of the program.
0 REM The first LET line initially also has line number 0, so is 0 REM coincidentally prevented from executing by these comments. The second LET 0 REM line moves the first one to line 30, and then it moves itself back to line 0 REM 10. Since moving the line being executed changes the control flow: this 0 REM creates an infinite loop. loop% LET loop% = 10 20 LET loop% = 30
99 Bottles of beer:
0 REM Prints the lyrics of the famous song: 0 REM http://www.99-bottles-of-beer.net/ (0 - 50) REM printing subroutine (0 - 49) LET rf%(sh%) = (0 - 41) (0 - 45) PRINT beer%;: PRINT " "; ((0 - 44) * (1 - ((beer% - 1) / (beer% - 1)))) PRINT "bottle"; (0 - 44) PRINT "bottles"; (0 - 42) PRINT " of beer"; 10 LET beer% = 99 12 LET loop% = 90 20 LET cf%(sh% + 1) = (0 - 50): LET cf%(sh%) = 21 30 PRINT " on the wall, "; 40 LET cf%(sh% + 1) = (0 - 50): LET cf%(sh%) = 41 50 PRINT ".": PRINT "Take one down and pass it around, "; 60 LET beer% = (beer% - 1) (70 * (1 - (beer% / beer%))) PRINT "No more bottles of beer"; 70 LET cf%(sh% + 1) = (0 - 50): LET cf%(sh%) = 71 80 PRINT ".": PRINT "" 100 PRINT "No more bottles of beer on the wall, no more bottles of beer." 110 PRINT "Go to the store and buy some more, 99 bottles of beer on the wall." 1000 END (loop% * (beer% / beer%)) LET loop% = 11 rf%(sh%) LET sh% = (sh% - 1) cf%(sh%) LET rf%(sh%) = cf%(sh%): LET sh% = (sh% + 1)
A reference implementation can be found at https://gist.github.com/quickdudley/5db00b97e33ec9405a013bd5e1aa3dc8