Grammar railroad diagram
Open
Nobody has claimed this yet.
enhancement
- Dominant language
- Python
- Stars
- 39.9k
- Forks
- 3.4k
- Avg merge
- 6h 51m
- Merged PRs (30d)
- 232
Description
Feature request
Using the grammar/lexer from HogQLParser.g4 and changing it a bit we can get a nice navigable railroad diagram to document it, see instructions at the top shown bellow:
//
// EBNF to be viewd at https://www.bottlecaps.de/rr/ui
//
// Copy and paste this at one url shown above in the 'Edit Grammar' tab
// then click the 'View Diagram' tab.
//
program ::= declaration* EOF
declaration ::= varDecl | statement
expression ::= columnExpr
varDecl ::= LET identifier ( COLONEQUALS expression )?
identifierList ::= nestedIdentifier (COMMA nestedIdentifier)* COMMA?
statement ::= returnStmt
| throwStmt
| tryCatchStmt
| ifStmt
| whileStmt
| forInStmt
| forStmt
| funcStmt
| varAssignment
| block
| exprStmt
| emptyStmt
returnStmt ::= RETURN expression? SEMICOLON?
throwStmt ::= THROW expression? SEMICOLON?
catchBlock ::= CATCH (LPAREN /*catchVar=*/identifier (COLON /*catchType=*/identifier)? RPAREN)? /*catchStmt=*/block
tryCatchStmt ::= TRY /*tryStmt=*/block catchBlock* (FINALLY /*finallyStmt=*/block)?
ifStmt ::= IF LPAREN expression RPAREN statement ( ELSE statement )?
whileStmt ::= WHILE LPAREN expression RPAREN statement SEMICOLON?
forStmt ::= FOR LPAREN
(/*initializerVarDeclr=*/varDecl | /*initializerVarAssignment=*/varAssignment | /*initializerExpression=*/expression)? SEMICOLON
/*condition=*/expression? SEMICOLON
(/*incrementVarDeclr=*/varDecl | /*incrementVarAssignment=*/varAssignment | /*incrementExpression=*/expression)?
RPAREN statement SEMICOLON?
forInStmt ::= FOR LPAREN LET identifier (COMMA identifier)? IN expression RPAREN statement SEMICOLON?
funcStmt ::= (FN | FUN) identifier LPAREN identifierList? RPAREN block
varAssignment ::= expression COLONEQUALS expression
exprStmt ::= expression SEMICOLON?
emptyStmt ::= SEMICOLON
block ::= LBRACE declaration* RBRACE
kvPair ::= expression ':' expression
kvPairList ::= kvPair (COMMA kvPair)* COMMA?
// SELECT statement
select ::= (selectSetStmt | selectStmt | hogqlxTagElement) SEMICOLON? EOF
selectStmtWithParens ::= selectStmt | withClause LPAREN selectSetStmt RPAREN | LPAREN selectSetStmt RPAREN | placeholder
subsequentSelectSetClause ::= (EXCEPT ALL (BY NAME)? | EXCEPT (BY NAME)? | UNION ALL (BY NAME)? | UNION DISTINCT (BY NAME)? | UNION (BY NAME)? | INTERSECT ALL (BY NAME)? | INTERSECT DISTINCT (BY NAME)? | INTERSECT (BY NAME)?) selectStmtWithParens
selectSetStmt ::= selectStmtWithParens (subsequentSelectSetClause)* orderByClause? limitAndOffsetClauseOptional?
limitAndOffsetClauseOptional
::= LIMIT columnExpr PERCENT? (COMMA columnExpr)? (WITH TIES)?
| LIMIT columnExpr PERCENT? (WITH TIES)? OFFSET columnExpr
| OFFSET columnExpr
selectStmt::=
/*with=*/withClause?
SELECT DISTINCT? topClause?
/*columns=*/selectColumnExprList
/*from=*/fromClause?
arrayJoinClause?
prewhereClause?
/*where=*/whereClause?
(USING? sampleClause)?
groupByClause? (WITH (CUBE | ROLLUP))? (WITH TOTALS)?
havingClause?
qualifyClause?
(USING sampleClause)?
windowClause?
orderByClause?
limitByClause?
(limitAndOffsetClause | offsetOnlyClause)?
settingsClause?
withClause ::= WITH RECURSIVE? withExprList
topClause ::= TOP DECIMAL_LITERAL (WITH TIES)?
fromClause ::= FROM joinExpr
arrayJoinClause ::= (LEFT | INNER)? ARRAY JOIN columnExprList
windowClause ::= WINDOW identifier AS LPAREN windowExpr RPAREN (COMMA identifier AS LPAREN windowExpr RPAREN)*
prewhereClause ::= PREWHERE columnExpr
whereClause ::= WHERE columnExpr
groupByClause ::= GROUP BY (
ALL
| (CUBE | ROLLUP) LPAREN columnExprList RPAREN
| GROUPING SETS LPAREN groupingSetList RPAREN
| columnExprList
)
groupingSetList ::= groupingSet (COMMA groupingSet)*
groupingSet ::= LPAREN columnExprList? RPAREN
havingClause ::= HAVING columnExpr
qualifyClause ::= QUALIFY columnExpr
orderByClause ::= ORDER BY orderExprList
projectionOrderByClause ::= ORDER BY columnExprList
limitByClause ::= LIMIT limitExpr BY columnExprList
limitAndOffsetClause
::= LIMIT columnExpr PERCENT? (COMMA columnExpr)? (WITH TIES)? // compact OFFSET-optional form
| LIMIT columnExpr PERCENT? (WITH TIES)? OFFSET columnExpr // verbose OFFSET-included form with WITH TIES
offsetOnlyClause ::= OFFSET columnExpr
settingsClause ::= SETTINGS settingExprList
valuesClause ::= VALUES valuesRow (COMMA valuesRow)*
valuesRow ::= LPAREN columnExpr (COMMA columnExpr)* RPAREN
joinExpr
::= joinExpr NATURAL? joinOp? JOIN joinExpr joinConstraintClause? //#JoinExprOp
| joinExpr POSITIONAL JOIN joinExpr joinConstraintClause? //#JoinExprPositional
| joinExpr joinOpCross joinExpr //#JoinExprCrossOp
| joinExpr PIVOT LPAREN columnExprList pivotColumnList (GROUP BY columnExprList)? RPAREN //#JoinExprPivot
| joinExpr UNPIVOT (INCLUDE NULLS)? LPAREN unpivotColumnList RPAREN //#JoinExprUnpivot
| tableExpr FINAL? sampleClause? //#JoinExprTable
| LPAREN joinExpr RPAREN //#JoinExprParens
joinOp
::= ((ALL | ANY | ASOF)? INNER | INNER (ALL | ANY | ASOF)? | (ALL | ANY | ASOF) | ANTI | SEMI | ASOF (ANTI | SEMI)) //#JoinOpInner
| ( (SEMI | ALL | ANTI | ANY | ASOF)? (LEFT | RIGHT) OUTER?
| (LEFT | RIGHT) OUTER? (SEMI | ALL | ANTI | ANY | ASOF)?
| ASOF (ANTI | SEMI) (LEFT | RIGHT) OUTER?
) //#JoinOpLeftRight
| ((ALL | ANY | ASOF)? FULL OUTER? | FULL OUTER? (ALL | ANY | ASOF)?) //#JoinOpFull
joinOpCross
::= CROSS JOIN
| COMMA
joinConstraintClause
::= ON columnExprList
| USING LPAREN columnExprList RPAREN
| USING columnExprList
sampleClause ::= SAMPLE ratioExpr PERCENT? (OFFSET ratioExpr)? (LPAREN identifier RPAREN)?
limitExpr ::= columnExpr ((COMMA | OFFSET) columnExpr)?
orderExprList ::= orderExpr (COMMA orderExpr)*
orderExpr ::= columnExpr (ASCENDING | DESCENDING | DESC)? (NULLS (FIRST | LAST))? (COLLATE STRING_LITERAL)?
ratioExpr ::= placeholder | numberLiteral (SLASH numberLiteral)?
settingExprList ::= settingExpr (COMMA settingExpr)*
settingExpr ::= identifier EQ_SINGLE literal
windowExpr ::= winPartitionByClause? winOrderByClause? winFrameClause?
winPartitionByClause ::= PARTITION BY columnExprList
winOrderByClause ::= ORDER BY orderExprList
withinGroupClause ::= WITHIN GROUP LPAREN orderByClause RPAREN
winFrameClause ::= (ROWS | RANGE) winFrameExtend
winFrameExtend
::= winFrameBound //#frameStart
| BETWEEN winFrameBound AND winFrameBound //#frameBetween
winFrameBound ::= (CURRENT ROW | UNBOUNDED PRECEDING | UNBOUNDED FOLLOWING | columnExpr PRECEDING | columnExpr FOLLOWING)
//rangeClause ::= RANGE LPAREN (MIN identifier MAX identifier | MAX identifier MIN identifier) RPAREN
// Columns
expr ::= columnExpr EOF
columnTypeExpr
::= columnTypeExpr LBRACKET DECIMAL_LITERAL? RBRACKET //#ColumnTypeExprArray // INTEGER[], VARCHAR[3]
| identifier LPAREN identifier columnTypeExpr (COMMA identifier columnTypeExpr)* COMMA? RPAREN //#ColumnTypeExprNested // Nested
| identifier LPAREN enumValue (COMMA enumValue)* COMMA? RPAREN //#ColumnTypeExprEnum // Enum
| identifier LPAREN columnTypeExpr (COMMA columnTypeExpr)* COMMA? RPAREN //#ColumnTypeExprComplex // Array, Tuple
| identifier LPAREN columnExprList? RPAREN //#ColumnTypeExprParam // FixedString(N)
| identifier identifier+ //#ColumnTypeExprCompound // TIME WITH TIME ZONE
| identifier //#ColumnTypeExprSimple // UInt64
// Restricted type expr for : ::= casts — no parenthesized variants to avoid ambiguity with function calls
columnTypeCastExpr
::= identifier identifier+ //#ColumnTypeCastExprCompound
| identifier //#ColumnTypeCastExprSimple
columnExprList ::= columnExpr (COMMA columnExpr)* COMMA?
selectColumnExprList ::= selectColumnExpr (COMMA selectColumnExpr)* COMMA?
selectColumnExpr
::= identifier COLON columnExpr //#ColumnExprAliasBefore
| columnExpr //#ColumnExprSelectValue
columnExpr
::= CASE /*caseExpr=*/columnExpr? (WHEN /*whenExpr=*/columnExpr THEN /*thenExpr=*/columnExpr)+ (ELSE /*elseExpr=*/columnExpr)? END //#ColumnExprCase
| CAST LPAREN columnExpr AS columnTypeExpr RPAREN //#ColumnExprCast
| TRY_CAST LPAREN columnExpr AS columnTypeExpr RPAREN //#ColumnExprTryCast
| DATE STRING_LITERAL //#ColumnExprDate
// | EXTRACT LPAREN interval FROM columnExpr RPAREN //#ColumnExprExtract // Interferes with a function call
| INTERVAL STRING_LITERAL //#ColumnExprIntervalString
| INTERVAL columnExpr interval //#ColumnExprInterval
| SUBSTRING LPAREN columnExpr FROM columnExpr (FOR columnExpr)? RPAREN //#ColumnExprSubstring
| TIMESTAMP STRING_LITERAL //#ColumnExprTimestamp
| TRIM LPAREN (BOTH | LEADING | TRAILING) string FROM columnExpr RPAREN //#ColumnExprTrim
| COLUMNS LPAREN STRING_LITERAL RPAREN //#ColumnExprColumnsRegex
| COLUMNS LPAREN columnExprList RPAREN //#ColumnExprColumnsList
| (COLUMNS LPAREN ASTERISK EXCLUDE LPAREN identifierList RPAREN REPLACE LPAREN columnsReplaceList RPAREN RPAREN
| LPAREN ASTERISK EXCLUDE LPAREN identifierList RPAREN REPLACE LPAREN columnsReplaceList RPAREN RPAREN
) //#ColumnExprColumnsExcludeReplace
| COLUMNS LPAREN ASTERISK EXCLUDE LPAREN identifierList RPAREN RPAREN //#ColumnExprColumnsExclude
| (COLUMNS LPAREN ASTERISK REPLACE LPAREN columnsReplaceList RPAREN RPAREN
| LPAREN ASTERISK REPLACE LPAREN columnsReplaceList RPAREN RPAREN
) //#ColumnExprColumnsReplace
| COLUMNS LPAREN ASTERISK RPAREN //#ColumnExprColumnsAll
| COLUMNS LPAREN identifier DOT ASTERISK EXCLUDE LPAREN identifierList RPAREN REPLACE LPAREN columnsReplaceList RPAREN RPAREN //#ColumnExprColumnsQualifiedExcludeReplace
| COLUMNS LPAREN identifier DOT ASTERISK EXCLUDE LPAREN identifierList RPAREN RPAREN //#ColumnExprColumnsQualifiedExclude
| COLUMNS LPAREN identifier DOT ASTERISK REPLACE LPAREN columnsReplaceList RPAREN RPAREN //#ColumnExprColumnsQualifiedReplace
| COLUMNS LPAREN identifier DOT ASTERISK RPAREN //#ColumnExprColumnsQualifiedAll
| ASTERISK COLUMNS LPAREN STRING_LITERAL RPAREN //#ColumnExprSpreadColumnsRegex
| ASTERISK COLUMNS LPAREN columnExprList RPAREN //#ColumnExprSpreadColumnsList
| identifier LPAREN /*columnExprs=*/columnExprList? RPAREN withinGroupClause //#ColumnExprFunctionWithinGroup
| identifier (LPAREN /*columnExprs=*/columnExprList? RPAREN) (LPAREN DISTINCT? /*columnArgList=*/columnExprList? RPAREN)? (FILTER LPAREN WHERE /*filterExpr=*/columnExpr RPAREN)? OVER LPAREN windowExpr RPAREN //#ColumnExprWinFunction
| identifier (LPAREN /*columnExprs=*/columnExprList? RPAREN) (LPAREN DISTINCT? /*columnArgList=*/columnExprList? RPAREN)? (FILTER LPAREN WHERE /*filterExpr=*/columnExpr RPAREN)? OVER identifier //#ColumnExprWinFunctionTarget
| identifier (LPAREN /*columnExprs=*/columnExprList? RPAREN)? LPAREN DISTINCT? /*columnArgList=*/columnExprList? (ORDER BY orderExprList)? RPAREN (FILTER LPAREN WHERE /*filterExpr=*/columnExpr RPAREN)? //#ColumnExprFunction
| columnExpr LPAREN selectSetStmt RPAREN //#ColumnExprCallSelect
| columnExpr LPAREN columnExprList? RPAREN //#ColumnExprCall
| hogqlxTagElement //#ColumnExprTagElement
| templateString //#ColumnExprTemplateString
| literal //#ColumnExprLiteral
// FIXME(ilezhankin) ::= this part looks very ugly, maybe there is another way to express it
| columnExpr LBRACKET columnExpr RBRACKET //#ColumnExprArrayAccess
| columnExpr LBRACKET columnExpr? COLON columnExpr? RBRACKET //#ColumnExprArraySlice
| columnExpr DOT DECIMAL_LITERAL //#ColumnExprTupleAccess
| columnExpr DOT identifier //#ColumnExprPropertyAccess
| columnExpr NULL_PROPERTY LBRACKET columnExpr RBRACKET //#ColumnExprNullArrayAccess
| columnExpr NULL_PROPERTY DECIMAL_LITERAL //#ColumnExprNullTupleAccess
| columnExpr NULL_PROPERTY identifier //#ColumnExprNullPropertyAccess
| columnExpr DOUBLECOLON columnTypeCastExpr //#ColumnExprTypeCast
| DASH columnExpr //#ColumnExprNegate
| /*left=*/columnExpr ( /*operator=*/ASTERISK // *
| /*operator=*/SLASH // /
| /*operator=*/PERCENT // %
) /*right=*/columnExpr //#ColumnExprPrecedence1
| /*left=*/columnExpr ( /*operator=*/PLUS // +
| /*operator=*/DASH // -
| /*operator=*/CONCAT // ||
) /*right=*/columnExpr //#ColumnExprPrecedence2
| /*left=*/columnExpr ( /*operator=*/EQ_DOUBLE // =
| /*operator=*/EQ_SINGLE // ==
| /*operator=*/NOT_EQ // !=
| /*operator=*/LT_EQ // <=
| /*operator=*/LT // <
| /*operator=*/GT_EQ // >=
| /*operator=*/GT // >
| /*operator=*/NOT? IN COHORT? // in, not in; in cohort; not in cohort
| /*operator=*/NOT? (LIKE | ILIKE) // like, not like, ilike, not ilike
| /*operator=*/REGEX_SINGLE // ~
| /*operator=*/REGEX_DOUBLE // =~
| /*operator=*/NOT_REGEX // !~
| /*operator=*/IREGEX_SINGLE // ~*
| /*operator=*/IREGEX_DOUBLE // =~*
| /*operator=*/NOT_IREGEX // !~*
) /*right=*/columnExpr //#ColumnExprPrecedence3
| columnExpr IGNORE NULLS //#ColumnExprIgnoreNulls
| columnExpr IS NOT? NULL_SQL //#ColumnExprIsNull
| columnExpr IS NOT? DISTINCT FROM columnExpr //#ColumnExprIsDistinctFrom
| columnExpr NULLISH columnExpr //#ColumnExprNullish
| NOT columnExpr //#ColumnExprNot
| columnExpr AND columnExpr //#ColumnExprAnd
| columnExpr OR columnExpr //#ColumnExprOr
// TODO(ilezhankin) ::= `BETWEEN a AND b AND c` is parsed in a wrong way ::= `BETWEEN (a AND b) AND c`
| columnExpr NOT? BETWEEN columnExpr AND columnExpr //#ColumnExprBetween
| /*<assoc=right>*/ columnExpr QUERY columnExpr COLON columnExpr //#ColumnExprTernaryOp
| columnExpr (AS identifier | AS STRING_LITERAL) //#ColumnExprAlias
| (tableIdentifier DOT)? ASTERISK (EXCLUDE LPAREN identifierList RPAREN)? //#ColumnExprAsterisk // single-column only
| LAMBDA identifier (COMMA identifier)* COMMA? COLON columnExpr //#ColumnExprColonLambda
| LPAREN selectSetStmt RPAREN //#ColumnExprSubquery // single-column only
| LPAREN columnExpr RPAREN //#ColumnExprParens // single-column only
| LPAREN columnExprList RPAREN //#ColumnExprTuple
| ARRAY? LBRACKET columnExprList? RBRACKET //#ColumnExprArray
| LBRACE (kvPairList)? RBRACE //#ColumnExprDict
| columnLambdaExpr //#ColumnExprLambda
| identifier COLONEQUALS columnExpr //#ColumnExprNamedArg
| HASH DECIMAL_LITERAL //#ColumnExprPositional
| columnIdentifier //#ColumnExprIdentifier
columnLambdaExpr::=
( LPAREN identifier (COMMA identifier)* COMMA? RPAREN
| identifier (COMMA identifier)* COMMA?
| LPAREN RPAREN
)
ARROW (columnExpr | block) //#ArrowLambda
| LAMBDA identifier (COMMA identifier)* COMMA? COLON columnExpr //#ColonLambda
columnsReplaceList ::= columnsReplaceItem (COMMA columnsReplaceItem)*
columnsReplaceItem ::= columnExpr AS identifier
hogqlxChildElement
::= hogqlxTagElement
| hogqlxText
| LBRACE columnExpr RBRACE
hogqlxText ::= HOGQLX_TEXT_TEXT
hogqlxTagElement
::= LT identifier hogqlxTagAttribute* SLASH_GT //#HogqlxTagElementClosed
| LT identifier hogqlxTagAttribute* GT hogqlxChildElement* LT_SLASH identifier GT //#HogqlxTagElementNested
hogqlxTagAttribute
::= identifier EQ_SINGLE string
| identifier EQ_SINGLE LBRACE columnExpr RBRACE
| identifier
withExprList ::= withExpr (COMMA withExpr)* COMMA?
withExpr
::= identifier withExprColumnNameList? (USING KEY withExprColumnNameList)? AS (NOT? MATERIALIZED)? LPAREN selectSetStmt RPAREN //#WithExprSubquery
// NOTE ::= asterisk and subquery goes before |columnExpr| so that we can mark them as multi-column expressions.
| columnExpr AS identifier //#WithExprColumn
withExprColumnNameList ::= LPAREN identifier (COMMA identifier)* RPAREN
// This is slightly different in HogQL compared to ClickHouse SQL
// HogQL allows unlimited ("*") nestedIdentifier-s "properties.b.a.a.w.a.s".
// We parse and convert "databaseIdentifier.tableIdentifier.columnIdentifier.nestedIdentifier.*"
// to just one ast.Field(/*chain=*/['a','b','columnIdentifier','on','and','on']).
columnIdentifier ::= placeholder | ((tableIdentifier DOT)? nestedIdentifier)
nestedIdentifier ::= identifier (DOT identifier)*
tableExpr
::= tableIdentifier //#TableExprIdentifier
| tableFunctionExpr //#TableExprFunction
| LPAREN selectSetStmt RPAREN //#TableExprSubquery
| LPAREN valuesClause RPAREN //#TableExprValues
| tableExpr PIVOT LPAREN columnExprList pivotColumnList (GROUP BY columnExprList)? RPAREN //#TableExprPivot
| tableExpr UNPIVOT (INCLUDE NULLS)? LPAREN unpivotColumnList RPAREN //#TableExprUnpivot
| tableExpr (alias | AS identifier) columnAliases? //#TableExprAlias
| hogqlxTagElement //#TableExprTag
| placeholder //#TableExprPlaceholder
pivotColumnList ::= FOR pivotColumn+
pivotColumn ::= columnExprTupleOrSingle IN LPAREN columnExprList RPAREN
unpivotColumnList ::= unpivotColumn (COMMA unpivotColumn)* COMMA?
unpivotColumn ::= columnExprTupleOrSingle FOR columnExprTupleOrSingle IN LPAREN columnExprList RPAREN (columnExprTupleOrSingle IN LPAREN columnExprList RPAREN)*
columnExprTupleOrSingle ::= LPAREN columnExprList RPAREN | columnExpr
columnAliases ::= LPAREN identifier (COMMA identifier)* RPAREN
tableFunctionExpr ::= identifier LPAREN tableArgList? RPAREN
tableIdentifier ::= (databaseIdentifier DOT)? nestedIdentifier
tableArgList ::= columnExpr (COMMA columnExpr)* COMMA?
// Databases
databaseIdentifier ::= identifier
// Basics
floatingLiteral
::= FLOATING_LITERAL
| DOT (DECIMAL_LITERAL | OCTAL_LITERAL)
| DECIMAL_LITERAL DOT (DECIMAL_LITERAL | OCTAL_LITERAL)? // can't move this to the lexer or it will break nested tuple access ::= t.1.2
numberLiteral ::= (PLUS | DASH)? (floatingLiteral | OCTAL_LITERAL | DECIMAL_LITERAL | HEXADECIMAL_LITERAL | INF | NAN_SQL)
literal
::= numberLiteral
| STRING_LITERAL
| NULL_SQL
interval ::= SECOND | MINUTE | HOUR | DAY | WEEK | MONTH | QUARTER | YEAR
keyword
// except NULL_SQL, INF, NAN_SQL
::= ALL | AND | ANTI | ANY | ARRAY | AS | ASCENDING | ASOF | BETWEEN | BOTH | BY | CASE
| CAST | COHORT | COLLATE | COLUMNS | CROSS | CUBE | CURRENT | DATE | DESC | DESCENDING
| DISTINCT | ELSE | END | EXCLUDE | EXTRACT | FILTER | FINAL | FIRST
| FOR | FOLLOWING | FROM | FULL | GROUP | HAVING | ID | IS
| GROUPING | IF | IGNORE | ILIKE | INCLUDE | IN | INNER | INTERVAL | JOIN | KEY
| LAMBDA | LAST | LEADING | LEFT | LIKE | LIMIT
| NAME | NATURAL | NOT | NULLS | OFFSET | ON | OR | ORDER | OUTER | OVER | PARTITION
| PIVOT | POSITIONAL | PRECEDING | PREWHERE | QUALIFY | RANGE | RECURSIVE | REPLACE | RETURN | RIGHT | ROLLUP | ROW
| ROWS | SAMPLE | SELECT | SEMI | SETS | SETTINGS | SUBSTRING
| THEN | TIES | TIMESTAMP | TOTALS | TRAILING | TRIM | TRUNCATE | TRY_CAST | TO | TOP
| UNBOUNDED | UNION | UNPIVOT | USING | VALUES | WHEN | WHERE | WINDOW | WITH
keywordForAlias
::= DATE | FIRST | ID | KEY
alias ::= IDENTIFIER | keywordForAlias // |interval| can't be an alias, otherwise 'INTERVAL 1 SOMETHING' becomes ambiguous.
identifier ::= IDENTIFIER | interval | keyword
enumValue ::= string EQ_SINGLE numberLiteral
placeholder ::= LBRACE columnExpr RBRACE
string ::= STRING_LITERAL | templateString
templateString ::= QUOTE_SINGLE_TEMPLATE stringContents* QUOTE_SINGLE
stringContents ::= STRING_ESCAPE_TRIGGER columnExpr RBRACE | STRING_TEXT
// These are magic "full template strings", which are used to parse "full text field" templates without the surrounding SQL.
// We will need to add F' to the start of the string to change the lexer's mode.
fullTemplateString ::= QUOTE_SINGLE_TEMPLATE_FULL stringContentsFull* EOF
stringContentsFull ::= FULL_STRING_ESCAPE_TRIGGER columnExpr RBRACE | FULL_STRING_TEXT
//Tokens
// Keywords
ALL ::= "ALL"
AND ::= "AND"
ANTI ::= "ANTI"
ANY ::= "ANY"
ARRAY ::= "ARRAY"
AS ::= "AS"
ASCENDING ::= "ASCENDING"
ASOF ::= "ASOF"
BETWEEN ::= "BETWEEN"
BOTH ::= "BOTH"
BY ::= "BY"
CASE ::= "CASE"
CAST ::= "CAST"
CATCH ::= "CATCH"
COHORT ::= "COHORT"
COLLATE ::= "COLLATE"
COLUMNS ::= "COLUMNS"
CROSS ::= "CROSS"
CUBE ::= "CUBE"
CURRENT ::= "CURRENT"
DATE ::= "DATE"
DAY ::= "DAY"
DESC ::= "DESC"
DESCENDING ::= "DESCENDING"
DISTINCT ::= "DISTINCT"
ELSE ::= "ELSE"
END ::= "END"
EXCEPT ::= "EXCEPT"
EXCLUDE ::= "EXCLUDE"
EXTRACT ::= "EXTRACT"
FINAL ::= "FINAL"
FILTER ::= "FILTER"
FINALLY ::= "FINALLY"
FIRST ::= "FIRST"
FN ::= "FN"
FOLLOWING ::= "FOLLOWING"
FOR ::= "FOR"
FROM ::= "FROM"
FULL ::= "FULL"
FUN ::= "FUN"
GROUP ::= "GROUP"
GROUPING ::= "GROUPING"
HAVING ::= "HAVING"
HOUR ::= "HOUR"
ID ::= "ID"
IF ::= "IF"
ILIKE ::= "ILIKE"
IGNORE ::= "IGNORE"
INCLUDE ::= "INCLUDE"
IN ::= "IN"
INF ::= "INF"
INNER ::= "INNER"
INTERSECT ::= "INTERSECT"
INTERVAL ::= "INTERVAL"
IS ::= "IS"
JOIN ::= "JOIN"
KEY ::= "KEY"
LAMBDA ::= "LAMBDA"
LAST ::= "LAST"
LEADING ::= "LEADING"
LEFT ::= "LEFT"
LET ::= "LET"
LIKE ::= "LIKE"
LIMIT ::= "LIMIT"
MATERIALIZED ::= "MATERIALIZED"
MINUTE ::= "MINUTE"
MONTH ::= "MONTH"
NAME ::= "NAME"
NATURAL ::= "NATURAL"
NAN_SQL ::= "NAN_SQL"
NOT ::= "NOT"
NULL_SQL ::= "NULL_SQL"
NULLS ::= "NULLS"
OFFSET ::= "OFFSET"
ON ::= "ON"
OR ::= "OR"
ORDER ::= "ORDER"
OUTER ::= "OUTER"
OVER ::= "OVER"
PARTITION ::= "PARTITION"
PIVOT ::= "PIVOT"
POSITIONAL ::= "POSITIONAL"
PRECEDING ::= "PRECEDING"
PREWHERE ::= "PREWHERE"
QUALIFY ::= "QUALIFY"
QUARTER ::= "QUARTER"
RANGE ::= "RANGE"
RECURSIVE ::= "RECURSIVE"
REPLACE ::= "REPLACE"
RETURN ::= "RETURN"
RIGHT ::= "RIGHT"
ROLLUP ::= "ROLLUP"
ROW ::= "ROW"
ROWS ::= "ROWS"
SAMPLE ::= "SAMPLE"
SECOND ::= "SECOND"
SELECT ::= "SELECT"
SEMI ::= "SEMI"
SETS ::= "SETS"
SETTINGS ::= "SETTINGS"
SUBSTRING ::= "SUBSTRING"
THEN ::= "THEN"
THROW ::= "THROW"
TIES ::= "TIES"
TIMESTAMP ::= "TIMESTAMP"
TO ::= "TO"
TOP ::= "TOP"
TOTALS ::= "TOTALS"
TRAILING ::= "TRAILING"
TRIM ::= "TRIM"
TRUNCATE ::= "TRUNCATE"
TRY ::= "TRY"
TRY_CAST ::= "TRY_CAST"
UNBOUNDED ::= "UNBOUNDED"
UNION ::= "UNION"
UNPIVOT ::= "UNPIVOT"
USING ::= "USING"
VALUES ::= "VALUES"
WEEK ::= "WEEK"
WHEN ::= "WHEN"
WHERE ::= "WHERE"
WHILE ::= "WHILE"
WINDOW ::= "WINDOW"
WITH ::= "WITH"
WITHIN ::= "WITHIN"
YEAR ::= "YEAR"
ARROW ::= "->"
ASTERISK ::= "*"
BACKQUOTE ::= "`"
BACKSLASH ::= "\\"
DOUBLECOLON ::= "::"
COLONEQUALS ::= ":="
COLON ::= ":"
COMMA ::= ","
CONCAT ::= "||"
DASH ::= "-"
DOLLAR ::= "$"
DOT ::= "."
EQ_DOUBLE ::= "=="
EQ_SINGLE ::= "="
GT_EQ ::= ">="
GT ::= ">"
HASH ::= "#"
IREGEX_SINGLE ::= "~*"
IREGEX_DOUBLE ::= "=~*"
LBRACE ::= "{" //: '{' -> pushMode(DEFAULT_MODE);
LBRACKET ::= "["
LPAREN ::= "("
LT_EQ ::= "<="
TAG_LT_SLASH ::= "</" //: '</' -> type(LT_SLASH), pushMode(HOGQLX_TAG_CLOSE);
TAG_LT_OPEN ::= "<" //: '' {isOpeningTag()}? -> type(LT), pushMode(HOGQLX_TAG_OPEN);
LT ::= "<"
LT_SLASH ::= "</"
NOT_EQ ::= "'!="|"<>"
NOT_IREGEX ::= "!~*"
NOT_REGEX ::= "!~"
NULL_PROPERTY ::= "?."
NULLISH ::= "??"
PERCENT ::= "%"
PLUS ::= "+"
QUERY ::= "?"
QUOTE_DOUBLE ::= '"'
QUOTE_SINGLE_TEMPLATE ::= "f'" //: '' -> pushMode(IN_TEMPLATE_STRING); // start of regular f'' template strings
QUOTE_SINGLE_TEMPLATE_FULL ::= "F'" //: '' -> pushMode(IN_FULL_TEMPLATE_STRING); // magic F' symbol used to parse "full text" templates
QUOTE_SINGLE ::= "'"
REGEX_SINGLE ::= "~"
REGEX_DOUBLE ::= "=~"
RBRACE ::= "}" //: '}' -> popMode;
RBRACKET ::= "]"
RPAREN ::= ")"
SEMICOLON ::= ";"
SLASH ::= "/"
SLASH_GT ::= "/>"
UNDERSCORE ::= "_"
Debug info
- [ ] PostHog Cloud, Debug information: [please copy/paste from https://us.posthog.com/settings/project-details#variables]
- [ ] PostHog Hobby self-hosted with `docker compose`, version/commit: [please provide]
- [ ] PostHog self-hosted with Kubernetes (deprecated, see [`Sunsetting Kubernetes support`](https://posthog.com/blog/sunsetting-helm-support-posthog)), version/commit: [please provide]
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start by locating HogQLParser.g4 and read the instructions at the top of the issue for the BottleCaps railroad-diagram workflow. Adapt the grammar as needed for the diagram tool, then verify that the resulting navigable diagram represents the documented grammar and is available to project users.
Written by the indexing model from the issue text.
Assessment
- Domain
- documentation
- Issue type
- Documentation
- Difficulty
- 3/5
- Estimated time
- 1-2 days
- Activity status
- Quiet
- Clarity
- Mostly clear
- Newbie friendliness
- 48/100