Syntax Reference
This page gives a compact grammar-style view of the accepted source syntax. The Language Reference explains the same surface in prose.
The notation is informal:
- quoted text is literal syntax;
?means optional;*means zero or more;+means one or more;|means choice.
Source File
source_file =
module_decl top_level_decl*
module_decl =
"module" ident ";"
top_level_decl =
record_decl
| enum_decl
| function
| process_decl
Records
record_decl =
"record" ident ";"
| "record" ident "{" record_field ("," record_field)* ","? "}"
record_field =
ident ":" type_ref
Fieldless records use the semicolon form. Braced records must declare at least one field.
Enums
enum_decl =
"enum" ident "{" enum_variant_list? "}"
enum_variant_list =
enum_variant ("," enum_variant)* ","?
enum_variant =
ident
| ident "(" type_ref ")"
Enums used as process state or message types must have at least one variant. Payload variants are accepted for process state and message enums. State enum payload constructors create immutable whole-state values.
Processes
process_decl =
"proc" ident "mailbox" "bounded" "(" number ")" "{"
process_member*
"}"
process_member =
state_alias
| message_alias
| init_function
| step_function
| source_function
state_alias =
"type" "State" "=" type_ref ";"
message_alias =
"type" "Msg" "=" type_ref ";"
The aliases and functions may appear in any order. State, Msg, and init
must each appear exactly once. Non-init/step functions are process-local
source helpers. Each message variant must resolve to exactly one step clause,
either through an explicit constructor pattern, through one wildcard pattern,
through one match msg step body, or through a state-match step for a specific
message pattern. A process cannot mix parameter-pattern/state-match step forms
with a match msg step body in this slice. Other process members are rejected.
Functions
function =
"fn" ident "(" params? ")" "->" type_ref
"!" effect_list
"~" ident_list
determinism
function_body
params =
function_param ("," function_param)* ","?
function_param =
param_binding
| pattern
param_binding =
ident ":" type_ref
pattern =
ident
| ident "(" ident ":" type_ref ")"
| "_"
effect_list =
"[" (effect ("," effect)* ","?)? "]"
effect =
"emit" | "spawn" | "send"
ident_list =
"[" (ident ("," ident)* ","?)? "]"
determinism =
"@det" | "@nondet"
Buildable source accepts bodies for init, step, module helpers, and
process-local helpers. It requires deterministic functions and empty
may-behavior lists. Normal source helpers are pure: they use ! [], perform no
statements, and are expanded before lowering.
Function Bodies
function_body =
";"
| "{" block_body "}"
| "{" match_body "}"
block_body =
statement* return_statement
match_body =
"match" ident "{" match_arm+ "}"
match_arm =
pattern "=>" "{" block_body "}"
Patterns are source-level binding and decomposition syntax. This source slice
admits constructor patterns, constructor payload bindings, and _ wildcards.
Buildable semantic consumers are normal source function signatures and match
bodies, fieldless enum init matches, and actor step message dispatch.
Actor step bodies may also match the current process state parameter when
the process state type is an enum. Normal source helpers, actor step dispatch,
and current-state matches accept constructor payload bindings. Source helper
calls still expand before lowering; they require a concrete enum constructor
value for pattern selection.
Buildable source requires bodies. init uses no parameters. Each
parameter-pattern step uses state: StateType followed by one message
constructor or wildcard pattern:
parameter_pattern_step_function =
"fn" "step" "(" "state" ":" type_ref ","
(ident | ident "(" ident ":" type_ref ")" | "_") ")"
"->" "ProcResult" "<" type_ref ">"
"!" effect_list "~" "[]" "@det"
"{" block_body "}"
The first type_ref must name the process state type. An ident after the
comma is a message constructor accepted by the process message type. A payload
pattern such as Assign(job: Job) binds the received payload as an immutable
transition-local value. _ is a wildcard pattern that covers accepted variants
without explicit clauses.
A match step uses a typed message parameter and a whole-body
match over that parameter:
match_step_function =
"fn" "step" "(" "state" ":" type_ref "," ident ":" type_ref ")"
"->" "ProcResult" "<" type_ref ">"
"!" effect_list "~" "[]" "@det"
"{" match_body "}"
Each match arm uses the same pattern syntax as parameter-pattern dispatch. The match scrutinee must be the typed message parameter in the current buildable step subset. Match arms are block-delimited and do not use comma separators. The step effect list applies to every generated transition, so each arm must use exactly the declared effects.
A state-match step uses the normal state parameter plus a message constructor
or wildcard pattern, then uses a whole-body match state:
state_match_step_function =
"fn" "step" "(" "state" ":" type_ref ","
(ident | ident "(" ident ":" type_ref ")" | "_") ")"
"->" "ProcResult" "<" type_ref ">"
"!" effect_list "~" "[]" "@det"
"{" "match" "state" "{" match_arm+ "}" "}"
State-match arms resolve against the declared process state enum. Payload
variants must bind their payload with an explicit type, such as
Working(job: Job). Fieldless variants must not bind a payload. Bindings are
immutable and transition-local. Each generated transition is keyed by the
message ID and the admitted current state ID; state changes still occur only by
returning a whole state value through Continue(...), Stop(...), or
Panic(...).
A normal source helper is a module-level function or a process-local function
whose name is not init or step:
source_function =
"fn" ident "(" (param_binding | pattern) ")"
"->" type_ref
"!" "[]" "~" "[]" "@det"
("{" block_body "}" | "{" match_body "}")
Helper block bodies must not contain statements. Helper match bodies match the function’s typed binding parameter. Helper calls and payload-bearing enum values share the same surface syntax:
call_or_payload_constructor =
ident "(" value_expr ")"
The checker resolves that form against the expected type. A declared enum
constructor becomes an immutable enum value; a declared helper is expanded in
init, step result values, and send payload values. Recursive helper call
cycles are rejected.
Statements
statement =
emit_statement
| process_ref_statement
| send_statement
emit_statement =
"emit" string_literal ";"
process_ref_statement =
"let" ident ":" process_ref_type "=" "spawn" ident ";"
process_ref_type =
"ProcessRef" "<" ident ">"
send_statement =
"send" ident ident payload_arg? ";"
payload_arg =
"(" value_expr ")"
return_statement =
"return" return_expr ";"
The identifier after let names an immutable process reference value. The
identifier after spawn is the process definition name. The ProcessRef<T>
annotation must name the same process definition.
The first identifier in send is a local process reference or a received
payload binding whose type is ProcessRef<T>. The second identifier is the
message variant to send. Payload variants require one payload value. Unit
variants reject payload values.
Types
type_ref =
ident
| ident "<" type_ref ("," type_ref)* ","? ">"
The built-in generic types accepted by checking are
ProcResult<StateType> as a step return type and
ProcessRef<ProcessName> in spawn bindings, message payload declarations, and
payload-binding step patterns.
Values
return_expr =
value_expr
| ident "(" value_expr ")"
value_expr =
ident
| ident "(" value_expr ")"
| ident "{" record_value_field ("," record_value_field)* ","? "}"
record_value_field =
ident ":" value_expr
The parenthesized value expression is typed during checking. It is a helper call
when ident names a visible source helper and a payload-bearing enum value when
ident names a constructor of the expected enum type.
init returns a state value. step returns Continue(value), Stop(value),
or Panic(value).
Literals
The literal surface is intentionally narrow:
- decimal numbers are accepted for mailbox bounds;
- string literals are accepted for
emit; - string escapes are not supported;
- newline and carriage return characters are not allowed inside string literals.
Identifiers
ident =
(ASCII letter | "_") (ASCII letter | ASCII digit | "_")*
as, let, mut, and var are reserved everywhere identifiers are accepted.
The single _ token is reserved for wildcard patterns. ProcResult and
ProcessRef are reserved type names because they name built-in transition and
process-reference types.