grammar.ebnf
/*
* Renyi: the grammar of the syntax level.
*
* Status: derived from the frozen syntax sketch (decision V11) and the
* corpus under decision V12. `crates/renyi_syntax/tests/grammar.rs` reads
* this file, runs it over every program of the corpus, the conformance
* suite and the library, and fails when it accepts a program the parser
* rejects or rejects one the parser accepts. The language reference
* (`docs/reference.md`) quotes its rules.
*
* Notation: W3C EBNF, as in the XML specification.
*
* A ::= B A is defined as B
* A B A followed by B
* A | B A or B
* A? A* A+ optional; zero or more; one or more
* ( A ) grouping
* A - B what A matches, unless B matches the same tokens
* 'text' a token with that spelling
*
* Terminals are the tokens of the lexical level (reference, section 1).
* A quoted literal is a reserved word, a phrase or a punctuation mark by
* its spelling; a phrase such as 'for each' is one token. The other
* terminals are the token classes, the names without a rule here:
*
* Identifier a value name: [a-z][a-z0-9_]*, two characters or more,
* not a reserved word
* TypeName a type, ability or variant name: [A-Z][A-Za-z0-9]*
* Member any word directly after a dot
* ReservedWord a reserved word that is not a phrase, as a method's name
* Integer 42, 1_000_000
* Decimal 19.99
* Text a "..." or """...""" literal; each {hole} holds one
* Expression, lexed as part of the literal
* PlainText a Text without holes
* RawText the "..." after `raw`: no holes, no escapes
* ClauseText the rest of the line after `purpose:`, `tags:`,
* `see also:` or `deprecated:`
* Newline a line break that ends a statement, a clause or a head
*
* The lexical level decides which line breaks are tokens: a line break
* inside parentheses, brackets or braces is none, nor is one after a
* comma or before a continuation word (reference, section 1); a run of
* line breaks is one Newline; the file ends with a Newline; comments are
* not tokens. Two line rules are not visible here: `return` keeps its
* value on its line, and a documentation clause's text runs to the end
* of its line.
*/
/* Sections 2 and 3: modules, imports, items */
Module ::= Newline? ModuleHeader (Import | Item)*
Declarations ::= Newline? ModuleHeader (Import | DeclaredItem)*
ModuleHeader ::= 'module' DottedName Newline DocClause*
DottedName ::= Identifier ('.' Member)*
Import ::= 'import' DottedName ('as' Identifier)?
('exposing' TypeName (',' TypeName)*)? Newline
Item ::= 'public'? (Function | TypeDefinition | Ability | Constant)
| Implementation
| Test
DeclaredItem ::= 'public'? (FunctionDeclaration | TypeDefinition | Ability | Constant)
| ImplDeclaration
| Implementation
| Test
/* Section 3: functions */
Function ::= FunctionHead Newline DocClause* Block 'end' Newline
FunctionDeclaration ::= FunctionHead Newline DocClause*
FunctionHead ::= 'function' (Identifier Parameters | ReservedWord MethodParameters) Signature
Parameters ::= '(' (Parameter (',' Parameter)*)? ')'
MethodParameters ::= '(' SelfParameter (',' Parameter)* ')'
Parameter ::= SelfParameter | Identifier ':' Type
SelfParameter ::= 'self' (':' Type)?
Signature ::= ('returns' Type)? ('or fails with' Type ('or' Type)*)?
('needs' Capabilities)? ForAny?
ForAny ::= 'for any' TypeName (',' TypeName)*
('where' Constraint ('and' Constraint)*)?
Constraint ::= TypeName 'can' Type
/* Section 11: capabilities, budgets and guards */
Capabilities ::= Capability (',' Capability)*
Capability ::= CapabilityPath Budget? ('only to' CapabilityPath ('or' CapabilityPath)*)?
CapabilityPath ::= DottedName ('(' PlainText ')')?
Budget ::= 'at most' Integer 'per' ('second' | 'minute' | 'hour' | 'day' | 'run')
/* Section 4: types */
Type ::= 'maybe' Type
| FunctionType
| TypeName ('of' TypeArguments)?
TypeArguments ::= Type 'to' Type | Type (',' Type)*
FunctionType ::= 'function' '(' (Type (',' Type)*)? ')' ('returns' Type)?
('or fails with' Type ('or' Type)*)? ('needs' Capabilities)?
TypeDefinition ::= 'type' TypeName TypeParameters?
(RecordDefinition | SumDefinition | SubtypeDefinition)
TypeParameters ::= 'of' TypeName (',' TypeName)*
RecordDefinition ::= Newline DocClause* (FieldLine | Derive)* 'end' Newline
FieldLine ::= 'has' Identifier ':' Type ('where' Expression)? ('as' PlainText)? Newline
Derive ::= 'can' TypeName ('by' Identifier (',' Identifier)*)? Newline
SumDefinition ::= 'is one of' Newline DocClause* (Variant | Derive)* 'end' Newline
Variant ::= TypeName ('(' VariantField (',' VariantField)* ')')? Newline
VariantField ::= Identifier ':' Type ('where' Expression)?
SubtypeDefinition ::= 'is' Type ('where' Expression)? Newline DocClause*
/* Section 5: abilities and implementations */
Ability ::= 'ability' TypeName TypeParameters?
('where' Requirement ('and' Requirement)*)? Newline
DocClause* MethodDeclaration* 'end' Newline
Requirement ::= 'self' 'can' Type
MethodDeclaration ::= FunctionHead Newline DocClause*
Implementation ::= 'ability' TypeName ('of' Type (',' Type)*)? 'for' Type ForAny? Newline
Function* 'end' Newline
ImplDeclaration ::= 'ability' TypeName ('of' Type (',' Type)*)? 'for' Type ForAny? Newline
MethodDeclaration* 'end' Newline
/* Sections 6 and 14: constants and tests */
Constant ::= 'let' Identifier ':' Type 'be' Newline? Expression Newline DocClause*
Test ::= 'test' PlainText ('needs' Capabilities)? ('replays' PlainText)? Newline
Block 'end' Newline
/* Section 13: documentation clauses */
DocClause ::= ('purpose' | 'tags' | 'see also' | 'deprecated') ':' ClauseText Newline
| 'expose as tool' Newline
| 'example' ':' Example Newline
Example ::= Expression 'fails' 'with' Pattern
| WithExpression 'is' WithExpression
/* Sections 6, 8, 9 and 12: statements */
Block ::= Newline? (Statement Newline)* Statement?
Statement ::= 'let' 'mutable'? Identifier (':' Type)? 'be' Newline? Expression
| 'change' Identifier 'to' Newline? Expression
| 'if' Expression 'then' Block
('otherwise' 'if' Expression 'then' Block)* ('otherwise' Block)? 'end'
| 'match' Expression Newline MatchArm* ('otherwise' Block)? 'end'
| 'for each' LoopSource ('where' Expression)? Ordering? Newline Block 'end'
| 'repeat until' Expression Newline Block 'end'
| 'run concurrently' ('within' Expression)? Newline Block 'end'
| 'return' Expression?
| 'fail' ('with' Expression)?
| 'crash' 'with' Expression
| 'break'
| 'continue'
| 'ignore' Expression
| 'check' Expression
| Call
| OrExpression ('otherwise' Outcome)+
MatchArm ::= 'when' Pattern ('where' Expression)? 'then' Block
LoopSource ::= Identifier (',' Identifier)* ('in' (OrExpression - Range) | Range)
Ordering ::= 'sorted by' OrExpression 'descending'?
/* Section 8: patterns */
Pattern ::= TypeName ('(' FieldPattern (',' FieldPattern)* ')')?
| 'nothing'
| ('some' | 'success' | 'failure') '(' Pattern ')'
| Identifier (':' Type)?
| Literal
FieldPattern ::= Identifier (':' Pattern)?
/* Section 7: expressions */
Expression ::= OrExpression ('otherwise' Outcome)*
Outcome ::= OrExpression
| 'fail' ('with' OrExpression)?
| 'return' OrExpression?
| 'crash' 'with' OrExpression
| 'break'
| 'continue'
OrExpression ::= AndExpression ('or' AndExpression)*
AndExpression ::= NotExpression ('and' NotExpression)*
NotExpression ::= 'not' NotExpression | Comparison
Comparison ::= WithExpression (ComparisonWord WithExpression)?
ComparisonWord ::= 'is' | 'is not' | 'is less than' | 'is at most'
| 'is greater than' | 'is at least'
WithExpression ::= Additive ('with' FieldUpdate (',' FieldUpdate)*)?
FieldUpdate ::= Identifier ':' Additive
Additive ::= Multiplicative (('+' | '-') Multiplicative)*
Multiplicative ::= Power (('*' | '/' | 'remainder') Power)*
Power ::= Postfix ('power' Power)?
Postfix ::= Receiver ('.' Member Arguments?)*
Receiver ::= Identifier Arguments?
| TypeName Arguments?
| Literal
| 'nothing'
| 'self'
| '(' Expression ')'
| Range
| IfExpression
| MatchExpression
| Query
Call ::= Identifier Arguments
| Receiver ('.' Member Arguments?)* '.' Member Arguments
Arguments ::= '(' (Argument (',' Argument)*)? ')'
Argument ::= (Identifier ':')? Expression
Literal ::= Integer | Decimal | '-' (Integer | Decimal)
| Text | 'raw' RawText
| 'true' | 'false'
| ListLiteral | MapLiteral
ListLiteral ::= '[' (Expression (',' Expression)*)? ']'
MapLiteral ::= '{' (MapEntry (',' MapEntry)*)? '}'
MapEntry ::= OrExpression ':' Expression
Range ::= 'from' Additive 'to' Additive ('by' Additive)?
IfExpression ::= 'if' Expression 'then' Newline? Outcome Newline?
('otherwise' 'if' Expression 'then' Newline? Outcome Newline?)*
'otherwise' Newline? Outcome Newline? 'end'
MatchExpression ::= 'match' Expression Newline? (MatchExpressionArm Newline?)*
('otherwise' Newline? Outcome Newline?)? 'end'
MatchExpressionArm ::= 'when' Pattern ('where' Expression)? 'then' Newline? Outcome
/* Section 10: queries */
Query ::= 'for each' LoopSource (',' LoopSource)*
('concurrently' ('within' OrExpression)?)?
('where' OrExpression)? Ordering?
('group by' OrExpression QueryTerminal? | QueryTerminal)
QueryTerminal ::= 'collect' Expression
| 'sum' Expression
| 'count'
| 'first'
| 'any' Expression
| 'all' Expression