SolaBasic cheatsheet
Every statement and every supplied function on one page, for when you know what you want and not what it is called. SOLABASIC-REFERENCE.md is the full account of each of these; SOLABASIC.md is the language definition and says where the boundary came from. This is the index to your own memory.
Syntax, all of it
' a comment, to the end of the line
REM so is this, and it may hold anything -- an apostrophe, an unclosed quote
Again: ' a label. A number at the start of a line is one too
total = total + 1
IF total < 3 THEN GOTO Again
a = 1 : PRINT a : PRINT "two" ' ':' joins statements; a line ends where it ends
LET a = 1 ' LET is optional and means nothing
Keywords and names are case-insensitive — PRINT, Print and print are
one word. Spaces between words are required: FORI=1TO10 is not a FOR.
There is no line continuation.
| Written | Is |
|---|---|
42 |
an Integer — no point and no exponent |
3.14 1.5E-3 |
a Double — anything else |
&HFF &O17 |
255 and 15, in hexadecimal and octal |
"text" |
a String. No escape, so CHR$(34) is how you write a " |
A% A& |
an Integer, 64-bit |
A# |
a Double |
A$ |
a String |
DEFINT I-N |
the default type for names starting I to N |
a name with no suffix and no DEF |
a Double |
DEFINT, DEFLNG, DEFDBL and DEFSTR, one per type, each taking letters or
ranges separated by commas, anywhere in the listing.
Ten rules that bite
Three types and no SINGLE |
QBasic’s default is a 32-bit float; here it is a Double, so a ported program prints more digits. A! is refused by name. |
INTEGER is 64 bits |
QBasic’s is 16 and overflows at 32767. A program relying on that failure will not fail here. |
/ always answers a Double |
7 / 2 is 3.5. 7 \ 2 is 3, and \ cuts towards nought. |
MOD takes the sign of its left side |
-7 MOD 2 is -1, as QBasic says and not as the machine would. |
| There is no boolean | True is -1 and false is 0, which is why NOT, AND and OR are bit operations and still read correctly. |
| A condition is true when it is not nought | IF count THEN ... works. |
| Assigning a Double to an Integer rounds | n% = 3 / 2 is 2. |
| Parameters pass by reference | Assigning to one assigns to the caller’s variable. Brackets pass a copy: Double (n). |
| An array name means one array in the whole listing | Bounds are settled while compiling and looked up by name, so two procedures cannot each DIM a Temp. |
Names beginning SOLA are reserved |
PRINT’s own rules are written in SolaBasic and compiled into your program. |
Every variable starts at nought, or at "", so reading one before assigning
is 0 and not an error. VAL is strict — the whole string must be a number.
Expressions, tightest first
^ |
raise to a power; always a Double, and right-associative |
- |
negation — so -2 ^ 2 is -4 |
* / |
multiply, divide |
\ |
integer divide, both sides rounded first |
MOD |
remainder |
+ - |
add, subtract; + also joins text |
= <> < <= > >= |
comparison, on numbers or on text |
NOT |
flip every bit |
AND |
bit-by-bit and |
OR XOR |
bit-by-bit or, exclusive or |
Every level is left-associative but ^. Brackets group.
Choosing
IF x > 0 THEN PRINT "positive" ELSE PRINT "not"
IF i > 10 THEN Done ' a bare label after THEN or ELSE is a GOTO
IF n = 1 THEN ' nothing after THEN opens a block
PRINT "one"
ELSEIF n = 2 THEN
PRINT "two"
ELSE
PRINT "many"
END IF
SELECT CASE n
CASE 1, 2
PRINT "one or two"
CASE 3 TO 6
PRINT "three to six"
CASE IS >= 10
PRINT "ten or more"
CASE ELSE
PRINT "none of those"
END SELECT
The SELECT CASE subject is evaluated once, the first matching CASE runs, and
nothing may come between SELECT CASE and its first CASE.
Repeating
FOR i = 1 TO 10 : PRINT i : NEXT i
FOR i = 10 TO 1 STEP -1 : PRINT i : NEXT
FOR a = 1 TO 2 : FOR b = 1 TO 2 : PRINT a; b : NEXT b, a ' innermost first
DO WHILE more : ... : LOOP
DO UNTIL done : ... : LOOP
DO : ... : LOOP WHILE more
DO : ... : LOOP UNTIL done
DO : ... : LOOP ' only EXIT DO leaves this one
WHILE k < 3 : k = k + 1 : WEND ' an older spelling of DO WHILE, with no EXIT
The limit and the step are worked out once, when the loop starts. The test
is before the body, except in the two LOOP WHILE/LOOP UNTIL shapes. A DO
tests at one end or at neither, never at both.
EXIT FOR EXIT DO |
leave the innermost enclosing loop of that kind |
EXIT SUB EXIT FUNCTION |
leave the procedure |
GOTO label |
any label in the same procedure; may not cross into one |
END |
stop the program, from anywhere |
Declaring
CONST Size = 10 ' worked out while compiling, stored nowhere
OPTION BASE 1 ' asked once, before the first DIM
DIM a(10) ' OPTION BASE to 10
DIM Grid(1 TO 8, 1 TO 8) ' both bounds said; up to eight dimensions
DIM Counts(20) AS INTEGER ' AS INTEGER, LONG, DOUBLE or STRING -- or a suffix
DIM SHARED Totals(12) ' visible inside every procedure
DIM Balance AS DOUBLE ' a plain variable, given a type
Bounds are constant expressions, because an array is made once at the size
the listing wrote. There is no REDIM. DECLARE is accepted and does nothing.
Procedures
SUB Greet (who$)
PRINT "hello, "; who$
END SUB
FUNCTION Square (x)
Square = x * x ' a FUNCTION answers by assigning to its own name
END FUNCTION
CALL Greet("world")
Greet "world" ' the same call
PRINT Square(5) ' in an expression, always with brackets
hello, world
hello, world
25
Both may be written anywhere at module level and called from above where they
are written. A FUNCTION that never assigns to its name answers nought.
Recursion works, and stops at about 254 levels with call depth exceeded.
| inside a procedure | |
|---|---|
| every variable | local to that call |
SHARED a, b() |
name module-level variables to see instead |
STATIC n |
keep its value between calls |
Printing
PRINT "answer: "; x ' ';' moves nowhere
PRINT a, b, c ' ',' moves to the next zone -- 14 columns
PRINT ' end the line
PRINT "open"; ' a separator at the end holds the line open
PRINT TAB(10); "at ten" ' column 10, counting from one
PRINT "x"; SPC(5); "y" ' five spaces from wherever it is
A number is written as a sign character — a minus, or a space where one would go — then the digits, then a trailing space. A string gets neither. There is no nought before the point, and the margin is 80.
PRINT USING "###.##"; 3.14159#
PRINT USING "value: ### units"; 9
PRINT USING "###"; 1; 2; 3 ' the format starts again per item
3.14
value: 9 units
1 2 3
| in a number field | in a text field | |||
|---|---|---|---|---|
# |
a digit position | ! |
the first character | |
. |
where the point goes | \ \ |
as many as the backslashes and the gap | |
, |
thousands separators | & |
the whole string | |
+ |
leading, always show the sign | |||
- |
trailing minus for a negative | |||
** |
two more positions, asterisk padding | |||
$$ |
two more, one a floating $ |
|||
^^^^ |
exponential, then D±dd |
_ makes the next character literal. A number too wide for its field is
written in full behind a % rather than cut.
Reading
INPUT n ' shows "? "
INPUT "NAME"; n$ ' shows "NAME? " -- ';' adds the question mark
INPUT "NAME", n$ ' shows "NAME" -- ',' does not
INPUT "TWO"; a, b ' one line, split on the comma
LINE INPUT s$ ' the line whole
Files
Sequential only. Channels 1 to 15, and the # is not part of the number.
OPEN "data.txt" FOR OUTPUT AS #1
PRINT #1, "a line"
WRITE #1, "Hans", 42
CLOSE #1
OPEN "data.txt" FOR INPUT AS #1
DO UNTIL EOF(1)
LINE INPUT #1, row$
PRINT row$
LOOP
CLOSE #1
a line
"Hans",42
OPEN p FOR INPUT/OUTPUT/APPEND AS #n |
read; write, replacing; write, onto the end |
CLOSE #n[, #n]... |
those; CLOSE alone closes them all |
PRINT #n, ... |
as PRINT, zones and all. PRINT #n, USING f; ... too |
WRITE #n, ... |
commas between, quotes round the text — the form INPUT # reads back |
INPUT #n, v[, v]... |
one line, split on commas, quotes taken off |
LINE INPUT #n, v$ |
the line whole |
EOF(n) |
true once there is no more to read |
Lines are ended with a line feed, where QBasic writes a carriage return and a line feed; a trailing carriage return is taken off when reading, so a file written by either can be read here.
The supplied functions
Twenty-seven, each compiled where it is called — there is no library in the file the compiler writes.
| numbers | text | |||
|---|---|---|---|---|
ABS(x) |
size without a sign | LEN(s$) |
how many characters | |
SGN(x) |
-1, 0 or 1 |
LEFT$(s$, n) RIGHT$(s$, n) |
the first or last n; clamps |
|
INT(x) |
the floor | MID$(s$, start[, n]) |
from start, all or n |
|
FIX(x) |
cut towards nought | INSTR([start, ]s$, part$) |
where it is, one-based, or 0 | |
SQR(x) |
square root | UCASE$(s$) LCASE$(s$) |
case folded | |
EXP(x) LOG(x) |
e to the x; natural log | LTRIM$(s$) RTRIM$(s$) |
spaces off the front, or the back | |
SIN COS TAN ATN |
radians | SPACE$(n) |
n spaces |
|
RND |
a Double, 0 to under 1 |
STRING$(n, s$) |
s$’s first character, n times |
|
RANDOMIZE n |
a statement — reseed | ASC(s$) CHR$(n) |
character to number, and back |
| between the two | |
|---|---|
STR$(x) |
the number as text — without the leading space PRINT adds |
VAL(s$) |
the number that text spells, strictly: the whole string or nothing |
What is not here
| not written yet | random-access files — GET, PUT, FIELD, LOF, SEEK; LBOUND, UBOUND |
| not yet, by the definition | ON ERROR, TYPE, REDIM, OPTION EXPLICIT |
| not coming | GOSUB, RETURN, ON n GOTO, DATA, READ, SINGLE, and the whole of the PC — SCREEN, PEEK, POKE |
Running it
./bin/solas programs/sola.sol # build the compiler, once
./bin/solvm programs/sola.sob prog.bas # prog.bas -> prog.bas.sob
./bin/solvm prog.bas.sob # run it
./bin/solvm programs/sola.sob prog.bas out.sob # name the output
./bin/solvm --dump out.sob # the instructions it produced
./bin/solvm programs/sola.sob # a demonstration, and how to run it
Where this is not QBasic, and what the compiler says when it refuses, are both in SOLABASIC-REFERENCE.md. The compiler is programs/sola.sol and its header is the argument for how it works.