rtlola-parser 0.1.2

A parser for RTLola specifications.
Documentation
// Status: WIP (Draft)


// Take a look at https://pest-parser.github.io/book/grammars/syntax.html
// TODO we can probably mark some rules as atomic or hidden

IncompleteSpec = { SOI ~ ImportStmts ~ Declaration* }
Spec = { SOI~ (BOM)? ~ ImportStmts ~ Declaration* ~ EOI }
ImportStmts = _{ ImportStmt* }
ImportStmt  = { "import " ~ Ident }
Declaration = _{IncludeStatement | TypeDecl | ConstantStream | InputStream | OutputStream | Trigger}
BOM = _{"\u{FEFF}"}
//////////////////////////////////////////////////
/////////////////// Statements ///////////////////
//////////////////////////////////////////////////

ParamList = { "(" ~(ParameterDecl ~("," ~ParameterDecl)*)? ~")"}
ParameterDecl = { Ident ~ (":" ~ Type)? }

SpawnDecl = { "spawn"~ ActivationCondition? ~SpawnWith? ~(SpawnIf | SpawnUnless)? }
SpawnIf = { "if" ~Expr }
SpawnUnless = { "unless" ~Expr }
SpawnWith = {"with " ~Expr }

FilterDecl = { "filter" ~ Expr }

ActivationCondition = { "@" ~ Expr }

CloseDecl = { "close" ~Expr }

IncludeStatement = { "include"~ StringLiteral}

TypeDecl = { "type "  ~ Ident ~"{" ~Ident~ ":"~Type~ ("," ~Ident~ ":"~Type)* ~ "}"}

ConstantStream = { "constant " ~ Ident ~ ":" ~ Type ~":=" ~Literal}

InputStream = { "input " ~ Ident ~ ParamList? ~ ":" ~ Type ~(","~ Ident~ ParamList? ~ ":" ~ Type)*}

OutputStream = { "output " ~ Ident ~ ParamList?~ (":" ~ Type)? ~ ActivationCondition? ~ SpawnDecl? ~ FilterDecl? ~CloseDecl? ~":="~ Expr}

Trigger = { "trigger " ~ ActivationCondition? ~ Expr ~ (StringLiteral ~ IdentList?)?}

IdentList = {"(" ~(Ident ~("," ~Ident)*) ~")"}

//////////////////////////////////////////////////
////////////////// Expressions ///////////////////
//////////////////////////////////////////////////

// TODO take a look at https://pest-parser.github.io/book/ and the usage of the PrecClimber

// Precedences:
// Atomic < TernaryExpr < BooleanDisExpr < BooleanConExpr 
//      < CompExpr < AddExpr < MultiExpr < ExpoExpr < UnaryExpr < DefaultExpr 
//      < FunctionExpr

Operation = _{ Add | Subtract | Power | Multiply | Divide | Mod | And | Or | BitAnd | BitOr | BitXor | ShiftLeft | ShiftRight | CompOp }
    Add      = { "+"  }
    Subtract = { "-"  }
    Multiply = { "*"  }
    Divide   = { "/"  }
    Mod      = { "%"  }
    Power    = { "**" }
    And      = { "∧" | "&&" | "and" }
    Or       = { "∨" | "||" | "or"  }
    Dot      = { "." }
    BitAnd   = { "&" }
    BitOr    = { "|" }
    BitXor   = { "^" }
    ShiftLeft = { "<<" }
    ShiftRight = { ">>" }

UnaryOperation = _{ Add | Subtract | Neg | BitNot }
    Neg      = { "!" | "¬" }
    BitNot   = { "~" }

Expr = { (Term ~ ( (Operation ~ Term) | (Dot ~ (IntegerLiteral | FunctionExpr | Ident)) | (OpeningBracket ~ Literal ~ ClosingBracket) )*) }
// TODO Do we need Term to exist for the precedence climber?
Term = _{ MissingExpression | Literal | ParenthesizedExpression | UnaryExpr | TernaryExpr | FunctionExpr | Ident | Tuple}

ParenthesizedExpression = {OpeningParenthesis ~ Expr ~ ClosingParenthesis | OpeningParenthesis ~ Expr ~ MissingClosingParenthesis}
OpeningParenthesis = {"("}
ClosingParenthesis = {")"}

MissingClosingParenthesis = {WHITESPACE* ~ &("then " | "else " |"output " | "input " | "trigger " | "constant " | "Type " | "include " | EOI)}
MissingExpression = {WHITESPACE* ~ &("then " | "else " |")"|"output " | "input " | "trigger " | "constant " | "Type " | "include " | EOI)}

// Functions
FunctionExpr = { FunctionSymbol ~ GenericParam? ~ FunctionArgs }
FunctionSymbol = _{ Ident }
GenericParam = { "<" ~ Type ~ ("," ~ Type)* ~ ">" }
FunctionArgs = { ( "(" ~ ")" ) | ( "(" ~ FunctionArg ~ ("," ~ FunctionArg)* ~ ")" ) }
FunctionArg = { (Ident ~ ":")? ~ Expr }

UnaryExpr = { UnaryOperation~ Term }

TernaryExpr = { "if"~ Expr~ "then"~ Expr~ "else"~ Expr }

Tuple = { "("~ (Expr~ (","~ Expr)+)?~ ")"}

OpeningBracket = { "[" }
ClosingBracket = _{ "]" }


//////////////////////////////////////////////////
//////////// Operators and Functions /////////////
//////////////////////////////////////////////////

LessThan = {"<"}
LessThanOrEqual = {"<=" | "≤"}
MoreThan = {">"}
MoreThanOrEqual = {">=" | "≥"}
NotEqual = {"!=" | "≠"}
Equal = { "="{1,2} }
CompOp = _{LessThanOrEqual | MoreThanOrEqual | LessThan | MoreThan | NotEqual | Equal}

Sum = {"Σ" | "sum"}
Count = {"#" | "count"}
//Product = {"Π" | "prod" | "product"}
Integral = {"∫"  | "integral"}
Average = { "avg"| "average" }

WindowOp = _{ Sum | /*Product |*/ Average | Count | Integral | WindowForall | WindowExists }
WindowForall = {"forall" | "∀" | "∧" | "conjunction" }
WindowExists = {"exists" | "∃" | "∨" | "disjunction" }


//////////////////////////////////////////////////
//////////////////// Literals ////////////////////
//////////////////////////////////////////////////

NumberLiteral = ${ NumberLiteralValue ~ NumberPostfix?}
NumberLiteralValue = @{ ("+" | "-")?~ Digit+~ (("." ~ !Letter) ~ Digit*)?~ ("e"~ ("+" | "-")?~ Digit+)? }
// `("." ~ !Letter)` is used to disambiguate from method call, thus,
// floating point numeric values with unit need a digit after period, i.e., `1.0Hz` instead of `1.Hz`

StringLiteral = _{ "\""~ String~ "\""}
String = @{("\\\""|!("\"") ~ ANY)*}
// A raw string literal, e.g., r#"a\"b"#
RawStringLiteral = _{ "r" ~ PUSH("#"*) ~ "\"" ~ RawString ~ "\"" ~ POP }
RawString = @{ (!("\"" ~ PEEK) ~ ANY)* }

BooleanLiteral = _{ True | False}
True = @{"true"| "⊤"}
False = @{"false"  | "⊥"}
Literal = { StringLiteral | RawStringLiteral | NumberLiteral | BooleanLiteral}

IntegerLiteral = @{ Digit+ }
SignedIntegerLiteral = @{ ("+" | "-")?~IntegerLiteral }
NumberPostfix = @{ LETTER+ }

Type = {"("~ (Type ~(","~Type)*)?~ ")" | Optional | Ident | "_"}  // _ => infer type
Optional = { Ident ~ "?" }  // Optional types are written `Int32?`

//////////////////////////////////////////////////
///////////////////// Names //////////////////////
//////////////////////////////////////////////////

Ident = @{  (Letter | "_") ~ (Letter | Digit | "_" | "::")* | WindowOp}

Parameter = { Ident } // Semantic Category

//////////////////////////////////////////////////
//////////////////// Symbols /////////////////////
//////////////////////////////////////////////////

Letter = { 'a'..'z' | 'A'..'Z'}
Digit = _{'0'..'9'}
Number = { '0'..'9'+ }
WHITESPACE = _{ " " | "\t" | "\r" | "\n"}
COMMENT = _{ ("/*" ~ (!"*/" ~ ANY)* ~ "*/") | ("//" ~(!("\n")~ANY)*~ "\n"? ) }