Constants and operators

Name fixed values with #CONSTANT and ENUMERATION, compute with every operator, avoid the precedence traps, and pick the right built-in maths and string functions.

In one line#

A constant is a name for a fixed value, written with # in front. Operators combine values. Most of them read as in any BASIC, but a few follow JavaScript rules, because BASIC compiles to JavaScript. This chapter lists every one, and the traps that come with them.

#CONSTANT#

#CONSTANT <NAME> <value>
#CONSTANT <NAME> = <value>

Read it back with #<NAME>.

#CONSTANT TITLE "Space Rocks"
#CONSTANT GRAVITY 9.81
#CONSTANT DOUBLE_G = #GRAVITY * 2
Console(#TITLE + " " + Str(#DOUBLE_G))   ' Space Rocks 19.62
  • The value is a number, a string, or an expression of other constants.
  • The name is case-insensitive: #title reads #TITLE.
  • A constant is usable above its own line. Constants are set before the first line of the program runs.
Console(Str(#MAX_LIVES))   ' 3
#CONSTANT MAX_LIVES 3

What is refused#

A name declared twice, or the name of a built-in constant:

#CONSTANT LIVES 3
#CONSTANT LIVES 5
Duplicate BASIC constant: #LIVES

The name of a catalogue command. The message points at the command:

#CONSTANT LEFT 1
BASIC commands require parentheses: Left(...)

Pick a name no command uses, such as #DIR_LEFT.

⚠️ Traps#

  • A constant and a variable spelled exactly alike, case included, break the program. It compiles, but it does not start. #SPEED and speed happen to work, because their first spellings differ; do not rely on it, and give them different names.

    #CONSTANT SPEED 5
    SPEED = 3
  • A constant name carries no sigil. #CONSTANT NAME$ "x" compiles, but the program does not start.

  • A misspelled constant is found only when the line runs. #SPEDE compiles, and stops the program with SPEDE is not defined.

  • Writing to a constant is found only when the line runs. #SPEED = 6 stops the program with Assignment to constant variable.

  • PureBasic's #NAME = 5 is not a declaration outside an ENUMERATION. Write #CONSTANT NAME 5.

⚠️ With DO … LOOP, functions do not see constants#

In a program with a frame loop, constants are set up inside the loop's own function. A FUNCTION that reads one stops the program.

#CONSTANT MAX_LIVES 3
FUNCTION Lives()
ENDFUNCTION #MAX_LIVES
DO
  Console(Str(Lives()))   ' stops: MAX_LIVES is not defined
  Sync()
LOOP

Copy the constant into a GLOBAL, and read the GLOBAL in the function:

#CONSTANT MAX_LIVES 3
GLOBAL maxLives = #MAX_LIVES
FUNCTION Lives()
ENDFUNCTION maxLives
DO
  Console(Str(Lives()))   ' 3
  Sync()
LOOP

The same limit applies to plain variables: see Variables and types.

Built-in constants: #AK_*#

aukimi defines 61 constants, all named #AK_…. They name the options that commands take, so your code does not depend on raw numbers. This is the complete list.

FamilyConstants and valuesUsed for
AUTO#AK_AUTO_ID = -1let a creation command pick the id
ERROR#AK_ERROR_IGNORE = 0, #AK_ERROR_REPORT = 1, #AK_ERROR_STOP = 2the error policy of ErrorMode
TEXT#AK_TEXT_ASCII = 0, #AK_TEXT_UTF16 = 1, #AK_TEXT_UTF8 = 2text encodings
MATCH#AK_MATCH_EXACT = 0, #AK_MATCH_FOLD_CASE = 1case-sensitive or not
ORDER#AK_ORDER_LOW_TO_HIGH = 0, #AK_ORDER_HIGH_TO_LOW = 1, #AK_ORDER_FOLD_CASE = 2sorting
ESCAPE#AK_ESCAPE_NATIVE = 0, #AK_ESCAPE_MARKUP = 1, #AK_ESCAPE_DATA = 2escaping text
LIST#AK_LIST_LAST = 0, #AK_LIST_FIRST = 1, #AK_LIST_BEFORE = 2, #AK_LIST_AFTER = 3positions in a list
DATA#AK_DATA_NULL = 0, #AK_DATA_TEXT = 1, #AK_DATA_NUMBER = 2, #AK_DATA_FLAG = 3, #AK_DATA_LIST = 4, #AK_DATA_RECORD = 5, #AK_DATA_PRETTY = 1JSON and dictionary values
PATTERN#AK_PATTERN_DOT_NEWLINE = 1, #AK_PATTERN_IGNORE_SPACES = 2, #AK_PATTERN_LINE_ANCHORS = 4, #AK_PATTERN_ANY_NEWLINE = 8, #AK_PATTERN_FOLD_CASE = 16regular expression options
TIME#AK_TIME_PART_YEAR = 0, #AK_TIME_PART_MONTH = 1, #AK_TIME_PART_WEEK = 2, #AK_TIME_PART_DAY = 3, #AK_TIME_PART_HOUR = 4, #AK_TIME_PART_MINUTE = 5, #AK_TIME_PART_SECOND = 6, #AK_TIME_LOCAL = 0, #AK_TIME_UTC = 1dates
CONFIG#AK_CONFIG_COMPACT = 1, #AK_CONFIG_SECTION_GAPS = 2, #AK_CONFIG_NO_BOM = 4preference files
HASH#AK_HASH_CRC32 = 1, #AK_HASH_SHA1 = 2, #AK_HASH_SHA256 = 3, #AK_HASH_HMAC = 256StringFingerprint, FileFingerprint
PACK#AK_PACK_FAST = 1, #AK_PACK_BALANCED = 6, #AK_PACK_MAX = 9, #AK_PACK_SAFE_LIMIT = 32000000compression
XML#AK_XML_ROOT = 0, #AK_XML_ELEMENT = 1, #AK_XML_COMMENT = 2, #AK_XML_CDATA = 3, #AK_XML_DOCTYPE = 4, #AK_XML_INSTRUCTION = 5, #AK_XML_OK = 0, #AK_XML_INVALID = 1, #AK_XML_PRETTY = 1, #AK_XML_NO_DECLARATION = 2XML nodes and output
ErrorMode(#AK_ERROR_REPORT)
Console(Str(#AK_ERROR_STOP))   ' 2
  • They are read like your own constants, in any case: #ak_error_stop works.
  • You cannot redefine one: #CONSTANT AK_ERROR_STOP 5 is a duplicate.
  • Flags whose values are powers of two (PATTERN, CONFIG, #AK_HASH_HMAC) combine with ||, the bitwise or.
  • There are no #PB_* or AGK constants. A program that uses them gets … is not defined when the line runs.

ENUMERATION and ENUMERATIONBINARY#

ENUMERATION [<Name>] [<start> [STEP <step>]]
  #<NAME> [= <value>]
  ...
ENDENUMERATION

ENUMERATIONBINARY [<Name>] [<start>]
  #<NAME> [= <value>]
  ...
ENDENUMERATION

An enumeration declares a run of constants, one per line.

ENUMERATION
  #RED
  #GREEN
  #BLUE
ENDENUMERATION
Console(Str(#RED) + Str(#GREEN) + Str(#BLUE))   ' 012
ENUMERATION 10 STEP 5
  #A
  #B
  #C = 100
  #D
ENDENUMERATION
Console(Str(#A) + " " + Str(#B) + " " + Str(#C) + " " + Str(#D))   ' 10 15 100 105
ENUMERATIONBINARY
  #FLAG_A
  #FLAG_B
  #FLAG_C
ENDENUMERATION
Console(Str(#FLAG_A) + " " + Str(#FLAG_B) + " " + Str(#FLAG_C))   ' 1 2 4
RuleDetail
ENUMERATIONstarts at 0 and adds 1, or starts at <start> and adds <step>
ENUMERATIONBINARYstarts at 1 (or <start>) and doubles: 1, 2, 4, 8…
#NAME = <value>sets this value; the next entries continue from it
valueswhole numbers: 0.5 is rounded to 1
<start>, <step>, <value>numbers, or expressions of other constants; no command calls
a named enumerationcontinues where it stopped when you open it again
ENUMERATION Tiles
  #FLOOR
  #WALL
ENDENUMERATION
ENUMERATION Tiles
  #DOOR
ENDENUMERATION
Console(Str(#DOOR))   ' 2

Refused:

You writeMessage
a line without # inside the blockBASIC enumeration accepts only #Constant entries
two names on one line (#A, #B)same message
ENUMERATIONBINARY 1 STEP 2ENUMERATIONBINARY does not accept STEP
an enumeration inside anotherNested BASIC enumerations are not supported
a named enumeration reopened in the other modeBASIC enumeration Tiles cannot change between sequential and binary modes
#A = Random(1, 2)BASIC enumeration values must be constant numeric expressions

A STEP needs a start value: write ENUMERATION 0 STEP 2.

Arithmetic#

OperatorMeaningExampleResult
+add7 + 29
-subtract, or negate7 - 25
*multiply7 * 214
/divide, with decimals7 / 23.5
DIVdivide, whole part7 DIV 23
^power7 ^ 249
Mod(a, b)remainderMod(7, 2)1
a = 7
b = 2
Console(Str(a / b))      ' 3.5
Console(Str(a DIV b))    ' 3
Console(Str(-7 DIV 2))   ' -3
Console(Str(Mod(a, b)))  ' 1
Console(Str(Mod(-7, 3))) ' -1
Console(Str(a ^ b))      ' 49
  • / always keeps the decimals, even between two integers.
  • DIV drops them, toward zero: -7 DIV 2 is -3.
  • Dividing by zero: / gives Infinity; DIV stops the program with BASIC integer division by zero.
  • The remainder takes the sign of the first number: Mod(-7, 3) is -1.

⚠️ MOD is written as a call#

The MOD operator of other BASICs is refused, because the catalogue has a Mod command:

r = 7 MOD 2
BASIC commands require parentheses: Mod(...)

Write Mod(7, 2). For decimals, FMod(7.5, 2) gives 1.5.

⚠️ DIV takes its two neighbours#

DIV works between two plain values: numbers, variables, array cells, record fields, or a group in parentheses on its left.

  • It is done before * and /: 2 * 7 DIV 2 is 2 * 3, so 6.

  • After a command call, it breaks the program, which does not start:

    s$ = "hello"
    half = Len(s$) DIV 2
  • Before a group in parentheses, it is refused:

    a = 9
    b = 4
    x = a DIV (b - 1)
    Unknown BASIC command: DIV()
    

Put the call or the group in a variable first:

a = 9
b = 4
d = b - 1
x = a DIV d
Console(Str(x))   ' 3

⚠️ ^ and the minus sign#

  • ^ groups from the right: 2 ^ 3 ^ 2 is 2 ^ 9, so 512.

  • A minus sign (or NOT) directly before a power is not allowed. The program does not start:

    x = 2
    y = -x ^ 2

    Write -(x ^ 2) for −4, or (-x) ^ 2 for 4. 2 ^ -2 is fine: 0.25.

Comparison#

OperatorMeaning
=equal
<>different
<, >less, greater
<=, >=less or equal, greater or equal
a = 5
same = (a = 5)
differ = (a <> 5)
Console(Str(same) + " " + Str(differ))   ' 1 0
loose = ("5" = 5)
Console(Str(loose))   ' 0: text is never equal to a number
IF a >= 5 THEN Console("five or more")   ' five or more
  • A comparison stored in a variable is 1 or 0. Inside IF, WHILE and UNTIL, it is simply true or false.
  • On the right of an assignment, = compares. same = (a = 5) does not change a. The parentheses are optional, but clearer.
  • Equality is strict: the text "5" is not the number 5. Convert first with Val or Str.
  • Text compares in alphabetical order of character codes: "B" < "a".

⚠️ Inside a call, = is not a comparison#

In a statement that is a command call, an = inside the parentheses assigns. The variable changes, silently.

a = 5
Console(Str(a = 1))   ' 1, and a is now 1
Console(Str(a))       ' 1

Compare in a variable first, or in an IF.

⚠️ == and != compare loosely#

== and != are accepted, but they are JavaScript's loose comparisons: text and numbers are converted before comparing.

c$ = "7"
n = 7
IF c$ = n THEN Console("never printed")
IF c$ == n THEN Console("== compares loosely")   ' == compares loosely

Use = and <>.

Logical operators: AND, OR, NOT#

x = 5
y = 0
IF x > 0 AND y = 0 THEN Console("both")         ' both
IF x < 0 OR y = 0 THEN Console("one of them")   ' one of them
IF NOT y THEN Console("y is zero")              ' y is zero
r = x > 0 AND y = 0
Console(Str(r))   ' 1
  • NOT is true for 0, an empty string and FALSE, and false for anything else.
  • The right side is evaluated only when needed: in a AND b, b is skipped when a is false.

⚠️ On plain numbers, AND and OR return one of their operands, not 1 or 0. Classic BASICs compute bits; aukimi does not.

v = 5 AND 3
Console(Str(v))   ' 3: the second operand
w = 0 OR 7
Console(Str(w))   ' 7

For bits, use the bitwise operators below.

Bitwise operators#

aukimiMeaningExampleResult
&&and%1100 && %10108
||or%1100 || %001115
~~exclusive or%1100 ~~ %10106
!not (every bit flipped)!0-1
<<shift left1 << 416
>>shift right, keeping the sign-16 >> 2-4
flags = %1100
Console(Str(flags && %1010))   ' 8
Console(Str(flags || %0011))   ' 15
Console(Str(flags ~~ %1010))   ' 6
Console(Str(!0))               ' -1
Console(Str(1 << 4))           ' 16
Console(Str(-16 >> 2))         ' -4

The symbols are bitwise, the words are logical. && is not AND, and || is not OR. This is the reverse of C and JavaScript.

Bitwise operators work on signed 32-bit integers.

⚠️ XOR does not exist. It compiles, but the program does not start. Write ~~.

x = 5 XOR 3

⚠️ A single & is not string concatenation. "a" & "b" gives 0. Concatenate with +.

⚠️ A bit test needs parentheses#

&& comes after = in the order of operations. flags && 4 = 4 means flags && (4 = 4).

flags = 4
IF flags && 4 = 4 THEN Console("never printed")
IF (flags && 4) = 4 THEN Console("bit set")   ' bit set
IF flags && 4 THEN Console("also set")         ' also set

Text#

+ joins strings. When one side is text and the other a number, the number is turned into text.

score = 10
Console("Score: " + Str(score))   ' Score: 10
Console("n" + 5)                  ' n5
Console(Str(5 + "5"))             ' 55
Console(Str(10 - "3"))            ' 7

⚠️ -, * and / turn text into a number, but + turns a number into text. Convert on purpose: Str to make text, Val or ValFloat to make a number.

Strings with {holes}, such as $"Score: {score}", are in Shorthands.

TRUE, FALSE, NULL#

WordValue
TRUE, FALSEtruth values (not the numbers 1 and 0)
NULLno value; the start value of a HANDLE

They are written in any case.

ready = TRUE
Console(Str(ready))   ' true
IF ready THEN Console("ready")   ' ready
IF ready = 1 THEN Console("never printed")
done = (3 > 2)
Console(Str(done))    ' 1

⚠️ TRUE is not 1. A variable set to TRUE prints true and is not equal to 1. Test it with IF ready THEN. A comparison, on the other hand, stores 1 or 0.

Order of operations#

From first to last. Operators on the same row go from left to right, except ^.

OrderOperators
1( ), calls
2DIV, with its two neighbours
3^ (from the right)
4NOT, !, the minus sign
5*, /
6+, -
7<<, >>
8<, <=, >, >=
9=, <> (and ==, !=)
10&&
11~~
12||
13AND
14OR

⚠️ The traps#

You writeIt meansWrite instead
NOT a = b(NOT a) = ba <> b
flags && 4 = 4flags && (4 = 4)(flags && 4) = 4
2 * 7 DIV 22 * (7 DIV 2) = 6t = 2 * 7 then t DIV 2
2 ^ 3 ^ 22 ^ 9 = 512(2 ^ 3) ^ 2 for 64
-x ^ 2refused: the program does not start-(x ^ 2)
1 < x < 5(1 < x) < 5, always truex > 1 AND x < 5
a = 1
b = 2
IF NOT a = b THEN Console("never printed")
IF a <> b THEN Console("a and b differ")   ' a and b differ
same = (a = b)
IF NOT same THEN Console("not the same")   ' not the same

Built-in functions#

Two kinds of functions look alike in your code.

Handled by the compiler#

NameWhat it does
Float(x)turns x into a number: Float("2.5") is 2.5
Max(a, b, …)the largest of any number of values
Min(a, b, …)the smallest of any number of values
PI3.141592653589793 (upper case only)
TRUE, FALSE, NULLsee above
Console(Str(Max(3, 9, 12)) + " " + Str(Min(4, -1, 2)))   ' 12 -1
Console(Str(Float(7) / 2))   ' 3.5

From the catalogue#

Everything else is a catalogue command, even the classic maths and string names. Each has its page in the command reference.

The catalogue's trigonometry works in degrees. Sin(90) is 1. The radian versions end in Rad.

Console(Str(Sin(90)))           ' 1
Console(Str(Cos(180)))          ' -1
Console(Str(ATan2(1, 1)))       ' 45
Console(Str(SinRad(PI / 2)))    ' 1
Console(Str(Sin(PI / 2)))       ' 0.027412133592044294: PI / 2 degrees
FunctionResultNote
Sin, Cos, Tanfrom an angle in degrees
ASin, ACos, ATan, ATan2(y, x)an angle in degrees
SinRad, CosRad, TanRadfrom an angle in radians
Sqrt(x), Pow(a, b), Abs(x)
Exp(x), Log(x), Log10(x)Log is the natural logarithm
Int(x), Trunc(x)the whole part, toward zeroInt(-3.7) is -3
Floor(x), Ceil(x)down, upFloor(-3.2) is -4
Round(x)the nearest whole number; .5 goes upRound(2.5) is 3, Round(-2.5) is -2
Mod(a, b), FMod(a, b)remainder; whole, decimalsign of a
Sign(x)-1, 0 or 1
Random(from, to)a whole number, both ends included
Str(n), Str(n, decimals)textStr(3.14159, 2) is 3.14
Val(s), Val(s, base)a whole numberVal("1.5") is 1, Val("ff", 16) is 255
ValFloat(s)a decimal numberValFloat("1.5") is 1.5
Len(s)the number of characters
Left(s, n), Right(s, n)the first or last n characters
Mid(s, start, length)part of sstart counts from 1
Upper(s), Lower(s)
Chr(code), Asc(s)a character from its code, and backChr(34) is "
Hex(n), Bin(n)text in base 16 or 2Hex(255) is FF
FindString(s, find)the position, from 1; 0 when absent
Console(Str(Int(-3.7)) + " " + Str(Floor(-3.7)))   ' -3 -4
Console(Mid("hello", 2, 3))                         ' ell
Console(Str(FindString("hello", "l")))              ' 3

⚠️ QBasic's RND and INSTR do not exist. They are refused as unknown commands. Write Random(from, to) and FindString(s, find).

x = RND(5)
p = INSTR("hello", "l")

⚠️ Int truncates; it does not round down. QBasic's INT(-3.7) is −4; aukimi's Int(-3.7) is −3. Use Floor for −4.

⚠️ PureBasic's Sin takes radians; aukimi's takes degrees. Use SinRad when your angles are in radians.

Coming from another BASIC#

You writeIn aukimi
a MOD bMod(a, b)
a XOR ba ~~ b
a AND b on bitsa && b
a OR b on bitsa || b
NOT a on bits!a
a & b to join text (Visual Basic)a + b
a \ b (whole division)a DIV b
#NAME = 5 (PureBasic)#CONSTANT NAME 5
CONST NAME = 5#CONSTANT NAME 5
RND, INSTR, INTRandom, FindString, Floor
Sin(x) with x in radiansSinRad(x)