@top Program { Query } @precedence { attr @left unaryop mulop @left addop @left binop @left terop @left and @left or @left } @skip { space | LineComment } commaSep<content> { content ("," content)* } kw<term> { @specialize[@name={term}]<Identifier, term> } Query { TagIdentifier ( WhereClause | LimitClause | OrderClause | SelectClause | RenderClause )* } WhereClause { kw<"where"> Expression } LimitClause { kw<"limit"> Expression } OrderClause { Order commaSep<OrderBy> } OrderBy { Expression OrderDirection? } SelectClause { kw<"select"> commaSep<Select> } RenderClause { kw<"render"> ( kw<"each"> | kw<"all"> )? PageRef } Select { Identifier | Expression kw<"as"> Identifier } OrderDirection { OrderKW } Value { Number | String | Bool | Regex | kw<"null"> | List } Attribute { Expression !attr "." Identifier } Call { Identifier "(" commaSep<Expression> ")" | Identifier "(" ")" } TopLevelVal { "." } ParenthesizedExpression { "(" Expression ")" } LogicalExpression { Expression !and kw<"and"> Expression | Expression !or kw<"or"> Expression } Expression { Value | Identifier | GlobalIdentifier | Attribute | TopLevelVal | ParenthesizedExpression | LogicalExpression | QueryExpression | UnaryExpression | BinExpression | TernaryExpression | Call | PageRef | Object } QueryExpression { "{" Query "}" } UnaryExpression { !unaryop NotKW Expression | !unaryop "!" Expression | !unaryop "-" Expression } BinExpression { Expression !binop "<" Expression | Expression !binop "<=" Expression | Expression !binop "=" Expression | Expression !binop "!=" Expression | Expression !binop ">=" Expression | Expression !binop ">" Expression | Expression !binop "=~" Expression | Expression !binop "!=~" Expression | Expression !binop InKW Expression | Expression !mulop "*" Expression | Expression !mulop "/" Expression | Expression !mulop "%" Expression | Expression !addop "+" Expression | Expression !addop "-" Expression } TernaryExpression { Expression !terop "?" Expression !terop ":" Expression } List { "[" commaSep<Expression> "]" | "[" "]" } KeyVal { String ":" Expression } Object { "{" commaSep<KeyVal> "}" | "{" "}" } Bool { BooleanKW } @tokens { space { std.whitespace+ } LineComment { "#" ![\n]* } TagIdentifier { @asciiLetter (@asciiLetter | @digit | "-" | "_" | "/" )* } Identifier { @asciiLetter (@asciiLetter | @digit | "-" | "_")* | "`" ![`]* "`" } GlobalIdentifier { "@" @asciiLetter (@asciiLetter | @digit | "-" | "_")* } String { "\"" ![\"]* "\"" | "'" ![']* "'" } PageRef { "[[" ![\]]* "]]" } Order { "order by" } Regex { "/" ( ![/\\\n\r] | "\\" _ )+ "/" } Number { std.digit+ } BooleanKW { "true" | "false" } InKW { "in" } NotKW { "not" } OrderKW { "asc" | "desc" } @precedence { Order, BooleanKW, InKW, OrderKW, NotKW, Identifier, Number } }