Skip to content

Language Reference

Complete reference for the Quale language. Every keyword, operator, expression form, statement type, built-in function, and comment syntax in one page.


48 reserved keywords. Keyword matching is case-sensitive (lowercase only). All other names (state names, entity types, field names) are contextual identifiers resolved by the parser.

KeywordCategoryDescription
bodyBlockAgent state layout and brain interface
worldBlockSimulation environment with entities
perceptionBlockWorld/agent state to brain sensor values
actionBlockBrain actuator outputs to world changes
dynamicsBlockPer-tick state cascade rules
fitnessBlockGates, metrics, and optimization objectives
evolveBlockEvolution experiment configuration
entityBlockEntity type definition inside a world
machineBlockState machine definition
sensorDeclarationBrain input node
actuatorDeclarationBrain output node
propertiesSub-blockEntity property declarations
stateDeclarationState variable or machine state
scopeMachineMachine scope (agent or world)
initialMachineInitial state of a machine
transitionMachineState transition rule
on_enterHandlerFires on entering a state or entity proximity
on_exitHandlerFires on leaving a state
on_crossHandlerFires when agent crosses an entity
on_passHandlerFires when agent passes an entity without entering
spawnEntityNumber of entity instances to place
queryWorldSpatial query declaration
importWorldExternal data import
letStatementLocal variable binding
whenStatementConditional guard
elseStatementAlternative branch of a conditional
matchExpressionPattern-matching or condition-based expression
forStatementLoop construct
inStatementIterable in a for-in expression
notOperatorLogical negation (keyword form)
andOperatorLogical conjunction (keyword form)
orOperatorLogical disjunction (keyword form)
recordStatementEmit a typed event record
consumeStatementRemove the current entity from the world
gateFitnessHard prerequisite for fitness scoring
metricFitnessPerformance measurement definition
maximizeFitnessHigher values produce higher fitness
rewardFitnessAccumulative positive contribution
penalizeFitnessValue subtracted from fitness
terminateFitnessEarly scenario termination condition
aggregateFitnessAggregation function for per-record/per-tick metrics
transformFitnessPost-processing step on aggregated metric
mutationEvolveMutation rate configuration sub-block
speciationEvolveSpeciation parameters sub-block
convergenceEvolveConvergence criteria sub-block
checkpointEvolveCheckpoint configuration sub-block
seed_fromEvolveSeed population from a previous experiment
topologyWorldSpatial structure declaration

Listed from lowest to highest precedence. Higher-precedence operators bind tighter.

LevelCategoryOperatorsAssociativity
1Ternary? :Right
2Logical ORorLeft
3Logical ANDandLeft
4Comparison> < >= <= == !=Non-associative
5Addition+ -Left
6Multiplication* /Left
7Unary-x !x not xRight (prefix)
8PrimaryLiterals, identifiers, ., (), [], matchLeft (postfix)
OperatorDescriptionExample
+Additionagent.speed + 1.0
-Subtraction1.0 - min(chk.distance / 2.0, 1.0)
*Multiplicationagent.speed * dt / 1000.0
/Division (division by zero returns 0)agent.speed / max_speed_ms
-xUnary negation-5.0
OperatorDescriptionExample
>Greater thanactuator.block > 0.5
<Less thanagent.speed < limit_ms
>=Greater than or equalagent.position >= world.length
<=Less than or equalactuator.block <= 0.5
==Equalchk.index == -1
!=Not equalchk.index != -1

Comparison operators are non-associative. Chaining like a < b < c is not supported; use a < b and b < c.

OperatorDescriptionExample
andLogical AND (both non-zero)actuator.block > 0.5 and elapsed_in_state > 1.0
orLogical OR (either non-zero)actuator.move_n > 0.5 or actuator.move_e > 0.5
notLogical NOT (keyword form)not agent.warning_acknowledged
!Logical NOT (symbol form)!agent.alive

Both not and ! perform the same operation: 0.0 becomes 1.0, any non-zero value becomes 0.0.

Used in statements only, not in expressions.

OperatorDescriptionExample
=Direct assignmentagent.accel = -5.0
+=Add and assignagent.warnings_acknowledged += 1
-=Subtract and assignagent.hunger -= 0.3
*=Multiply and assignagent.confidence *= 0.99
/=Divide and assignagent.score /= 2.0
OperatorDescriptionContext
..Range0..1 in type annotations
->Transition arrowtransition A -> B in machines, pattern -> value in match
.Dot accessagent.speed, world.tick, chk.distance

TypeExamplesStorage
Integer0, 42, -1, 300float64
Float0.5, 1.0, 3.6, -5.0float64
Booleantrue, falsefloat64 (1.0 / 0.0)
String"route-data.csv"Interned int index

All numeric values are stored as float64 on the VM stack. true and false are contextual identifiers (not keywords) that resolve to 1.0 and 0.0.

hunger -- bare identifier (resolved from scope)
dt -- let-bound local variable
waypoint -- entity type name
agent.speed -- agent state
world.max_speed -- world constant
actuator.brake -- brain output value
chk.distance -- query result field (built-in)
chk.position -- query result field (promoted from entity property)
sensor.food_nearby.directions -- sensor metadata
min(chk.distance / 2.0, 1.0)
max(0, agent.speed + agent.accel * dt)
abs(agent.accel)
clamp((value - 0.5) / 1.5, 0, 1)
sqrt(agent.distance)
nearest_ahead(waypoint, agent.position)
speed_zone_at(agent.position)
sensor food_nearby[direction] -- indexed sensor access in for loops
agent.warning_active ? 1.0 : 0.0
agent.ticks_alive > 0 ? agent.idle_ticks / agent.ticks_alive : 1.0

Ternary is right-associative. Nesting is supported: a ? b ? 1 : 2 : 3.

(agent.speed + agent.accel * dt)
(value - 0.5) / 1.5

Evaluates conditions top-to-bottom, returns the value of the first matching branch.

match {
when stn.distance > 0.16: zone_limit
when stn.distance > 0.08: 60.0
when stn.distance > 0.03: 45.0
else: 15.0
}

Compares a target expression against patterns. _ is the wildcard default.

match direction {
0 -> "north"
1 -> "east"
2 -> "south"
3 -> "west"
_ -> "unknown"
}

Creates a local variable binding. Evaluated once when the line executes. Scoped to the enclosing block.

let dt = world.tick
let max_speed_ms = world.max_speed / 3.6
let chk = nearest_ahead(waypoint, agent.position)
let moved = actuator.move_n > 0.5 or actuator.move_e > 0.5

Conditional guard. Multiple when blocks at the same level are evaluated independently (not mutually exclusive).

-- Block form
when actuator.emergency_stop > 0.8 {
agent.accel = -5.0
agent.stress += 0.05
}
-- Single-line form
when not moved: agent.idle_ticks += 1

Mutually exclusive branches. Evaluates top-to-bottom; only the first matching branch executes.

when actuator.emergency_stop > 0.8 {
agent.accel = -5.0
} else when actuator.brake > 0.3 {
agent.accel = -2.0
} else {
agent.accel = -0.1
}

Modifies agent state, world state (in world machines), or entity state.

agent.accel = -5.0 -- direct assignment
agent.hunger -= 0.3 -- subtract and assign
agent.warnings_acknowledged += 1 -- add and assign
agent.confidence *= 0.99 -- multiply and assign
agent.position += agent.speed * dt / 1000.0

Deep dot paths are supported for assignment targets:

world.preceding_train.position += 1.0

Writes a value to a brain input node. Only valid inside perception blocks.

sensor current_speed = agent.speed / max_speed_ms
sensor warning_level = agent.warning_active ? 1.0 : 0.0

Emits a typed event record to the scenario event log. Records are consumed by per-record fitness metrics. Fields can use shorthand (bare name) or explicit key-value pairs.

record waypoint_visit {
stopped: 1.0,
arrival_time: agent.elapsed
}
record decision {
blocked: 1.0,
was_threat: malicious,
correct: malicious > 0.5 ? 1.0 : 0.0
}

Removes the current entity from the world. Only valid inside entity interaction handlers (on_cross). For grid worlds, triggers the entity’s respawn timer.

consume()

Transitions to a named state inside a machine. Only valid inside machine state handlers.

-> monitoring

FunctionSignatureDescription
min(a, b)(float, float) -> floatReturns the smaller of two values
max(a, b)(float, float) -> floatReturns the larger of two values
abs(x)(float) -> floatReturns the absolute value
clamp(val, lo, hi)(float, float, float) -> floatClamps val to [lo, hi]
sqrt(x)(float) -> floatReturns the square root
-- From Human Factors
sensor waypoint_near = 1.0 - min(chk.distance / 2.0, 1.0)
-- From Human Factors
agent.speed = max(0, agent.speed + agent.accel * dt)

All values are stored as float64 at runtime. Type annotations on state declarations are documentation and optional constraint hints.

AnnotationStorageSemantics
floatfloat64General-purpose floating point
intfloat64Conceptually integer; no fractional enforcement
boolfloat64true = 1.0, false = 0.0
0..1float64Float in [0.0, 1.0]; enforced by dynamics clamp 0..1
secondsfloat64Unit annotation (documentation only)
m/sfloat64Unit annotation (documentation only)
m/s2float64Unit annotation (documentation only)
kmfloat64Unit annotation (documentation only)
km/hfloat64Unit annotation (documentation only)
stringint (enum)Interned at compile time; stored as an enum index

Unit annotations have no runtime effect. state speed: m/s = 0 and state speed: float = 0 compile to identical bytecode.

The 0..1 range type uses the .. operator to denote a bounded float. The dynamics block’s clamp 0..1 directive enforces this range at the end of each tick for all dynamics-managed states.


SyntaxTypeBehavior
-- textLine commentDiscarded by the parser
--! textNote commentPreserved in AST; shown by quale check
--!! textCritical commentPreserved in AST; fails quale check --strict
--[ text ]--Block commentMulti-line; discarded by the parser
-- Regular comment (ignored by the compiler)
--! This sensor range was chosen based on grid size
--!! Do not exceed 300 ticks without dynamics
--[ Block comment
can span multiple lines
useful for temporarily disabling code ]--

Every .quale file contains one or more of these seven top-level blocks:

ConstructSyntaxRequired
Bodybody <Name> { ... }Yes (one per evolve)
Worldworld <Name> { ... }Yes (one per evolve)
Perceptionperception <Name> { ... }Yes (one per evolve)
Actionaction <Name> { ... }Yes (one per evolve)
Dynamicsdynamics <Name> { ... }No
Fitnessfitness <Name> { ... }Yes (one per evolve)
Evolveevolve <Name> { ... }Yes (at least one)

Multiple evolve blocks are allowed in a project. Select between them with the --run CLI flag.


NamespaceAccessDescription
agent.*Read/WriteAgent state declared in the body block
world.*Read-only (write in world machines)World constants and state
actuator.*Read-onlyBrain output values
sensor.*Metadata onlySensor configuration (e.g., .directions)
engine.*Read-only (fitness only)Engine-provided metrics (engine.complexity, engine.nodes)
<query_result>.*Read-onlyFields from spatial query results (.distance, .index, plus promoted properties)

Used in per-record and per-tick fitness metrics.

FunctionDescription
avgMean of accumulated values
sumSum of accumulated values
minMinimum of accumulated values
maxMaximum of accumulated values
metric accuracy {
per record decision: correct
aggregate: avg
}

Declared in the world block, callable from perception, action, machines, and entity handlers.

QuerySignatureReturns
nearest_ahead(type, pos)-> distance, index, propertiesNext entity of type ahead of position
speed_zone_at(pos)-> limitSpeed limit at position (scalar)
QuerySignatureReturns
nearest(type, pos, direction)-> distance, propertiesNearest entity of type in a direction
at(type, pos)-> bool, propertiesEntity presence at a grid cell
FieldFallback value
.distancePositive infinity
.index-1
Any property0.0