Expressions
Expression Language
The Cade Console includes an expression evaluator for testing scoring logic interactively. Expressions are used with the eval command and in watched expressions.
Variable References
Variables are referenced using ${variable.name} syntax. Nested paths and array access are supported.
cade:debug> eval ${score}
Result: 1000
cade:debug> eval ${player.bonus.multiplier}
Result: 3
cade:debug> eval ${targets[0].value}
Result: 500
Variables resolve against the current player’s game state. Use the player command to switch context between players before evaluating.
Operators
Arithmetic
| Operator | Description | Example |
|---|---|---|
+ | Addition | ${score} + 100 |
- | Subtraction | ${score} - 50 |
* | Multiplication | ${score} * 2 |
/ | Division | ${score} / 10 |
% | Modulus | ${score} % 1000 |
^ | Power | 2 ^ 10 |
Comparison
| Operator | Description | Example |
|---|---|---|
> | Greater than | ${score} > 1000 |
< | Less than | ${score} < 500 |
>= | Greater than or equal | ${combo} >= 3 |
<= | Less than or equal | ${balls.locked} <= 2 |
== | Equal | ${multiball.active} == true |
!= | Not equal | ${bonus.level} != 0 |
Logical
| Operator | Description | Example |
|---|---|---|
&& | Logical AND | ${multiball.active} && ${jackpot.lit} |
|| | Logical OR | ${bonus.active} || ${combo} > 5 |
! | Logical NOT | !${game.over} |
Ternary
The ternary operator selects between two values based on a condition:
cade:debug> eval ${combo} >= 3 ? ${score} * 2 : ${score}
Result: 2000
Built-in Functions
Math
| Function | Description | Example |
|---|---|---|
min(a, b) | Returns the smaller value | min(${score}, 10000) |
max(a, b) | Returns the larger value | max(${bonus}, 0) |
abs(x) | Returns the absolute value | abs(${delta}) |
round(x) | Rounds to the nearest integer | round(${score} / 3) |
floor(x) | Rounds down to the nearest integer | floor(${multiplier} * 1.5) |
ceil(x) | Rounds up to the nearest integer | ceil(${score} / 7) |
String
String functions operate on string values and are accessed via the string.* namespace.
| Function | Description | Example |
|---|---|---|
string.length(s) | Returns the number of characters | string.length(${player.name}) |
string.concat(s1, s2, ...) | Concatenates two or more strings | string.concat(${first}, " ", ${last}) |
string.contains(s, sub) | Returns true if s contains sub | string.contains(${mode.name}, "ball") |
string.substring(s, start, len) | Extracts len characters starting at start (0-based) | string.substring(${code}, 0, 3) |
string.to_upper(s) | Converts all characters to uppercase | string.to_upper(${label}) |
string.to_lower(s) | Converts all characters to lowercase | string.to_lower(${label}) |
string.trim(s) | Removes leading and trailing whitespace | string.trim(${input}) |
string.replace(s, old, new) | Replaces all occurrences of old with new | string.replace(${path}, "/", ".") |
string.matches(s, pattern) | Returns true if s matches the regex pattern | string.matches(${device}, "^left_") |
string.split(s, delim) | Splits s on delim and returns a list of strings | string.split(${tags}, ",") |
string.join(list, delim) | Joins a list of strings with delim and returns a string | string.join(${parts}, "-") |
string.to_int(s) | Parses s as an integer | string.to_int(${count_str}) |
string.to_bool(s) | Parses s as a boolean ("true"/"false") | string.to_bool(${flag_str}) |
string.from_int(i) | Converts an integer to its string representation | string.from_int(${score}) |
string.from_bool(b) | Converts a boolean to "true" or "false" | string.from_bool(${active}) |
cade:debug> eval string.length("hello")
Result: 5
cade:debug> eval string.contains(${player.name}, "Guest")
Result: false
cade:debug> eval string.to_upper("multiball")
Result: "MULTIBALL"
cade:debug> eval string.replace("left_inlane", "_", "-")
Result: "left-inlane"
cade:debug> eval string.split("a,b,c", ",")
Result: ["a", "b", "c"]
cade:debug> eval string.join(${tag_list}, " | ")
Result: "jackpot | multiball | bonus"
List
List functions operate on list values and are accessed via the list.* namespace. Lists are ordered, zero-indexed collections. Functions that return a modified list always return a new list — existing lists are never mutated.
Inspection
| Function | Description | Example |
|---|---|---|
list.length(list) | Returns the number of elements | list.length(${targets}) |
list.empty(list) | Returns true if the list has no elements | list.empty(${queue}) |
list.contains(list, elem) | Returns true if elem is in the list | list.contains(${active_modes}, "multiball") |
list.index_of(list, elem) | Returns the index of the first occurrence of elem, or -1 | list.index_of(${players}, ${current}) |
Access
| Function | Description | Example |
|---|---|---|
list.first(list) | Returns the first element | list.first(${scores}) |
list.last(list) | Returns the last element | list.last(${scores}) |
list.get(list, index) | Returns the element at index (0-based) | list.get(${targets}, 2) |
list.slice(list, start, end) | Returns a sublist from start to end (exclusive) | list.slice(${events}, 0, 5) |
list.random(list) | Returns a random element | list.random(${bonuses}) |
Modification
| Function | Description | Example |
|---|---|---|
list.append(list, elem) | Returns a new list with elem added at the end | list.append(${hits}, "target_a") |
list.prepend(list, elem) | Returns a new list with elem added at the beginning | list.prepend(${queue}, "jackpot") |
list.insert(list, index, elem) | Returns a new list with elem inserted at index | list.insert(${steps}, 1, "bonus") |
list.remove(list, elem) | Returns a new list with the first occurrence of elem removed | list.remove(${modes}, "attract") |
list.remove_at(list, index) | Returns a new list with the element at index removed | list.remove_at(${items}, 0) |
list.reverse(list) | Returns a new list in reverse order | list.reverse(${history}) |
list.sort(list) | Returns a new list with elements in ascending order | list.sort(${scores}) |
list.shuffle(list) | Returns a new list with elements in random order | list.shuffle(${deck}) |
list.unique(list) | Returns a new list with duplicate elements removed (first occurrence kept) | list.unique(${events}) |
Construction
| Function | Description | Example |
|---|---|---|
list.from_element(elem) | Creates a single-element list | list.from_element(${score}) |
list.concat(list1, list2) | Returns a new list combining both lists | list.concat(${left}, ${right}) |
list.join(list, delim) | Joins a list of strings with delim and returns a string | list.join(${tags}, ", ") |
Set Operations
| Function | Description | Example |
|---|---|---|
list.difference(list1, list2) | Returns elements in list1 not present in list2 | list.difference(${all}, ${seen}) |
list.intersection(list1, list2) | Returns elements present in both lists | list.intersection(${required}, ${collected}) |
cade:debug> eval list.length(${targets})
Result: 5
cade:debug> eval list.contains(${active_modes}, "multiball")
Result: true
cade:debug> eval list.first(${scores})
Result: 4200
cade:debug> eval list.append(${hits}, "spinner")
Result: ["ramp", "orbit", "spinner"]
cade:debug> eval list.sort(${scores})
Result: [500, 1000, 4200, 8750]
cade:debug> eval list.intersection(${required_targets}, ${hit_targets})
Result: ["left_ramp", "right_orbit"]
Module Namespace
The expression evaluator supports querying module state through the module namespace. This enables expressions that check whether a module is active, how many instances are running, or read module-scoped variables.
| Expression | Returns | Description |
|---|---|---|
module.<name>.active | Boolean | Whether the module’s mode is currently active |
module.<name>.instance_count | Integer | Number of active instances (for stackable modules) |
module.<name>.<variable> | Value | A module-scoped variable value |
Module variables are registered under both their qualified name (module.<moduleName>.<varName>) and a short name (<varName>). If two modules define a variable with the same short name, a collision warning is logged and only the qualified name is available.
cade:debug> eval module.multiball.active
Result: true
cade:debug> eval module.multiball.instance_count
Result: 2
cade:debug> eval module.multiball.active && module.bonus.active
Result: false
cade:debug> eval module.multiball.jackpot_value
Result: 50000
Tab Completion in Expressions
The expression completion provider offers context-aware suggestions while typing expressions. After the eval keyword, pressing Tab completes:
- Variable names inside
${...}references - Function names (e.g.,
mi<Tab>completes tomin) - Function arguments based on the function signature
The completion system parses partial expressions with error recovery, so completions remain available even when the expression is incomplete.
Examples
Scoring Calculations
cade:debug> eval (${baseScore} + ${bonus}) * ${multiplier}
Result: 7500
cade:debug> eval ${targets.completed} * 500 * ${bonus.multiplier}
Result: 5000
Conditional Logic
cade:debug> eval ${combo} >= 3 ? ${score} * 2 : ${score}
Result: 2000
cade:debug> eval ${balls.locked} >= 3 ? "start_multiball" : "lock_more"
Result: "lock_more"
Boolean Checks
cade:debug> eval ${multiball.active} && ${jackpot.lit}
Result: false
cade:debug> eval ${score} > 1000 || ${bonus.level} > 2
Result: true
Combining Functions
cade:debug> eval max(${score}, ${highScore})
Result: 15000
cade:debug> eval min(${bonus.multiplier}, 5) * ${basePoints}
Result: 2500
Expression Watching
Expressions can be watched to continuously evaluate and display updated results. The watch panel shows watched expression values alongside watched variables, updating in real time as game state changes.