rtlola-parser 0.4.0

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 ~ GlobalTagLists ~ ImportStmts ~ Declaration* }
Spec = { SOI~ (BOM)? ~ GlobalTagLists ~ ImportStmts ~ Declaration* ~ EOI }
ImportStmts = _{ ImportStmt* }
ImportStmt  = { "import " ~ Ident }
Declaration = _{IncludeStatement | TypeDecl | ConstantStream | InputStream | OutputStream | MirrorStream | SimpleTrigger }
BOM = _{"\u{FEFF}"}

// ///////////////// Statements ///////////////////

ParamListDecl = _{ "(" ~ ParamList ~")"}
LambdaParamListDecl = _{ Ident | "(" ~ ParamList ~")"}
ParamList = { (ParameterDecl ~("," ~ParameterDecl)*)? }
ParameterDecl = { Ident ~ (":" ~ Type)? }

SpawnDecl = { "spawn"~ ActivationCondition? ~ (SpawnWhen | SpawnWith){0,2} }
SpawnWith = { "with " ~Expr }
SpawnWhen = { "when " ~Expr }

ActivationCondition = { "@" ~ (TopLevelAC | "(" ~ TopLevelAC ~ ")")}
  TopLevelAC = _{GlobalActivationCondition | LocalActivationCondition | AcExpr}
    GlobalActivationCondition = { "Global" ~ "(" ~ AcExpr ~ ")"}
    LocalActivationCondition = {"Local" ~ "(" ~ AcExpr ~ ")"}
      AcExpr = {NumberLiteral | PositiveBooleanExpr}
        PositiveBooleanExpr = {
            True
            | (Ident | "(" ~ PositiveBooleanExpr ~ ")") ~ ((And | BitAnd | Or | BitOr) ~ PositiveBooleanExpr)*
            | "(" ~ PositiveBooleanExpr ~ ")"
        }

CloseDecl = { "close" ~ ActivationCondition? ~ ("when" ~Expr)? }


EvalDecl = { "eval" ~ ActivationCondition? ~ (EvalWhen | EvalWith){0,2} }
EvalWhen = { "when " ~ Expr}
EvalWith = { "with " ~ Expr}
SimpleEvalDecl = { ActivationCondition? ~ ":=" ~ Expr }

IncludeStatement = { "include"~ StringLiteral}

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

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

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

OutputStream = { TagLists? ~ (TriggerDecl | NamedOutputDecl) ~ ParamListDecl?~ (":" ~ Type)? ~
 (((SpawnDecl| CloseDecl)* ~ EvalDecl ~ (SpawnDecl | EvalDecl | CloseDecl)*)| SimpleEvalDecl)
}
TriggerDecl = { "trigger" }
NamedOutputDecl = { "output " ~ Ident }

MirrorStream = { "output " ~ Ident ~ "mirrors " ~ Ident ~ "when " ~ Expr}

SimpleTriggerDecl = { "trigger " }
SimpleTrigger = { TagLists? ~ SimpleTriggerDecl ~ ActivationCondition? ~ Expr ~ StringLiteral? }

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

TagLists = { TagList ~ TagList* }
TagList = { "#" ~ "[" ~ TagItem ~ ("," ~ TagItem)* ~ "]" }
GlobalTagLists = _{ GlobalTagList* }
GlobalTagList = { "#!" ~ "[" ~ TagItem ~ ("," ~ TagItem)* ~ "]" }
TagItem = { Ident ~ ("=" ~ StringLiteral)? }


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

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

Expr = { (Term ~ ( (Operation ~ Term) | (Dot ~ (IntegerLiteral | FunctionExpr | Ident)) | (OpeningBracket ~ Literal ~ ClosingBracket) )*) }
// TODO Do we need Term to exist for the precedence climber?
Term = _{ LambdaExpr | 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 }


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

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

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

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

// ////////////////// 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"| "⊤")  ~ !(Ident)}
False = @{("false"  | "⊥")  ~ !(Ident)}
Literal = { StringLiteral | RawStringLiteral | NumberLiteral | BooleanLiteral }
ConstantLiteral = { StringLiteral | RawStringLiteral | NumberLiteral | BooleanLiteral | TupleLiteral }
TupleLiteral = {"(" ~ ConstantLiteral ~(","~ ConstantLiteral)+ ~ ")"}

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

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

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

// look at https://www.unicode.org/reports/tr31/#R1 and  https://www.unicode.org/reports/tr31/#D1
Ident = @{ VarianceAggregation | (XID_START | "_") ~ (XID_CONTINUE | "_" | "::" | "'")* }
VarianceAggregation = _{ "σ²" }
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"? ) }