⬅ 🏠 Home
The grammar of the language in Extended Backus-Naur Form (EBNF).
( a | b | ... )= a or b or ...[ ... ]= zero or one{ ... }= zero or more
file ::= block
import ::= [ "from" id ] "import" id { "," id } [ as id { "," id } ]
type-def ::= "type" type [ ":" type ] ( newline block | "when" [ conditions ] )
conditions ::= ( newline indent { condition newline } dedent | condition )
condition ::= expression [ "!" expression ]
type-tuple ::= "(" [ type ] { "," type } ")"
class ::= "class" id [ fun-args ] [ ":" ( type | type-tuple ) ] ( newline block )
generics ::= "[" id { "," id } "]"
id ::= { character }
id-maybe-type ::= id [ ":" type ]
type ::= ( id [ generics ] | type-tuple ) [ "->" type ]
type-tuple ::= "(" [ type { "," type } ] ")"
block ::= indent { expr-or-stmt } dedent
expr-or-stmt ::= ( statement | expression ) [ comment ]
statement ::= control-flow-stmt
| definition
| reassignment
| type-def
| "retry"
| "pass"
| class
| type-def
| comment
| import
expression ::= "(" expression ")"
| expression "?or" expression
| "return" [ expression ]
| expression "as" id
| control-flow-expr
| newline block
| collection
| index
| key-value
| operation
| anon-fun
| call
| "_"
reassignment ::= expression ( ":=" | "+=" | "-=" | "*=" | "/=" | "^=" | ">>=" | "<<=" ) expression
call ::= expression [ ( "." | "?." ) ] id tuple [ "!" ] [ newline match-cases ]
raise ::= "!" id { "," id }
collection ::= tuple | set | list | map
tuple ::= "(" { expression } ")"
set ::= "{" { expression } "}" | set-builder
set-builder ::= "{" expression "|" expression { "," expression } "}"
list ::= "[" { expression } "]" | list-builder
list-builder ::= "[" expression "|" expression { "," expression } "]"
slice ::= expression ( "::" | "::=" ) expression [ "::" expression ]
range ::= expression ( ".." | "..=" ) expression [ ".." expression ]
index ::= expression "[" expression "]"
definition ::= "def" ( variable-def | fun-def | operator-def )
variable-def ::= [ "fin" ] ( id-maybe-type | collection ) [ ":=" expression ] [ forward ]
operator-def ::= [ "pure" ] overridable-op [ "(" [ id-maybe-type ] ")" ] "->" type
[ ":=" ( expr-or-stmt | newline block ) ]
fun-def ::= [ "pure" ] id fun-args [ "->" type ] [ raise ]
[ ":=" ( expr-or-stmt | newline block ) ]
fun-args ::= "(" [ fun-arg ] { "," fun-arg } ")"
fun-arg ::= id-maybe-type [ ":=" expression ]
forward ::= "forward" id { "," id }
anon-fun ::= "\" [ id-maybe-type { "," id-maybe-type } ] ":=" expression
operation ::= relation [ ( equality | instance-eq | boolean-logic ) relation ]
relation ::= arithmetic [ comparison relation ]
arithmetic ::= term [ additive arithmetic ]
term ::= inner-term [ ( multiclative | range | slice ) term ]
inner-term ::= factor [ power inner-term ]
factor ::= [ unary ] ( literal | id | expression )
overrideable-op ::= additive | multiplicative | power | "=" | "<" | ">"
unary ::= "not" | additive
additive ::= "+" | "-"
multiplicative ::= "*" | "/"
power ::= "^" | "mod"
instance-eq ::= "is" | "isa"
equality ::= "=" | "!="
comparison ::= "<=" | ">=" | "<" | ">"
boolean-logic ::= "and" | "or"
literal ::= number | string | "None"
number ::= real | integer | e-notation
real ::= integer "." integer | "." integer | integer "."
integer ::= { digit }
e-notation ::= ( integer | real ) "E" [ "-" ] integer
string ::= """ { character } """
newline-block ::= newline block | expr-or-stmt
one-or-more-expr ::= expression { "," expression }
control-flow-expr::= if | match
if ::= "if" one-or-more-expr "then" newline-block [ "else" newline-block ]
match ::= "match" one-or-more-expr "with" newline match-cases
match-cases ::= indent { match-case { newline } } dedent
match-case ::= expression "=>" expr-or-stmt
control-flow-stmt::= while | foreach | "break" | "continue"
while ::= "while" one-or-more-expr "do" newline-block
foreach ::= "for" one-or-more-expr "in" expression "do" newline-block
newline ::= newline-char
newline-char ::= \n | \r\n
comment ::= "#" { character } newlineAn expression is used in a situation where an expression is required.
This allows the parser to short-circuit if something is definitely not an expression where it should be.
However, we cannot always know in advance whether something is an expression, e.g. when it is a function call.
Those cases should be verified by the type checker.
An expr-or-stmt may be used when it does not matter whether something is an expression or statement, such as the body of a loop.
We do not systematically desugar multiple delimited by commas, or a single expression, to tuples, as is the case in Python.
This prevents ambiguity in the grammar as specified above, and also prevents confusing situations such as (0) and 0 being equal.
Instead, we only do this in specific contexts, such as in the conditional of control flows.