Test Spec Grammar
Grammar reference for test specification files used by gdbtestengine
Grammar reference for .spec files used by gdbtestengine.
File Structure
A spec file is a sequence of blank lines, comments, directives, and procedure blocks.
spec-file = { blank | comment | directive | proc-block }
Comments
comment = '--' { any-char }
Comments run from -- to the end of the line. They may
appear on their own line or after a statement.
Hexadecimal Style
The engine auto-detects the hex notation from the first hex literal in the spec. All output (reports, live progress) mirrors the same style.
| Style | Syntax | Example |
|---|---|---|
| Oberon | digit { hex-digit } 'H' |
040013800H |
| C | '0x' hex-digit { hex-digit } |
0x40013800 |
Oberon hex literals that start with a letter require a leading zero
(eg. 0FFH, not FFH).
Lexical Elements
Tokens
| Token | Pattern / character(s) |
|---|---|
| IDENT | [a-zA-Z_][a-zA-Z0-9_]* |
| INTEGER | '-'? [0-9]+ |
| HEX | '0x' [0-9a-fA-F]+ or [0-9][0-9a-fA-F]* 'H' |
| DOT | . |
| PLUS | + |
| MINUS | - |
| EQUALS | = |
| HASH | # |
| LESS | < |
| GREATER | > |
| LESSEQUAL | <= |
| GREATEREQUAL | >= |
| LBRACKET | [ |
| RBRACKET | ] |
| COLON | : |
A bare - followed by a digit is a negative INTEGER when the context
is unary (start of token stream, or after =, #, <, >, <=,
>=, [). Otherwise it is a MINUS token.
Keywords
All keywords are case-sensitive IDENTs:
proc at entry exit line
check trace halt
start stop option when after all checks hits
arg local return mem
TRUE FALSE NIL
as signed unsigned
Values
value = integer | hex-literal | bool | nil | const-ref
integer = INTEGER
hex-literal = HEX
bool = 'TRUE' | 'FALSE'
nil = 'NIL'
const-ref = module-name '.' const-name
A const-ref references a named constant from an Oberon module. It
is resolved at engine start-up from the .alst files (CONST sections).
Cross-module constants (eg. CLK.PLL1 where PLL1 = MCU.X) are
resolved via import aliases.
Signedness
Comparison semantics depend on the value type:
| Value type | Default |
|---|---|
| integer | signed |
| hex | unsigned |
| const-ref | unsigned |
| NIL | unsigned |
| TRUE / FALSE | unsigned |
An explicit override may follow the expected value:
signedness = 'as' ( 'signed' | 'unsigned' )
Example: check arg x >= 0 as unsigned
Addresses
Static addresses are resolved at engine start-up.
address = hex-literal | const-ref
In location specifiers only, a plain integer is also accepted as a module-relative byte offset (see Locations below).
Variable References
Variable references support composable field and array access:
var-ref = IDENT { '.' IDENT | '[' INTEGER ']' }
Examples: x, cfg.src, arr[2], rec.data[3].field
Array indices use [N] (single integer in brackets). The parser
distinguishes array indices from bit-ranges by lookahead: [N] is an
index, [H:L] is a bit-range.
Qualified Names
Procedure references use a module-qualified form:
qual-name = module-name '.' proc-name
Example: CLK.ConfigPLL, UART.Configure
Indirect Memory
An indirect memory reference is evaluated at run-time. The base is read from a variable or static address, then an optional offset is applied.
indirect = '[' source { ('+' | '-') offset-value } ']'
source = 'arg' var-ref
| 'local' var-ref
| var-ref
| hex-literal
| const-ref
offset-value = integer | hex-literal | const-ref
The arg and local keywords are optional when the variable name
is unambiguous (see Optional Keywords below).
Examples:
[arg dev + 10H] -- arg dev, offset +10H
[local buf - 4] -- local buf, offset -4
[0x40000000 + 0x100] -- static address + offset
[arg dev + UARTdef.USART1] -- const-ref offset
Bit-Range
A bit-range extracts a contiguous field from a 32-bit word. It may appear after any target (mem, arg, local, or composed access).
bit-range = '[' high ':' low ']'
high = INTEGER
low = INTEGER
Example: check mem MCU.FLASH_ACR [3:0] = 0
Locations
Locations specify where in a procedure to set a breakpoint.
location = 'at' location-spec
location-spec = 'entry'
| 'exit'
| 'line' INTEGER
| HEX
| INTEGER
| Form | Meaning |
|---|---|
at entry |
Post-prologue address (after push + sub sp) |
at exit |
Pre-epilogue address (before add sp + pop) |
at line N |
Source line number (from .alst files) |
at 0xADDR |
Absolute hex address |
at N |
Module-relative byte offset (decimal, from .alst files) |
The entry and exit addresses are computed from the prologue and
epilogue instructions in the .alst file. All four procedure kinds
are handled (normal, leaf, interrupt, leaf_interrupt).
The at N form (plain decimal integer) is the module-relative byte
offset – the number shown after the dot in .alst assembly lines.
Targets
A target identifies what to check or trace at a breakpoint hit.
target = explicit-target [ bit-range ]
| auto-target [ bit-range ]
explicit-target = 'arg' var-ref
| 'local' var-ref
| 'return'
| 'mem' address
| 'mem' indirect
auto-target = indirect -- auto-detected as mem
| hex-literal -- auto-detected as mem
| const-ref -- auto-detected as mem
| var-ref -- auto-detected as arg or local
Optional Keywords
The arg, local, and mem keywords may be omitted when the meaning
is unambiguous:
- A bare
[...]expression is treated asmem [...]. - A bare hex literal is treated as
mem address. - A bare
Module.Constis treated asmem Module.Const. - A bare identifier (or dotted name) is matched against the procedure's parameters and locals by the resolver.
Comparison Operators
Six Oberon-style operators are available for check actions:
| Operator | Token | Meaning |
|---|---|---|
= |
EQUALS | equal |
# |
HASH | not equal |
> |
GREATER | greater than |
< |
LESS | less than |
>= |
GREATEREQUAL | greater or equal |
<= |
LESSEQUAL | less or equal |
Comparisons are performed as 32-bit values. The signedness rule determines whether the comparison is signed or unsigned (see Values / Signedness above).
Boolean guard: ordered operators (>, <, >=, <=) are not
permitted with TRUE or FALSE expected values. Use = or #
only.
Actions
Actions appear under a location block, indented by four spaces.
action = check-action | trace-action
check-action = 'check' target op value [ signedness ] [ 'halt' ]
trace-action = 'trace' target
op = '=' | '#' | '>' | '<' | '>=' | '<='
check
Evaluates the target and compares the result to the expected value using the given operator. Records PASS or FAIL.
The optional halt keyword causes the engine to stop execution if the
check fails. On halt, breakpoints are cleared and the target MCU
remains halted at the failure point.
trace
Evaluates the target and records its value. No comparison is performed; the result is always TRACE (neither PASS nor FAIL).
Directives
Directives appear at the top level (no indentation).
option
option-dir = 'option' option-name '=' option-value
| Option | Default | Description |
|---|---|---|
timeout |
30 | Wall-clock timeout in seconds |
max-hits |
500 | Maximum total breakpoint hits |
halt-enabled |
true | Honour per-action halt keywords |
halt-on-fail |
false | Halt on any check failure |
Setting halt-enabled = false disables all per-action halt keywords
without editing the spec. Setting halt-on-fail = true halts on
every check failure regardless of per-action annotations.
start
start-dir = 'start' 'proc' qual-name location [ 'when' condition ]
Defines a sentinel breakpoint that opens the execution window. Before the start sentinel fires, all check and trace actions are suppressed.
If no start directive is present, the window is open from the
beginning.
stop
stop-dir = 'stop' 'proc' qual-name location [ 'when' condition ]
| 'stop' 'after' 'all' 'checks'
| 'stop' 'after' INTEGER 'hits'
| 'stop' 'after' INTEGER 's'
| Form | Meaning |
|---|---|
stop proc M.P at loc |
Stop when procedure location is hit |
stop after all checks |
Stop once every check action has been evaluated |
stop after N hits |
Stop after N total breakpoint hits |
stop after N s |
Wall-clock timeout (same as option timeout) |
Conditions
condition = 'when' 'hits' '=' INTEGER
| 'when' var-ref '=' value
Each sentinel maintains its own hit counter. when hits = N fires
on the Nth hit at that sentinel's breakpoint.
Procedure Blocks
proc-block = 'proc' qual-name NEWLINE
{ location-block }
location-block = INDENT location NEWLINE
{ action-line }
action-line = INDENT INDENT action NEWLINE
Indentation is two spaces per level: locations at column 2, actions at column 4.
Complete Example
-- SignalSync init-phase test
option timeout = 15
option max-hits = 50
start proc CLK.ConfigPLL at entry
stop after 6 hits
proc CLK.ConfigPLL
at entry
check arg pllId = CLK.PLL1
check arg cfg.src = CLK.PLLsrc_HSI16
check mem MCU.FLASH_ACR [3:0] = 0H
trace mem MCU.RCC_CFGR1
proc UART.Configure
at entry
check arg dev # NIL
check arg dev.uartId = UARTdef.USART1
check arg dev.CR1 = 050013800H
trace arg dev.BRR
proc SignalSync.run
at entry
trace arg tid
at exit
check local count > 0
Environment Variables
When running inside GDB, the engine reads its configuration from
environment variables (set via (gdb) set environment or the shell).
| Variable | Default | Description |
|---|---|---|
SPEC |
– | Spec filename or relative path (required) |
RTK_PROJECT_DIR |
cwd | Project root (must contain rdb/, *.elf, *.map) |
RTK_GDB_TESTS_SPECS_DIR |
rdb-gdb-tests/specs |
Spec subdirectory within project |
RTK_REPORTS_DIR |
– | Report output directory |
Spec path resolution:
- With
RTK_GDB_TESTS_SPECS_DIR:project_dir / RTK_GDB_TESTS_SPECS_DIR / SPEC - Without:
project_dir / SPEC
Report directory defaults to reports/ alongside the spec file.
Last updated: 28 March 2026