Statements¶
Statement lookup for assignments, calls, selection/iteration statements,
RETURN, EXIT, CONTINUE, labels, JMP, label resolution, and the current
reachability-analysis boundary.
Related: Expressions, Runtime
IEC 61131-3 Edition 3.0 (2013) - Section 7.3.3
This specification defines statement syntax for trust-syntax parser.
1. Statement Overview (Table 72)¶
The ST language statements are:
| Category | Statements |
|---|---|
| Assignment | :=, ?= |
| Call | Function, Function Block, Method |
| Control | RETURN |
| Selection | IF...THEN...END_IF, CASE...OF...END_CASE |
| Iteration | FOR...END_FOR, WHILE...END_WHILE, REPEAT...END_REPEAT |
| Jump | EXIT, CONTINUE, JMP |
| Empty | ; |
Implementation note:
- JMP statements are parsed and labels are resolved/validated. The current
diagnostics pass already reports unreachable statements after terminators and
constant-IF branches, but truST does not yet build a full control-flow
graph for whole-body reachability analysis. IEC 61131-3 Ed.3 Table 72 defines
the statement forms but does not require a particular unreachable-code
diagnostic algorithm.
Statement Termination¶
All statements are terminated with a semicolon ;.
Maximum Length¶
The maximum allowed length of statements is Implementer specific.
Multiple statements form a sequence and execute in the order written:
A := 1;
B := A + 2;
C := B * 3;
2. Assignment Statement (Section 7.3.3.2)¶
Syntax¶
variable := expression;
Examples (Table 72)¶
| No. | Description | Example |
|---|---|---|
| 1a | Elementary type | A := B; |
| 1b | With implicit conversion | A_Real := B_Int; |
| 1c | User-defined type | A_Struct1 := B_Struct1; |
| 1d | Array assignment | C_Array1 := D_Array1; |
| 1e | FB instance | A_Instance1 := B_Instance1; |
| 1f | With expression | CV := CV + 1;, C := SIN(X); |
Rules¶
- Left side must be a modifiable variable (not CONSTANT, not VAR_INPUT)
- Right side expression type must be compatible with left side type
- For structured types, both sides must be the same type
- Implicit type conversion follows defined rules (e.g., INT to REAL)
Assignment Attempt (Section 6.6.6.7)¶
interface1 ?= interface2;
Checks if assignment is valid (for interface references). If the instance implements the interface, assigns; otherwise, assigns NULL.
Rules:
- For IEC object-oriented references, ?= accepts a writable REF_TO class or
interface target and a class/interface reference source (or NULL). Runtime
compatibility is checked against inheritance and implemented interfaces.
Success stores the compatible identity; failure stores NULL. (IEC
61131-3 Ed.3, 6.6.6.7.2, Table 52)
- For elementary, aggregate, and truST POINTER TO values, ?= is a
product-defined checked copy: the source must be NULL or have the exact
same reference family and referenced type. Cross-family REF_TO /
POINTER TO attempts are invalid.
- Assignment attempt may store NULL; callers must test before dereference.
A null dereference reports a runtime error before the target statement makes
any write.
3. Call Statements (Section 7.3.3.2.4)¶
Call statements share one syntax skeleton and then refine the callable target: function name, function-block instance, or method receiver.
3.1 Shared Call Syntax¶
callable(arguments);
| Call style | Syntax | Example |
|---|---|---|
| Formal | param := value, param => target |
ADD(IN1 := A, IN2 := B) |
| Non-formal | positional | ADD(A, B) |
Shared rules:
- Formal calls assign inputs/in-outs with
:=and outputs with=>; ordering is not significant for binding. (IEC 61131-3 Ed.3, 6.6.1.4.2) - Actuals are evaluated exactly once, left to right in source order. Formal names do not reorder evaluation. A writable actual is resolved once at its source position.
- Formal calls may omit inputs and outputs. A function or method input then uses its declaration initializer or type default for that call. An omitted function-block input uses the instance's stored input, initialized from its declaration initializer or type default when the instance is created. An omitted output has no caller copy target. (IEC 61131-3 Ed.3, 6.6.1.4.2)
VAR_IN_OUTis never optional. Its actual must be a writable, non-constant variable of the exact compatible type; no implicit conversion or temporary is permitted. (IEC 61131-3 Ed.3, 6.6.1.4.1 and 6.6.1.6)- A complete non-formal call supplies every declared input, output, and in-out
in declaration order, excluding execution-control parameters
ENandENO. Positional outputs and in-outs therefore require writable actuals. (IEC 61131-3 Ed.3, 6.6.1.4.2) - IEC calls do not mix formal and non-formal styles. truST intentionally also
accepts a positional prefix followed by formal assignments, but rejects any
positional argument after the first formal assignment. This non-portable
extension is recorded as
DEV-018. - In that extension, the positional prefix occupies consecutive eligible
parameters in declaration order, excluding
ENandENO. The formal suffix may be written in any order but cannot bind a parameter already occupied by the prefix. =>is only valid forVAR_OUTPUT/ENObindings and is invalid for non-output parameters.:=is valid only forVAR_INPUT,VAR_IN_OUT, andEN. (IEC 61131-3 Ed.3, 6.6.1.4.2)- A named parameter may appear at most once and must belong to the selected callable.
- The same caller storage, including overlapping aggregate projections, cannot
be connected to more than one writable
VAR_OUTPUT,VAR_IN_OUT, orENOparameter in one call. Such a call is ambiguous and rejected. Reading a storage location as an input while mapping it once as writable is valid. - On normal return, all output/in-out values are captured, all targets are validated, and the transfer commits as a unit. A target failure cannot leave an earlier target written.
ENis evaluated first even when written later.EN := FALSEskips every other actual, target resolution, and the body, preserves caller targets and function-block/receiver state, copies onlyFALSEto a connectedENO, and returns the declared type default for a value-producing callable. SeeIEC_DECISIONS.md.
At the parser boundary, an empty argument list and complete positional or
formal argument lists are accepted without claiming callable resolution or
type compatibility. A positional prefix followed by a formal suffix is
retained for the documented truST extension. A positional actual after the
first formal actual is also retained as a complete call node so semantic
analysis can issue the binding diagnostic; parser acceptance does not make
that ordering valid. Named := and => forms preserve their operator and
target syntax, including selected, indexed, and dereferenced output targets.
The parser rejects a leading comma, a doubled trailing comma, a missing comma between actuals, a named input without its expression, a named output without its target, an unclosed argument list, or a call statement without its required terminator. Recovery stops at the bounded call or statement boundary and must not reinterpret a malformed actual as a sibling statement.
3.2 Function Calls¶
Functions may appear as standalone call statements or inside expressions:
function_name(parameters);
variable := function_name(parameters);
Examples:
Y := SIN(X);
Z := MAX(A, B, C);
Distance := SQRT(X**2 + Y**2);
LogMessage('System started');
Result := SafeDivide(EN := Enabled, A := Num, B := Den, ENO => Success);
3.3 Function Block Calls¶
Function-block calls execute an instance and may update or read retained state:
fb_instance(parameters);
Examples:
MyTimer(IN := Start, PT := T#5s);
Elapsed := MyTimer.ET;
TimerDone := MyTimer.Q;
MyFB(EN := Condition, Input := X, ENO => WasExecuted);
Input assignment may happen inline or through the instance fields before the call:
MyTimer.IN := Start;
MyTimer.PT := T#5s;
MyTimer();
MyTimer(PT := T#10s);
3.4 Method Calls¶
Methods are invoked on an instance receiver:
instance.method_name(parameters);
Examples:
Motor1.Start();
Motor1.SetSpeed(NewSpeed := 1000);
Status := Motor1.GetStatus();
4. RETURN Statement (Section 7.3.3.2.4; truST DEV-022)¶
Syntax¶
RETURN;
// or
RETURN expression; // truST extension for functions/value-returning methods
Examples¶
FUNCTION Max : INT
VAR_INPUT A, B: INT; END_VAR
IF A > B THEN
RETURN A;
END_IF;
RETURN B;
END_FUNCTION
// In FB/Program (early exit)
IF Error THEN
RETURN;
END_IF;
Rules¶
- IEC 61131-3 Ed.3 section 7.3.3.2.4 defines bare
RETURN. The value-bearingRETURN expression;form is the truST extension recorded asDEV-022. RETURN expression;is valid only for functions and methods with a return value, and the expression must be compatible with that declared type.RETURN;is also valid for functions and methods when the implicit return variable has already been assigned on that control-flow path.- In programs, function blocks, and procedures,
RETURN;performs an early exit. A value-bearing return is rejected there.
5. IF Statement (Section 7.3.3.3.2)¶
Syntax¶
IF condition THEN
statements
END_IF;
IF condition THEN
statements
ELSE
statements
END_IF;
IF condition1 THEN
statements
ELSIF condition2 THEN
statements
ELSIF condition3 THEN
statements
ELSE
statements
END_IF;
Examples¶
// Simple IF
IF Temperature > MaxTemp THEN
Alarm := TRUE;
END_IF;
// IF-ELSE
IF Sensor THEN
Output := TRUE;
ELSE
Output := FALSE;
END_IF;
// IF-ELSIF-ELSE (quadratic formula)
D := B*B - 4.0*A*C;
IF D < 0.0 THEN
NROOTS := 0;
ELSIF D = 0.0 THEN
NROOTS := 1;
X1 := -B / (2.0*A);
ELSE
NROOTS := 2;
X1 := (-B + SQRT(D)) / (2.0*A);
X2 := (-B - SQRT(D)) / (2.0*A);
END_IF;
Rules¶
- Conditions must evaluate to BOOL
- ELSIF can appear multiple times
- ELSE is optional
- First TRUE condition's block executes; rest skipped
6. CASE Statement (Section 7.3.3.3.3)¶
Syntax¶
CASE selector OF
value1: statements
value2, value3: statements
value4..value5: statements
ELSE statements
END_CASE;
Examples¶
// With integer selector
CASE Mode OF
0: Output := 'Idle';
1: Output := 'Running';
2: Output := 'Paused';
ELSE Output := 'Unknown';
END_CASE;
// Multiple values and ranges
TW := WORD_BCD_TO_INT(THUMBWHEEL);
TW_ERROR := 0;
CASE TW OF
1, 5: DISPLAY := OVEN_TEMP;
2: DISPLAY := MOTOR_SPEED;
3: DISPLAY := GROSS - TARE;
4, 6..10: DISPLAY := STATUS(TW - 4);
ELSE
DISPLAY := 0;
TW_ERROR := 1;
END_CASE;
// With enumeration
CASE TrafficLight OF
Green: AllowPass := TRUE;
Amber: AllowPass := FALSE; PrepareStop := TRUE;
Red: AllowPass := FALSE; PrepareStop := FALSE;
END_CASE;
Rules¶
- Selector must be an elementary data type. (IEC 61131-3 Ed.3, 7.3.3.3.3)
- Case labels are literals, enumerated values, or subranges; label types must
match the selector. Unqualified enum members, typed enum literals
(
Type#Value), and the reviewed truST qualified enum-member spelling (Type.Member) are accepted. Parsing the qualified spelling does not bypass semantic enum resolution or selector compatibility. (IEC 61131-3 Ed.3, 7.3.3.3.3; Table 72) - Ranges use
..syntax (e.g.,1..10); multiple values are comma-separated. Integer range bounds are inclusive constant expressions of the selector type. The lower bound must not exceed the upper bound. - ELSE executes when the selector matches no label; otherwise no statements execute (ELSE optional). (IEC 61131-3 Ed.3, 7.3.3.3.3)
- trust-hir warns when ELSE is omitted unless the selector is an enum and the labels cover all enum values.
- Duplicate scalar labels, scalar/range collisions, and overlapping ranges are
compile-time errors after constant evaluation. The closed truST policy is
recorded in
docs/IEC_DECISIONS.md. - The selector is evaluated exactly once. Accepted source has disjoint labels; the selected branch is the first matching branch in source order.
7. FOR Statement (Section 7.3.3.4.2)¶
Syntax¶
FOR control_var := initial TO final DO
statements
END_FOR;
FOR control_var := initial TO final BY increment DO
statements
END_FOR;
Examples¶
// Simple FOR loop
FOR I := 1 TO 10 DO
Sum := Sum + I;
END_FOR;
// With step value
FOR I := 0 TO 100 BY 10 DO
Values[I / 10] := GetSample(I);
END_FOR;
// Counting down
FOR I := 10 TO 1 BY -1 DO
Countdown[I] := I;
END_FOR;
// Search with EXIT
J := 101;
FOR I := 1 TO 100 BY 2 DO
IF WORDS[I] = 'KEY' THEN
J := I;
EXIT;
END_IF;
END_FOR;
Rules¶
- Control variable, initial, and final must be expressions of the same integer type (ANY_INT).
- Increment must be an expression of the same integer type.
- If BY is omitted, increment defaults to 1 in the control variable's type.
- Initial, final, and increment are evaluated exactly once, in that order, before the first termination test. Their values are captured for the loop.
- The control variable and variables used by the initial and final expressions must NOT be modified in the loop body. A variable used only by the captured increment may be modified.
- Test is performed at the start of each iteration.
- A zero increment is
RuntimeError::ForStepZerobefore any control-variable or body mutation. Increment overflow isRuntimeError::Overflowbefore a wrapped value can be stored. - On normal completion the control variable holds the first value beyond the
bound. A zero-iteration loop leaves it at the initial value.
EXITleaves the current iteration value, andCONTINUEperforms the normal increment. These closed implementer-specific choices are recorded indocs/IEC_DECISIONS.md.
Termination Test¶
- Positive increment: terminates when
control_var > final - Negative increment: terminates when
control_var < final - A positive increment with
initial > final, or a negative increment withinitial < final, executes zero iterations.
8. WHILE Statement (Section 7.3.3.4.3)¶
Syntax¶
WHILE condition DO
statements
END_WHILE;
Examples¶
// Basic WHILE
J := 1;
WHILE J <= 100 AND WORDS[J] <> 'KEY' DO
J := J + 2;
END_WHILE;
// Processing until complete
WHILE NOT ProcessComplete DO
ProcessNextItem();
END_WHILE;
Rules¶
- Condition must evaluate to BOOL
- Condition tested BEFORE each iteration
- If condition is initially FALSE, body never executes
- Error if termination cannot be guaranteed (infinite loop)
- Should NOT be used for inter-process synchronization
Implementation note (trust-hir): termination-guarantee analysis is not implemented; see docs/IEC_DEVIATIONS.md.
9. REPEAT Statement (Section 7.3.3.4.4)¶
Syntax¶
REPEAT
statements
UNTIL condition
END_REPEAT;
Examples¶
// Basic REPEAT
J := -1;
REPEAT
J := J + 2;
UNTIL J = 101 OR WORDS[J] = 'KEY'
END_REPEAT;
// Read until valid
REPEAT
Value := ReadInput();
UNTIL Value >= 0 AND Value <= 100
END_REPEAT;
Rules¶
- Condition must evaluate to BOOL
- Condition tested AFTER each iteration
- Body executes AT LEAST ONCE
- Loop terminates when condition becomes TRUE
- Error if termination cannot be guaranteed
Implementation note (trust-hir): termination-guarantee analysis is not implemented; see docs/IEC_DEVIATIONS.md.
10. EXIT Statement (Section 7.3.3.4.6)¶
Syntax¶
EXIT;
Behavior¶
Exits the innermost enclosing loop (FOR, WHILE, or REPEAT).
Example¶
SUM := 0;
FOR I := 1 TO 3 DO
FOR J := 1 TO 2 DO
SUM := SUM + 1;
IF FLAG THEN
EXIT; // Exits inner FOR loop only
END_IF;
SUM := SUM + 1;
END_FOR;
SUM := SUM + 1;
END_FOR;
// If FLAG=FALSE: SUM=15
// If FLAG=TRUE: SUM=6
Rules¶
- Must be inside a loop
- Only exits innermost loop
- If EXIT supported, it must work for all loop types (FOR, WHILE, REPEAT)
11. CONTINUE Statement (Section 7.3.3.4.5)¶
Syntax¶
CONTINUE;
Behavior¶
Jumps to the end of the current iteration, proceeding to the next iteration.
Example¶
SUM := 0;
FOR I := 1 TO 3 DO
FOR J := 1 TO 2 DO
SUM := SUM + 1;
IF FLAG THEN
CONTINUE; // Skip rest of inner loop body
END_IF;
SUM := SUM + 1;
END_FOR;
SUM := SUM + 1;
END_FOR;
// If FLAG=FALSE: SUM=15
// If FLAG=TRUE: SUM=9
Rules¶
- Must be inside a loop
- Affects only innermost loop
- If CONTINUE supported, it must work for all loop types
12. Label Statement (Section 7.3.3, Table 72)¶
Syntax¶
label: statement
Examples¶
Start: X := 1;
JMP Start;
Rules¶
- Labels are identifiers and are case-insensitive
- Labels are scoped to the enclosing POU or ACTION body; every ACTION has its own label scope
- Labels must be unique within the same label scope
- JMP targets must resolve to a label in the same scope (Table 72)
- A jump cannot enter or leave an
ACTION...END_ACTIONconstruct (IEC 61131-3 Ed.3 §8.1.6)
13. JMP Statement¶
Syntax¶
JMP label;
Rules¶
labelmust resolve to a declared label in the same POU or ACTION body- Forward and backward jumps are both allowed after label resolution
- A target in an enclosing POU is not visible from an ACTION, and a target in an ACTION is not visible from the enclosing POU or another ACTION
- Reachability diagnostics currently cover terminator-following statements and constant-branch dead code; full CFG-based jump analysis is still pending
Example¶
Start: X := 1;
JMP Start;
14. Empty Statement¶
Syntax¶
;
Use Case¶
Placeholder where statement is syntactically required but no action needed.
CASE Mode OF
0: ; // Do nothing for mode 0
1: ProcessMode1();
2: ProcessMode2();
END_CASE;
Implementation Notes for trust-syntax Parser¶
AST Node Types¶
Statement
├── Assignment (variable: LValue, expression: Expression)
├── AssignmentAttempt (variable: LValue, expression: Expression)
├── FunctionCall (name: String, arguments: [Argument])
├── FBCall (instance: String, arguments: [Argument])
├── MethodCall (object: Expression, method: String, arguments: [Argument])
├── Return (expression: Option<Expression>)
├── If (condition: Expression, then_branch: [Statement],
│ elsif_branches: [(Expression, [Statement])], else_branch: Option<[Statement]>)
├── Case (selector: Expression, cases: [(CaseLabel, [Statement])], else_branch: Option<[Statement]>)
├── For (control_var: String, initial: Expression, final: Expression,
│ step: Option<Expression>, body: [Statement])
├── While (condition: Expression, body: [Statement])
├── Repeat (body: [Statement], condition: Expression)
├── Exit
├── Continue
├── Label (name: String, statement: Statement)
└── Empty
Parsing Considerations¶
- Simple statements end with
;; block statements end with their matchingEND_xxx;terminator. - IF/CASE/FOR/WHILE/REPEAT are block statements
- Nested blocks must match correctly
- CASE labels can be values, ranges, or comma-separated lists
- FOR increment can be negative
Malformed-source recovery¶
IEC 61131-3 Ed.3 section 7.3.3.3, sections 7.3.3.4.2 through
7.3.3.4.4, and Table 72 define the required selection and iteration syntax;
section 6.3.2 and Table 5 define numeric and typed-literal syntax. The
documented #identifier extension is governed by
docs/specs/01-lexical-elements.md, and test-POU terminators are governed by
docs/specs/04-pou-declarations.md. truST applies the following fail-closed
product recovery contract whenever these accepted source forms are malformed:
- missing
THENafterELSIF,OFafter aCASEselector, or:after a CASE label produces a parse diagnostic; - a
FORstatement diagnoses a missing control variable,:=,TO, orDO; WHILEdiagnoses a missingDO, andREPEATdiagnoses a missingUNTIL;- missing block or POU terminators diagnose before an outer terminator or end-of-file is consumed;
- a missing statement semicolon diagnoses at the following statement boundary,
and a missing
THENafter anIFcondition diagnoses before its body; - a typed based numeric literal with a leading sign remains represented in the partial tree but produces the stable invalid-leading-sign diagnostic;
- the truST
#identifierextension diagnoses a number sign without its required following identifier; - unknown tokens are diagnosed and recovery advances or stops at a known statement/block synchronization boundary; and
- unclosed expression delimiters and excessive nesting terminate through bounded recovery with a diagnostic.
Within a bounded expression-recovery scan, a comma or closing delimiter is a top-level synchronization token only when no nested parenthesis or bracket is open. Balanced nested parentheses and brackets therefore leave the owning closing delimiter available to close the outer construct. If a nested bracket remains open, an encountered parenthesis cannot close the outer construct; the scan stops at the next configured statement boundary and leaves that boundary available to its owner.
Recovery may retain an incomplete syntax node so editor features can continue, but any such diagnostic makes the parse unsuccessful. The partial tree is not an accepted program and cannot be compiled as one.
Semantic Analysis¶
- Type check all expressions
- Verify control variable constraints in FOR
- Ensure EXIT/CONTINUE are inside loops
- Check CASE label uniqueness and type compatibility
- Verify assignment target is modifiable
Error Conditions¶
Compile-time Errors¶
- Type mismatch in assignment
- Assignment to CONSTANT or VAR_INPUT
- EXIT/CONTINUE outside loop
- Duplicate CASE labels
- CASE label type mismatch
- Non-boolean condition in IF/WHILE/REPEAT
- FOR control variable not integer type
- JMP target label not declared in scope
- Duplicate label declaration
Runtime Errors¶
- FOR increment evaluates to zero
- FOR increment overflows the control variable type
- Division by zero in expression
- Array index out of bounds
- Null reference dereference
An unmatched CASE without ELSE completes without executing a branch; it is
not a runtime error (IEC 61131-3 Ed.3 section 7.3.3.3.3).