Grammar
The following is the non-normative EBNF grammar for Raven.
(* Raven language EBNF grammar
This grammar captures the structural form of Raven source code.
Contextual rules and parsing details are described in the language specification.
NOTE: Non-normative. Context-sensitive parsing and validation live outside this EBNF. *)
CompilationUnit ::= {ImportDirective} {AliasDirective} {TopLevelItem} EOF ;
TopLevelItem ::= Declaration | Statement ;
(* `#pragma` and the conditional `#if` / `#elif` / `#else` / `#endif`
forms are structured directive trivia handled by the lexer. `#[` starts an
attached macro. Freestanding macros use the `Name!` envelope. *)
ImportDirective ::= 'import' QualifiedName ('.' '*')? ; (* Namespace imports require '.*'; applying '.*' to a type imports its static members and nested types *)
AliasDirective ::= 'alias' Identifier '=' Type ; (* Target may be a fully qualified name, type expression, or predefined type: bool | byte | char | decimal | double | float | int | long | nint | nuint | object | sbyte | short | string | uint | ulong | unit | ushort | '()' *)
(* ---------- Modifiers ---------- *)
MemberModifiers ::= { MemberModifier } ;
MemberModifier ::= AccessModifier
| 'static' | 'abstract' | 'virtual' | 'override' | 'final' | 'sealed' | 'new'
| 'readonly' | 'required'
| 'async' | 'extern' | 'partial' ; (* Contextual restriction: 'partial' currently applies to methods, properties, and events *)
AccessModifier ::= 'public' | 'internal' | 'protected' | 'private' ;
AccessorModifier ::= AccessModifier | 'async' | 'ref' | 'out' | 'in' ;
TypeModifiers ::= { TypeModifier } ;
TypeModifier ::= AccessModifier | 'fileprivate' | 'abstract' | 'sealed' | 'open' | 'partial' | 'static' | 'ref' | 'readonly' ;
TopLevelFunctionModifiers ::= { TopLevelFunctionModifier } ;
TopLevelFunctionModifier ::= AccessModifier | 'fileprivate' | 'async' | 'extern' | 'unsafe' | 'static' ;
TopLevelConstModifiers ::= { TopLevelConstModifier } ;
TopLevelConstModifier ::= AccessModifier | 'fileprivate' | 'static' ;
(* Contextual restriction: top-level members accept public/internal/fileprivate
accessibility. `static` is parsed for recovery but is semantically invalid
because top-level members are already implicitly static. *)
(* Semantic note: a type declaration marked with [TopLevel] remains a normal
type declaration syntactically; the marker promotes accessible static
members through namespace lookup when top-level member imports are enabled. *)
(* ---------- Declarations ---------- *)
Declaration ::= NamespaceDeclaration
| TopLevelFunctionDeclaration
| MacroDeclaration
| TopLevelConstDeclaration
| ExtensionDeclaration
| EnumDeclaration
| UnionDeclaration
| ClassDeclaration
| StructDeclaration
| InterfaceDeclaration
| DelegateDeclaration ;
NamespaceDeclaration ::= 'namespace' Identifier '{' {ImportDirective} {AliasDirective} {TopLevelItem} '}' ;
TopLevelFunctionDeclaration
::= {AttributeList | MacroAttributeList}
TopLevelFunctionModifiers?
'func' Identifier TypeParameterList?
'(' ParameterList? ')'
ReturnTypeClause?
WhereClauseList?
( Block | '=>' Expression ) ;
MacroDeclaration ::= {AttributeList}
TopLevelFunctionModifiers?
'macro' Identifier TypeParameterList?
'(' MacroParameterList? ')'
ReturnTypeClause?
WhereClauseList?
{MacroCapabilityClause}
( Block | '=>' Expression ) ;
MacroCapabilityClause ::= MacroCapabilityKeyword 'by' Expression ;
MacroCapabilityKeyword ::= 'keywords' | 'tokens' | 'tokenKinds'
| 'highlighting' | 'fragments' | 'symbols'
| 'completion' | 'projection' ;
MacroParameterList ::= MacroParameter {',' MacroParameter} ;
MacroParameter ::= {AttributeList} 'on'? AccessModifier? ScopedModifier? RefKindModifier? ParamsModifier? ParameterBindingKeyword?
Identifier ':' Type VarParamsSuffix? ['=' Expression] ;
(* Exactly one `on` parameter selects attached application. It is supplied by
the compiler and must name a supported Raven.CodeAnalysis syntax type. *)
TopLevelConstDeclaration ::= {AttributeList | MacroAttributeList}
TopLevelConstModifiers?
'const' VariableDeclarators TypeTerminator? ;
ExtensionKeyword ::= 'extension'
(* The extension identifier is optional. When omitted, the compiler synthesizes an internal name (e.g., via mangling) and the declaration cannot be referenced by name. *)
(* Named `fileprivate` extensions also emit a mangled container metadata name so the generated type does not expose a stable cross-file/importable identity. *)
(* Unnamed extensions are intended for local/assembly-private augmentation; public APIs should use a stable explicit name. *)
ExtensionDeclaration ::= ExtensionKeyword Identifier? TypeParameterList?
'for' Type
WhereClauseList?
ExtensionBody ;
ExtensionBody ::= '{' {ExtensionMember} '}' ;
ExtensionMember ::= ExtensionMethodDeclaration
| ExtensionPropertyDeclaration
| OperatorDeclaration
| ConversionOperatorDeclaration ;
ExtensionMethodDeclaration
::= ExtensionMethodModifiers?
'func' Identifier TypeParameterList?
'(' ParameterList? ')'
ReturnTypeClause?
WhereClauseList?
( Block | '=>' Expression ) ;
ExtensionMethodModifiers ::= AccessModifier ;
ExtensionPropertyDeclaration
::= ExtensionPropertyModifiers?
PropertyBindingKeyword
Identifier ':' Type PropertyBody ;
ExtensionPropertyModifiers ::= AccessModifier ;
ReturnTypeClause ::= '->' Type ;
ParameterList ::= Parameter {',' Parameter} ;
Parameter ::= {AttributeList} AccessModifier? ScopedModifier? RefKindModifier? ParamsModifier? ParameterBindingKeyword?
Identifier ':' Type VarParamsSuffix? ['=' Expression] ;
ScopedModifier ::= 'scoped' ;
ParamsModifier ::= 'params' ;
VarParamsSuffix ::= '...' ; (* Convenience collector marker. `params` and `...` are mutually exclusive on the same parameter. *)
RefKindModifier ::= 'ref' | 'out' | 'in' ;
ParameterBindingKeyword ::= 'let' | 'val' | 'var' | 'const' ;
AttributeList ::= '[' Attribute {',' Attribute} ']' ;
Attribute ::= QualifiedName [AttributeArgumentList] ;
MacroAttributeList ::= '#[' MacroAttribute {',' MacroAttribute} ']' ;
MacroAttribute ::= Identifier [TypeArgumentList] [AttributeArgumentList] ;
EnumDeclaration ::= TypeModifiers?
'enum' Identifier EnumBaseList?
'{' EnumMembers? '}' ;
EnumBaseList ::= ':' Type ;
EnumMembers ::= EnumMember {',' EnumMember} [','] ;
EnumMember ::= Identifier ['=' Expression] ;
ClassDeclaration ::= TypeModifiers? 'class' Identifier TypeParameterList?
PrimaryConstructorAccessibility? PrimaryConstructor? BaseList?
WhereClauseList? PermitsClause?
ClassBody? ;
RecordDeclaration ::= TypeModifiers? 'record' ['class' | 'struct'] Identifier TypeParameterList?
PrimaryConstructorAccessibility? PrimaryConstructor? BaseList?
WhereClauseList? PermitsClause?
ClassBody? ;
PermitsClause ::= 'permits' Type {',' Type} ;
PrimaryConstructor ::= '(' ParameterList? ')' ;
PrimaryConstructorAccessibility ::= AccessModifier ;
StructDeclaration ::= TypeModifiers? 'struct' Identifier TypeParameterList?
PrimaryConstructorAccessibility? PrimaryConstructor? BaseList?
WhereClauseList?
ClassBody? ;
InterfaceDeclaration ::= TypeModifiers? 'interface' Identifier TypeParameterList?
BaseList?
WhereClauseList?
ClassBody? ;
UnionDeclaration ::= TypeModifiers? 'union' ('class' | 'struct')? Identifier TypeParameterList?
UnionPrimaryList? BaseList?
WhereClauseList?
UnionDeclarationBody?
TypeTerminator? ;
UnionDeclarationBody ::= '{' UnionCaseList '}' ;
UnionPrimaryList. ::= '(' UnionAlternativeTypeList ')' ;
UnionAlternativeTypeList ::= UnionAlternativeType ('|' UnionAlternativeType)* ;
UnionAlternativeType ::= Type | 'null' ; (* 'null' is union-declaration-only syntax for nullable active contents, not a general Type *)
DelegateDeclaration ::= TypeModifiers? 'delegate' Identifier TypeParameterList?
'(' ParameterList? ')'
ReturnTypeClause?
WhereClauseList?
TypeTerminator? ;
UnionCaseList ::= {UnionCaseClause} ;
UnionCaseClause ::= 'case' Identifier (UnionCaseParameterList | CaseFieldClause)? UnionCaseTerminator? ;
UnionCaseParameterList ::= '(' UnionCaseParameter {',' UnionCaseParameter} ')' ;
UnionCaseParameter ::= Parameter | Type ;
CaseFieldClause ::= '{' {CaseField} '}' ;
CaseField ::= Identifier ':' Type ('=' Expression)? UnionCaseTerminator? ;
UnionCaseTerminator ::= ',' | TypeTerminator ;
(* Case-construction surface forms are semantically equivalent:
Ok(2)
Ok<int>(2)
Result<int, E>.Ok(2)
.Ok(2) (target-typed member binding)
Unqualified `Ok` forms are valid only when case resolution is unambiguous;
otherwise qualification (or an alias) is required.
Resolution of `.Ok` / `Result.Ok` is performed by the binder against the
union's declared case set.
Commas are optional between cases; newline separators remain valid. *)
BaseList ::= ':' Type {',' Type} ;
(* In declaration-oriented separated lists such as parameter lists, type
parameter lists, type argument lists, and enum-member lists, Raven also
accepts newline as a boundary. Some lists, such as enum-member lists, also
admit alternate explicit separators like `;`. The syntax tree keeps the
separated-list shape and records `SyntaxKind.None` for implicit separator
slots. Same-line omission is still an error and recovers with a missing
expected separator token. *)
ClassBody ::= '{' {ClassMember} '}' ;
TypeTerminator ::= end-of-line-trivia | ';' ;
ClassMember ::= FieldDeclaration
| ConstDeclaration
| ParameterlessConstructorDeclaration
| FinallyDeclaration
| MethodDeclaration
| ConversionOperatorDeclaration
| OperatorDeclaration
| InvocationOperatorDeclaration
| ConstructorDeclaration
| EventDeclaration
| PropertyDeclaration
| IndexerDeclaration
| DelegateDeclaration
| MacroMemberInvocation ;
MacroMemberInvocation ::= QualifiedName '!'
( ArgumentList
| [ArgumentList] MacroTokenTree )
TypeTerminator? ;
FieldDeclaration ::= MemberModifiers? 'field' VariableDeclarators ';' ; (* `readonly`/`required` are MemberModifiers *)
ConstDeclaration ::= MemberModifiers? 'const' VariableDeclarators ';' ;
VariableDeclarators ::= VariableDeclarator {',' VariableDeclarator} ;
VariableDeclarator ::= Identifier (':' Type)? ['=' Expression] ;
ParameterlessConstructorDeclaration ::= MemberModifiers?
'init'
( Block | '=>' Expression ) ;
ConstructorDeclaration ::= MemberModifiers?
'init' '(' ParameterList? ')'
ConstructorInitializer?
( Block | '=>' Expression ) ;
FinallyDeclaration ::= MemberModifiers?
'finally'
Block ;
ConstructorInitializer ::= ':' 'base' ArgumentList ;
MethodDeclaration ::= MemberModifiers?
'func'
ExplicitInterfaceSpecifier?
Identifier TypeParameterList?
'(' ParameterList? ')'
ReturnTypeClause?
WhereClauseList?
( Block | '=>' Expression ) ;
ConversionOperatorDeclaration
::= MemberModifiers?
'func' ( 'explicit' | 'implicit' )
'(' ParameterList? ')'
ReturnTypeClause?
( Block | '=>' Expression ) ;
OperatorDeclaration ::= MemberModifiers?
'func' OverloadableOperator
'(' ParameterList? ')'
ReturnTypeClause?
( Block | '=>' Expression ) ;
OverloadableOperator ::= '+' | '-' | '*' | '/' | '%' | '^' | '&' | '&&' | 'and'
| '|' | '||' | 'or' | '==' | '!=' | '<' | '<=' | '>'
| '>=' | '!' | '++' | '--' ;
ExplicitInterfaceSpecifier ::= Type '.' ;
InvocationOperatorDeclaration
::= MemberModifiers?
'self' TypeParameterList?
'(' ParameterList? ')'
ReturnTypeClause?
WhereClauseList?
( Block | '=>' Expression ) ;
PropertyDeclaration ::= MemberModifiers?
PropertyBindingKeyword
ExplicitInterfaceSpecifier? Identifier ':' Type PropertyBody ; (* `required` is a MemberModifier *)
PropertyBindingKeyword ::= 'val' | 'var' ;
PropertyBody ::= AccessorList
| '=>' Expression
| '=' Expression ;
IndexerDeclaration ::= MemberModifiers?
ExplicitInterfaceSpecifier? Identifier BracketedParameterList ':' Type AccessorList ;
EventDeclaration ::= MemberModifiers?
ExplicitInterfaceSpecifier? 'event' Identifier ':' Type ( EventAccessorList | ';' ) ;
AccessorList ::= '{' Accessor {Accessor} '}' ;
Accessor ::= AccessorModifier? ('get' | 'set' | 'init')
( Block | '=>' Expression | ';' ) ;
EventAccessorList ::= '{' EventAccessor {EventAccessor} '}' ;
EventAccessor ::= AccessorModifier? ('add' | 'remove')
( Block | '=>' Expression | ';' ) ;
BracketedParameterList ::= '[' ParameterList? ']' ;
(* ---------- Generics & constraints ---------- *)
TypeParameterList ::= '<' TypeParameter {',' TypeParameter} '>' ;
TypeParameter ::= Variance? Identifier InlineConstraints? ;
InlineConstraints ::= ':' TypeParameterConstraintList ;
Variance ::= 'out' | 'in' ;
WhereClauseList ::= WhereClause { WhereClause } ;
WhereClause ::= 'where' Identifier ':' TypeParameterConstraintList ;
TypeParameterConstraintList
::= TypeParameterConstraint {',' TypeParameterConstraint} ;
TypeParameterConstraint ::= 'class' (* reference type constraint *)
| 'struct' (* value type constraint *)
| 'notnull' (* non-null constraint *)
| 'unmanaged' (* unmanaged constraint *)
| 'new' '(' ')' (* public parameterless ctor *)
| 'allows' 'ref' 'struct'
| Type ; (* base class / interface constraint *)
(* ---------- Statements ---------- *)
(* Newline tokens act as implicit statement terminators unless the parser is
in a continuation context that still requires more of the current
expression—such as immediately after '=' or a binary operator. In those
cases the newline is preserved as trivia on the next token instead of
ending the statement. Other terminators, including ';', '}', and keywords
like 'else' that conclude the enclosing construct, serve the same role when
they appear. *)
Statement ::= LocalDeclaration
| LetElseStatement
| ReturnStatement
| ThrowStatement
| BreakStatement
| ContinueStatement
| FunctionStatement
| BlockStatement
| IfStatement
| IfPatternStatement
| LoopStatement
| WhileStatement
| WhilePatternStatement
| ForStatement
| TryStatement
| LockStatement
| UnsafeStatement
| MacroContributionStatement
| AssignmentStatement
| ExpressionStatement ;
BlockStatement ::= Block ;
LocalDeclaration ::= ScopedModifier? ('let' | 'val' | 'var' | 'const') LocalVariableDeclarators ;
LetElseStatement ::= BindingKeyword Pattern '=' Expression 'else' DivergingStatement ;
(* Semantic rule: DivergingStatement must not have a reachable endpoint. It
exits through return, throw, break, or continue; successful pattern
bindings are introduced into the surrounding block scope. *)
DivergingStatement ::= EmbeddedStatement ;
UseDeclarationStatement ::= 'use' LocalVariableDeclarators ['in' Block] ;
LocalVariableDeclarators ::= LocalVariableDeclarator {',' LocalVariableDeclarator} ;
LocalVariableDeclarator ::= Identifier (':' Type)? '=' Expression ;
ReturnStatement ::= 'return' Expression? ;
ThrowStatement ::= 'throw' Expression ;
BreakStatement ::= 'break' [Identifier] ;
ContinueStatement ::= 'continue' [Identifier] ;
MacroContributionStatement
::= ('expand' | 'replace' | 'introduce' | 'fragment' | 'token') Expression ;
(* Macro contribution words are contextual and these statement forms are
available only inside macro declarations. *)
FunctionStatement ::= {AttributeList | MacroAttributeList}
('async' | 'unsafe' | 'extern')*
'func' Identifier TypeParameterList?
'(' ParameterList? ')'
ReturnTypeClause?
WhereClauseList?
Block ;
ExpressionStatement ::= Expression ;
IfStatement ::= 'if' Expression EmbeddedStatement ['else' ElseEmbeddedStatement] ;
IfPatternStatement ::= 'if' BindingKeyword Pattern '=' Expression EmbeddedStatement ['else' ElseEmbeddedStatement] ;
(* NOTE: In statement contexts, '{' that starts a new statement (for example after a newline,
or after headers such as if/while/for) begins a block statement/body and is not parsed as an object initializer.
This disambiguation is performed by the parser (context-sensitive), not by this EBNF. *)
WhileStatement ::= 'while' Expression EmbeddedStatement ;
WhilePatternStatement ::= 'while' BindingKeyword Pattern '=' Expression EmbeddedStatement ;
LoopStatement ::= 'loop' EmbeddedStatement ;
LockStatement ::= 'lock' Expression EmbeddedStatement ;
ForStatement ::= 'await'? 'for' ForBindingKeyword? ForIterationTarget? 'in' Expression ForStepClause? EmbeddedStatement ;
ForBindingKeyword ::= 'val' | 'let' | 'var' ;
ForIterationTarget ::= TypedForIdentifierTarget | Identifier | Pattern ;
TypedForIdentifierTarget ::= Identifier ':' Type ;
ForStepClause ::= 'by' Expression ;
(* Parser rule: a non-block embedded statement must start on the next line.
`else if` remains a dedicated exception and may stay on the same line. *)
EmbeddedStatement ::= Block | Statement ;
ElseEmbeddedStatement ::= Block | IfStatement | Statement ;
TryStatement ::= 'try' Block (CatchClauseList [FinallyClause] | FinallyClause) ;
CatchClauseList ::= CatchClause {CatchClause} ;
CatchClause ::= 'catch' CatchPattern? [WhenClause] Block ;
CatchPattern ::= Pattern | '(' Pattern ')' ;
FinallyClause ::= 'finally' Block ;
UnsafeStatement ::= 'unsafe' Block ;
(* Keep statement assignments consistent with expression assignments:
- LHS must be assignable OR a deconstruction designation.
- General patterns are NOT assignment targets.
- Only deconstruction-shaped designations (`(...)`, `[...]`, nested variable
designations) participate on the LHS. Property patterns, member/case
patterns, nominal deconstruction heads, comparison/range heads, and other
match-only pattern forms do not. *)
AssignmentStatement ::= Assignable AssignmentOperator Expression
| DeconstructionAssignmentStatement ;
DeconstructionAssignmentStatement
::= VariableDesignation AssignmentOperator Expression ;
(* ---------- Expressions & precedence (lowest → highest) ---------- *)
Expression ::= MatchExpression
| AssignmentExpression {PostfixMatchExpressionSuffix} ;
MatchExpression ::= 'match' Expression '{' MatchArmList '}' ;
PostfixMatchExpressionSuffix
::= 'match' '{' MatchArmList '}' ;
MatchArmList ::= MatchArm {MatchArm} ;
MatchArm ::= MatchArmBindingKeyword? Pattern MatchGuardClause? '=>' Expression MatchArmTerminator? ;
MatchArmBindingKeyword ::= 'val' | 'let' | 'var' ;
MatchGuardClause ::= 'when' Expression ;
MatchArmTerminator ::= end-of-line-trivia | ';' ;
AssignmentExpression ::= Assignable AssignmentOperator AssignmentExpression
| DeconstructionAssignment
| ConditionalNullCoalesceExpression ;
DeconstructionAssignment ::= VariableDesignation AssignmentOperator AssignmentExpression ;
AssignmentOperator ::= '=' | '+=' | '-=' | '*=' | '/=' | '%=' | '&=' | '|=' | '??=' ;
Assignable ::= Identifier
| MemberAccessExpression
| MemberBindingExpression
| ElementAccessExpression ;
(* ---------- Patterns ---------- *)
Pattern ::= OrPattern [PatternGuardClause] ;
PatternGuardClause ::= 'when' (Pattern | Expression) ;
OrPattern ::= AndPattern {'or' AndPattern} ;
AndPattern ::= UnaryPattern {'and' UnaryPattern} ;
UnaryPattern ::= 'not' UnaryPattern | PrimaryPattern ;
PrimaryPattern ::= ParenthesizedPattern
| RangePattern
| ComparisonPattern
| ConstantPattern
| VariablePattern
| DeclarationPattern
| PositionalPattern
| SequencePattern
| DictionaryPattern
| DiscardPattern
| MemberPattern
| NominalDeconstructionPattern
| PropertyPattern ;
ConstantPattern ::= Literal | ConstantPatternOperand ;
ConstantPatternOperand ::= Identifier ;
(* A bare identifier in pattern position never introduces a binding.
Bindings require an explicit BindingKeyword (let/val/var).
A bare identifier is context-sensitive:
- If the scrutinee is a discriminated union and the identifier matches a case name
in the union's declared case set, it is treated as a DU case pattern.
- If it binds to an in-scope value, it is a value/constant pattern (matches by equality).
- Otherwise it is interpreted as a type name and forms a DeclarationPattern.
This disambiguation is performed by the binder. *)
BindingKeyword ::= 'let' | 'val' | 'var' ;
VariablePattern ::= BindingKeyword VariableDesignation ;
(* NOTE: A single Identifier can be parsed as either ConstantPatternOperand or as the `Type` in a
DeclarationPattern. The binder resolves the ambiguity by preferring a value/constant pattern
when the identifier resolves to a value symbol in scope; otherwise it is treated as a type. *)
DeclarationPattern ::= Type [VariableDesignation] ;
PositionalPattern ::= '(' PatternElement ',' PatternElement {',' PatternElement} ')' [PatternWholeDesignation] ;
PatternElement ::= [PatternElementName] Pattern ;
SequencePattern ::= '[' SequencePatternElementList? ']' [PatternWholeDesignation] ;
SequencePatternElementList
::= SequencePatternElement {',' SequencePatternElement} ;
SequencePatternElement ::= [SequencePatternPrefix] Pattern
| '...' ;
SequencePatternPrefix ::= '..' [IntegerLiteral]
| '...' ;
DictionaryPattern ::= '[' DictionaryPatternEntryList? ']' [PatternWholeDesignation] ;
DictionaryPatternEntryList
::= DictionaryPatternEntry {',' DictionaryPatternEntry} ;
DictionaryPatternEntry ::= Expression ':' Pattern ;
(* Pattern elements may be named with `name: pattern`.
When the pattern is a declaration/capture pattern, the nested pattern still
controls whether a binding keyword is required. Examples:
- (Age: 42, Name: val name)
- Person(Items: val items, Name: val name, Age: 42)
For tuple-backed positional patterns, the name is descriptive metadata.
For `Deconstruct`-backed positional/nominal patterns, the binder matches
named elements against deconstruction parameters/members by name. *)
PatternElementName ::= Identifier ':' ;
(* Binder note: at most one open-ended rest segment (`.. pattern`, `... pattern`,
or bare `...`) is permitted in a sequence pattern. Fixed-length array
captures may arise from these segments, while fixed-size segments use
`..N pattern` and may appear multiple times. *)
DiscardPattern ::= '_' | Type '_' ;
MemberPattern ::= MemberPath [ '(' PatternList? ')' ] [PatternWholeDesignation] ;
MemberPath ::= '.' Identifier
| Type '.' Identifier ;
PatternList ::= Pattern {',' Pattern} ;
PatternElementList ::= PatternElement {',' PatternElement} ;
NominalDeconstructionPattern
::= Type '(' PatternElementList? ')' [PatternWholeDesignation] ;
(* Binder note: when Type resolves to a discriminated-union case name for the
current scrutinee, this form is interpreted as a DU case deconstruction
pattern rather than a regular nominal deconstruction pattern. Otherwise the type must be
deconstructable (for example a record type or a nominal type with an
accessible `Deconstruct` method). *)
PropertyPattern ::= Type? '{' PropertySubpatternList? '}' PatternWholeDesignation? ;
PropertySubpatternList ::= PropertySubpattern {',' PropertySubpattern} ;
PropertySubpattern ::= PropertyMemberPath ':' Pattern ;
PropertyMemberPath ::= Identifier {'.' Identifier} ;
PatternWholeDesignation ::= VariableDesignation ;
ParenthesizedPattern ::= '(' Pattern ')' ;
ComparisonPattern ::= RelationalOperator ComparisonPatternOperand ;
RelationalOperator ::= '<' | '<=' | '>' | '>=' | '==' | '!=' ;
ComparisonPatternOperand ::= Expression ; (* binder should enforce “constant-ish” rules *)
RangePattern ::= RangeLower '..' RangeUpper ;
RangeLower ::= RangePatternOperand? ;
RangeUpper ::= RangePatternOperand? ;
RangePatternOperand ::= Expression ; (* binder should enforce “constant-ish” rules; parser stops at `..` when parsing the lower bound *)
VariableDesignation ::= VariableDesignationCore [':' Type] ; (* Whole-pattern designations may omit the BindingKeyword when an outer construct (for example `if let`, `while let`, `for let`, or a match-arm binding keyword) supplies the binding mode. Without an outer binding keyword, omitted binding defaults to an immutable capture. *)
VariableDesignationCore ::= SingleVariableDesignation | ParenthesizedVariableDesignation ;
SingleVariableDesignation ::= [BindingKeyword] Identifier ;
ParenthesizedVariableDesignation
::= '(' VariableDesignation {',' VariableDesignation} ')' ;
ConditionalNullCoalesceExpression
::= LogicalOrExpression {'??' LogicalOrExpression} ;
LogicalOrExpression ::= LogicalAndExpression {'||' LogicalAndExpression} ;
LogicalAndExpression ::= BitwiseOrExpression {'&&' BitwiseOrExpression} ;
BitwiseOrExpression ::= BitwiseXorExpression {'|' BitwiseXorExpression} ;
BitwiseXorExpression ::= BitwiseAndExpression {'^' BitwiseAndExpression} ;
BitwiseAndExpression ::= EqualityExpression {'&' EqualityExpression} ;
EqualityExpression ::= RelationalExpression {('==' | '!=') RelationalExpression} ;
RelationalExpression ::= TypeTestExpression
{('<' | '>' | '<=' | '>=') TypeTestExpression} ;
TypeTestExpression ::= RangeExpression { 'as' Type } [ 'is' Pattern ] ;
RangeExpression ::= RangeStart '..' RangeEnd
| ShiftExpression ;
RangeStart ::= RangeIndex? ;
RangeEnd ::= RangeIndex? ;
RangeIndex ::= '^'? AdditiveExpression ;
ShiftExpression ::= AdditiveExpression {('<<' | '>>') AdditiveExpression} ;
AdditiveExpression ::= MultiplicativeExpression {('+' | '-') MultiplicativeExpression} ;
MultiplicativeExpression ::= UnaryExpression {('*' | '/' | '%') UnaryExpression} ;
UnaryExpression ::= LambdaExpression
| PostfixExpression
| ('+' | '-' | '!') UnaryExpression
| 'fixed' UnaryExpression
| StackAllocExpression
| 'await' UnaryExpression
| CastExpression ;
StackAllocExpression ::= 'stackalloc' Type '[' Expression ']' ;
LambdaExpression ::= 'async'? LambdaParameterClause ReturnTypeClause? '=>' Expression ;
LambdaParameterClause ::= '(' LambdaParameterList? ')' | Parameter ;
LambdaParameterList ::= LambdaParameter {',' LambdaParameter} ;
LambdaParameter ::= Parameter
| PositionalPattern
| SequencePattern ;
CastExpression ::= '(' Type ')' UnaryExpression ;
PostfixExpression ::= PrimaryExpression { PostfixTrailer } ;
PostfixTrailer ::= ArgumentList
| MemberAccessTrailer
| ElementAccessTrailer
| SuppressNullableWarningTrailer
| PropagateTrailer
| ConditionalAccessTrailer
| ObjectInitializer
| WithExpression ;
ArgumentList ::= '(' [Argument {',' Argument}] ')' ;
Argument ::= [Identifier ':'] ArgumentSpread? Expression ;
ArgumentSpread ::= '...' ;
AttributeArgumentList ::= '(' [AttributeArgument {',' AttributeArgument}] ')' ;
AttributeArgument ::= [Identifier ':'] Expression ; (* Attribute named arguments must use ':'; '=' is not valid syntax. *)
MemberAccessTrailer ::= '.' Identifier ;
ElementAccessTrailer ::= '[' Expression ']' ;
SuppressNullableWarningTrailer
::= '!' ;
(* `?` is context-sensitive in postfix position:
- If followed by `.`, `(`, or `[`, it forms conditional access: `?.`, `?(`, `?[`.
- Otherwise it is the Result-propagation postfix operator: `<expr>?`. *)
PropagateTrailer ::= '?' ;
ConditionalAccessTrailer ::= '?' (MemberAccessTrailer | ElementAccessTrailer | ArgumentList) ;
(* ---------- Object initializers and with expressions ---------- *)
ObjectInitializer ::= '{' { ObjectInitializerEntry } '}' ;
ObjectInitializerEntry ::= ObjectInitializerAssignmentEntry
| ObjectInitializerExpressionEntry ;
ObjectInitializerAssignmentEntry
::= Identifier AssignmentOperator Expression WithEntryTerminator? ;
ObjectInitializerExpressionEntry
::= Expression WithEntryTerminator? ;
WithExpression ::= 'with' '{' { WithEntry } '}' ;
WithEntry ::= WithAssignment
| WithExpressionEntry ;
WithAssignment ::= Identifier AssignmentOperator Expression WithEntryTerminator? ;
WithExpressionEntry ::= Expression WithEntryTerminator? ;
WithEntryTerminator ::= ',' | end-of-line-trivia | ';' ;
MemberBindingExpression ::= '.' Identifier ;
(* Member binding is target-typed; for discriminated unions, `.Case(...)` binds
to a case on the expected union type. *)
(* ---------- Primaries ----------
NOTE: Block and IfExpression can appear as expressions.
While and for constructs exist only as statements. *)
PrimaryExpression ::= Literal
| DefaultExpression
| TypeOfExpression
| NameOfExpression
| FreestandingMacroExpression
| CollectionExpression
| ArrayExpression
| Identifier
| MemberBindingExpression
| TupleExpression
| ParenthesizedExpression
| TryExpression
| UnsafeExpression
| ReturnExpression
| ThrowExpression
| BreakExpression
| ContinueExpression
| YieldExpression
| MacroContributionExpression
| Block
| IfExpression
| IfPatternExpression
| InterpolatedStringExpression ;
DefaultExpression ::= 'default' | 'default' '(' Type ')' ;
TypeOfExpression ::= 'typeof' '(' Type ')' ;
NameOfExpression ::= 'nameof' '(' NameOfOperand ')' ;
FreestandingMacroExpression ::= QualifiedName '!'
( ArgumentList
| [ArgumentList] MacroTokenTree ) ;
MacroTokenTree ::= '{' /* lossless raw source with balanced braces */ '}' ;
(* `nameof` is a compile-time-only expression that produces the unqualified name of the referenced symbol.
The operand is syntactic and is validated by the binder. *)
NameOfOperand ::= Identifier
| MemberAccessExpression
| MemberBindingExpression
| QualifiedName ;
TryExpression ::= 'try' ['?'] Expression ;
UnsafeExpression ::= 'unsafe' Block ;
ReturnExpression ::= 'return' Expression ;
ThrowExpression ::= 'throw' Expression ;
BreakExpression ::= 'break' [Identifier] ;
ContinueExpression ::= 'continue' [Identifier] ;
YieldExpression ::= 'yield' ['from'] Expression ;
MacroContributionExpression
::= ('expand' | 'replace' | 'introduce') Expression ;
(* Macro contribution expressions are contextual to macro declarations. *)
Block ::= '{' {Statement} '}' ;
IfExpression ::= 'if' Expression Expression 'else' Expression ;
IfPatternExpression ::= 'if' BindingKeyword Pattern '=' Expression Expression 'else' Expression ;
InterpolatedStringExpression
::= '"' {InterpolatedStringContent} '"' ;
InterpolatedStringContent ::= InterpolatedStringText | Interpolation ;
InterpolatedStringText ::= /* text segment without ${ or closing quote */ ;
Interpolation ::= '${' Expression '}' ;
(* Collection elements may also be separated by an implicit newline boundary.
In the syntax tree that boundary is represented by `SyntaxKind.None`,
not a concrete newline token. *)
CollectionExpression ::= ['!'] '[' [CollectionElement {CollectionSeparator CollectionElement} [CollectionSeparator]] ']' ;
ArrayExpression ::= '[|' [CollectionElement {CollectionSeparator CollectionElement} [CollectionSeparator]] '|]' ;
CollectionSeparator ::= ',' ;
CollectionElement ::= Expression
| Expression ':' Expression
| '...' Expression
| '...' Expression ':' Expression
| CollectionComprehensionElement ;
ComprehensionTarget ::= Identifier | Pattern ;
CollectionComprehensionElement
::= 'for' ['let' | 'val' | 'var'] ComprehensionTarget 'in' Expression ['if' Expression] '=>' Expression
| 'for' ['let' | 'val' | 'var'] ComprehensionTarget 'in' Expression ['if' Expression] '=>' Expression ':' Expression ;
TupleExpression ::= '(' TupleElement {',' TupleElement} ')' ;
TupleElement ::= [Identifier ':'] Expression ;
ParenthesizedExpression ::= '(' Expression ')' ;
Literal ::= NumericLiteral
| StringLiteral
| EncodedStringLiteral
| MultilineStringLiteral
| 'true' | 'false' | 'null' ;
(* ---------- Types ---------- *)
Type ::= UnionType ;
UnionType ::= FunctionType {'|' FunctionType} ;
ByRefType ::= '&' Type ;
PointerType ::= '*' Type ;
FunctionType ::= FunctionParameterClause '->' Type
| NullableType ;
FunctionParameterClause ::= FunctionTypeParameterList | NullableType ;
FunctionTypeParameterList
::= '(' [Type {',' Type}] ')' ;
NullableType ::= PrimaryType ['?'] ;
PrimaryType ::= TupleType
| GenericType
| SimpleType
| ParenthesizedType ;
SimpleType ::= BuiltinType | QualifiedName ;
BuiltinType ::= 'bool'
| 'char'
| 'sbyte'
| 'byte'
| 'short'
| 'ushort'
| 'int'
| 'uint'
| 'long'
| 'ulong'
| 'nint'
| 'nuint'
| 'float'
| 'double'
| 'decimal'
| 'string'
| 'object'
| 'unit'
| '(' ')' ;
GenericType ::= QualifiedName '<' TypeList '>' ;
TypeList ::= Type {',' Type} ;
TupleType ::= '(' TupleTypeElement {',' TupleTypeElement} ')' ;
TupleTypeElement ::= [Identifier ':'] Type ;
ParenthesizedType ::= '(' Type ')' ;
QualifiedName ::= Identifier {'.' Identifier} ;
(* `Union.Case` is parsed as a QualifiedName/member chain and may be rebound by
semantics as a discriminated-union case reference. *)
Identifier ::= /* ASCII letter, '_' or '$' followed by letters, digits, '_' or '$'.
The single '_' token is reserved for discards. */ ;
StringLiteral ::= '"' {InterpolatedStringText | Interpolation} '"' ;
EncodedStringLiteral ::= (StringLiteral | MultilineStringLiteral) EncodingSuffix ;
EncodingSuffix ::= 'u8' | 'ascii' ; (* suffix must be adjacent to the closing delimiter; interpolation is not permitted in encoded literals *)
InterpolatedStringText ::= /* content with escapes */ ;
Interpolation ::= '$' Identifier | '$' '{' Expression '}' ;
(* Multiline string literals are triple-quoted strings that may span multiple lines.
They are raw (no escape-sequence decoding). Interpolation is permitted using the same
forms as regular strings: `$Identifier` and `${ Expression }`.
NOTE: Non-normative. The lexer produces the triple-quoted literal as a single token;
the parser/binder interpret interpolation within its raw text. *)
MultilineStringLiteral ::= '"""' {MultilineInterpolatedContent} '"""' ;
MultilineInterpolatedContent ::= MultilineInterpolatedText | Interpolation ;
MultilineInterpolatedText ::= /* raw text segment; may include newlines; stops before an interpolation start or closing delimiter */ ;
(* ---------- Numeric literals (lexical forms) ---------- *)
(* Numeric literals are lexically resolved by the lexer. This grammar documents the allowed surface forms.
Examples:
1 (* integer *)
1.0 (* decimal-point real *)
.0 (* leading-dot real *)
1e3 (* exponent real *)
0xFF (* hex integer *)
0b1010 (* binary integer *)
Suffixes (optional, case-insensitive):
B -> byte
L -> long
F -> float
D -> double
M -> decimal
If no suffix is present, the default type is determined by the lexer:
- integer literals default to int (or a wider integral type if specified/required by the binder)
- real literals default to double
*)
NumericLiteral ::= IntegerLiteral NumericSuffix?
| RealLiteral NumericSuffix? ;
NumericSuffix ::= 'b' | 'B'
| 'l' | 'L'
| 'f' | 'F'
| 'd' | 'D'
| 'm' | 'M' ;
IntegerLiteral ::= DecimalIntegerLiteral
| HexIntegerLiteral
| BinaryIntegerLiteral ;
DecimalIntegerLiteral ::= DecimalDigits ;
HexIntegerLiteral ::= '0' ('x' | 'X') HexDigits ;
BinaryIntegerLiteral ::= '0' ('b' | 'B') BinaryDigits ;
(* Real literals support:
- a decimal point: 1.0, 1., .0
- an exponent: 1e3, 1.0e-3, .5E+2
A real literal must contain either a '.' or an exponent part (or both). *)
RealLiteral ::= RealWithDot ExponentPart?
| DecimalDigits ExponentPart ;
RealWithDot ::= DecimalDigits '.' DecimalDigits?
| '.' DecimalDigits ;
ExponentPart ::= ('e' | 'E') Sign? DecimalDigits ;
Sign ::= '+' | '-' ;
DecimalDigits ::= DecimalDigit { DecimalDigit | '_' } ;
HexDigits ::= HexDigit { HexDigit | '_' } ;
BinaryDigits ::= BinaryDigit { BinaryDigit | '_' } ;
DecimalDigit ::= '0'..'9' ;
HexDigit ::= DecimalDigit | 'a'..'f' | 'A'..'F' ;
BinaryDigit ::= '0' | '1' ;