WHITESPACE = _{ " " | "\n" | "\t" }
COMMENT = _{ "//" ~ (!"\n" ~ ANY)* ~ "\n" }
root = _{ SOI ~ main ~ EOI }
main = _{ block? }
// Literals:
literal = { null | number | bool | text | identifier }
unsigned = @{
'0'..'9' ~ ('0'..'9' | "_")* ~ ("." ~ ('0'..'9' | "_")*)?
~ ("e" ~ "_"* ~ ("+" | "-")? ~ ('0'..'9' | "_")+ )?
}
null = @{ "null" }
sign = @{ "+" | "-" }
number = @{ sign? ~ unsigned }
bool = @{ "true" | "false" }
escaped = @{ !"\"" ~ ("\\" ~ controlCode | ANY)}
// see: https://stackoverflow.com/questions/19176024/
controlCode = @{
"\"" | "\\" | "/" | "b" | "f" | "n" | "r" | "t" | "u" ~ ('0'..'9' | 'a'..'f' | 'A'..'F'){4}
}
text = @{ "\"" ~ escaped* ~ "\"" }
identifier = @{
!reserved ~ identifierStr
}
identifierStr = @{ (ASCII_ALPHA | "_") ~ ( ASCII_ALPHANUMERIC | "_")* }
reserved = @{
("_" | "true" | "false" | "and" | "or" | "not" | "if" | "then" | "else" | "let"
| "for" | "int" | "in" | "null" | "import" | "as" | "text" | "type" | "bool"
| "float" | "number" | "any") ~ !( ASCII_ALPHANUMERIC | "_")
}
// Templates:
templateString = ${ "`" ~ templateEscaped* ~ "`" }
templateEscaped = ${ !"`" ~ ("\\" ~ templateControlCode | interpolation | ANY) }
templateControlCode = ${ "`" | "$" }
interpolation = !{ "${" ~ expression ~ "}" }
// Expressions:
expression = { prefixOp* ~ term ~ postfixOp* ~ (binaryOp ~ prefixOp* ~ term ~ postfixOp*)* }
binaryOp = _{
orOp | andOp | equalsOp | notEqualsOp | typeMatchesOp | greaterEqualOp | greaterOp
| lesserEqualOp | lesserOp | lesserEqualOp | isContainedOp | plusOp | minusOp | timesOp
| dividedOp | remainderOp | defaultOp | juxtapositionOp
}
orOp = { "or" }
andOp = { "and" }
equalsOp = { "==" }
notEqualsOp = { "!=" }
typeMatchesOp = { "#" }
greaterOp = { ">" }
greaterEqualOp = { ">=" }
lesserOp = { "<" }
lesserEqualOp = { "<=" }
isContainedOp = { "in" }
plusOp = { "+" }
minusOp = { "-" }
timesOp = { "*" }
dividedOp = { "/" }
remainderOp = { "%" }
defaultOp = { "?" }
juxtapositionOp = { "" }
prefixOp = _{ notOp }
notOp = { "not" }
postfixOp = _{ accessOp | castInt | castFloat | castText }
accessOp = { "." ~ identifier }
pathOp = { "[" ~ (
expression ~ ("," ~ expression )* ~ ","?
) ~ "]" }
castInt = { "as" ~ "int" }
castFloat = { "as" ~ "float" }
castText = { "as" ~ "text" }
term = _{
list
| listComprehension
| dict
| dictComprehension
| conditional
| literal
| templateString
| import
| "(" ~ expression ~ ")"
}
list = { "[" ~ (
listItem ~ ("," ~ listItem )* ~ ","?
)? ~ "]" }
listItem = { flatExpression | expression }
flatExpression = { "..." ~ expression }
dict = { "{" ~ (
dictItem ~ ("," ~ dictItem)* ~ ","?
)? ~ "}" }
dictItem = { flatExpression | keyValue }
keyValue = { (text | identifier) ~ (":" ~ expression)? ~ ifGuard? }
conditional = { "if" ~ expression ~ "then" ~ expression ~ "else" ~ expression }
// Comprehensions:
listComprehension = { "[" ~ expression ~ (forClause) ~ ifGuard? ~ "]" }
dictComprehension = { "{" ~ keyValueClause ~ (forClause) ~ ifGuard? ~ "}" }
forClause = { "for" ~ pattern ~ "in" ~ expression }
ifGuard = { "if" ~ expression }
keyValueClause = { expression ~ ":" ~ expression }
// Patterns:
pattern = {
wildcard
| matchIdentifier
| literal // any literal not an identifier...
| matchList
| matchHead
| matchTail
| matchDict
| matchDictStrict
}
wildcard = { "_" }
matchIdentifier = { identifier ~ (":" ~ typeExpression)?}
matchList = { "[" ~ (
pattern ~ ("," ~ pattern )* ~ ","?
)? ~ "]" }
matchHead = { "[" ~ (pattern ~ ",")* ~ ".." ~ "]" }
matchTail = { "[" ~ ".." ~ ("," ~ pattern)* ~ "]" }
matchDict = { "{" ~ (
matchDictItem ~ ("," ~ matchDictItem)* ~ "," ~ ".."
)? ~ "}" }
matchDictStrict = { "{" ~ (
matchDictItem ~ ("," ~ matchDictItem)* ~ ","?
)? ~ "}" }
matchDictItem = {
text ~ ":" ~ pattern
| identifier ~ ":" ~ !matchIdentifier ~ pattern
| matchIdentifier
}
// Bindings:
binding = { destructuringBiding | patternMatchBinding | typeDefinition }
patternMatchBinding = { "let" ~ identifier ~ pattern ~ "=" ~ block }
destructuringBiding = { "let" ~ pattern ~ "=" ~ block }
typeDefinition = { "type" ~ identifier ~ "=" ~ typeExpression }
block = {
(binding ~ ";")* ~ expression
| binding ~ (";" ~ binding )* ~ ";"?
}
// Import statements:
import = { "import" ~ text ~ ("as" ~ importFormat)? ~ ("or" ~ expression)? }
importFormat = _{ importFormatText }
importFormatText = { "text" }
// Types:
primitive = { "any" | "null" | "bool" | "int" | "float" | "number" | "text" }
typeExpression = { typeTerm ~ ("|" ~ typeTerm)*}
typeTerm = _{
optionalType
| listType
| tupleType
| recordType
| strictRecordType
| dictionaryType
| primitive
| identifier
}
optionalType = { "?" ~ typeExpression }
listType = { "[" ~ typeExpression ~ "]" }
dictionaryType = { "{" ~ typeExpression ~ "}" }
tupleType = {"(" ~ (
typeExpression ~ ("," ~ typeExpression )* ~ ","?
)? ~ ")"}
recordType = { "{" ~ (
typeItem ~ ("," ~ typeItem )* ~ ","?
)? ~ ".." ~ "}" }
strictRecordType = { "{" ~ (
typeItem ~ ("," ~ typeItem )* ~ ","?
)? ~ "}" }
typeItem = { (identifier | text) ~ ":" ~ typeExpression }