Grammar¶
Working direction
Consolidated V1 source grammar extracted from the accepted source-form pages. Parser snapshots should use this page as the grammar map and the linked topic pages as the source of semantic rules.
This page gives Catalyst's source grammar in EBNF-like notation: which token sequences form source syntax and how expressions group. It is a parser map only; focused source-form pages own name resolution, type checking, dispatch, ownership, cleanup, lowering, diagnostics, and runtime evaluation order.
When this page and a focused source-form page disagree, treat the focused page as canonical until the contradiction is fixed here.
The grammar is deliberately permissive. Many forms that this page accepts are rejected later by semantic validation: move operand shape, catch as err requiring a block, declaration guards being comptime, aggregate-suffix targets being types, and similar restrictions live on the focused pages. The reason is diagnostic quality — a structurally-valid AST lets the compiler explain why a construct is illegal and suggest a fix, where a tighter grammar would only report "failed to parse." Treat productions here as "what the parser accepts," not "what the language allows."
Notation¶
rule = production
"token" = literal source token
<token> = named lexical token or trivia/gap class defined by a linked lexical page
name = another grammar rule
a | b = alternative
[ a ] = optional
{ a } = zero or more repetitions
( a ) = grouping
(* ... *) = inline parser-relevant note attached to the preceding production
separator means a newline or ; accepted by the statement-list grammar. Regular comments are trivia between grammar tokens; declaration and module documentation comments are lexical tokens that participate in the metadata blocks described by Comments.
Lexical Tokens¶
identifier = <non-keyword-identifier>
doc_comment = <declaration-doc-comment>
module_doc_comment = <module-doc-comment>
name_path = identifier { "." identifier } (* resolvable qualified name; see member_suffix for postfix "." on expressions *)
reserved_word = declaration_word | type_word | control_flow_word | operator_word | primitive_value_word
declaration_word = "fn" | "const" | "var" | "pub" | "priv" | "import" | "comptime"
type_word = "struct" | "enum" | "error" | "contract" | "impl" | "dyn" | "opaque" | "as" | "is"
control_flow_word = "if" | "else" | "for" | "in" | "while"
| "return" | "break" | "continue"
| "try" | "catch" | "defer"
| "move"
operator_word = "and" | "or" | "not" | "orelse"
primitive_value_word
= "true" | "false" | "void" | "null" | "undefined"
literal = integer_literal | float_literal | code_point_literal | string_literal
primitive_literal = "true" | "false" | "void" | "null" | "undefined"
Numeric, string, code point, suffix, escape, and comment token rules: Literals, Comments. The reserved-word filter for <non-keyword-identifier> is owned by Keywords.
is is reserved for future runtime type or pattern tests; no V1 production consumes it. Self is a type-keyword, not an identifier; see Keywords.
Source Files¶
source_file = { separator } [ module_metadata_block { separator } ] { file_item { separator } } eof
module_metadata_block
= module_doc_comment { metadata_gap module_doc_comment } [ metadata_gap ]
file_item = import_decl | top_level_decl
top_level_decl = metadata_block [ visibility ] decl_without_visibility
metadata_block = { metadata_item [ metadata_gap ] }
metadata_item = doc_comment | attribute
metadata_gap = <metadata-gap>
attribute_block = { attribute }
visibility = "pub" | "priv"
import_decl = "import" expression
decl_without_visibility
= const_decl
| function_decl
| impl_decl
const_decl = "const" binding_pattern [ ":" type_expr ] [ "=" expression ] [ decl_guard ]
decl_guard = "if" expression
<metadata-gap> is whitespace and regular-comment trivia that keeps adjacent metadata tokens in one metadata block; it excludes semicolon separators and blank source lines. The trailing [ metadata_gap ] on module_metadata_block allows non-separator trivia before the block-terminating blank line. Blank-line termination, module/declaration doc separation, and invalid doc targets are owned by Comments.
Initializer omission is semantic validation. Ordinary const declarations require an initializer unless finalized declaration metadata supplies an accepted value source.
Function Declarations¶
function_decl = [ "comptime" ] "fn" identifier parameter_list [ return_type ] [ decl_guard ] [ function_body ]
parameter_list = "(" [ parameter { "," parameter } [ "," ] ] ")"
parameter = attribute_block [ "comptime" ] binding_pattern ":" type_expr [ "=" expression ]
return_type = [ "comptime" ] type_expr
function_body = block | "=>" expression
function_body is grammatically optional in every context; validation decides where a body is required or forbidden. In contract bodies, the presence of function_body distinguishes a default method from a required operation. Opaque static returns are accepted only where the focused function and contract-dispatch pages allow them. See Semantic Contracts and Contract Dispatch.
Declaration-position signatures may wrap inside incomplete syntax such as a parameter list or return type, but a body opener attaches only on the same logical line as the completed signature. Statement-position else/catch obey the same-line rule. See Blocks and Statements.
Implementation Declarations¶
impl_decl = "impl" impl_endpoint "as" impl_endpoint [ decl_guard ] impl_body
impl_body = "{" { separator | impl_member } "}"
impl_member = metadata_block function_decl { separator }
impl_endpoint = impl_endpoint_primary { member_suffix | impl_endpoint_parameter_list }
impl_endpoint_primary
= identifier
| "Self"
| "(" type_expr ")"
impl_endpoint_parameter_list
= "(" [ impl_endpoint_parameter { "," impl_endpoint_parameter } [ "," ] ] ")"
impl_endpoint_parameter
= impl_binder_parameter
| type_expr
impl_binder_parameter
= "comptime" identifier ":" type_expr
impl_endpoint_parameter_list is a type-postfix-shaped header, not an expression call: ordinary entries are type arguments, and comptime name: type_expr entries introduce inline implementation binders. See Implementation Declarations.
Type Constructors¶
struct_type = metadata_block "struct" [ contract_entry_list ] struct_body
contract_type = metadata_block "contract" [ type_argument_list ] contract_body
enum_type = "enum" enum_body
error_set_type = "error" error_set_body
contract_entry_list = "(" [ contract_entry { "," contract_entry } [ "," ] ] ")"
contract_entry = [ "pub" ] type_expr
type_argument_list = "(" [ type_expr { "," type_expr } [ "," ] ] ")"
struct_body = "{" { separator | struct_member } "}"
struct_member = metadata_block [ "priv" ] ( field_decl | function_decl ) { separator }
field_decl = [ "const" ] identifier ":" type_expr [ "=" expression ]
[ decl_guard ]
contract_body = "{" { separator | contract_member } "}"
contract_member = metadata_block function_decl { separator }
enum_body = "{" [ enum_case { "," enum_case } [ "," ] ] "}"
enum_case = identifier
error_set_body = "{" [ error_case { "," error_case } [ "," ] ] "}"
error_case = identifier
Blocks and Statements¶
block = "{" statement_list "}"
statement_list = { separator } [ statement { separator statement } { separator } ]
(* commas are not interchangeable with separators *)
statement = local_decl
| defer_stmt
| expression
local_decl = attribute_block ( "var" | "const" ) binding_pattern [ ":" type_expr ] "=" expression
[ decl_guard ]
defer_stmt = "defer" statement
Newlines and ; separate statements only at parser-acceptable boundaries.
Binding Patterns¶
binding_pattern = identifier
| "_"
| struct_binding_pattern
| array_binding_pattern
struct_binding_pattern
= "{" [ struct_binding_entry { "," struct_binding_entry } [ "," ] ] "}"
struct_binding_entry
= attribute_block
( identifier
| "&" identifier
| identifier "=" identifier
| identifier "=" "&" identifier
| "_" "=" identifier )
array_binding_pattern
= "[" [ array_binding_entry { "," array_binding_entry } [ "," ] ] "]"
array_binding_entry = identifier | "_"
Array binding patterns share bracket spelling with array literals; parser context disambiguates them.
Type Expressions¶
type_expr = type_union
type_union = type_intersection { "|" type_intersection }
type_intersection = type_error_return { "&" type_error_return }
type_error_return = type_prefix [ "!" [ type_error_slot ] ]
type_error_slot = type_error_slot_primary { call_suffix | member_suffix }
type_error_slot_primary
= identifier
| "(" type_expr ")"
type_prefix = "?" type_prefix
| "*" [ "const" ] pointer_pointee
| "[" "]" [ "const" ] type_prefix
| "[" expression "]" type_prefix
| type_postfix
pointer_pointee = "opaque"
| "dyn" type_expr
| "impl" type_expr
| type_prefix
type_postfix = type_primary { call_suffix | member_suffix }
type_primary = identifier
| "Self"
| "(" type_expr ")"
| function_type
| struct_type
| contract_type
| enum_type
| error_set_type
The error slot after ! is an identifier chain with optional member/call suffixes or a parenthesized type_expr. Composed sets and anonymous error { ... } operands must be parenthesized: Module!(ReadError | ParseError), Module!(error { InvalidModule }). T! inference is body-only; bodyless function types require a complete error set. See Operators, Error Types.
function_type = attribute_block "fn" function_type_parameter_list return_type
function_type_parameter_list
= "(" [ function_type_parameter { "," function_type_parameter } [ "," ] ] ")"
function_type_parameter
= [ identifier ":" ] type_expr
V1 rejects opaque static returns in bodyless function type expressions.
Expressions¶
Expression precedence is encoded from lowest to highest.
expression = assignment_expr
assignment_expr = fallback_expr [ "=" fallback_expr ]
fallback_expr = or_expr { fallback_suffix }
fallback_suffix = orelse_suffix
| catch_suffix
orelse_suffix = "orelse" or_expr
catch_suffix = "catch" ( "as" identifier block | or_expr )
or_expr = and_expr { "or" and_expr }
and_expr = equality_expr { "and" equality_expr }
equality_expr = ordering_expr [ ( "==" | "!=" ) ordering_expr ]
ordering_expr = range_expr [ ( "<" | "<=" | ">" | ">=" ) range_expr ]
range_expr = additive_expr [ ( ".." | "..=" ) additive_expr ]
(* omitted-bound forms (..end, start.., ..) live only in index_selector *)
additive_expr = multiplicative_expr { ( "+" | "-" ) multiplicative_expr }
multiplicative_expr = prefix_expr { ( "*" | "/" | "%" ) prefix_expr }
prefix_expr = ( "&" | "try" | "not" | "-" ) prefix_expr
| "move" move_operand
| postfix_expr
postfix_expr = primary_expr { call_suffix | index_suffix | member_suffix | deref_suffix | optional_unwrap_suffix | aggregate_suffix }
call_suffix = "(" [ argument_list ] ")"
index_suffix = "[" index_selector "]"
member_suffix = "." identifier
deref_suffix = ".*"
optional_unwrap_suffix
= ".?"
aggregate_suffix = "{" [ aggregate_field_list ] "}"
argument_list = expression { "," expression } [ "," ]
index_selector = expression
| [ expression ] ( ".." | "..=" ) [ expression ]
aggregate_field_list
= aggregate_field { "," aggregate_field } [ "," ]
aggregate_field = "." identifier "=" expression
primary_expr = literal
| primitive_literal
| identifier
| "Self"
| "(" expression ")"
| block
| if_expr
| while_expr
| for_expr
| return_expr
| break_expr
| continue_expr
| comptime_expr
| leading_dot_expr
| array_literal
| contextual_aggregate
| struct_type
| contract_type
| enum_type
| error_set_type
array_literal = "[" [ expression { "," expression } [ "," ] ] "]"
leading_dot_expr = "." identifier
contextual_aggregate
= "." "{" [ aggregate_field_list ] "}"
In if, while, and for headers, a { that can start the required body terminates the header expression. Parenthesize a trailing aggregate construction: if (ReadyState{ .ok = true }) { ... }.
Control Flow Expressions¶
if_expr = "if" if_condition block [ "else" ( if_expr | block ) ]
if_condition = expression
| optional_binding_condition
optional_binding_condition
= "const" binding_pattern [ ":" type_expr ] [ "=" expression ]
(* "=" omittable only when binding_pattern is a bare identifier (same-name sugar) *)
| "var" binding_pattern [ ":" type_expr ] "=" expression
while_expr = "while" while_condition block
while_condition = expression
| "const" binding_pattern [ ":" type_expr ] "=" expression
for_expr = "for" [ "var" ] loop_binding "in" expression block
loop_binding = binding_pattern [ ":" type_expr ]
return_expr = "return" [ expression ]
break_expr = "break"
continue_expr = "continue"
comptime_expr = "comptime" expression
Move Places¶
move parses broadly; V1 validation accepts only movable local places. See Move Expressions.
Attribute Syntax¶
Related Details¶
- Comments: lexical comments, documentation comments, metadata grouping, and trivia.
- Literals: literal token spelling, suffixes, escapes, and aggregate literal forms.
- Operators: source operator spelling, precedence, associativity, and deferred operator boundaries.
- Blocks and Statements: parser-contextual statement separation and continuation rules.
- Conditionals, Loops, and Optional Bindings: control-flow semantics.
- Destructuring: binding pattern boundaries.
- Functions: function declarations, parameters, defaults, function types, and error-return inference.
- Modules and Imports, Imports, and Conditional Declarations: source-file declarations, imports, and declaration guards.
- Types: type-expression semantics, constraint types, intersections, and
dyn/implboundaries. - Structs and Methods, Enums, Errors, and Semantic Contracts: type constructor semantics.
- Implementation Declarations: implementation endpoint validation.
- Move Expressions: move operand validation.
- Attributes: attribute target validation.