Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Expression Evaluation

This page describes how WCL evaluates expressions: operator precedence, type rules, short-circuit behavior, and error conditions.

Operator Precedence

Operators are listed from lowest to highest precedence:

LevelOperatorsAssociativity
1?: (ternary)Right
2|| (logical or)Left
3&& (logical and)Left
4==, !=Left
5<, >, <=, >=, =~Left
6+, -Left
7*, /, %Left
8! (unary not), - (unary negation)Right (prefix)
9.field, [index], (call) (postfix)Left

Ternary Expression

condition ? then_value : else_value

The condition must evaluate to bool. Only the selected branch is evaluated.

Logical Operators

OperatorTypesResult
a || bbool, boolbool
a && bbool, boolbool
!aboolbool

Short-circuit evaluation: || does not evaluate the right operand if the left is true. && does not evaluate the right operand if the left is false.

Equality Operators

OperatorTypesResult
a == bany matching typesbool
a != bany matching typesbool

Equality is deep structural equality for lists and maps. Comparing values of different types always returns false for == and true for != (no implicit coercion).

Comparison Operators

OperatorTypesResult
a < bint, float, stringbool
a > bint, float, stringbool
a <= bint, float, stringbool
a >= bint, float, stringbool
a =~ bstring, stringbool

The =~ operator matches the left operand against the right operand as a regular expression (RE2 syntax). Returns true if there is any match.

Comparing across incompatible types (e.g., int with string) produces error E050.

Arithmetic Operators

OperatorTypesResultNotes
a + bint, intint
a + bfloat, floatfloat
a + bstring, stringstringConcatenation
a + blist, listlistConcatenation
a - bint, intint
a - bfloat, floatfloat
a * bint, intint
a * bfloat, floatfloat
a / bint, intintInteger division; error E051 if b == 0
a / bfloat, floatfloatIEEE 754; b == 0.0 produces infinity
a % bint, intintRemainder; error E051 if b == 0
-aintintUnary negation
-afloatfloatUnary negation

Field Access and Indexing

obj.field        // access named field of a map or block
list[0]          // index into a list (0-based)
map["key"]       // index into a map by string key

Out-of-bounds list indexing produces error E054. Accessing a missing map key returns null.

Function Calls

value.to_string()
list.map(x => x * 2)
string.split(",")

Postfix call syntax is used for built-in functions and lambda application. Calling an unknown function produces error E052.

String Interpolation

let msg = "Hello, ${name}! Port is ${port + 1}."

Interpolated expressions (${...}) are evaluated and converted to their string representation before concatenation. Any expression type is allowed inside ${}.

Query Expressions

let services = query(service | .port > 1024)

Query expressions run the pipeline query engine against the current document scope and return a list of matching resolved values. Queries are evaluated after scope construction is complete.

Ref Expressions

let api = ref(svc-api)

A ref expression resolves to the block or value with the given identifier. If no matching identifier is found, error E053 is produced.

Lambda Expressions

let double = x => x * 2
let add    = (a, b) => a + b
let clamp  = (v, lo, hi) => v < lo ? lo : (v > hi ? hi : v)

Lambdas capture their lexical scope. They are first-class values and can be passed to built-in higher-order functions.

Error Conditions

CodeCondition
E050Type mismatch in operator or function call
E051Division or modulo by zero
E052Call to unknown function
E053ref() target identifier not found
E054List index out of bounds
E040Reference to undefined name
E041Cyclic dependency between names