Program structure
Write a program the compiler accepts: lines and colons, the six comment forms, names and case, strings, the parentheses rule, and the guards that refuse a line.
In one line#
A BASIC program is a list of lines. Each line holds one statement, or several
separated by :. The compiler reads the whole program before it runs, and it
refuses a line it cannot understand instead of guessing.
This chapter covers the shape of a program. The values it works on are in Variables and types, and the operators in Constants and operators.
Lines and statements#
<statement>
<statement> : <statement> [: <statement> ...]
<label>:
<label>: <statement> [: <statement> ...]
x = 10
y = 20 : total = x + y
Console(Str(total)) ' 30
- One statement per line, or several separated by
:. - Indentation is free. Spaces and tabs at the start of a line mean nothing. Blank lines are allowed anywhere.
- A
:inside a string or inside brackets does not split the line:"time: 12:30"stays one string. - A block can live on one line:
FOR i = 1 TO 5 : total = total + i : NEXT.
A single-line IF keeps everything after THEN#
After THEN, every : belongs to the IF. All the statements run only when the
condition is true.
lives = 0
IF lives = 0 THEN Console("Game over") : lives = 3
Console(Str(lives)) ' 3
Details in Control flow.
Labels#
A name followed by : at the start of a line is a label, the target of
GOSUB and GOTO. The label may carry statements on the same line.
GOSUB Greet
Console("back") ' printed second
END
Greet: Console("hello") : RETURN
⚠️ Because of this rule, a : right after the first word of a line always
declares a label. start: x = 1 is the label start with x = 1 as its body,
and the compiler asks for its RETURN: BASIC label start requires RETURN.
What does not exist#
| Classic feature | In aukimi |
|---|---|
Line numbers (10 x = 1) | refused: Not a BASIC statement |
Line continuation (_ at the end of a line) | refused: write the statement on one line |
; as a comment | not a comment: the text after it is compiled as code |
Lines that are only a Markdown code fence (three backticks, with or without a language name) are ignored. Code copied from a web page with its fence still runs, and its line numbers stay right.
Comments#
Six forms are accepted.
| Form | Where | Example |
|---|---|---|
' | anywhere; the rest of the line is a comment | x = 1 ' note |
// | anywhere; the rest of the line is a comment | x = 1 // note |
REM | start of a line, or after a statement | REM note |
` (backtick) | start of a line, or after a statement | x = 1 ` note |
REMSTART … REMEND | block; both words start their own line | see below |
/* … */ | block; /* starts its line | see below |
' A full-line comment
REM Another full-line comment
// A C-style line comment
x = 1 ' after a statement
y = 2 // after a statement too
z = 3 ` the backtick form
REMSTART
A block of notes
over several lines
REMEND
/* A C-style block
over several lines */
Console(Str(x + y + z)) ' 6
Use
'or//. They are the only two forms that are safe everywhere. The other four exist so that code from other dialects keeps compiling.
⚠️ A : inside a REM or backtick comment splits the line#
The line splitter stops at ' and //. It does not stop at REM or at a
backtick. A colon after them ends the comment, and the rest is compiled as code.
x = 1 REM note: this part is compiled
Not a BASIC statement: this part is compiled
The same happens with a full-line backtick comment that contains a colon. A
full-line REM is safe; a REM after a statement is not.
⚠️ Block comments have three rules#
-
REMSTART,REMENDand/*start their line.x = 1 REMSTARTis not a block comment. -
Everything on the line of
/*is comment, and so is the rest of the line after*/. Code written there never runs:/* setup */ Console("never shown") Console("shown") ' shown -
A line inside a block must not start with one word followed by
:. The compiler readsTODO:orNote:as a label and refuses the program.REMSTART TODO: tidy this up REMENDBASIC label TODO requires RETURNWrite
TODO - tidy this up, or use'lines.
A block that is never closed is an error: REMSTART requires REMEND, or
/* requires */.
Case and names#
Everything is case-insensitive: keywords, command names, variables, user
functions, record fields and constants. IF, If and if are the same word.
Console, CONSOLE and console are the same command.
The first spelling of a name is kept, and every later spelling is rewritten to it.
Score = 1
SCORE = SCORE + 1
score = score + 1
Console(Str(sCoRe)) ' 3
if score = 3 then Console("keywords in lower case work too")
Two exceptions:
| Name | Rule |
|---|---|
PI | upper case only. pi or Pi is an unknown name: the program stops with … is not defined. |
TRUE, FALSE, NULL | any case. |
area = PI * 2 ^ 2
Console(Str(area)) ' 12.566370614359172
Writing a name#
- Letters, digits and
_. It starts with a letter or_. - A variable may end with a sigil:
$,#,%or&. What a sigil does is in Variables and types. - A constant is written with
#in front:#MAX_LIVES. See Constants and operators.
Which names can share a spelling#
| Pair | Same spelling allowed? |
|---|---|
a variable and a record field (speed and s.speed) | yes, they never meet |
| a variable and a user FUNCTION | yes |
a variable and a catalogue command (left and Left) | yes, once the program assigns the variable with name = … |
| a user FUNCTION and a catalogue command | yes, and the user function wins |
a constant and a variable (#SPEED and SPEED) | ⚠️ no: spelled alike, the program does not start |
TYPE Ship
speed
ENDTYPE
s AS Ship
s.speed = 4
speed = 9
Console(Str(s.speed) + " " + Str(speed)) ' 4 9
left = 10
x = left + 1
Console(Str(x) + Left("abc", 1)) ' 11a
FUNCTION Sin(v)
ENDFUNCTION v * 2
Console(Str(Sin(90))) ' 180: your Sin, not the catalogue's
⚠️ JavaScript words are not free#
BASIC compiles to JavaScript. A variable named after a JavaScript keyword compiles, but the program does not start.
class = 3
Avoid these names: class, const, debugger, default, enum, export,
extends, import, in, instanceof, let, new, super, switch,
this, throw, typeof, var, void, await. Also avoid names that begin
with _b or __basic, and nextFrame: the compiler uses them for itself.
Strings#
"<text>"
- Double quotes only. A
'starts a comment, so'text'is not a string. - Two escapes exist:
\"for a quote, and\\for a backslash. - Every other backslash is a plain backslash. A Windows path works as
written, and
\nis two characters, not a new line. - A string stays on one line.
path$ = "media\background5.jpg"
quote$ = "She said \"hi\""
slash$ = "a\\b"
Console(path$) ' media\background5.jpg
Console(quote$) ' She said "hi"
Console(slash$) ' a\b
For a new line or a quote, build the string with Chr:
twoLines$ = "first" + Chr(10) + "second"
quoted$ = Chr(34) + "aukimi" + Chr(34)
Console(quoted$) ' "aukimi"
⚠️ The doubled quote of QBasic and Visual Basic ("") is not supported.
It compiles, but the program does not start. Write \" or Chr(34).
say$ = "say ""hi"""
⚠️ After a \", do not put a : or a ' in the same string. The line
splitter does not know the escape, so it thinks the string has ended there.
label$ = "a\"b: c"
Not a BASIC statement: c"
Use Chr(34) in such a string. Strings with {holes} are written $"…": see
Shorthands.
Literals#
| Kind | Examples |
|---|---|
| integer | 42, -7 |
| decimal | 3.5, .5, 5. |
| exponent | 1.25e2, 1.5E-3 |
| hexadecimal | 0x1F, 0XFF |
| octal | 0c17 |
| binary | %1010 |
| string | "text" |
| truth and nothing | TRUE, FALSE, NULL |
| π | PI |
d = 1.25e2
e = 0x1F
f = 0c17
g = %1010
Console(Str(d) + " " + Str(e) + " " + Str(f) + " " + Str(g)) ' 125 31 15 10
A malformed literal is refused on its line: Malformed BASIC numeric literal: 0x1G.
⚠️ $FF (PureBasic) and &HFF (QBasic, Visual Basic) are not hexadecimal
here. Write 0xFF.
How each type stores these values is in Variables and types.
The parentheses rule#
A command call always carries parentheses, even with no argument.
<Command>([<argument> [, <argument> ...]])
elapsed# = Timer()
width = DeviceWidth()
Sync()
The bare forms of AGK, DarkBASIC and PureBasic are refused:
PRINT "hello"
Sync
x = DeviceWidth
BASIC commands require parentheses: Print(...)
BASIC commands require parentheses: Sync(...)
BASIC commands require parentheses: DeviceWidth(...)
This is what lets the compiler tell a command from a variable. A name the
program assigns (left = 10) is a variable; a name followed by ( is a call.
⚠️ A variable used only through GLOBAL, LOCAL or CATCH, and never
assigned with a plain name = …, is still read as the command of the same
name. LOCAL left = 3 in a function is refused with
BASIC commands require parentheses: Left(...). Pick another name.
The two guards#
Two checks run on every line.
Unknown command#
A call whose name is not a catalogue command, a compiler built-in, a user FUNCTION, an array, a list or a map is refused. When a catalogue command is close in spelling, the compiler names it.
Prnt("hello")
Unknown BASIC command: Prnt() — did you mean Print()?
Blorp(1, 2)
Unknown BASIC command: Blorp()
The suggestion is offered when the name is at most one letter off (short names, up to four letters) or two letters off (longer names).
Not a statement#
A statement must be an assignment, a call, or start with a keyword. Anything else is refused, and the message quotes the start of the line.
go to the shop
VAR n = 5
Not a BASIC statement: go to the shop
Not a BASIC statement: VAR n = 5
What the guards do not catch#
- A call after a dot (
obj.method(...)) is not checked against the catalogue. That is how JavaScript objects stay reachable. - An unfinished expression, such as
x = 1 +orx =, passes both guards. The program then does not start. - A name that is read but never assigned compiles, and stops the program
when it runs (
… is not defined). See Variables and types.
Print draws, Console logs#
| Command | What it does |
|---|---|
Print(...) | draws text on the game screen; a game calls it every frame |
Console(...) | writes a line in the editor's console |
Debug(...) | the same as Console, under PureBasic's name |
ConsoleWarning(...), ConsoleError(...) | a warning or an error line in the console |
ClearConsole() | empties the console |
To check a value while you work, use Console. Every example in this manual
does.
A minimal program#
Straight-line: runs once#
Without a frame loop, the program runs from top to bottom once, then stops. This is the right shape for a test, a tool, or anything that writes to the console.
name$ = "aukimi"
Console("Hello, " + name$) ' Hello, aukimi
With a frame loop: runs every frame#
A game puts its setup above DO, and the frame body between DO and LOOP.
Sync() ends each frame.
frames = 0
DO
frames = frames + 1
Print("Frame " + Str(frames))
Sync()
LOOP
A loop without Sync() would never let the screen draw, so it is refused:
DO
x = x + 1
LOOP
DO/LOOP requires SYNC() so the loop can yield a frame
⚠️ In a program with DO … LOOP, a FUNCTION does not see the plain variables
set above DO. Declare shared values with GLOBAL. The reason, and the fix,
are in Variables and types.
There is one top-level DO … LOOP per program. Everything else about frames,
Wait, Sleep and tasks is in
Game loop and tasks.
Coming from another BASIC#
| You write | In aukimi |
|---|---|
PRINT "x", Sync, Debug "x" | Print("x"), Sync(), Debug("x") |
PRINT to see a value | Console(...): Print draws on the game screen |
10 GOTO 20 | no line numbers; use labels |
_ to continue a line | one statement per line |
"" inside a string | \" or Chr(34) |
&HFF, $FF | 0xFF |
pi | PI |
; comment | ' comment |