# Expression reference

The AX expression language is used in mapping and condition nodes, and allows arbitrary data transformations in script form. It is the most powerful way we provide to manipulate data.

This reference will give an overview over the available syntax elements, then follow with detailed descriptions and examples for all operators and library functions.

## Syntax

### Formal grammar

```
statement ::= bool_op
bool_op ::= [bool_op BOOL_OP] not_op
not_op ::= ["not"] in_op
in_op ::= [in_op "in"] comp_op
comp_op ::= [comp_op COMPARISON_OP] add_op
add_op ::= [add_op ADD_OP] mult_op
mult_op ::= [mult_op MUL_OP] parens
parens ::= "(" statement ")" | atom
atom ::= literal | parameter | function_call | if_expr
literal ::= bool | number | string | list | object | lambda
bool ::= "true" | "false" | "TRUE" | "FALSE" | "True" | "False"
number ::= ["+"|"-"] "0".."9"+ ["." "0".."9"+] [["+"|"-"] ("e"|"E") "0".."9"+]
string ::= ESCAPED_STRING
list ::= "[" (statement ("," statement)*)? "]"
object ::= "{" (statement ":" statement ("," statement ":" statement)* )? "}"
lambda ::= "[" NAME ("," NAME)* "->" statement "]"
parameter ::= "#" dotted_access
dotted_access ::= indexed_access ("." ["#"] indexed_access)*
indexed_access ::= NAME ("[" ( ("#" NAME) | "0".."9"+ | ESCAPED_STRING ) "]")*
function_call ::= NAME "(" (statement ("," statement)*)? ")"
if_expr ::= "if" statement "then" statement "else" statement
COMPARISON_OP ::= "==" | "!=" | "<=" | "<" | ">=" | ">"
ADD_OP ::= "+" | "-"
MUL_OP ::= "//" | "*" | "/" | "%"
BOOL_OP ::= "and" | "or" | "xor"
```

`ESCAPED_STRING`

are characters quoted in `"`

, i.e. `"foo"`

. If the string contains `"`

, it is masked as the sequence `\"`

.

`NAME`

is a sequence of letters, digits or underscores, i.e. `foo_123`

. The first character must not be a digit.

### Operator precedence

The following table lists all operators, ordered by their precedence, from least binding (evaluated last) to most binding (evaluated first). Operators in the same group are evaluated from left to right.

Operator(s) | Description |
---|---|

`or` , `and` , `xor` | Boolean operations |

`not x` | Boolean negation |

`in` | Membership/substring test |

`==` , `!=` , `<=` , `<` , `>=` , `>` | Equality and comparisons |

`+` , `-` | Addition and subtraction |

`*` , `/` , `//` , `%` | Multiplication, division, floor division, modulo |

`(x)` , `#x.y[z]` , `f(x)` , `if ... then ... else` | Grouping, variable access, function calls, conditionals |

This means that `2 + 3 * 4`

is equivalent to `2 + (3 * 4)`

and yields 14, while both `(2 + 3) * 4`

or `int(2 + 3) * 4`

would yield 20.

### Literals

All of the types discussed in data types can be created using an expression. This is called a literal.

#### Booleans

Booleans can be spelled as `true`

/`True`

/`TRUE`

and `false`

/`False`

/`FALSE`

.

#### Numbers

Numbers can have an optional sign (`+`

/`-`

) and decimal part (`.123`

). Additionally, they support scientific E notation.

Examples:

```
1
-2
3.14159
+1234.56
1.23e5 (= 123 000)
```

#### Strings

String literals are any characters quoted in `"`

. If the character `"`

itself should appear in a string, it must be written ("masked") as `\"`

.

There are no other quotation rules, which makes the string equivalent to "raw strings" in other languages.

Examples:

```
"" (the empty string)
"foo"
"this is a string"
"\"Hello\", she said"
```

#### Lists

Lists are formed from comma-separated expressions within `[]`

:

```
[] (the empty list)
["foo"] (one-element list containing the string "foo")
[1, 2, 3]
["foo", 2, {}] (values of any type can appear in a list)
```

#### Objects

Similar to lists, objects are comma-separated key-value pairs enclosed by `{}`

. Key and value are separated by the character `:`

. While both key and value can be arbitrary expressions, the key must result in a string value when evaluated:

```
{} (the empty object)
{"a": "b", "b": "c"}
{upper(#x): lower(#x)} (Maps the upper-case spelling of #x to its lower-case spelling)
{"a": []} (all types are allowed as value)
{[]: "a"} (not allowed!)
```

This syntax is compatible with JSON objects, but note that strings must not contain any escape sequences (except `\"`

)

## Parameter access

Parameters can be accessed by using `#`

plus the parameter name:

```
param1 = "a"
```

```
#param1
```

```
"a"
```

List elements can be extracted by index (starting at 0):

```
param1 = ["a", "b", "c"]
```

```
#param1[1]
```

```
"b"
```

The index value can itself be a parameter:

```
param1 = ["a", "b", "c"]
param2 = 0
```

```
#param1[#param2]
```

```
"a"
```

Objects can be navigated similarly, by using their key strings:

```
param1 = {"a": 1, "b": 2, "c": 3}
```

```
#param1["c"]
```

```
3
```

However, if the key is constant and does not contain special characters, `.`

can be used as shorthand:

```
param1 = {"a": 1, "b": 2, "c": 3}
```

```
#param1.c
```

```
3
```

It's possible to combine these to access nested structures:

```
complex_object = {
"some_key": "a",
"letters": {
"a": [1], "b": [2, 3], "c": [4, 5, 6]
}
}
letter = "b"
```

```
#complex_object.letters[#letter][0]
```

```
2
```

## Operators

`+`

, `*`

, …)

Arithmetic (#### number + number

Adds two numbers.

```
param1 = 2
param2 = 3
```

```
#param1 + #param2
```

```
5
```

#### number - number

Subtracts two numbers.

```
param1 = 3
param2 = 2
```

```
#param1 - #param2
```

```
1
```

#### number * number

Multiplies two numbers.

```
param1 = 2
param2 = 3
```

```
#param1 * #param2
```

```
6
```

#### number / number

Divides two numbers.

```
param1 = 3
param2 = 2
```

```
#param1 / #param2
```

```
1.5
```

WARNING

Output is a **decimal**.

#### number // number

Divides two numbers, rounding towards zero to give a whole number.

```
param1 = 14
param2 = 5
```

```
#param1 // #param2
```

```
2
```

#### number % number

Returns the remainder of the division x // y, so that `x == (x // y) * y + (x % y)`

```
param1 = 14
param2 = 5
```

```
#param1 % #param2
```

```
4
```

`==`

, `>=`

, …)

Comparison (#### Equality (==)

Compares two values and checks if they are equal.

Lists are equal if they have the same length, and `a[i] == b[i]`

for every index i.

Objects are equal if they have the same set of keys, and `a[k] == b[k]`

for every key k.

```
param1 = "a"
param2 = "b"
```

```
#param1 == #param2
```

```
false
```

```
param1 = "a"
param2 = "a"
```

```
#param1 == #param2
```

```
true
```

```
param1 = 2
param2 = 3
```

```
#param1 == #param2
```

```
false
```

```
param1 = 2
param2 = 2
```

```
#param1 == #param2
```

```
true
```

```
param1 = [1, 2]
param2 = "c"
```

```
[1, 2, "c"] == #param1 + [#param2]
```

```
true
```

#### Inequality (!=)

The inverse of `==`

, i.e. the same as `not (a == b)`

.

```
param1 = "a"
param2 = "b"
```

```
#param1 != #param2
```

```
true
```

```
param1 = "a"
param2 = "a"
```

```
#param1 != #param2
```

```
false
```

```
param1 = 2
param2 = 3
```

```
#param1 != #param2
```

```
true
```

```
param1 = 3
param2 = 3
```

```
#param1 != #param2
```

```
false
```

#### Greater than/less than (<, <=, >, >=)

Only available if both operands are numbers. Apply `int()`

or `numeric()`

first if necessary.

`#a <= #b`

is equivalent to `#a < #b or #a == #b`

.

```
2 < 3 -> true
3 < 3 -> false
3 <= 3 -> true
3 > 2 -> true
3 > 3 -> false
3 >= 3 -> true
```

`and`

, `or`

, `not`

, `xor`

)

Logical (These operators are used to combine boolean (yes/no) statements, e.g. "the first condition, but not the second one".

#### Logical NOT

Negates the logical value of its following boolean value.

Truth Table:

parameter 1 | Output |
---|---|

true | false |

false | true |

```
param1 = true
```

```
not #param1
```

```
false
```

```
param1 = false
```

```
not #param1
```

```
true
```

#### Logical AND

Logical conjunction connects two boolean values by the operator *and*.

Truth Table:

parameter 1 | parameter 2 | Output |
---|---|---|

true | true | true |

true | false | false |

false | true | false |

false | false | false |

```
param1 = true
param2 = true
```

```
#param1 and #param2
```

```
true
```

```
param1 = true
param2 = false
```

```
#param1 and #param2
```

```
false
```

#### Logical OR

Logical disjunction connects two boolean values by the operator *or*.

Truth Table:

parameter 1 | parameter 2 | Output |
---|---|---|

true | true | true |

true | false | true |

false | true | true |

false | false | false |

```
param1 = true
param2 = false
```

```
#param1 or #param2
```

```
true
```

```
param1 = false
param2 = false
```

```
#param1 or #param2
```

```
false
```

#### Logical XOR

Exclusive or, i.e. "one but not both". Equivalent to `(a or b) and not (a and b)`

.

Truth Table:

parameter 1 | parameter 2 | Output |
---|---|---|

true | true | false |

true | false | true |

false | true | true |

false | false | false |

`+`

, `*`

)

Concatenation (#### string + string

Concatenates two or more strings.

```
param1 = "foo"
param2 = "bar"
```

```
#param1 + #param2
```

```
"foobar"
```

#### list + list

Returns a new list containing all elements of the first, followed by all elements of the second.

```
param1 = [1, 2]
param2 = [2, 3]
```

```
#param1 + #param2
```

```
[1, 2, 2, 3]
```

#### string * number

Repeats a string.

```
param1 = "ab"
```

```
#param1 * 3
```

```
"ababab"
```

`in`

#### string in string

Checks if first argument is a substring of the second (case sensitive). For convenience, numbers are also allowed and implicitly cast to string.

```
"oob" in "foobar" -> true
"FOO" in "foobar" -> false
2 in "123" -> true
```

#### value in list

Checks if value is an element of the list (exact match, i.e. `==`

).

```
3 in [1, 2, 3] -> true
"3" in [1, 2, 3] -> false
"foo" in ["foo", "bar"] -> true
"foo" in ["foobar"] -> false
```

`if ... then ... else`

This construct first evaluates the expression following **if**, and implicitly casts it to bool. If it is **true**, the expression following **then** will be evaluated and returned, otherwise the expression following **else**.

WARNING

All three sections must be present – the else part cannot be omitted.

```
input_boolean = true
input_string1 = "a"
input_string2 = "b"
```

```
if #input_boolean then #input_string1 else #input_string2
```

```
"a"
```

```
value = 3
divisor = 0
```

```
if #value > 0 and #divisor > 0 then
#value / #divisor
else
0
```

```
0
```

This construct can be nested to test more complicated conditions. However, each such nesting must have all three sections.

Here is an example of a nested if/then/else:

```
number = 7
```

```
if #number = 0 then
"exactly zero"
else
if #number > 10 then
"kinda big"
else
"could be just right or smaller than 0"
```

```
"could be just right or smaller than 0"
```

## User-defined functions/lambdas

Several functions allow customization by calling a user-defined function, also called a **lambda expression**. For example, in case of `filter`

, the function receives two arguments: the list itself, and a lambda expression. The expression is then evaluated for each item in the list; if it yields True, the element is kept, otherwise it is discarded.

A simple lambda expression which would multiply its two arguments:

```
[argument1, argument2 -> #argument1 * #argument2]
```

The most basic case, which just returns its argument:

```
[x -> #x]
```

Within the lambda expression, the arguments work just like parameters in a mapping node, but they are not available outside of it; we say they are *private* to the lambda expression. If an argument has the same name as a parameter, it takes priority and the parameter value cannot be used within the lambda.

The names of arguments do not matter for functionality. For example `filter()`

will always use the first argument for the item value and the second for the item index, no matter what they are named.