sqlode/query_analyzer/expr_parser
Recursive-descent parser that turns a list of SQL tokens into the
expression-aware IR (query_ir.Expr / query_ir.SelectCore /
query_ir.Stmt).
The parser is deliberately permissive: it models the SQL subset
sqlode needs to reason about (the constructs exercised by the
fixtures in test/fixtures/complex_sql/) and falls back to
query_ir.RawExpr / query_ir.UnstructuredStmt with an explicit
reason string when it hits a construct it does not understand.
The downstream analyzer surfaces these as
AnalysisError.UnsupportedExpression, so “silent fallback to
StringType” never happens — every gap is tied to a concrete IR
node an operator can point at.
Every public parsing entry takes an engine: model.Engine parameter
so MySQL-only constructs (ON DUPLICATE KEY UPDATE,
LIMIT offset, count) can be recognised without polluting the
PostgreSQL / SQLite paths. The engine is threaded through every
internal helper that (directly or transitively) parses another
expression, select core, or statement. Pure token-shape helpers
(paren collection, comma splitting, keyword scanning, etc.) do not
receive it — they are dialect-agnostic.
Precedence roughly follows PostgreSQL’s operator table:
- OR
- AND
- NOT
- IS [NOT] NULL/TRUE/FALSE, IS [NOT] DISTINCT FROM
- =, <>, !=, <, >, <=, >=, LIKE, ILIKE, IN, BETWEEN, SIMILAR TO, @>, <@, ?|, ?&, &&
- +, -, ||, JSON ops (->, ->>, #>, #>>)
- *, /, %
- unary -, +
- ::type cast
- function calls / atoms
Values
pub fn parse_expr(
tokens: List(lexer.Token),
engine: model.Engine,
) -> query_ir.Expr
pub fn parse_select_core(
tokens: List(lexer.Token),
engine: model.Engine,
) -> #(query_ir.SelectCore, List(lexer.Token))
pub fn parse_stmt(
tokens: List(lexer.Token),
engine: model.Engine,
) -> query_ir.Stmt
Parse a full statement from its token list. Never fails; unknown
constructs surface as UnstructuredStmt(reason, tokens) with the
raw tokens preserved for legacy passes and the reason string
bubbled up to analyzer diagnostics.