|
0 |
SICKBAY
|
|
1 |
=======
|
|
2 |
|
|
3 |
This document describes the SICKBAY programming language. SICKBAY is an
|
|
4 |
esoteric dialect of BASIC with two salient features:
|
|
5 |
|
|
6 |
* While most BASICs support a call stack which is used to implement `GOSUB`
|
|
7 |
and `RETURN`, SICKBAY uses a _call ring buffer_, which supports not only
|
|
8 |
`GOSUB` and `RETURN` but also `PROLONG` and `CUTSHORT`.
|
|
9 |
* While some BASICs support computed line numbers in `GOTO` and `GOSUB`,
|
|
10 |
SICKBAY supports computed line numbers only in line number definitions.
|
|
11 |
It thus lacks an `IF` statement because, similar to [Strelnokoff][], it
|
|
12 |
doesn't need one.
|
|
13 |
|
|
14 |
[Strelnokoff]: http://catseye.tc/projects/strelnokoff/
|
|
15 |
|
|
16 |
Syntax
|
|
17 |
------
|
|
18 |
|
|
19 |
A SICKBAY program is a series of lines. Each line must have a line number
|
|
20 |
(which may be an expression.) Unlike BASIC, adjacent tokens must be
|
|
21 |
separated by one or more spaces, if they would otherwise look like one word
|
|
22 |
(e.g. you need `PRINT A%`, not `PRINTA%`.)
|
|
23 |
|
|
24 |
The language's syntax is defined by the following EBNF (plus some
|
|
25 |
pseudo-productions for terminals) grammar:
|
|
26 |
|
|
27 |
SICKBAY ::= {Line}.
|
|
28 |
Line ::= IntExpr Stmt {":" Stmt} Newline.
|
|
29 |
Stmt ::= "REM" ArbText
|
|
30 |
| "LET" IntVar "=" IntExpr
|
|
31 |
| "GOTO" IntConst
|
|
32 |
| "GOSUB" IntConst
|
|
33 |
| "RETURN" | "END"
|
|
34 |
| "PROLONG" IntConst
|
|
35 |
| "CUTSHORT"
|
|
36 |
| "DIM" "RING" "(" IntExpr ")"
|
|
37 |
| "PRINT" (StrConst | IntExpr | "CHR$" IntExpr) [";"]
|
|
38 |
| "INPUT" (IntVar | "CHR$" IntVar)
|
|
39 |
.
|
|
40 |
IntExpr ::= IntVar
|
|
41 |
| IntConst
|
|
42 |
| "RND%" "(" IntExpr ")"
|
|
43 |
| "(" IntExpr IntOp IntExpr ")"
|
|
44 |
.
|
|
45 |
IntOp ::= "+" | "-" | "*" | "/".
|
|
46 |
IntVar ::= IntId ["(" IntExpr ")"].
|
|
47 |
IntId ::= /[A-Z][A-Z0-9]%/.
|
|
48 |
IntConst ::= /[0-9][0-9]*/.
|
|
49 |
StrConst ::= /"[^"]*"/.
|
|
50 |
ArbText ::= /[^\n]*/.
|
|
51 |
Newline ::= /\n+/.
|
|
52 |
|
|
53 |
Semantics
|
|
54 |
---------
|
|
55 |
|
|
56 |
Many of the SICKBAY statements have meanings very similar to those in BASIC,
|
|
57 |
and I appeal to your knowledge of that language to make this description
|
|
58 |
complete.
|
|
59 |
|
|
60 |
### Execution ###
|
|
61 |
|
|
62 |
Lines are executed in numerical order, which may have nothing to do with the
|
|
63 |
order they appear in the program text; however, if two lines have the same
|
|
64 |
line number, the one which appears first in the program text takes precedence
|
|
65 |
(the other lines with the same number are not "seen" during execution.)
|
|
66 |
Execution begins initially from the lowest-numbered line in the program.
|
|
67 |
|
|
68 |
Line numbers are "live"; they are recomputed from their expressions each time
|
|
69 |
execution progresses from one line to the next. (Two acceptable ways to
|
|
70 |
implement this are: every time a variable _x_ changes, recalculate the line
|
|
71 |
number of every line that uses _x_ in its line expression; or, just before
|
|
72 |
any jump or proceeding to the next line, recalculate all line numbers.)
|
|
73 |
|
|
74 |
Attempting to proceed to the next line when there are no more higher-numbered
|
|
75 |
lines in the program causes `END`. `END` is an alias for `RETURN`. `RETURN`
|
|
76 |
(or `CUTSHORT`) with nothing on the call ring buffer ends the program and
|
|
77 |
returns to the operating system.
|
|
78 |
|
|
79 |
The call ring buffer is of fixed size, and contains line numbers (concrete
|
|
80 |
line numbers, not expressions.) If no size is chosen before any
|
|
81 |
`GOSUB`/`RETURN`/`PROLONG`/`CUTSHORT` is executed, a default size of 10 line
|
|
82 |
numbers will be used. A `DIM RING` statement may be executed to set the size
|
|
83 |
of the ring buffer if it has not yet been set. (If it has already been set,
|
|
84 |
an error occurs.)
|
|
85 |
|
|
86 |
`GOSUB` pushes the current line number onto the top of the call ring buffer
|
|
87 |
and moves execution to the line with the number given to it. `RETURN` pops a
|
|
88 |
line number from the top of the call ring buffer and moves execution to the
|
|
89 |
next line in the program strictly following that line number. `RETURN` does
|
|
90 |
not continue to execute remaining statements on the same line as the `GOSUB`
|
|
91 |
after colons (see clarifying example below.)
|
|
92 |
|
|
93 |
`PROLONG` pushes the given line number onto the _bottom_ of the call ring
|
|
94 |
buffer. `CUTSHORT` pops a line number from the _bottom_ of the call ring
|
|
95 |
buffer. Neither of these change the flow of execution immediately. The
|
|
96 |
practical effect of `PROLONG` is to pretend that a `GOSUB` was made from a
|
|
97 |
line number before the first real `GOSUB` was ever made, effectively adding
|
|
98 |
some code that will be executed after the program ends. The practical effect
|
|
99 |
of `CUTSHORT` is to make the program end prematurely, when attempting to
|
|
100 |
`RETURN` to the rootmost caller (initially this would be the "main program".)
|
|
101 |
|
|
102 |
If space in the call ring buffer is exhausted, an error occurs.
|
|
103 |
|
|
104 |
In `GOTO` and `GOSUB`, if the given line number does not exist at the time
|
|
105 |
the statement is executed, an error occurs.
|
|
106 |
|
|
107 |
### Variables ###
|
|
108 |
|
|
109 |
All variables initially have the value zero. Any variable may be used as an
|
|
110 |
array; the variable itself is just an alias for the first element of the
|
|
111 |
array, i.e. `H% = H%(0)`. Arrays don't have bounds and don't need
|
|
112 |
dimensioning.
|
|
113 |
|
|
114 |
Integers may be negative. However, the syntax for integer constants only
|
|
115 |
allows non-negative integers; to give a negative constant, an expression such
|
|
116 |
as `(0 - 100)` must be used. Note that this means a negative line number
|
|
117 |
cannot be jumped to, as `GOTO` et al must be followed by an integer constant,
|
|
118 |
not an expression. (However, a negative line number may be _returned_ to, as
|
|
119 |
it is possible to write a program which begins executing at a negative line
|
|
120 |
number and makes a `GOSUB` from it.)
|
|
121 |
|
|
122 |
Operators have no precedence; parentheses must be used around all operations
|
|
123 |
(see grammar).
|
|
124 |
|
|
125 |
Like Strelnokoff, `/` is integer division, truncating downwards, and
|
|
126 |
evaluating to zero if the divisor is zero (there is no division by zero
|
|
127 |
error.)
|
|
128 |
|
|
129 |
The `RND%(`_n_`)` function evaluates to an integer from 0 to _n_-1, chosen
|
|
130 |
randomly. If _n_ is zero or negative, an error occurs.
|
|
131 |
|
|
132 |
### I/O ###
|
|
133 |
|
|
134 |
Integer expressions may be printed; they are formatted as decimal numerals,
|
|
135 |
possibly preceded by a negative sign, but, unlike most BASICs, not preceded
|
|
136 |
or followed by any spaces. The ASCII character for a given integer value may
|
|
137 |
be printed with the `PRINT CHR$` form. Literal strings may also be printed,
|
|
138 |
but only one thing may (and exactly one thing must) be printed per `PRINT`
|
|
139 |
statement (so to just print a blank line, print a null string literal.)
|
|
140 |
Anything printed with a `PRINT` statement will be followed by a newline,
|
|
141 |
unless the semicolon is given after the statement, which suppresses the
|
|
142 |
newline.
|
|
143 |
|
|
144 |
The `INPUT IntVar` form accepts an integer, formatted as decimal numerals,
|
|
145 |
possibly preceded by a negative sign, from the input stream, and places it in
|
|
146 |
the variable. Any whitespace preceding, and the first whitespace following
|
|
147 |
the integer is swallowed up; if the integer is not followed by at least one
|
|
148 |
whitespace character, an error occurs. The `INPUT CHR$ IntVar` form accepts
|
|
149 |
a single character from the input stream and places its ASCII value in the
|
|
150 |
variable.
|