ABCalc Expression Language — Complete Syntax Reference
This document is a single-file reference for the full syntax of the ABCalc expression language. It covers everything needed to write complex expressions and custom functions, and serves as the authoritative guide for adding new built-in functions to the ABCalc codebase.
Related pages: Values · Operators · Expressions · Functions · Variables
Table of Contents
- Literal Values
- Variables and Memory
- Operators
- Indexing and Slicing
- Expressions and Statements
- Function Calls
- Custom Function Definitions
- Type System
1. Literal Values
Integer Numbers
Integer literals can be written in four bases. Underscores and number group separators (e.g., ,) are allowed anywhere for readability.
| Form | Prefix | Example |
|---|---|---|
| Decimal | (none) | 123456, 1_000_000 |
| Hexadecimal | 0x |
0xFFFF, 0xBE_EF |
| Octal | 0o |
0o1777 |
| Binary | 0b |
0b1010_1010 |
Negative integer literals use a leading - sign: -42. ABCalc automatically promotes integers to long (64-bit) or BigInteger when the value overflows.
123456 // decimal
0xFF // 255 in hex
0b1101 // 13 in binary
0o17 // 15 in octal
1_000_000 // one million (underscores for readability)
Floating-Point Numbers
Floating-point literals support standard and scientific notation. The decimal separator is locale-dependent (typically . or ,; see ABCalc settings). A number with no integer part may omit the leading zero.
3.14
.5 // same as 0.5
1E-2 // 0.01 (scientific)
6.022E23 // Avogadro's number
1.5e+18
Booleans
true false // case-insensitive: TRUE, False, etc.
Integer 0 is treated as false and any non-zero integer as true when a boolean is expected.
When ternary logic is enabled, null acts as undefined.
Strings
Strings may be delimited by single quotes '…', double quotes "…", or backquotes `…`.
Single-quote and double-quote strings are decoded — escape sequences are replaced:
| Escape | Character |
|---|---|
\' |
Single quote |
\" |
Double quote |
\\ |
Backslash |
\n |
Newline |
\r |
Carriage return |
\t |
Tab |
\b |
Backspace |
\f |
Form feed |
\v |
Vertical tab |
\0 |
Null (code 0) |
\a |
Bell (code 7) |
\uXXXX |
Unicode code point (4 hex digits) |
\xXXXX |
Unicode code point (4 hex digits) |
Verbatim / raw strings — no escape processing:
- Backquote strings:
`no escapes here, \n is literal` @"…"prefix:@"no escapes, \n stays as-is"
Strings are concatenated with +:
'Hello' + ', ' + 'World!' // "Hello, World!"
Characters
A single character in single quotes is a char value, not a string:
'A' // character A (Unicode 65)
'\n' // newline character
Characters compare equal to their Unicode code points: 'A' == 65 is true.
Dates and Time Periods
Date/time literals are enclosed in #…#. The format follows system regional settings or ISO 8601.
Keywords (no quotes or #):
| Keyword | Meaning |
|---|---|
now |
Current date and time |
today |
Current date (no time) |
yesterday |
Current date minus 24 hours (midnight, the beginning of the previous day) |
tomorrow |
Current date plus 24 hours (midnight, the beginning of the next day) |
Date literals:
#2024-12-15# // ISO 8601 date
#2024-12-15T10:30:00Z# // ISO 8601 datetime
#15.12.2024# // European format (locale-dependent)
#15.12.2024 10:30:05#
#in 3 days#
#5 years ago#
#before 2 months#
#3 days later#
Time period (TimeSpan) literals:
#5:00# // 5 hours
#00:03:00# // 3 minutes
#2.12:00:00# // 2 days, 12 hours
#5 min 30 sec#
#5d3h30min#
#3 years 1 day#
Important for custom functions: Date literals are evaluated at parse time, not call time. Use
Today()andNow()functions instead of#today#or#now#inside custom function bodies to get the current date at runtime.
Complex Numbers
i // imaginary unit (√−1)
3i // 0+3i
4+2i // complex: real part 4, imaginary part 2
(7+5i) * (3-2i) // complex arithmetic (parentheses required here)
Vectors
Vectors are written with square brackets and semicolon-separated numeric components:
[1; 2; 3] // 3-component vector
[1.0; 0.0; 0.0] // unit X vector
Arithmetic operators (+, -, *, /) work component-wise on vectors of the same dimension. Dot product, cross product, and other vector operations are available via built-in functions.
Lists
Lists are parenthesized semicolon-separated values. Commas also work but may conflict with locale decimal/group separators, so semicolons are preferred.
(1; 2; 3)
('a'; 'b'; 'c')
(1; 'hello'; true; null) // mixed types allowed
Use MakeList(n) or MakeList(n; defaultValue) to construct lists programmatically:
MakeList(5) // (null; null; null; null; null)
MakeList(3; 0) // (0; 0; 0)
MakeList(4; "x") // ("x"; "x"; "x"; "x")
Percent Values
A % suffix marks a value as a percent:
5% // 5 percent
(1+2)% // 3 percent
Percent values interact with arithmetic operators as expected: 200 + 10% yields 220.
Named Constants
| Name | Value |
|---|---|
pi |
π ≈ 3.14159265358979… |
e |
Euler's number ≈ 2.71828182845905… |
phi |
Golden ratio ≈ 1.61803398874989… |
unit_XXX |
Conversion unit (see Convert() function) |
Constants are read-only — they cannot be assigned.
Previous Result (@)
The @ character alone refers to the result of the most recently evaluated expression. Returns null if no previous result exists.
100 * 5 // result: 500
@ / 2 // result: 250 (half of the previous 500)
2. Variables and Memory
Variables ("memory cells") are named storage slots. Names are case-insensitive (MyVar and myvar refer to the same cell). A variable that has never been set returns null.
Reserved names: mc0–mc9 (mapped to the memory buttons in the UI). Constants (pi, e, phi, unit_XXX) cannot be used as variable names.
Basic Assignment
a := 42 // standard assignment (always available)
myVar := 'hello'
If the option to use = for assignment is enabled in settings, plain = also assigns:
x = 100 // only if the setting is on
Compound Assignment
| Operator | Meaning |
|---|---|
+= |
Add and store |
-= |
Subtract and store |
*= |
Multiply and store |
/= |
Divide and store |
&= |
Bitwise AND and store |
|= |
Bitwise OR and store |
^= |
Bitwise XOR and store |
a := 10; a += 5; a // 15
b := 0b1111; b &= 0b1010; b // 10
Indexed Element Update
A string or list stored in a variable can be updated element-by-element:
mc0 := "abcdefg";
mc0[3] := 'X';
mc0 // "abcXefg"
3. Operators
Operator Precedence (Highest to Lowest)
| Priority | Operators | Associativity |
|---|---|---|
| 1 (highest) | ! !! !!! (postfix factorial) |
Left |
| 2 | % (postfix percent) |
Left |
| 3 | ** ^(Programmer) ↑ (exponentiation) |
Right |
| 4 | Unary - + not ¬ bit_not ~(Programmer) !(Programmer) √ ∛ ∜ |
Right |
| 5 | * / \\ div mod %(Programmer) |
Left |
| 6 | + - |
Left |
| 7 | << >> |
Left |
| 8 | < <= > >= ≤ ≥ |
Left |
| 9 | == = != <> ≠ IN NOT IN LIKE NOT LIKE |
Left |
| 10 | BIT_AND &(Programmer) |
Left |
| 11 | BIT_XOR ^(Programmer) |
Left |
| 12 | BIT_OR \|(Programmer) |
Left |
| 13 | and && ∧ |
Left |
| 14 | xor ⊕ ⊻ |
Left |
| 15 | or \|\| ∨ |
Left |
| 16 (lowest) | ? : (ternary) |
Right |
Assignment operators (:=, +=, -=, *=, /=, &=, \|=, ^=) have the lowest binding of all and are not part of the table above because they are statements, not sub-expressions.
Factorial (postfix !)
Applied after an integer value or parenthesized expression. Multiple ! characters give multifactorials.
5! // 120 (standard factorial)
10!! // double factorial
(3+2)! // 120
Percent (postfix %)
Marks a value as a percentage. Arithmetic between a number and a percent uses the percent contextually:
100 + 10% // 110 (add 10% of 100)
200 - 5% // 190 (subtract 5% of 200)
100 * 5% // 5 (5% of 100)
50 / 5% // 1000 (50 divided by 5%)
5% + 2% // 7% (percent + percent = percent)
Exponentiation
2 ** 10 // 1024
2 ** 0.5 // √2 ≈ 1.4142…
In Programmer's layout, ^ is exponentiation. In standard layout, ^ is bitwise XOR — use ** to be unambiguous.
Unary Operators
| Operator | Meaning |
|---|---|
- |
Arithmetic negation |
+ |
Unary positive (no-op) |
not, ¬ |
Logical NOT |
bit_not |
Bitwise NOT (ones complement) |
√ (U+221A) |
Square root |
∛ (U+221B) |
Cube root |
∜ (U+221C) |
Fourth root |
! (Programmer) |
Logical NOT |
~ (Programmer) |
Bitwise NOT |
-5
not true // false
bit_not 0b1010 // ones complement
√16 // 4
∛27 // 3
Multiplicative Operators
| Operator | Meaning |
|---|---|
*, ×, ∙ |
Multiplication |
/, ∶, ÷ |
Division (always floating-point) |
\\, div |
Integer division (truncates both operands first) |
mod |
Remainder (modulus) |
% (Programmer) |
Remainder |
10 / 4 // 2.5
10 \\ 3 // 3 (integer division)
10 div 3 // 3 (same as \\)
10 mod 3 // 1
7.9 \\ 2.1 // 3 (truncates to 7 and 2, then divides)
//is not integer division — it starts a line comment. Use\\ordiv.
Time periods can be multiplied or divided by a number:
#00:01:00# * 5 // 5-minute TimeSpan
#01:00:00# / 4 // 15-minute TimeSpan
Additive Operators
| Operator | Meaning |
|---|---|
+ |
Addition; string concatenation |
- |
Subtraction |
Date arithmetic:
#2024-01-01# + #00:02:00# // 2024-01-01 00:02:00
#2024-03-01# - #2024-01-01# // TimeSpan of 60 days
#2024-01-01# - #00:01:00# // 2023-12-31 23:59:00
Relational Operators
Equality / Inequality
| Operator | Meaning |
|---|---|
==, = |
Equal (use == when = might be assignment) |
!=, <>, ≠ |
Not equal |
Values of incompatible types are considered not equal. Characters compare equal to their Unicode code points.
Comparison
< <= ≤ > >= ≥
IN / NOT IN
'cat' IN ('dog'; 'cat'; 'fish') // true
42 NOT IN (1; 2; 3) // true
'hi' IN 'this is a hint' // true (substring check)
LIKE / NOT LIKE
Pattern matching for strings. % matches any sequence; _ matches any single character.
'HelloWorld' LIKE 'Hello%' // true
'2024-08-28' LIKE '2024-08-__' // true
'Test123' NOT LIKE 'Test__' // false
Bitwise Operators
Operate on the integer part of values.
| Operator | Meaning |
|---|---|
<< |
Left shift |
>> |
Right shift |
BIT_AND |
Bitwise AND |
BIT_OR |
Bitwise OR |
BIT_XOR |
Bitwise XOR |
Programmer-layout symbols: & (AND), \| (OR), ^ (XOR).
1 << 4 // 16
0b1100 BIT_AND 0b1010 // 8 (0b1000)
0b1100 BIT_OR 0b1010 // 14 (0b1110)
0b1100 BIT_XOR 0b1010 // 6 (0b0110)
Logical Operators
| Operator | Meaning |
|---|---|
and, &&, ∧ |
Logical AND (short-circuit) |
or, ||, ∨ |
Logical OR (short-circuit) |
xor, ⊕, ⊻ |
Logical XOR |
not, ¬ |
Logical NOT |
Precedence: not > and > xor > or.
Short-circuit: the right-hand side of and is not evaluated when the left is false; the right-hand side of or is not evaluated when the left is true.
true or false and true // true (and binds tighter)
(1 == 1) || false // true
true xor true // false
By default, null or a non-boolean operand in a logical expression causes an error. Enable Ternary Logic in settings to use Kleene's three-valued logic, where null is treated as Unknown.
Ternary Operator
condition ? value_if_true : value_if_false
Only one branch is evaluated.
x > 0 ? x : -x // absolute value of x
a != null ? a * 2 : 0 // null-safe multiplication
Assignment Operators
:= is the primary assignment operator (always available). = may also assign if the setting is enabled.
x := 10
x += 1 // x := x + 1
x -= 1
x *= 2
x /= 4
x &= 0xFF
x |= 0x01
x ^= 0b1010
Null Propagation
null propagates through arithmetic: any operation with a null operand yields null.
null + 1 // null
5 * null // null
null / 2 + 10 // null
Guard with ternary or iff():
x != null ? x * 2 : 0
iff(x != null; x * 2; 0)
4. Indexing and Slicing
Applies to strings, lists, and variables holding strings or lists. Indices are zero-based.
Single Element
"abcdef"[2] // 'c'
(10; 20; 30)[1] // 20
Range / Slice
expr[lower..upper] // from lower (inclusive) to upper (exclusive)
expr[lower..] // from lower to the end
expr[..upper] // from the start to upper (exclusive)
Caret (^) — Count from End
^1 = last element, ^2 = second-to-last, etc.
"abcde"[^1] // 'e'
"abcde"[^2] // 'd'
"abcde"[..^1] // "abcd"
"abcde"[1..^1] // "bcd"
(1;2;3;4)[^2..] // (3;4)
5. Expressions and Statements
Statement Groups
Multiple expressions separated by ; (semicolon) form a sequence. The value of a sequence is the value of its last expression.
a := 1; b := 2; a + b // yields 3
Wrap a sequence in {…} to treat it as a single expression (required when passing a group as a function argument or in if/while bodies):
{ a := 1; a * 2 } // yields 2
Conditionals
if / else statement:
if (condition) { consequent }
if (condition) { consequent } else { alternative }
if (a < 5) { a += 1; }
if (x > 0) x else -x // braces optional for single expressions
No semicolon is needed after an if/else block.
iff() function (expression form):
iff(condition; value_if_true; value_if_false)
iff(x < 0; -x; x) // same as: x < 0 ? -x : x
ifs() function (multi-branch):
ifs(cond1; val1; cond2; val2; ...; default)
ifs(score >= 90; 'A'; score >= 80; 'B'; score >= 70; 'C'; 'F')
Loops
while (condition) { body }
Maximum 65535 iterations (safety limit).
i := 1; total := 0;
while (i <= 10) { total += i; i += 1; }
total // 55
Flow control inside loops:
| Keyword | Effect |
|---|---|
break |
Exit the loop |
continue |
Skip to the next iteration |
return expr |
Exit the loop and the entire expression with a value |
list := (3; 7; 1; 9; 4);
i := 0;
while (i < Count(list)) {
if (list[i] > 5) { return list[i]; };
i += 1;
}
// returns 7 (first element greater than 5)
Return
return expr exits the current expression (or function body) immediately and produces expr as the result:
a := 1; if (a > 0) { return a * 10; }; a + 1
// returns 10, never reaches "a + 1"
Comments
// single-line comment (to end of line)
/* block comment
spanning multiple lines */
/// documentation comment (used to describe custom functions — see below)
//always starts a comment. It is not integer division. Use\\ordivfor integer division.
6. Function Calls
Function names are case-insensitive. Arguments are separated by ; (semicolons). Commas also work but may conflict with locale decimal/group separators.
Sqrt(16) // 4
Round(3.14159; 2) // 3.14
Max(1; 2; 3) // 3
Log(100; 10) // 2
Function calls are expressions and can be nested:
Max(Min(a; b); c)
Abs(Round(x; 2) - Round(y; 2))
7. Custom Function Definitions
Custom functions can be defined inline within an expression or stored in a library file (.abcf, ABCalc Plus).
Syntax Forms
Full body:
fn FunctionName(param1; param2)
{
// body: one or more statements
return result;
};
Arrow (short) form:
fn FunctionName(param1; param2) => expression;
Documentation Comments
A /// comment immediately before fn attaches a description. In library files, documented functions appear in the ABCalc function list alongside built-in functions.
/// Returns the hypotenuse of a right triangle.
fn Hypotenuse(a; b) => Sqrt(a*a + b*b);
Optional Parameters
Optional parameters follow mandatory ones and declare a default value with =:
fn MyRound(value; up = false) => up ? Ceil(value) : Trunc(value);
fn Power(base; exp = 2) => base ** exp;
Power(3) // 9 (uses default exp=2)
Power(3; 3) // 27
Return Value
A function returns the value of its last expression, or an explicit return:
fn Clamp(x; lo; hi) {
if (x < lo) { return lo; };
if (x > hi) { return hi; };
x // implicit return
};
Memory Scope
Inline functions share memory with the outer expression. Library functions have isolated memory. In both cases, parameter names shadow same-named memory cells.
Complete Example
/// Computes the sum of integers from 1 to n.
fn GaussSum(n) => n * (n + 1) / 2;
/// Finds the first list element satisfying a threshold.
fn FirstAbove(lst; threshold)
{
i := 0;
while (i < Count(lst)) {
if (lst[i] > threshold) { return lst[i]; };
i += 1;
};
return null;
};
GaussSum(10) // 55
FirstAbove((3; 7; 1; 9; 4); 5) // 7
8. Type System
Value Types
| Type | Examples | Notes |
|---|---|---|
int / long |
42, 0xFF |
Auto-promotes to BigInteger on overflow |
BigInteger |
Very large integers | Arbitrary precision |
double |
3.14, 1E-5 |
Default floating-point type |
BigDecimal |
Arbitrary-precision decimal | Used when precision matters |
bool |
true, false |
Also accepts integer 0/non-zero |
string |
'text' |
Immutable sequence of chars |
char |
'A' |
Single Unicode character |
DateTime |
#2024-01-01# |
Date and/or time |
TimeSpan |
#1:30:00# |
Duration |
ComplexNumber |
3+2i |
Real + imaginary |
Vector |
[1;2;3] |
Multi-component numeric |
Percent |
5% |
Contextual percentage |
null |
(absent value) | Propagates through arithmetic |
Automatic Type Promotion
- Integer arithmetic:
int→long→BigInteger(on overflow) - Mixed integer+float: integer is widened to
doubleorBigDecimal - Division (
/) returns an integer (when possible) or a floating-point result
Character ↔ Integer
A char value equals its Unicode code point in comparisons and arithmetic:
'A' == 65 // true
'A' + 1 // 66 (becomes integer)
Null
null propagates through arithmetic. Use iff() or the ternary operator to guard.