hd-lang

#Grammar

Status: language specification draft.

This chapter collects the hd-lang grammar in EBNF.

  1. grammar.scope.syntaxThe grammar specifies syntactic form, not name resolution, typing, exhaustiveness, or runtime behavior.
  2. grammar.scope.tokensThe grammar consumes the token stream produced by Lexical Structure.
  3. grammar.scope.layout-tokensNEWLINE, INDENT, DEDENT, SUITE_END, and EOF are abstract layout tokens.
  4. grammar.scope.commentsComments do not appear in this grammar.
  5. grammar.scope.consolidatedThe feature chapters refine the semantic constraints on these productions, but do not maintain separate extension grammars.
fn demo() -> i32:
    x := +1
    x + 1

#Source Files And Suites

A source file is a sequence of top-level items:

source_file = { NEWLINE | top_level_item }, EOF ;

top_level_item = use_decl
               | tests_block
               | decorated_decl
               | declaration
               | top_level_statement
               ;

top_level_statement = suite_statement
                    | simple_statement, NEWLINE
                    ;
  1. grammar.suite.same-lineA same-line suite_body ends at SUITE_END.
  2. grammar.suite.indentedAn indented suite_body begins on the following logical line.
  3. grammar.suite.nonemptyThe production requires at least one statement in an indented body; use pass when an explicit no-op body is required.
  4. grammar.suite.local-declarationsNamed declarations and implementations may also occur in executable block suites.
  5. grammar.suite.use-top-levelUse declarations remain top-level items.
  6. grammar.suite.methodsMethods occur inside trait and implementation declarations through their dedicated grammar productions.
  7. grammar.limit.nestingAn implementation may limit how deeply brackets, suites, closures, and string interpolations nest within one another. The limit is implementation-defined.
  8. grammar.limit.nesting.errorSource that nests deeper than that limit is an error, reported on the first construct past it. Error: nesting-too-deep.

#Error Recovery

Error recovery limits cascades from rejected syntax:

Error example
fn broken(a: i32, b: i32, c: i32) -> i32:
    value := a and b < c < a  # error: syntax-error
    +1

fn unfinished() -> i32:
    values := [  # error: unclosed-delimiter
        +1
    +2
  1. grammar.recovery.statementAfter the first parsing error in a statement, an implementation must suppress further parsing errors rooted in that statement.
  2. grammar.recovery.unclosedAfter an unclosed-delimiter error, an implementation must suppress parsing errors on later lines within that delimiter.

Why. One diagnostic identifies the root cause without presenting its syntactic fallout as independent mistakes.

#Test Blocks

A file may hold one tests: block, whose items are compiled only for tests:

tests_block = "tests", ":", NEWLINE, INDENT, tests_item, { tests_item },
              DEDENT ;

tests_item = use_decl
           | decorated_decl
           | declaration
           | top_level_statement
           ;
fn late_fee(days: i32) -> i32:
    if days > 30: 5 else: 0

tests:
    use std.testing.assert_equal

    fn overdue() -> i32: 31

    it("charges a fee after 30 days"):
        assert_equal(late_fee(overdue()), 5, reason="one day late")
  1. grammar.tests.blockA tests: block is a top-level item that holds its module's test-only items.
  2. grammar.tests.item-formsIts items take the forms of top-level items: use declarations, decorated declarations, declarations, and statements.
  3. grammar.tests.top-levelA tests: block may appear only at module top level. A tests: block inside a suite or inside another tests: block is an error. Error: syntax-error.
  4. grammar.tests.onceA file may have at most one tests: block. A second block is an error. Error: duplicate-tests-block.
  5. grammar.tests.registrationEach statement of the block must be a test registration call, as module.testing.position-statements specifies.
  6. grammar.tests.keywordtests is a reserved word, so it never names a declaration or binding.
Error example
fn helper() -> void:
    tests:  # error: syntax-error
        pass

tests:
    it("first block"):
        pass

tests:  # error: duplicate-tests-block
    it("second block"):
        pass

Why. Like Rust's #[cfg(test)] mod tests, one block groups a file's test-only code, and the normal build never sees it.

See also: Tests Blocks, Standard Testing.

#Statements

Statements, including local declarations, follow this grammar:

statement = suite_statement
          | function_decl
          | data_decl
          | enum_decl
          | trait_decl
          | type_decl
          | impl_decl
          | simple_statement, NEWLINE
          ;

suite_statement = defer_statement
                | suite_expression
                | trailing_block_call
                | "_", ":=", suite_right_side
                | identifier, ":=", { identifier, ":=" },
                  suite_right_side
                | "let", pattern, [ ":", type ], "=",
                  suite_right_side
                | "using", identifier, [ ":", type ], "=",
                  suite_right_side
                | let_else_statement
                | postfix_expression, ( "=" | compound_assign_op ),
                  suite_right_side
                | "return", suite_right_side
                | "break", suite_right_side
                ;

suite_right_side = suite_expression | trailing_block_call ;

defer_statement = "defer", ":", suite_body ;

simple_statement = let_statement
                 | using_statement
                 | discard_statement
                 | assignment_statement
                 | return_statement
                 | break_statement
                 | continue_statement
                 | expression_statement
                 ;

let_statement = "let", pattern, [ ":", type ], "=",
                closed_expression ;

let_else_statement = "let", pattern, [ ":", type ], "=",
                     closed_expression, "else", ":", suite_body ;

using_statement = "using", identifier, [ ":", type ], "=",
                  closed_expression ;

discard_statement = "_", ":=", closed_expression ;

assignment_statement = postfix_expression, ( "=" | compound_assign_op ),
                       closed_expression ;

compound_assign_op = "+=" | "-=" | "*=" | "/=" | "%="
                   | "&=" | "|=" | "^=" | "<<=" | ">>="
                   ;

return_statement = "return", [ closed_expression ] ;
break_statement = "break", [ closed_expression ] ;
continue_statement = "continue" ;
expression_statement = closed_expression ;

inline_statement = "let", pattern, [ ":", type ], "=", inline_expression
                 | "using", identifier, [ ":", type ], "=", inline_expression
                 | "_", ":=", inline_expression
                 | postfix_expression, ( "=" | compound_assign_op ),
                   inline_expression
                 | "return", [ inline_expression ]
                 | "break", [ inline_expression ]
                 | continue_statement
                 | inline_expression
                 ;

#Let Statements

A let statement binds the names of a pattern to the parts of its initializer:

struct Point:
    x: i32
    y: i32

fn total(point: Point, pair: (i32, i32)) -> i32:
    let Point { x, y: py } = point
    let (low, _) = pair
    let sum = x + py
    sum = sum + low
    sum
  1. grammar.stmt.let-patternA let takes any match pattern: a name, _, a literal, or a tuple, data, or variant pattern, nested to any depth.
  2. grammar.stmt.let-pattern.tupleA multi-name let is a tuple pattern, so its names stand in parentheses, as in let (a, b) = ....
  3. grammar.stmt.let-list.bareA multi-name let without parentheses, as in let a, b = ... or let mut log, db = ..., is an error whose fix-it adds the parentheses. Error: syntax-error.
  4. grammar.stmt.let-pattern.parenthesizedParentheses around one pattern with no comma form no pattern, so let (a) = ... is an error. Error: syntax-error.
  5. grammar.stmt.let-pattern.mutIn a let pattern, mut may precede each name the pattern binds, as in let mut user = ..., let (mut log, db) = ..., and let Point { mut tags, name } = ....
  6. grammar.stmt.let-mut.per-nameA mut belongs to the one name it precedes. What it makes writable is a semantic rule of Writable Places.
  7. grammar.stmt.let-mut.only-letOnly let accepts it: mut user := ... and for mut item in items: are errors. Error: syntax-error.
  8. grammar.stmt.let-pattern.semanticWhether a pattern may fail, and so needs an else, is a semantic rule of Let Patterns.
Error example
fn pair() -> (List[i32], List[i32]): ([1], [2])

fn invalid() -> void:
    let (first, second) = pair()  # valid: no mut needed
    mut total := 0  # error: syntax-error
    let mut log, db = pair()  # error: syntax-error
    let a, b = pair()  # error: syntax-error
    let (only) = pair()  # error: syntax-error

Why. In let mut log, db, a reader may take mut as covering both names. The parentheses show that it belongs to log alone. A multi-name let uses them even without mut, since one shape reads better than two spellings.

#Let-Else Statements

A let-else statement adds else: and a block, which runs when the pattern does not match:

fn find(id: i32) -> i32?:
    if id == 0: .Some(7) else: .None

fn sum(id: i32) -> i32?:
    let .Some(first) = find(id) else: return .None
    let .Some(second) = find(id + 1) else:
        return .None
    .Some(first + second)
  1. grammar.stmt.let-elseA let statement may end in else, :, and a suite body, as the let_else_statement production states.
  2. grammar.stmt.let-else.same-lineThe else body may be a same-line suite, as in let .Some(user) = find(id) else: return .None, or an indented suite.
  3. grammar.stmt.let-else.closedThe initializer of a let-else is a closed_expression, so it cannot end in a suite. Parentheses hold such an initializer, as in let .Some(v) = (if c: a else: b) else: return.
  4. grammar.stmt.let-else.not-inlineA let-else is never a same-line suite body. In if ok: let .Some(v) = f() else: return, layout ends the if suite before else, so the else belongs to the if.
  5. grammar.stmt.let-else.semanticThe else block must diverge, which is a semantic rule of Let Patterns.

Why. An else body that returns early keeps the matched names at the function's own indentation. Rust's let ... else and Swift's guard let ... else read the same way.

#Short Binding Lists

A := binding binds exactly one name; destructuring uses let:

fn pair() -> (i32, i32): (1, 2)

fn split() -> i32:
    total := 0
    let (low, high) = pair()
    total + low + high
  1. grammar.stmt.short-binding.one-name:= binds exactly one name, as in total := 0.
  2. grammar.stmt.short-binding.let-onlyA pattern before :=, such as (a, b) := pair, Point { x, y } := p, or .Some(v) := found, is an error whose fix-it writes let and =, as in let (a, b) = pair. Error: missing-let.
  3. grammar.stmt.short-binding.bare-listA bare list before :=, as in a, b := pair, is an error whose fix-it writes let (a, b) = pair. Error: syntax-error.
  4. grammar.stmt.typed-binding.letA binding with a type annotation must begin with let. So x: T = v is an error whose fix-it writes let x: T = v, or x := v without the type. Error: missing-let.
Error example
struct Point:
    x: i32
    y: i32

fn sum(p: Point, pair: (i32, i32)) -> i32:
    Point { x, y } := p  # error: missing-let
    (low, high) := pair  # error: missing-let
    a, b := pair  # error: syntax-error
    display_name: string = "Ada"  # error: missing-let
    x + y

Note. For x: T = v, the missing-let message reads: "a binding with a type annotation must begin with 'let'; write let x: T = ..., or x := ... without the type". It does not call the binding mutable.

Why. One way to destructure is easier to read and to write than two. let already takes every pattern, and := stays the short form for one name.

#Discard And Defer Statements

  1. grammar.stmt.discardThe dedicated discard forms make _ := expression a statement without making the placeholder _ an identifier or a binding pattern.
  2. grammar.stmt.discard.suiteThe suite form of a discard exists for the same reason when the discarded expression owns an indented suite.
  3. grammar.stmt.deferdefer is parsed wherever a suite statement is accepted; the semantic rules in Control Flow restrict it to executing cleanup scopes.
  4. grammar.stmt.usingA statement that starts with the contextual word using and a name on the same line is a using_statement. The semantic rules are in Using Bindings.
  5. grammar.stmt.using.one-nameA using statement binds exactly one name: no pattern and no mut.
fn twice(n: i32) -> i32: n * 2

fn demo() -> void:
    _ := twice(21)   # the value is discarded

#Suite Statements

  1. grammar.stmt.suiteA suite_statement is a statement whose outermost expression owns a suite.
  2. grammar.stmt.suite.endIts final DEDENT, or the SUITE_END of a same-line suite, terminates the statement; it does not require another NEWLINE.
  3. grammar.stmt.suite.right-sideThis separate production is what permits value := if ..., let callback = fn ..., and similar direct right-hand-side forms.
  4. grammar.stmt.suite.trailing-blockEvery right-hand side that accepts a suite expression, after :=, let ... =, =, _ :=, return, and break, also accepts a trailing block call.
  5. grammar.stmt.chainA chain of bindings continues only with single names, as in a := b := if c: 1 else: 2.
  6. grammar.stmt.suite.in-delimitersA suite expression nested inside delimiters remains part of its enclosing expression, and the enclosing statement ends normally after the closing delimiter.
fn choose(flag: bool) -> i32:
    y := if flag: +1 else: +2
    y

#Statements Ending At A Newline

  1. grammar.stmt.closedA statement that ends at NEWLINE takes a closed_expression, which cannot end in a suite. Layout emits no NEWLINE after a suite's SUITE_END or DEDENT.
  2. grammar.stmt.closed.suite-alternativesOnly the suite_statement alternatives may end in a suite.
  3. grammar.stmt.closed.examplesThus y := if c: 1 else: 2 is a statement, but _ := y := if c: 1 else: 2 and return y := if c: 1 else: 2 are syntax errors. Error: syntax-error.
  4. grammar.stmt.closed.parenthesizedParenthesizing the inner binding makes them valid.
Error example
fn choose(flag: bool) -> i32:
    _ := y := if flag: 1 else: 2  # error: syntax-error
    return y := if flag: 1 else: 2  # error: syntax-error

#Same-Line Suite Bodies

  1. grammar.inline.statementA same-line suite body is an inline_statement.
  2. grammar.inline.closed-by-layoutLayout closes a same-line suite at the end of its logical line and at any comma at the suite's own delimiter depth.
  3. grammar.inline.no-commaThe body therefore contains no comma at that depth and no indented suite.
  4. grammar.inline.no-ifIt also contains no same-line if at that depth: if a: if b: 1 else: 2 else: 3, fn f() -> i32: if c: 1 else: 2, and defer: if flag: pass are syntax errors. Error: syntax-error.
  5. grammar.inline.nested-ifParentheses nest a conditional, as in if a: (if b: 1 else: 2) else: 3, and an indented body may hold one.
  6. grammar.inline.else-ifelse if continues the same conditional rather than nesting one.
  7. grammar.inline.loopsSame-line for and while loops may still appear directly in a same-line suite.
  8. grammar.inline.let-patternA let statement may be a same-line suite body, because a pattern's commas stand inside brackets, as in if ok: let (a, b) = pair and if ok: let Point { x, y } = p.
  9. grammar.inline.bare-commaThe bare comma forms still close the suite, so if ok: a, b := pair and if ok: let a, b = pair are syntax errors. Error: syntax-error.

Note. The late_fee example starts its if in an indented function body, not a same-line suite, so it is valid.

Error example
fn pair() -> (i32, i32): (1, 2)

fn pick(a: bool, b: bool) -> i32:
    v := if a: if b: 1 else: 2 else: 3  # error: syntax-error
    v

fn sign(x: i32) -> i32: if x < 0: -1 else: 1  # error: syntax-error

fn release(flag: bool) -> void:
    defer: if flag: pass  # error: syntax-error
    if flag: a, b := pair()  # error: syntax-error
    if flag: let a, b = pair()  # error: syntax-error

Note. A binding is scoped to its block, so a name that a same-line suite binds is never read after the suite ends. The unused-local-binding warning reports it:

fn pair() -> (i32, i32): (1, 2)

fn split(ready: bool) -> void:
    if ready: let (low, high) = pair()  # warning: unused-local-binding

#Expressions Followed By Another Token

  1. grammar.continued.positionsWhere another token follows an expression inside brackets, the grammar uses continued_expression.
  2. grammar.continued.position-listThose positions are the header of a control-flow expression written directly inside brackets, a comprehension clause, and a map key. They are also a spread before ... and a parameter decorator.
  3. grammar.continued.no-same-line-suiteA continued_expression cannot end in a same-line suite, because layout would extend that suite over the following token.
  4. grammar.continued.layoutLayout ends a same-line suite only at a line boundary outside brackets, at a comma or closing delimiter at its depth, or before else.
  5. grammar.continued.indented-suiteA continued_expression may end in an indented suite, except a closure body.
  6. grammar.continued.no-closureAfter an indented closure body inside brackets, the next line must start with , or a closing delimiter, so indented_suite_expression has no closure alternative.
  7. grammar.closed.outside-bracketsOutside brackets, an expression followed by another token cannot end in any suite.
  8. grammar.closed.header-positionsA control-flow header in a statement and a match guard therefore take a closed_expression.
  9. grammar.closed.bracketed-suiteA suite may still appear inside brackets within the header, as in if check(fn(x): ...):.
  10. grammar.closed.indented-headerBut a statement if fn() -> bool:, followed by the closure's indented body and then a line beginning : 1 else: 2, is a syntax error. Its header ends in an indented suite. Error: syntax-error.
  11. grammar.closed.nested-statementsThe statements of a suite nested inside brackets follow the same rule, because they are statements too.
Error example
fn pick() -> i32:
    if fn() -> bool:  # error: syntax-error
        true
    : 1 else: 2

See also: Physical And Logical Lines.

#Semantic Statement Rules

  1. grammar.stmt.semanticWhether a statement may appear in a particular value-producing block is a semantic rule.
  2. grammar.stmt.breakIn particular, break is valid only inside a loop, and break with a value is valid only in a loop with an else suite.
  3. grammar.stmt.assign-placeThe left side of an assignment must resolve to a reassignable local, a field or element of a writable place, or a call place. Other postfix expressions are errors. Error: invalid-assignment-target.
  4. grammar.stmt.compound-assignA compound assignment place op= value takes the same left side as an assignment, and its right side follows the same forms as =. Compound Assignment defines it.
  5. grammar.stmt.no-copy-assignmentA statement place ...= value is a syntax error. Error: syntax-error.
fn demo() -> void:
    let x = 1
    x = x + 1   # x is a reassignable local
Error example
fn invalid() -> void:
    let value = 1
    value ...= 2  # error: syntax-error

See also: Data Embedding.

#Declarations

A declaration is an optionally public named declaration or an implementation:

declaration = [ "pub" ], ( function_decl
                         | data_decl
                         | enum_decl
                         | trait_decl
                         | type_decl )
            | impl_decl
            ;
  1. grammar.decl.impl-no-pubpub is not accepted before an impl declaration.

Why. Implementations are not independently named module members.

#Functions

function_decl = "fn", callable_name, [ function_generic_params ], parameter_clause,
                [ "->", function_result_type ], [ requirement_clause ], ":",
                suite_body ;

function_result_type = result_type ;
opaque_result_type = "impl", bound_trait_type ;

callable_name = identifier, [ "!" ] ;

suite_body = inline_suite_body
           | NEWLINE, INDENT, statement, { statement }, DEDENT
           ;

inline_suite_body = inline_statement, SUITE_END ;

parameter_clause = "(", [ parameter_list ], ")" ;
parameter_list = parameter, { ",", parameter }, [ "," ] ;

parameter = receiver_parameter
          | { parameter_decorator }, value_parameter
          ;

parameter_decorator = "@", continued_expression ;
value_parameter = [ "mut" ], identifier, ":", type, [ "=", expression ]
                | [ "mut" ], identifier, "...", ":", type
                ;

receiver_parameter = "self" | "mut", "self" | "&", "self" | "&", "mut", "self"
                   | "*", "self" | "*", "mut", "self" ;
  1. grammar.fn.receiverThe receiver forms are valid only for methods.
  2. grammar.fn.receiver-ref&self and &mut self are receivers that take a reference; Reference Receivers states their meaning.
  3. grammar.fn.receiver-counted*self and *mut self are receivers that take a counted reference, for a method that keeps self. Reference Receivers states their meaning.
  4. grammar.fn.param-mutmut before a parameter name marks a parameter whose value changes. Parameters states its meaning.
  5. grammar.fn.decorator-param-targetsAmong function parameters, parameter decorators are valid only on value parameters of module-level named functions and of methods. Variant payload parameters also accept them, as Enums states.
  6. grammar.fn.decorator.linesWithin a multiline parameter clause, each decorator may occupy its own prefix line; delimiter line breaks do not terminate the parameter.
  7. grammar.fn.vararg-nameA vararg parameter writes ... after its name, as in values...: List[i32]; it takes no default and must be the final positional parameter.
  8. grammar.fn.semanticDefault-argument ordering and the requirement-free rule are semantic constraints defined in Functions.
  9. grammar.fn.opaque-result-nestedimpl may occur in a named-function result annotation, including nested ordinary type arguments, tuples and optional types.
  10. grammar.fn.opaque-result.positionsimpl is invalid in parameters, fields, bindings, closure annotations, function types and requirement rows. Error: syntax-error.
  11. grammar.fn.opaque-bound.no-nestingAn impl type must not appear inside another impl bound. Error: syntax-error.
Error example
fn scaled(values...: List[i32], factor: i32) -> i32: factor  # error: nonfinal-vararg

#Data Types

data_decl = "struct", identifier, [ type_params ], ":", data_suite ;

data_suite = "pass", SUITE_END
             | NEWLINE, INDENT,
               ( "pass", NEWLINE
               | data_member, { data_member } ), DEDENT
             ;

data_member = { decorator_line }, ( data_field | embedded_field ), NEWLINE
              ;

data_field = [ "pub" ], identifier, ":", type, [ "=", closed_expression ] ;
embedded_field = named_type ;
  1. grammar.data.no-mut-modifiermut is not a struct-member modifier: mut name: string and mut Base are errors. Error: mutable-field-modifier.
  2. grammar.data.embeddedAn embedded field must denote a struct type and must not include mut.
  3. grammar.data.embedded.no-pubAn embedded field takes no pub marker, so pub Base in a struct body is an error. Error: syntax-error.
  4. grammar.data.defaultField default expressions have the requirement-free constraint specified in Field Defaults.
  5. grammar.data.struct-onlyThis declaration is written with struct and declares a struct (Struct Declarations). data is a reserved word that starts no declaration, so data Point: is an error. Error: syntax-error.
Error example
pub struct Base:
    id: string

pub struct Post:
    pub Base  # error: syntax-error
    pub title: string

data Point:  # error: syntax-error
    x: i32

Why. An embedded field is always public.

See also: Struct Declarations.

#Enums

enum_decl = "enum", identifier, [ type_params ],
            [ enum_parameter_clause ], ":",
            NEWLINE, INDENT, enum_variant, { enum_variant }, DEDENT ;

enum_variant = { decorator_line }, identifier, [ variant_parameter_clause ],
               [ "->", variant_shared_data ], NEWLINE ;

enum_parameter_clause = "(", [ enum_parameter_list ], ")" ;
enum_parameter_list = enum_parameter, { ",", enum_parameter }, [ "," ] ;
enum_parameter = [ identifier, ":" ], type, [ "=", expression ] ;
variant_parameter_clause = "(", [ data_parameter_list ], ")" ;
data_parameter_list = data_parameter, { ",", data_parameter }, [ "," ] ;
data_parameter = { parameter_decorator }, [ identifier, ":" ], type ;

variant_shared_data = identifier, argument_clause ;
  1. grammar.enum.variant-resultThe optional variant result initializes constructor data shared by every variant, as in NotFound -> StatusCode(404).
  2. grammar.enum.no-result-typeAfter ->, a variant names its enum and gives an argument clause. A result type there, as in IntLit(value: i64) -> Expr[i64], is an error. Error: variant-result-type-removed.
  3. grammar.enum.no-type-paramsType parameters after a variant's name, as in If[T](cond: Expr[bool]), are an error. Error: variant-result-type-removed.
  4. grammar.enum.defaultsOnly shared enum constructor parameters may declare defaults.
  5. grammar.enum.defaults.rulesTheir ordering and requirement-free constraints follow function-parameter defaults.
  6. grammar.enum.payload-decoratorA variant payload parameter may carry parameter decorators, as in Moved(to: string, @rename("why") reason: string). They attach facts to that payload member.

Why. A variant result type and variant type parameters were the GADT forms, which hd removed. A typed request and its response pair through a trait with an associated type instead.

#Traits And Implementations

trait_decl = "trait", identifier, [ type_params ],
             [ "<", supertrait_bounds ],
             ( NEWLINE
             | ":", NEWLINE, INDENT,
               trait_member, { trait_member }, DEDENT )
             ;

supertrait_bounds = bound_trait_type, { "&", bound_trait_type } ;

trait_member = associated_type_decl
             | { decorator_line }, "fn", callable_name, [ function_generic_params ],
               parameter_clause,
               "->", function_result_type, [ requirement_clause ],
               ( NEWLINE | ":", suite_body )
             ;

impl_decl = "impl", [ generic_params ], impl_header_types,
            [ "by", identifier ],
            ( NEWLINE
            | ":", NEWLINE, INDENT,
              impl_member, { impl_member }, DEDENT )
            ;

impl_header_types = trait_type, "for", type
                  | type
                  ;

impl_member = associated_type_decl
            | { decorator_line }, method_decl
            | intrinsic_method
            | derivation_line
            ;

intrinsic_method = "@", "intrinsic", NEWLINE, "fn", callable_name,
                   [ function_generic_params ], parameter_clause,
                   "->", function_result_type, NEWLINE ;

derivation_line = ( identifier | "Self" ), ( "=" | "+=" ), closed_expression,
                  NEWLINE ;

method_decl = [ "pub" ], "fn", callable_name, [ function_generic_params ],
              parameter_clause, [ "->", function_result_type ],
              [ requirement_clause ], ":", suite_body ;

associated_type_decl = "type", identifier, [ "<", supertrait_bounds ],
                       [ "=", type ], NEWLINE ;
  1. grammar.impl.inherentimpl T: is an inherent implementation.
  2. grammar.impl.traitimpl Trait for T: is a trait implementation.
  3. grammar.impl.delegation-fieldExcept when E is Structure, impl Trait for T by E delegates the trait to the embedded field E of T and may omit its body.
  4. grammar.trait.markerA trait declaration without a body is a marker trait.
  5. grammar.impl.bodylessA trait implementation may omit its body when the trait is a marker or when every trait method has a default.
  6. grammar.impl.pub-methodA pub method is permitted only in an inherent implementation; trait method visibility follows the trait. A pub trait method or trait implementation method is an error. Error: trait-method-visibility.
  7. grammar.trait.method-endA bodyless trait method ends at NEWLINE; a default method has : followed by a suite.
  8. grammar.trait.supertraittrait Child < Parent: declares Parent as a supertrait and opens the body with :.
  9. grammar.decl.bound-vs-colonIn declarations, < introduces a bound (a supertrait or a generic parameter bound), while : means "has type" or opens a suite.
  10. grammar.trait.method-receiverA function member whose first parameter is a receiver (self, mut self, &self, or &mut self) is a method; a receiverless member is an associated function.
  11. grammar.trait.associated-typeAssociated type declarations omit = in a trait requirement and provide = type in an implementation.
  12. grammar.trait.associated-type.boundOnly a trait's associated type declaration may name bounds after <. Bounds in an implementation's declaration are an error. Error: syntax-error.
  13. grammar.impl.inline-boundsGeneric implementations state every bound inline in their generic parameter list; the language has no separate bound clause.
  14. grammar.impl.by-structureimpl Trait for T by Structure declares a derivation template or a derivation block, as Typed Derivation defines.
  15. grammar.impl.by-no-traitimpl T by Structure, without a trait, declares a derivation block with no trait, as Derivation Blocks With No Trait defines.
  16. grammar.impl.by-no-trait.otherIn a header without a trait, by followed by any other name is a semantic error, not a grammar error, as trait.by.no-trait defines.
  17. grammar.impl.derivation-lineA derivation_line is a member line. Its placement and meaning are defined in Member Lines.
  18. grammar.impl.derivation-line.formsWhich right sides a member line accepts is a semantic rule of Member Lines, not a grammar rule.
  19. grammar.impl.intrinsic-methodAn intrinsic_method is an implementation method without a body. Intrinsic Methods defines where one is valid.
Error example
pub trait Display:
    pub fn to_string(self) -> string  # error: trait-method-visibility

trait Named:
    fn name(self) -> string

trait Greeter: Named  # error: syntax-error

See also: Trait Delegation.

#Type Declarations

type_decl = "type", identifier, [ type_params ],
            ( "=", ( type | row_alias_target ) | "(", type, ")" ), NEWLINE ;
row_alias_target = "$", requirement_row ;
  1. grammar.type-decl.aliasThe = form declares a transparent alias.
  2. grammar.type-decl.newtypeThe parenthesized form declares a nominal single-field newtype.
  3. grammar.type-decl.row-alias.dollarAn = form whose right side is a row after $, as in type AppRow = $ Db + Cache or type NoRow = $(), declares a row alias.
  4. grammar.type-decl.row-alias.bareA right side that joins keys with + but has no $, as in type AppRow = Db + Cache, parses, and req.row.alias.dollar.missing rejects it.
  5. grammar.type-decl.alias.bare-trait.reference-valuesA right side of one trait without $, as in type Store = Db, parses as a type, and types.trait.value.dyn-required.reference-values rejects it.
  6. grammar.type-decl.row-alias.no-and& joins bounds only, so type Both = $ Db & Cache is an error. Error: syntax-error.
Error example
trait Db

trait Cache

type AppRow = $ Db + Cache

type Both = $ Db & Cache  # error: syntax-error

See also: Row Aliases.

#Generic Parameters And Bounds

type_params = "[", type_parameter, { ",", type_parameter }, [ "," ], "]" ;
generic_params = "[", generic_parameter, { ",", generic_parameter }, [ "," ], "]" ;
function_generic_params = "[", function_generic_parameter,
                          { ",", function_generic_parameter }, [ "," ], "]" ;

type_parameter = [ variance ], identifier, [ "<", trait_bounds ],
                 [ type_default ]
               | row_parameter_decl, [ type_default ] ;
generic_parameter = identifier, [ "<", trait_bounds ]
                  | row_parameter_decl ;
row_parameter_decl = "$", identifier ;
function_generic_parameter = generic_parameter, [ type_default ] ;
type_default = "=", type_argument ;
variance = "+" | "-" ;

trait_bounds = bound_trait_type, { "&", bound_trait_type } ;
bound_trait_type = qualified_name, [ bound_type_arguments ] ;
bound_type_arguments = "[", bound_type_argument_list, [ "," ], "]" ;
bound_type_argument_list = type_argument, { ",", type_argument },
                           { ",", associated_type_binding }
                         | associated_type_binding,
                           { ",", associated_type_binding }
                         ;
associated_type_binding = identifier, "=", type ;
trait_type = qualified_name, [ type_arguments ] ;

#Multiple Bounds

  1. grammar.generic.bound.andSeveral trait bounds on one type are joined with &, as in T < Eq & Hash, and the type implements every one.
  2. grammar.generic.bound.and.positions& joins bounds in every bound position: a generic parameter, a supertrait list, and an implementation's generic parameters.
  3. grammar.generic.bound.and.examplesFor example, trait Ord < Eq & PartialOrd and impl[T < Eq & Hash] Hash for Bag[T] join bounds with &.
  4. grammar.generic.bound.and.types-onlyBounds appear only in type positions, so a bound's & never conflicts with the bitwise & of an expression.
trait Named:
    fn name(self) -> string

trait Tagged:
    fn tag(self) -> string

fn label[T < Named & Tagged](value: T) -> string:
    value.name() + value.tag()   # T implements both traits

Why. & means both at once, as in Java <T extends A & B>, TypeScript and Scala 3 A & B, and Swift P & Q. It leaves + to requirement rows, so each operator has one meaning.

#Associated Type Bindings In Bounds

  1. grammar.generic.bindingA trait in a generic parameter bound may end its bracketed arguments with associated type bindings. So I < Supplier[Item = T] requires I to implement Supplier with I::Item equal to T.
  2. grammar.generic.binding.orderBindings follow every positional type argument.
  3. grammar.generic.binding.positions-key.reference-valuesBindings are valid in trait_bounds, and in supertrait_bounds as in trait Summable < Add[Out = Self]. They are also valid in a dyn_type, as in the trait value type &dyn Supplier[Item = i32], and in a requirement_key, as in $ Store[Item = User].
  4. grammar.generic.binding.trait-type-onlyThe trait of an implementation header is a trait_type, and the qualifier of a trait-qualified call or a method reference is an expression_trait_type. Neither takes a binding in its arguments. A binding there is an error. Error: syntax-error.
  5. grammar.generic.binding.named-typeA binding in a named_type has no meaning; Binding Positions rejects it, and a trait value type writes its bindings after dyn.
Error example
trait Supplier:
    type Item
    fn get(self) -> Self::Item

struct Constant:
    value: string

impl Supplier[Item = string] for Constant:  # error: syntax-error
    fn get(self) -> string: self.value

See also: Associated Type Bindings.

#Type-Argument Default Syntax

A generic parameter may end in = and a default type argument, after its bound:

trait Supplier:
    type Item
    fn get(self) -> Self::Item

struct Constant: pass

impl Supplier for Constant:
    type Item = string
    fn get(self) -> string: "constant"

fn pick[T, I < Supplier[Item = T] = Constant](source: I) -> T:
    source.get()
  1. grammar.generic.defaultA type_default may end a generic parameter of a struct, enum, trait, type declaration, function, or method, after any bound.
  2. grammar.generic.default.bindingA binding sits inside a bound trait's brackets, while a default follows them at the level of the parameter list. I < Supplier[Item = T] = Constant has both.
  3. grammar.generic.default.implThe generic parameters of an implementation use generic_params, which has no default. A default there is an error. Error: syntax-error.
  4. grammar.generic.default.semanticDefault order, the names a default may use, and when it applies are semantic rules of Type-Argument Defaults.
Error example
struct Box[T]:
    value: T

impl[T = i32] Box[T]:  # error: syntax-error
    fn get(self) -> T: self.value

#Generic Parameter Modifiers

  1. grammar.generic.varianceVariance markers are valid on generic type declarations, not function generic parameters.
  2. grammar.generic.row-markerA $ before a generic parameter's name, as in fn map[T, U, $R], declares a row parameter, as Row Parameters specifies.
  3. grammar.generic.row-marker.no-boundA row parameter takes no bound, so $R < Log is an error. Error: syntax-error.
  4. grammar.generic.row-marker.defaultA row parameter's default is a row type argument, as in $R = $().
  5. grammar.generic.bound.no-mutA bound takes no mut prefix, so T < mut Named is an error. Error: syntax-error.
Error example
trait Log

fn run[$R < Log](job: fn() -> void $ R) -> void $ R:  # error: syntax-error
    job()

See also: Keywords And Reserved Words.

#Types

type = reference_access_type, { "?" }
     | function_type
     ;

reference_access_type = [ "mut" ], reference_type
                      | ref_type
                      ;

ref_type = "&", [ "mut" ], reference_type
         | "*", [ "mut" ], reference_type
         ;

reference_type = opaque_result_type
               | named_type
               | dyn_type
               | "Self"
               | tuple_type
               | grouped_type
               | associated_type_projection
               | context_type
               ;

named_type = qualified_name, [ bound_type_arguments ] ;
dyn_type = "dyn", bound_trait_type ;
type_arguments = "[", type_argument, { ",", type_argument }, [ "," ], "]" ;
type_argument = type
              | row_type_argument
              ;
row_type_argument = "$", requirement_row ;

tuple_type = "(", ")"
           | "(", type, ",", [ type_list ], ")"
           | "(", rest_type, ",", ")"
           ;
rest_type = type, "..." ;

grouped_type = "(", type, ")" ;

function_type = "fn", [ "!" ], "(", [ type_list ], ")",
                "->", type, [ requirement_clause ] ;
type_list = type, { ",", type }, [ ",", rest_type ], [ "," ]
          | rest_type, [ "," ]
          ;

result_type = reference_access_type, { "?" }
            | result_function_type
            ;
result_function_type = "fn", [ "!" ], "(", [ type_list ], ")",
                       "->", result_type ;

associated_type_projection = ( qualified_name | "Self" ), "::", identifier ;

qualified_name = identifier, { ".", identifier } ;

requirement_clause = "$", requirement_row ;
requirement_row = requirement_list
                | "(", ")"
                ;
requirement_list = requirement_key, { "+", requirement_key } ;
requirement_key = bound_trait_type ;

#Requirement Clauses

  1. grammar.type.row.plus-keysA requirement row lists separate requirement keys joined by +, as in $ Db + Cache.
  2. grammar.type.row.singleA single key is written alone, as in $ Console.
  3. grammar.type.row.empty$() is the empty row.
  4. grammar.type.row.one-formEvery position writes a row the same way: a header, a bodyless trait method, a function type, a row type argument, and $.Context[...].
  5. grammar.type.row.one-form.examplesThe header fn load(id: UserId) -> User $ Db + Cache: and the type fn(UserId) -> User $ Db + Cache write one row.
  6. grammar.type.row.no-mut-keyA requirement key has no mut prefix, so $ R + mut Logger is an error. Error: syntax-error.
  7. grammar.type.row.no-parenthesesParentheses never surround a nonempty row, so the former $(A + B) is an error. Error: syntax-error.
  8. grammar.type.row.in-type.commaInside a type, a comma after a key ends the row, so fn f(cb: fn() -> i32 $ A, B) -> i32: is an error. Error: syntax-error.
  9. grammar.type.row.key-bindingA requirement key may end its bracketed arguments with associated type bindings, as a bound does: $ Store[Item = User].
  10. grammar.type.row.key-binding.everywhereEvery place that names a key takes the same form, including $.use, $.with, $.context, and $.Context[...]. In a provider entry the binding's = sits inside the brackets, as in $.with(Store[Item = User]=store).
Error example
trait Clock

trait Logger

fn grouped() -> void $(Clock + Logger): pass  # error: syntax-error

fn run(callback: fn() -> void $ Clock, Logger) -> void: pass  # error: syntax-error

#Row Operators

  1. grammar.type.row.plus-only+ is the only row operator: it joins keys into one row.
  2. grammar.type.row.plus-rows-only+ has no bound meaning; several bounds on one type are joined with &, as in T < A & B.
  3. grammar.type.row.no-subtractionA - between requirement keys, as in the former $ R - K, is an error. Error: syntax-error.
  4. grammar.type.row.extensionRemoving a key from a callback row is written by extension instead, as Requirement Polymorphism specifies.
Error example
trait Logger

fn drop_logger[R](callback: fn() -> void $ R) -> void $ R - Logger: callback()  # error: syntax-error

#Row Type Arguments

  1. grammar.type.row-argumentFor a row-kinded generic parameter, a type argument may be a row after $, as in Fn[(), void, $ Logger + Clock], or $() for the empty row.
  2. grammar.type.row-argument.dollarA row argument always starts with $, even for one key or a row alias, as in Fn[(), void, $ Db].
  3. grammar.type.row-argument.bareA bare name for a row-kinded parameter parses as a type, which req.row.slot.bare rejects.
  4. grammar.type.row-argument.kindUsing a row argument for a type-kinded parameter, or a type for a row-kinded one, is an error. Error: generic-kind-mismatch.
Error example
struct Box[T]:
    value: T

fn invalid(value: Box[$()]) -> void: pass  # error: generic-kind-mismatch

#Modifiers, Optionality, And Grouping

  1. grammar.type.mutmut is a type modifier.
  2. grammar.type.mut.suspend-onlySemantic rules accept the modifier only in mut Suspend[T] and reject it on every other type.
  3. grammar.type.mut.no-functionmut never directly precedes fn: mut fn() -> i32 as a type, or mut fn() -> i32: as a closure header, is an error. Error: syntax-error.
  4. grammar.type.optionalOptionality applies to the complete reference access type and may be nested.
  5. grammar.type.optional.functionIn fn() -> T?, ? belongs to the innermost result type; an optional function type must be grouped, as in (fn() -> T)?.
  6. grammar.type.groupParentheses group types; unlike a one-element tuple type, grouping has no trailing comma.
  7. grammar.type.dyn.reference-valuesA dyn_type writes dyn and a trait name with its arguments and bindings, as in the referent of &dyn Supplier[Item = i32]. A bare descriptor is rejected as a value type.
  8. grammar.type.dyn.mutA mut modifier precedes dyn, so dyn mut Counter is an error. Error: syntax-error.
  9. grammar.type.dyn.optional.reference-values? applies to the whole trait value reference: *dyn Display? is an optional reference.
  10. grammar.type.row-ownerInside a type, a requirement clause following nested function types likewise belongs to the innermost ungrouped function type. That holds in a parameter type, a field type, and a type argument.
  11. grammar.type.row-owner.groupedParentheses select an outer owner.
  12. grammar.type.ref&T and &mut T write local reference types. Reference Types states their syntax rules.
  13. grammar.type.counted-ref*T and *mut T write counted reference types, with the same syntax rules. A * in a type is never multiplication or a dereference.
fn readers() -> (fn() -> i32)?:
    .None   # the group makes the function itself optional

#Tuple Rest Types

A rest_type is a type followed by .... It may end a tuple type or a function type's parameter list:

fn total(pair: (i32, List[i32]...), callback: fn(i32, List[i32]...) -> i32) -> i32:
    callback(pair...)

fn only(values: (List[i32]...,)) -> usize: values._0.len()
  1. grammar.type.restA rest element may be the last element of a tuple type or of a function type's parameter list. Alone in a tuple type, it keeps the one-element trailing comma, as in (List[i32]...,).
  2. grammar.type.rest.elsewhere... anywhere else in a type is an error. This covers values: i32..., List[i32...], (List[i32]...), and a rest element before another element. Error: syntax-error.
Error example
fn early(values: (List[i32]..., i32)) -> i32: 0  # error: syntax-error
fn sum(values: i32...) -> i32: 0                 # error: syntax-error

#Header Requirement Clauses

A header owns the requirement clause at its end, so a function-typed result with its own row is parenthesized:

fn make() -> fn() -> i32 $ Console + Log:            # make requires both keys
    _ := $.use(Console, Log)
    fn() -> i32: 1

fn wrap() -> (fn() -> i32 $ Log + Trace) $ Console:  # the result requires both
    _ := $.use(Console)
    fn() -> i32 $ Log + Trace:
        _ := $.use(Log, Trace)
        2
  1. grammar.type.header.ownerA declaration or closure header owns the requirement clause directly before its :.
  2. grammar.type.header.trait-methodA bodyless trait method owns the clause directly before its line end.
  3. grammar.type.header.resultIts result is a result_type, whose function types, however nested, carry no requirement clause, so the clause cannot attach to the result.
  4. grammar.type.header.parenthesized-resultA function-typed result with its own row is parenthesized.
  5. grammar.type.header.appliesThe rule applies to named functions, methods, trait methods, and closures: in fn() -> fn() -> i32 $ Console:, the closure requires Console.
  6. grammar.type.header.two-clausesWriting fn() -> i32 $ Log $ Console as a declaration result is an error. Error: syntax-error.
Error example
trait Log

fn make() -> fn() -> i32 $ Log $ Console:  # error: syntax-error
    fn() -> i32 $ Log: 1

See also: Requirements and Suspension.

#Use Declarations

A use declaration imports a path or a group of names:

use_decl = "use", use_path, [ "as", identifier ], NEWLINE
         | [ "pub" ], "use", use_path, ".", use_group, NEWLINE
         ;

use_path = use_root, { ".", identifier } ;
use_root = "pkg"
         | "std"
         | "dep", ".", identifier
         | "self"
         | "super", { ".", "super" }
         ;

use_group = "{", use_item, { ",", use_item }, [ "," ], "}" ;
use_item = identifier, [ "as", identifier ] ;
  1. grammar.use.no-import-exportimport and export are not declaration keywords.
  2. grammar.use.contextual-wordspkg, std, dep, and super are contextual use-root words, and as is contextual before an alias.
  3. grammar.use.needs-rootuse begins a use declaration only when a use root follows it.
  4. grammar.use.self-rootThe reserved word self also acts as a relative use root.

See also: Keywords And Reserved Words.

#Expressions

The expression grammar is ordered from lowest to highest precedence.

expression = binding_expression ;

binding_expression = identifier, ":=", binding_expression
                   | conditional_expression
                   ;

conditional_expression = if_expression
                       | for_expression
                       | while_expression
                       | match_expression
                       | closure_expression
                       | context_scope
                       | range_expression
                       ;

suite_expression = statement_if_expression
                 | statement_for_expression
                 | statement_while_expression
                 | statement_match_expression
                 | closure_expression
                 | context_scope
                 ;

closed_expression = identifier, ":=", closed_expression
                  | range_expression
                  ;

inline_expression = identifier, ":=", inline_expression
                  | inline_suite_expression
                  | range_expression
                  ;

continued_expression = identifier, ":=", continued_expression
                     | continued_conditional_expression
                     ;

continued_conditional_expression = indented_suite_expression
                                 | range_expression
                                 ;

indented_suite_expression = indented_if_expression
                          | indented_for_expression
                          | indented_while_expression
                          | match_expression
                          | "$", ".", "with", "(", context_entries, ")",
                            ":", indented_suite_body
                          ;

inline_suite_expression = inline_for_expression
                        | inline_while_expression
                        | inline_closure_expression
                        | inline_context_scope
                        ;

range_expression = logical_or_expression,
                   [ "..", [ logical_or_expression ]
                   | "..=", logical_or_expression ]
                 | "..", [ logical_or_expression ]
                 | "..=", logical_or_expression
                 ;

logical_or_expression = logical_and_expression,
                        { "||", logical_and_expression } ;
logical_and_expression = comparison_expression,
                         { "&&", comparison_expression } ;

comparison_expression = pipe_expression,
                        [ comparison_operator, pipe_expression ] ;
comparison_operator = "==" | "!=" | "<" | "<=" | ">" | ">=" | "is" ;

pipe_expression = bitwise_or_expression,
                  { "|>", bitwise_or_expression } ;

bitwise_or_expression = bitwise_xor_expression,
                        { "|", bitwise_xor_expression } ;
bitwise_xor_expression = bitwise_and_expression,
                         { "^", bitwise_and_expression } ;
bitwise_and_expression = shift_expression,
                         { "&", shift_expression } ;
shift_expression = additive_expression,
                   { ( "<<" | ">>" ), additive_expression } ;
additive_expression = multiplicative_expression,
                      { ( "+" | "-" ), multiplicative_expression } ;
multiplicative_expression = unary_expression,
                            { ( "*" | "/" | "%" ), unary_expression } ;

unary_expression = prefix_expression
                 | power_expression
                 ;
prefix_expression = ( "+" | "-" | "~" | "!" ), unary_expression
                  | "&", [ "mut" ], unary_expression
                  ;
power_expression = ( postfix_expression | dereference_expression ),
                   [ "**", unary_expression ] ;
dereference_expression = "*", ( dereference_expression
                              | postfix_expression
                              | prefix_expression ) ;

postfix_expression = primary_expression, { postfix_suffix } ;
postfix_suffix = ".", identifier, [ "::", function_type_arguments ]
               | "[", expression, "]"
               | argument_clause
               | suspension_call_suffix
               | "?"
               ;
suspension_call_suffix = "!", [ "::", function_type_arguments ],
                         argument_clause ;

#Precedence And Associativity

  1. grammar.expr.binding:= is right-associative and has the lowest precedence.
  2. grammar.expr.no-comparison-chainComparisons do not chain, so a < b < c is an error. Error: comparison-chaining.
  3. grammar.expr.powerExponentiation is right-associative.
  4. grammar.expr.power.unaryThe right operand of ** may therefore begin with a unary operator.
  5. grammar.expr.pipe|> is left-associative and binds more tightly than comparison and more loosely than |.
  6. grammar.expr.pipe.stepEach pipe step is a bitwise_or_expression, so a step with a comparison, &&, ||, or a control-flow expression needs parentheses.
  7. grammar.expr.prefix-operandA & or &mut at the start of an operand is a prefix operator of unary_expression, and a * there starts a dereference_expression, which ** takes whole as its base. Reference Expressions states their rules.
Error example
inside := 0 < value < 10  # error: comparison-chaining

#Range Syntax

A range expression joins two bounds with .. or ..=. A .. may leave out either bound or both, and a ..= may leave out its start:

fn parts(text: string, n: i32) -> (string, string, string, string, string, i32):
    let total = 0
    for i in 0..n + 1:
        total = total + i
    (text[1..], text[..2], text[0..=1], text[..=1], text[..], total)
  1. grammar.expr.range.precedence.. and ..= bind more loosely than || and more tightly than control-flow expressions and :=, as in Rust. So 0..n + 1 is 0..(n + 1).
  2. grammar.expr.range.non-assocA range expression is non-associative: a range bound is never itself an unparenthesized range, so a..b..c is an error. Error: syntax-error.
  3. grammar.expr.range.open-endAfter .., an end bound is present exactly when the next token can begin an operand. So text[1..], for i in 0..:, and f(1.., x) have no end bound.
  4. grammar.expr.range.open-startA .. at the start of an operand begins a range with no start bound, as in text[..2].
  5. grammar.expr.range.fullA .. with neither bound, as in text[..], is a range expression, the full range.
  6. grammar.expr.range.inclusive-toA ..= at the start of an operand begins a range with no start bound, as in text[..=2].
  7. grammar.expr.range.inclusive-needs-endA ..= must have an end bound. A ..= with no end bound, as in 1..= or a bare ..=, is an error. Error: syntax-error.
  8. grammar.expr.range.pipe-stepA pipe step is a bitwise_or_expression, so a range in a pipe step needs parentheses.
Error example
fn invalid() -> void:
    a := 0..1..2       # error: syntax-error
    b := 1..=          # error: syntax-error

Why. A range sits below ||, as in Rust, so arithmetic and comparisons in a bound need no parentheses. Leaving out a bound always means "from the start" or "to the end". So text[1..] reads the same in any position, and text[..] is the whole string.

#Multi-Name Bindings

A binding expression binds one name, so a list or a tuple may hold one as its last element:

fn pair() -> (i32, i32): (1, 2)

fn items(a: (i32, i32)) -> List[(i32, i32)]:
    [a, b := pair()]
  1. grammar.expr.multi-binding.elementA list item or a tuple element may be a binding expression. So [a, b := value] and (a, b := value) each hold a and the binding b := value, as [a, (b := value)] does.
  2. grammar.expr.multi-binding.let-onlyA name list before := inside an expression, as in ((a, b) := value) or [(a, b) := value], is an error. Its fix-it writes let (a, b) = value as a statement before the expression. Error: syntax-error.
Error example
fn pair() -> (i32, i32): (1, 2)

wrapped := [(a, b) := pair()]  # error: syntax-error
whole := ((low, high) := pair())  # error: syntax-error

Why. Destructuring is a let statement, on its own line. Python's := also takes one name, and Go's := is a statement. The single-name binding, as in if (n := f()) > 0:, stays an expression.

#Bang And Dot Tokens

  1. grammar.expr.bang-suffix!( or !::[ after a completed operand begins a suspension call suffix at ordinary call precedence.
  2. grammar.expr.prefix-notA ! at the start of an operand is the prefix logical-not operator of unary_expression, so !fetch!(id) negates a suspending call's result.
  3. grammar.expr.not-equal!= is a single token by longest match: f!=g is the comparison f != g.
  4. grammar.expr.member-identifierA member suffix takes an identifier after ., so the tuple selection t._0._1 is two member suffixes.
  5. grammar.expr.no-numeric-memberAn integer or floating-point literal after . forms no suffix, so t.0 and t.0.1 are errors. Error: syntax-error.
Error example
first := pair.0       # error: syntax-error
second := nested.0.1  # error: syntax-error

#Type Arguments In Expressions

In an expression, an explicit type-argument list follows ::, the type-argument marker. A type keeps its plain brackets:

struct Box[T]:
    value: T

fn first[T](items: List[T]) -> T: items[0]

fn demo(names: List[string]) -> Box[string]:
    head := first::[string](names)
    Box::[string] { value: head }
  1. grammar.expr.type-arguments.markerIn an expression, an explicit type-argument list follows ::: a generic function takes first::[string](names), a method parser.parse::[User](text), and a reference Json::decode::[User].
  2. grammar.expr.type-arguments.type-nameA type name in an expression also takes its type arguments after ::. This holds in a data expression such as Box::[i32] { value: 1 } and in a qualified call such as Add::[i32]::add(price, 5).
  3. grammar.expr.type-arguments.in-typesIn a type, type arguments follow the name directly, as in List[i32], with no ::.
  4. grammar.expr.index-only[ directly after a completed operand always begins an indexing suffix, never type arguments, so handlers[i](event) indexes handlers and calls the element.
  5. grammar.expr.type-arguments.unmarkedA data expression or qualified call whose type name takes brackets without ::, as in Box[i32] { value: 1 } or Add[i32]::add(price, 5), is an error. Error: syntax-error.
Error example
struct Box[T]:
    value: T

fn boxed() -> Box[i32]:
    Box[i32] { value: 1 }  # error: syntax-error

Why. Each form has one reading that a parser can see. Brackets after an expression always index, and ::[ always gives type arguments. So neither a reader nor a tool needs name resolution to tell them apart. Rust marks expression type arguments the same way, as in parse::<i32>().

#Method Type Arguments

  1. grammar.expr.method-type-arguments.validAn explicit method type-argument list is valid only when the selected member is generic and the expression proceeds to an ordinary call.
  2. grammar.expr.method-type-arguments.bangA bang call writes the ! on the name and the list after it, as the declaration fn fetch![T](...) does. The calls are fetch!::[User](key), parser.load!::[User](text), and Store::load!::[User](key).
Error example
struct Identity: pass

impl Identity:
    fn echo![Value](value: Value) -> Value:
        value

fn run!() -> i32:
    Identity::echo::[i32]!(42)  # error: syntax-error

#Primary Expressions

primary_expression = literal
                   | "self"
                   | string_expression
                   | generic_function_reference
                   | qualified_name
                   | contextual_variant_expression
                   | trait_qualified_call
                   | method_reference
                   | context_use
                   | context_create
                   | tuple_or_group_expression
                   | list_expression
                   | map_expression
                   | data_expression
                   | pipe_placeholder
                   | "pass"
                   ;

pipe_placeholder = "_" ;

generic_function_reference = qualified_name, "::", function_type_arguments ;
function_type_arguments = "[", function_type_argument,
                          { ",", function_type_argument }, [ "," ], "]" ;
function_type_argument = type_argument | "_" ;
contextual_variant_expression = ".", identifier ;
trait_qualified_call = expression_trait_type, "::", identifier,
                       ( [ "::", function_type_arguments ], argument_clause
                       | suspension_call_suffix ) ;

method_reference = expression_trait_type, "::", identifier,
                   [ "::", function_type_arguments ] ;

expression_trait_type = qualified_name, [ "::", type_arguments ] ;
expression_named_type = qualified_name, [ "::", bound_type_arguments ] ;

literal = boolean_literal
        | suffixed_literal
        | float_literal
        | integer_literal
        | char_literal
        ;

string_expression = interpreted_string_expression
                  | interpreted_multiline_string_expression
                  | prefixed_string_expression
                  ;

interpreted_string_expression = '"', { string_segment }, '"' ;
interpreted_multiline_string_expression = '"""',
                                          { multiline_string_segment },
                                          '"""' ;
string_segment = string_text
               | escape_sequence
               | "$", identifier
               | "$", "self"
               | "${", expression, "}"
               ;
multiline_string_segment = multiline_string_text
                         | escape_sequence
                         | "$", identifier
                         | "$", "self"
                         | "${", expression, "}"
                         ;
prefixed_string_expression = string_prefix, '"',
                             { prefixed_string_segment }, '"'
                           | string_prefix, '"""',
                             { prefixed_multiline_segment }, '"""' ;
prefixed_string_segment = prefixed_string_character
                        | "$", identifier
                        | "$", "self"
                        | "${", expression, "}"
                        ;
prefixed_multiline_segment = prefixed_multiline_character
                           | "$", identifier
                           | "$", "self"
                           | "${", expression, "}"
                           ;

tuple_or_group_expression = "(", ")"
                          | "(", expression, ")"
                          | "(", tuple_element, ",", [ tuple_elements ], ")"
                          | "(", tuple_spread, ",", ")"
                          ;
tuple_elements = tuple_element, { ",", tuple_element },
                 [ ",", tuple_spread ], [ "," ]
               | tuple_spread, [ "," ]
               ;
tuple_element = expression ;
tuple_spread = continued_expression, "..." ;

list_expression = "[", [ list_items ], "]"
                | list_comprehension
                ;
list_items = list_item, { ",", list_item }, [ "," ] ;
list_item = expression
          | continued_expression, "..."
          ;

map_expression = "{", [ map_items ], "}"
               | map_comprehension
               ;
map_items = map_item, { ",", map_item }, [ "," ] ;
map_item = continued_expression, ":", expression ;

data_expression = expression_named_type, "{", [ data_items ], "}" ;
data_items = [ "...", expression, "," ],
             data_field_item, { ",", data_field_item }, [ "," ]
             | "...", expression, [ "," ]
             ;
data_field_item = identifier, ":", expression
                | identifier
                ;

#Pipe Placeholder

  1. grammar.primary.pipe-placeholder_ in expression position is the pipe placeholder.
  2. grammar.primary.pipe-placeholder.semanticThe grammar accepts it as any primary expression; Pipe Expressions limits it to pipe steps.
fn wrap(text: string, mark: string) -> string:
    "$mark$text$mark"

fn demo(text: string) -> string:
    text |> wrap(_, "*")   # _ marks where the piped value goes

#Copies In Data Expressions

  1. grammar.primary.update-spreadA prefix ... in a data expression begins a copy-update spread, which copies the named members of its operand.
struct Point:
    x: i32
    y: i32

fn move_right(p: Point) -> Point:
    Point { ...p, x: p.x + 1 }

See also: Data Embedding.

#Field Shorthand In Data Expressions

  1. grammar.primary.field-shorthandA data_field_item that is a bare identifier is field shorthand: x means x: x, as in Point { x, y }.
  2. grammar.primary.field-shorthand.mixedShorthand and labeled items may be mixed in one data expression, as in Point { x, y: 0 }, and may follow a copy-update spread.
struct Point:
    x: i32
    y: i32

fn build(x: i32, y: i32) -> Point:
    Point { x, y }

Why. It mirrors the bare field of a data pattern, so building and destructuring a value read alike.

See also: Data Literals.

#Reflection

  1. grammar.primary.no-reflection-syntaxDeclaration reflection has no dedicated syntax.
@derive(Debug)
struct Point:
    x: i32

fn label(p: Point) -> string:
    debug(p)   # "Point { x: 1 }": the name reaches text through a derivation

#Suffixed Literals

  1. grammar.primary.literal-callA suffixed_literal token and a prefixed_string_expression are primary expressions, each standing for a call as Literal Suffixes specifies.
  2. grammar.pattern.no-literal-callliteral_pattern admits neither a suffixed literal nor a prefixed string, so 5s or r"a" in a pattern is an error. Error: syntax-error.
Error example
fn describe(count: i32) -> string:
    match count:
        5px => "five"  # error: syntax-error
        _ => "other"

See also: Literal Suffixes.

#Prefixed Strings

The rules of Suffixed Literals cover a prefixed string too:

Error example
fn describe(text: string) -> string:
    match text:
        r"a" => "letter"  # error: syntax-error
        _ => "other"
  1. grammar.primary.prefix-after-dotA prefixed string directly after . is an error. This holds whether a module path stands before the dot, as in marks.tag"item", or a value, as in value.tag"item". Error: qualified-string-prefix.
Error example
use pkg.marks

fn label() -> string:
    marks.tag"item"  # error: qualified-string-prefix

fn quote(value: string) -> string:
    value.tag"item"  # error: qualified-string-prefix

Note. The qualified-string-prefix message says that only a bare name in scope can be a string prefix, and suggests importing it. For text.r"\d+" it reads: "only a bare name in scope can be a string prefix; write use std.text.r, then r"..."".

Why. A bare prefix keeps one rule for every name a literal calls, and use already names where it comes from. A path before a prefix would need its own resolution, beside module paths, methods, and fields.

See also: Prefixed Strings.

#Forms Resolved By Name

  1. grammar.primary.resolutionName resolution distinguishes a data expression from a map expression and an enum variant selection from ordinary field access.
  2. grammar.primary.function-type-argumentEach function type argument is a type or the inference placeholder _.
  3. grammar.primary.placeholderThe placeholder is not part of ordinary type_arguments and therefore cannot occur in a type such as List[_].
  4. grammar.primary.qualified-type-argumentsIn a qualified call such as Type::name::[T](...), Trait::name::[T](...), or Type::name!::[T](...), type arguments of the qualifying type or trait stay before the member's ::. One example is Add::[Money]::add.
  5. grammar.primary.member-type-argumentsMethod-level type arguments follow the member name, as in the dot call parser.parse::[User](text).
  6. grammar.primary.member-type-arguments.rulesThat list is valid only when the selected member is generic, and it follows the explicit-list rules of Generic Functions.
struct Point:
    x: i32
    y: i32

fn demo() -> i32:
    p := Point { x: 1, y: 2 }   # a data expression: Point names a struct type
    m := {"x": +1}              # a map expression
    p.x + m.get("x").unwrap_or(+0)

#Prefix And Suffix ...

  1. grammar.primary.list-spreadA list element ending in ... is a spread that expands a list's elements in place.
  2. grammar.primary.ellipsis-positionsThe two positions of ... never overlap.
  3. grammar.primary.prefix-copyA prefix ... always copies: its one position is the copy-update spread of a data expression.
  4. grammar.primary.suffix-spreadsA suffix ... always spreads: it expands the elements or entries of its operand in arguments, list elements, and provider-context entries, as in $.with(ctx...).
  5. grammar.primary.tuple-spreadThe last element of a tuple expression may also be a suffix spread, as in (1, 2, [3, 4]...). A spread alone keeps the one-element trailing comma, (xs...,), so (xs...) and a spread before another element are errors. Error: syntax-error.
  6. grammar.primary.prefix-elsewhereA prefix ... anywhere else, including before a provider-context entry, is an error. Error: syntax-error.
Error example
trait Tag:
    fn name(self) -> string

fn widen(base: $.Context[$ Tag]) -> $.Context[$ Tag]:
    $.context(...base)  # error: syntax-error

See also: List And Map Expressions.

#Member References

  1. grammar.primary.method-referenceA :: member without an argument clause, such as User::domain, Json::decode::[User], or user::domain, is a method_reference.
  2. grammar.primary.method-reference.meaningMethod References gives its meaning; with an argument clause, the same form is a call.
  3. grammar.primary.method-reference.no-bangA reference with type arguments directly followed by !( is not a bang call of that reference. So Identity::echo::[i32]!(42) is an error, and the call is Identity::echo!::[i32](42). Error: syntax-error.
struct User:
    domain: string

impl User:
    fn label(self) -> string: self.domain

fn demo(user: User) -> string:
    f := user::label
    f()

#Calls And Arguments

argument_clause = "(", [ argument_list ], ")" ;
argument_list = positional_argument, { ",", positional_argument },
                [ ",", named_argument, { ",", named_argument } ], [ "," ]
              | named_argument, { ",", named_argument }, [ "," ]
              ;

positional_argument = expression
                    | continued_expression, "..."
                    ;
named_argument = identifier, "=", expression ;
  1. grammar.call.positional-firstPositional arguments, including positional spreads, must precede named arguments. Error: argument-order.
Error example
resize(width=640, 480)  # error: argument-order

See also: Positional Spreads, for what a spread fills.

#Trailing Blocks

A call whose final parameter is a zero-argument function may use an indented trailing block. The call may stand as a complete statement, or as the complete right-hand side of :=, let ... =, =, _ :=, return, or break:

trailing_block_call = postfix_expression, ":", indented_suite_body ;
indented_suite_body = NEWLINE, INDENT, statement, { statement }, DEDENT ;
  1. grammar.call.trailing-blockA call whose final parameter is a zero-argument function may use an indented trailing block. The call may stand as a complete statement, or as the complete right-hand side of :=, let ... =, =, _ :=, return, or break.
  2. grammar.call.trailing-block.no-argumentsWhen there are no ordinary arguments, the call omits (), as in transaction:.
  3. grammar.call.trailing-block.acceptedThis production is accepted only at delimiter depth zero when the call is the complete statement or one of those complete right-hand sides.
  4. grammar.call.trailing-block.eligibleIt is also accepted only when name and type resolution identify a callable with an eligible final parameter. A trailing block on any other call is an error. Error: trailing-block-ineligible.
  5. grammar.call.trailing-block.next-lineIts body must begin on the next logical line. A body on the same line is an error. Error: syntax-error.
  6. grammar.call.trailing-block.not-headerIt is not accepted in an if, while, for, or match header or inside brackets. A trailing block there is an error. Error: trailing-block-position.
Error example
fn run(callback: fn() -> i32) -> i32: callback()

values := [run:  # error: trailing-block-position
    1]

The next-line rule also rules out a trailing block call as a same-line suite body. Written on the call's line, the body breaks the next-line rule. Written on the next line, it is an indented suite, which a same-line suite never holds (grammar.inline.no-comma):

Error example
fn trailing(body: fn() -> void) -> void: body()

fn close_all(close: bool) -> void:
    if close: trailing(): pass  # error: syntax-error
Error example
fn trailing(body: fn() -> void) -> void: body()

fn close_all(close: bool) -> void:
    if close: trailing:  # error: syntax-error
        pass

The valid forms indent the if body, or pass the closure as an argument:

fn trailing(body: fn() -> void) -> void: body()

fn close_all(close: bool) -> void:
    if close:
        trailing:
            pass
    if close: trailing(fn(): pass)

#Closures

closure_expression = closure_header, suite_body ;
inline_closure_expression = closure_header, inline_suite_body ;
closure_header = "fn", [ "!" ], closure_parameter_clause,
                 [ "->", result_type ], [ requirement_clause ], ":" ;

closure_parameter_clause = "(", [ closure_parameter_list ], ")" ;
closure_parameter_list = closure_parameter,
                         { ",", closure_parameter }, [ "," ] ;
closure_parameter = [ "mut" ], identifier, [ ":", type ] ;
  1. grammar.closure.parameter-typesOmitted closure parameter types require an expected function type.
  2. grammar.closure.annotationsA standalone or otherwise ambiguous closure must provide enough annotations to determine its complete function type.
  3. grammar.closure.param-mutA closure parameter may begin with mut, as a function parameter may.

See also: Closure Annotations, which says when a closure may infer its result type.

#Control-Flow Expressions

if_expression = "if", continued_expression, ":", suite_body,
                { "else", "if", continued_expression, ":", suite_body },
                [ "else", ":", suite_body ]
                ;

for_expression = "for", pattern, "in", continued_expression, ":",
                 suite_body, [ "else", ":", suite_body ]
                 ;

while_expression = "while", continued_expression, ":", suite_body,
                   [ "else", ":", suite_body ]
                   ;

indented_if_expression = "if", continued_expression, ":", indented_suite_body
                       | "if", continued_expression, ":", suite_body,
                         { "else", "if", continued_expression, ":",
                           suite_body },
                         "else", [ "if", continued_expression ], ":",
                         indented_suite_body
                       ;

indented_for_expression = "for", pattern, "in", continued_expression,
                          ":", ( indented_suite_body
                               | suite_body, "else", ":",
                                 indented_suite_body )
                          ;

indented_while_expression = "while", continued_expression, ":",
                            ( indented_suite_body
                            | suite_body, "else", ":", indented_suite_body )
                            ;

inline_for_expression = "for", pattern, "in", closed_expression, ":",
                        inline_suite_body, [ "else", ":", inline_suite_body ]
                        ;

inline_while_expression = "while", closed_expression, ":", inline_suite_body,
                          [ "else", ":", inline_suite_body ]
                          ;

match_expression = "match", continued_expression, ":", NEWLINE, INDENT,
                   match_arm, { match_arm }, DEDENT
                   ;

statement_if_expression = "if", closed_expression, ":", suite_body,
                          { "else", "if", closed_expression, ":",
                            suite_body },
                          [ "else", ":", suite_body ]
                          ;

statement_for_expression = "for", pattern, "in", closed_expression,
                           ":", suite_body, [ "else", ":", suite_body ]
                           ;

statement_while_expression = "while", closed_expression, ":", suite_body,
                             [ "else", ":", suite_body ]
                             ;

statement_match_expression = "match", closed_expression, ":", NEWLINE,
                             INDENT, match_arm, { match_arm }, DEDENT
                             ;

match_arm = pattern, [ "if", closed_expression ], "=>", arm_body ;
arm_body = suite_expression
         | simple_statement, NEWLINE
         | NEWLINE, INDENT, statement, { statement }, DEDENT
         ;
  1. grammar.flow.for-in-bracketsA for loop is an expression, so it may also appear inside brackets, as in [for x in xs: body] or (for (k, v) in m: body).
  2. grammar.flow.if-elseAn if used where a value is required must have an else; statement-position if may omit it.
  3. grammar.flow.semanticThe if and loop rules above are semantic rules, not separate grammar productions.
  4. grammar.flow.for-patternA for loop or a comprehension for clause takes a pattern before in, as let does. So for (key, value) in entries uses a tuple pattern, and for Point { x, y } in points a data pattern.
  5. grammar.flow.for-list.bareA bare list, as in for key, value in entries, is an error whose fix-it adds the parentheses. Error: syntax-error.
Error example
fn names(scores: Map[string, i32]) -> List[string]:
    for (name, score) in scores:  # valid
        pass
    for name, score in scores:  # error: syntax-error
        pass
    [for (name, score) in scores => name]

Why. A for loop and a let take the same patterns, so one shape reads the same in both. The commas of a pattern stand inside brackets, so a same-line suite may hold the loop.

See also: Let Statements, For Loops for which patterns a loop accepts, and Loop Values for a loop's type.

#Patterns

pattern = "_"
        | literal_pattern
        | binding_pattern_atom
        | variant_pattern
        | data_pattern
        | tuple_pattern
        | unit_pattern
        | range_pattern
        ;

literal_pattern = boolean_literal
                | [ "-" ], ( integer_literal | float_literal )
                | string_literal
                | char_literal
                ;

binding_pattern_atom = [ "mut" ], identifier ;

variant_pattern = qualified_variant_name, [ pattern_argument_clause ]
                | ".", identifier, [ pattern_argument_clause ]
                | identifier, pattern_argument_clause
                ;
qualified_variant_name = identifier, ".", identifier,
                         { ".", identifier } ;
pattern_argument_clause = "(", [ pattern_argument_list ], ")" ;
pattern_argument_list = positional_pattern,
                        { ",", positional_pattern },
                        [ ",", named_pattern, { ",", named_pattern } ], [ "," ]
                      | named_pattern, { ",", named_pattern }, [ "," ]
                      ;
positional_pattern = pattern ;
named_pattern = identifier, "=", pattern ;

data_pattern = qualified_name, "{", [ data_pattern_fields ], "}" ;
data_pattern_fields = data_pattern_field,
                      { ",", data_pattern_field }, [ "," ] ;
data_pattern_field = [ "mut" ], identifier
                   | identifier, ":", pattern
                   ;

tuple_pattern = "(", pattern, ",", [ tuple_pattern_elements ], ")"
              | "(", spread_pattern, ",", ")"
              ;
tuple_pattern_elements = pattern, { ",", pattern },
                         [ ",", spread_pattern ], [ "," ]
                       | spread_pattern, [ "," ]
                       ;
spread_pattern = ( "_" | binding_pattern_atom ), "..." ;

unit_pattern = "(", ")" ;

range_pattern = range_pattern_bound, ( "..=" | ".." ), range_pattern_bound
              | range_pattern_bound, ".."
              | ( "..=" | ".." ), range_pattern_bound
              ;
range_pattern_bound = [ "-" ], integer_literal ;
  1. grammar.pattern.variantVariant patterns may use a qualified enum variant name or .Variant when the matched value's type supplies one enum.
  2. grammar.pattern.bare-variantThe unqualified identifier, pattern_argument_clause form parses so that a checker can report it as an error. Error: bare-variant-pattern.
  3. grammar.pattern.positional-namesPositional binding names need not match payload field names.
  4. grammar.pattern.namedIn a payload list, only field=pattern is a named pattern.
  5. grammar.pattern.named-lastNo positional pattern may follow a named pattern. Error: pattern-order.
  6. grammar.pattern.mut-let-onlymut may precede a binding name only in a let pattern. In a match arm's pattern, as in .Some(mut user) => ..., it is an error. Error: syntax-error.
  7. grammar.pattern.data-fieldIn a data pattern, bare field binds that field's value to a new name of the same spelling.
  8. grammar.pattern.data-field.nestedfield: pattern matches the field against a nested pattern, and field: name binds it to name.
  9. grammar.pattern.data-unlistedUnlisted fields are ignored.
  10. grammar.pattern.tuple-spreadThe last element of a tuple pattern may be a spread pattern, a name or _ followed by ..., as in (a, b, xs...). Alone, it keeps the one-element trailing comma, (xs...,), so (xs...) and a spread pattern before another element are errors. Error: syntax-error.
  11. grammar.pattern.unit() is the unit pattern. A tuple pattern needs a comma, so () is never a tuple pattern.
  12. grammar.pattern.rangeA range pattern takes one of five forms: a..=b, a..b, a.., ..=b, or ..b.
  13. grammar.pattern.range.boundEach bound of a range pattern is an integer literal, which a - may precede. A bound of any other kind, such as 'a', 1.5, or a name, is an error. Error: syntax-error.
Error example
enum Pair:
    Values(left: i32, right: i32)

fn value_or_zero(value: i32?) -> i32:
    match value:
        Some(number) => number  # error: bare-variant-pattern
        .None => 0

fn invalid(value: Pair) -> i32:
    match value:
        Pair.Values(left=l, r) => l + r  # error: pattern-order

fn rebind(value: Pair) -> i32:
    match value:
        Pair.Values(mut l, r) => l + r  # error: syntax-error
fn bucket(n: i32) -> string:
    match n:
        ..=-1 => "negative"
        0 => "zero"
        1..10 => "small"
        10..=99 => "medium"
        100.. => "large"

fn sign(n: i32) -> string:
    match n:
        ..0 => "negative"
        _ => "other"

Why. The five forms are Rust's, as of Rust 1.80, so the same bounds read the same in both languages. Every range expression form but a bare .. is also a pattern.

See also: Match Expressions.

#Labels

  1. grammar.label.bracketLabels follow their brackets.
  2. grammar.label.bracesInside Type { ... }, in data expressions and data patterns alike, a field label is followed by :.
  3. grammar.label.parenthesesInside parentheses, in named arguments, variant payloads, and payload patterns, a label is followed by =.
  4. grammar.label.data-pattern-equalsA data pattern written with field = pattern is an error. Error: syntax-error.
Error example
struct Point:
    x: i32
    y: i32

fn first(p: Point) -> i32:
    match p:
        Point { x = 0, y } => y  # error: syntax-error
        Point { x, y: _ } => x

#Comprehensions

list_comprehension = "[", comprehension_clauses, "=>", expression, "]" ;

map_comprehension = "{", comprehension_clauses, "=>",
                    continued_expression, ":", expression, "}" ;

comprehension_clauses = comprehension_for,
                        { comprehension_for | comprehension_if } ;
comprehension_for = "for", pattern, "in", continued_expression ;
comprehension_if = "if", continued_expression ;
  1. grammar.comprehension.first-forThe first clause must be for.
  2. grammar.comprehension.orderLater for and if clauses execute from left to right.
  3. grammar.comprehension.no-letComprehensions do not have a let clause; := binding expressions may be used inside guards or result expressions.

#Requirements And Provider Contexts

context_use = "$", ".", "use", "(", requirement_key,
              { ",", requirement_key }, [ "," ], ")" ;

context_create = "$", ".", "context", "(", context_entries, ")" ;
context_type = "$", ".", "Context", "[", type_argument, "]" ;
context_scope = "$", ".", "with", "(", context_entries, ")",
                ":", suite_body ;
inline_context_scope = "$", ".", "with", "(", context_entries, ")",
                       ":", inline_suite_body ;

context_entries = context_entry, { ",", context_entry }, [ "," ] ;
context_entry = requirement_key, "=", expression
              | continued_expression, "..."
              ;
  1. grammar.row.setsRequirement rows denote unordered sets of keys after name resolution.
  2. grammar.row.parameter.markedA generic parameter declared $R is a row parameter, and a row writes it by name as a complete requirement key.
  3. grammar.row.parameter.extensionListing a row parameter beside other keys extends that row with them.

#Annotations

decorated_decl = decorator_line, { decorator_line },
                 ( [ "pub" ], ( data_decl | enum_decl | function_decl
                              | trait_decl | newtype_decl )
                 | impl_decl ) ;

decorator_line = "@", ( derive_decorator | closed_expression ), NEWLINE ;

newtype_decl = "type", identifier, [ type_params ], "(", type, ")", NEWLINE ;

derive_decorator = "derive", "(", qualified_name,
                   { ",", qualified_name }, [ "," ], ")" ;
  1. grammar.annot.item-targetsDecorator lines may precede a struct, enum, function, trait, newtype, or implementation declaration.
  2. grammar.annot.member-targetsDecorator lines may also precede a struct field, an enum variant, and a method of a trait or implementation. The data_member, enum_variant, trait_member, and impl_member productions show this.
  3. grammar.annot.alias-no-decoratorAny decorator before a transparent alias is an error. Error: syntax-error.
  4. grammar.annot.derive-traitsThe names in a derive_decorator are traits. Opting In defines which traits it accepts.

Member metadata written away from a declaration uses a derivation block with no trait, an impl_decl, as grammar.impl.by-no-trait states.

See also: Typed Derivation.