use radixdb_core::SmartString;
use super::ast::*;
use super::parser::Parser;
use super::precedence::Precedence;
use super::token::{Token, TokenType};
fn validate_cast_type_name(type_name: &str) -> Result<(), String> {
let upper = type_name.to_ascii_uppercase();
let (base, arguments) = match upper.split_once('(') {
Some((base, suffix)) => {
let Some(arguments) = suffix.strip_suffix(')') else {
return Err(format!("invalid CAST target type: {type_name}"));
};
(base, Some(arguments))
}
None => (upper.as_str(), None),
};
if arguments.is_none()
&& base.contains('.')
&& base.split('.').all(|component| !component.is_empty())
{
return Ok(());
}
if !matches!(
base,
"INTEGER"
| "INT"
| "BIGINT"
| "SMALLINT"
| "TINYINT"
| "FLOAT"
| "DOUBLE"
| "REAL"
| "DECIMAL"
| "NUMERIC"
| "TEXT"
| "VARCHAR"
| "CHAR"
| "STRING"
| "CLOB"
| "BOOLEAN"
| "BOOL"
| "TIMESTAMP"
| "DATETIME"
| "TIME"
| "DATE"
| "JSON"
| "JSONB"
| "UUID"
| "BYTES"
| "BLOB"
| "BINARY"
| "VARBINARY"
| "VECTOR"
) {
return Err(format!("unknown CAST target type: {type_name}"));
}
let Some(arguments) = arguments else {
return Ok(());
};
let values: Vec<&str> = arguments.split(',').map(str::trim).collect();
let parse_positive = |value: &str| value.parse::<usize>().ok().filter(|&parsed| parsed > 0);
match base {
"VARCHAR" | "CHAR" => {
if values.len() == 1 && parse_positive(values[0]).is_some() {
Ok(())
} else {
Err(format!("{base} requires one positive length"))
}
}
"DECIMAL" | "NUMERIC" => {
let Some(precision) = values.first().and_then(|value| parse_positive(value)) else {
return Err(format!("{base} requires a positive precision"));
};
if values.len() > 2 || precision > 38 {
return Err(format!("invalid {base} precision/scale: {arguments}"));
}
if let Some(scale) = values.get(1).and_then(|value| value.parse::<usize>().ok()) {
if scale <= precision {
Ok(())
} else {
Err(format!("{base} scale cannot exceed precision"))
}
} else if values.len() == 1 {
Ok(())
} else {
Err(format!("invalid {base} precision/scale: {arguments}"))
}
}
"VECTOR" => {
if values.len() == 1
&& parse_positive(values[0]).is_some_and(|dimension| dimension <= u16::MAX as usize)
{
Ok(())
} else {
Err("VECTOR requires one dimension between 1 and 65535".to_string())
}
}
_ => Err(format!("type modifiers are not supported for {base}")),
}
}
fn window_frame_rank(bound: &WindowFrameBound) -> i128 {
match bound {
WindowFrameBound::UnboundedPreceding => i128::MIN,
WindowFrameBound::Preceding(expression) => match expression.as_ref() {
Expression::IntegerLiteral(value) => -(value.value as i128),
_ => unreachable!("window offsets are validated before frame ordering"),
},
WindowFrameBound::CurrentRow => 0,
WindowFrameBound::Following(expression) => match expression.as_ref() {
Expression::IntegerLiteral(value) => value.value as i128,
_ => unreachable!("window offsets are validated before frame ordering"),
},
WindowFrameBound::UnboundedFollowing => i128::MAX,
}
}
fn window_frame_is_ordered(start: &WindowFrameBound, end: Option<&WindowFrameBound>) -> bool {
if matches!(start, WindowFrameBound::UnboundedFollowing)
|| end.is_some_and(|bound| matches!(bound, WindowFrameBound::UnboundedPreceding))
{
return false;
}
window_frame_rank(start)
<= end
.map(window_frame_rank)
.unwrap_or_else(|| window_frame_rank(&WindowFrameBound::CurrentRow))
}
impl Parser {
pub fn parse_expression(&mut self, precedence: Precedence) -> Option<Expression> {
if !self.enter_expression() {
return None;
}
let result = self.parse_expression_inner(precedence);
self.expression_depth -= 1;
result
}
fn parse_expression_inner(&mut self, precedence: Precedence) -> Option<Expression> {
let mut left = self.parse_prefix_expression()?;
while !self.peek_token_is(TokenType::Eof) && precedence < self.peek_precedence() {
if !self.is_infix_token() {
return Some(left);
}
self.next_token();
left = self.parse_infix_expression(left)?;
}
Some(left)
}
fn is_infix_token(&self) -> bool {
match self.peek_token.token_type {
TokenType::Operator => true,
TokenType::Keyword => {
let kw = self.peek_token.literal.to_uppercase();
matches!(
kw.as_str(),
"AND"
| "OR"
| "XOR"
| "LIKE"
| "ILIKE"
| "GLOB"
| "REGEXP"
| "RLIKE"
| "IS"
| "IN"
| "BETWEEN"
| "NOT"
)
}
TokenType::Punctuator => {
matches!(self.peek_token.literal.as_str(), "." | "(" | "[")
}
_ => false,
}
}
fn parse_prefix_expression(&mut self) -> Option<Expression> {
match self.cur_token.token_type {
TokenType::Identifier => Some(self.parse_identifier()),
TokenType::Integer => self.parse_integer_literal(),
TokenType::Float => self.parse_float_literal(),
TokenType::String => Some(self.parse_string_literal()),
TokenType::Parameter => self.parse_parameter(),
TokenType::Keyword => self.parse_keyword_expression(),
TokenType::Operator => self.parse_unary_expression(),
TokenType::Punctuator => self.parse_punctuator_expression(),
TokenType::Error => {
self.add_error(self.cur_token.literal.to_string());
None
}
_ => {
if self.cur_token.token_type == TokenType::Eof {
if self.current_clause.is_empty() {
self.add_error("unexpected end of input, expected expression".to_string());
} else {
self.add_error(format!(
"expected expression after {}",
self.current_clause
));
}
} else {
self.add_error(format!(
"unexpected '{}', expected expression",
self.cur_token.literal
));
}
None
}
}
}
fn parse_identifier(&self) -> Expression {
if !self.cur_token.quoted && !self.peek_token_is_punctuator("(") {
let upper = self.cur_token.literal.to_uppercase();
if upper == "CURRENT_DATE" || upper == "CURRENT_TIME" || upper == "CURRENT_TIMESTAMP" {
return Expression::FunctionCall(Box::new(FunctionCall {
token: self.cur_token.clone(),
function: SmartString::new(&upper),
arguments: vec![],
is_distinct: false,
order_by: vec![],
filter: None,
}));
}
}
Expression::Identifier(Identifier::new(
self.cur_token.clone(),
self.cur_token.literal.clone(),
))
}
fn parse_integer_literal(&mut self) -> Option<Expression> {
match self.cur_token.literal.parse::<i64>() {
Ok(value) => Some(Expression::IntegerLiteral(IntegerLiteral {
token: self.cur_token.clone(),
value,
})),
Err(error) => {
self.add_error(format!(
"integer literal {} is out of range for i64: {}",
self.cur_token.literal, error
));
None
}
}
}
fn parse_float_literal(&mut self) -> Option<Expression> {
self.parse_finite_float_value().map(|value| {
Expression::FloatLiteral(FloatLiteral {
token: self.cur_token.clone(),
value,
})
})
}
fn parse_finite_float_value(&mut self) -> Option<f64> {
let source = self.cur_token.literal.as_str();
match source.parse::<f64>() {
Ok(value) if !value.is_finite() => {
self.add_error(format!(
"float literal {} is out of finite f64 range",
source
));
None
}
Ok(value)
if value == 0.0
&& source.split(['e', 'E']).next().is_some_and(|mantissa| {
mantissa.bytes().any(|b| matches!(b, b'1'..=b'9'))
}) =>
{
self.add_error(format!("float literal {} underflows finite f64", source));
None
}
Ok(value) => Some(value),
Err(e) => {
self.add_error(format!("could not parse {} as float: {}", source, e));
None
}
}
}
fn parse_string_literal(&self) -> Expression {
let literal = &self.cur_token.literal;
let value = if literal.len() >= 2 {
let inner = &literal[1..literal.len() - 1];
if inner.contains('\\') {
let mut result = String::with_capacity(inner.len());
let mut chars = inner.chars().peekable();
while let Some(c) = chars.next() {
if c == '\\' {
match chars.peek() {
Some('n') => {
result.push('\n');
chars.next();
}
Some('t') => {
result.push('\t');
chars.next();
}
Some('r') => {
result.push('\r');
chars.next();
}
Some('\'') => {
result.push('\'');
chars.next();
}
Some('\\') => {
result.push('\\');
chars.next();
}
_ => result.push(c), }
} else {
result.push(c);
}
}
SmartString::from_string(result)
} else {
SmartString::new(inner)
}
} else {
literal.clone()
};
Expression::StringLiteral(StringLiteral {
token: self.cur_token.clone(),
value,
type_hint: None,
})
}
fn parse_parameter(&mut self) -> Option<Expression> {
let name = self.cur_token.literal.clone();
let index = if name == "?" {
if self.positional_parameter_style
== Some(super::parser::PositionalParameterStyle::Explicit)
{
self.add_error("cannot mix '?' and '$n' positional parameters".to_string());
return None;
}
self.positional_parameter_style =
Some(super::parser::PositionalParameterStyle::Anonymous);
self.next_parameter_index()
} else if let Some(stripped) = name.strip_prefix('$') {
if self.positional_parameter_style
== Some(super::parser::PositionalParameterStyle::Anonymous)
{
self.add_error("cannot mix '?' and '$n' positional parameters".to_string());
return None;
}
self.positional_parameter_style =
Some(super::parser::PositionalParameterStyle::Explicit);
match stripped.parse::<usize>() {
Ok(0) => {
self.add_error(
"positional parameter indexes start at $1; $0 is invalid".to_string(),
);
return None;
}
Ok(idx) => idx,
Err(e) => {
self.add_error(format!("invalid parameter index: {}", e));
return None;
}
}
} else if name.starts_with(':') {
0
} else {
self.add_error(format!("invalid parameter format: {}", name));
return None;
};
Some(Expression::Parameter(Parameter {
token: self.cur_token.clone(),
name,
index,
field: None,
}))
}
fn parse_keyword_expression(&mut self) -> Option<Expression> {
let keyword = self.cur_token.literal.to_uppercase();
match keyword.as_str() {
"TRUE" => Some(Expression::BooleanLiteral(BooleanLiteral {
token: self.cur_token.clone(),
value: true,
})),
"FALSE" => Some(Expression::BooleanLiteral(BooleanLiteral {
token: self.cur_token.clone(),
value: false,
})),
"NULL" => Some(Expression::NullLiteral(NullLiteral {
token: self.cur_token.clone(),
})),
"CASE" => self.parse_case_expression(),
"CAST" => self.parse_cast_expression(),
"EXTRACT" => self.parse_extract_expression(),
"EXISTS" => self.parse_exists_expression(),
"NOT" => self.parse_not_expression(),
"INTERVAL" => self.parse_interval_literal(),
"DEFAULT" => Some(Expression::Default(DefaultExpression {
token: self.cur_token.clone(),
})),
"TIMESTAMP" | "DATE" | "TIME" => {
if self.peek_token_is(TokenType::String) {
self.parse_typed_literal()
} else {
Some(Expression::Identifier(Identifier::new(
self.cur_token.clone(),
self.cur_token.literal.clone(),
)))
}
}
"LEFT" | "RIGHT" | "CHAR" | "FIRST" | "LAST" | "TRUNCATE" => {
if self.peek_token_is_punctuator("(") {
let ident = Expression::Identifier(Identifier::new(
self.cur_token.clone(),
self.cur_token.literal.clone(),
));
self.next_token(); self.parse_function_call(ident)
} else {
Some(Expression::Identifier(Identifier::new(
self.cur_token.clone(),
self.cur_token.literal.clone(),
)))
}
}
_ if !Self::is_reserved_keyword(&keyword) => Some(Expression::Identifier(
Identifier::new(self.cur_token.clone(), self.cur_token.literal.clone()),
)),
_ => {
if self.current_clause.is_empty() {
self.add_error(format!(
"'{}' cannot be used here, expected expression",
keyword
));
} else {
self.add_error(format!(
"'{}' cannot be used in {} clause, expected expression",
keyword, self.current_clause
));
}
None
}
}
}
fn parse_unary_expression(&mut self) -> Option<Expression> {
let token = self.cur_token.clone();
let operator = self.cur_token.literal.clone();
if operator == "*" {
if self.peek_token.is_eof()
|| self.peek_token.is_punctuator(",")
|| self.peek_token.is_punctuator(";")
|| self.peek_token.is_punctuator(")")
|| self.peek_token_is_keyword("FROM")
|| self.peek_token_is_keyword("WHERE")
|| self.peek_token_is_keyword("ORDER")
|| self.peek_token_is_keyword("GROUP")
|| self.peek_token_is_keyword("HAVING")
|| self.peek_token_is_keyword("LIMIT")
|| self.peek_token_is_keyword("UNION")
|| self.peek_token_is_keyword("INTERSECT")
|| self.peek_token_is_keyword("EXCEPT")
{
return Some(Expression::Star(StarExpression { token }));
}
}
self.next_token();
if operator == "-"
&& (self.cur_token_is(TokenType::Integer) || self.cur_token_is(TokenType::Float))
{
if self.cur_token_is(TokenType::Integer) {
if self.cur_token.literal == "9223372036854775808" {
return Some(Expression::IntegerLiteral(IntegerLiteral {
token: self.cur_token.clone(),
value: i64::MIN,
}));
}
if let Ok(value) = self.cur_token.literal.parse::<i64>() {
return Some(Expression::IntegerLiteral(IntegerLiteral {
token: self.cur_token.clone(),
value: -value,
}));
}
} else {
let value = self.parse_finite_float_value()?;
return Some(Expression::FloatLiteral(FloatLiteral {
token: self.cur_token.clone(),
value: -value,
}));
}
}
let right = self.parse_expression(Precedence::Prefix)?;
Some(Expression::Prefix(PrefixExpression::new(
token,
operator,
Box::new(right),
)))
}
fn parse_punctuator_expression(&mut self) -> Option<Expression> {
match self.cur_token.literal.as_str() {
"(" => self.parse_grouped_expression(),
"*" => Some(Expression::Star(StarExpression {
token: self.cur_token.clone(),
})),
_ => {
self.add_error(format!(
"unexpected punctuator: {} at {}",
self.cur_token.literal, self.cur_token.position
));
None
}
}
}
fn parse_grouped_expression(&mut self) -> Option<Expression> {
let token = self.cur_token.clone();
if self.peek_token_is_keyword("SELECT") {
self.next_token(); let subquery = self.parse_select_statement()?;
if !self.peek_token_is_punctuator(")") {
self.add_error(format!(
"expected ')' after scalar subquery, got {}",
Self::format_token_for_error(&self.peek_token)
));
return None;
}
self.next_token();
return Some(Expression::ScalarSubquery(ScalarSubquery {
token,
subquery: Box::new(subquery),
}));
}
self.next_token();
if self.cur_token_is_punctuator(")") {
return Some(Expression::Identifier(Identifier::new(
self.cur_token.clone(),
SmartString::const_new("()"),
)));
}
let first_expr = self.parse_expression(Precedence::Lowest)?;
if self.peek_token_is_punctuator(",") {
let mut expressions = vec![first_expr];
while self.peek_token_is_punctuator(",") {
self.next_token(); self.next_token();
if let Some(expr) = self.parse_expression(Precedence::Lowest) {
expressions.push(expr);
}
}
if !self.peek_token_is_punctuator(")") {
self.add_error(format!(
"expected ')', got {}",
Self::format_token_for_error(&self.peek_token)
));
return None;
}
self.next_token();
return Some(Expression::ExpressionList(Box::new(ExpressionList {
token,
expressions,
})));
}
if !self.peek_token_is_punctuator(")") {
self.add_error(format!(
"expected ')', got {}",
Self::format_token_for_error(&self.peek_token)
));
return None;
}
self.next_token();
Some(first_expr)
}
fn parse_infix_expression(&mut self, left: Expression) -> Option<Expression> {
match self.cur_token.token_type {
TokenType::Operator => self.parse_binary_expression(left),
TokenType::Keyword => self.parse_keyword_infix(left),
TokenType::Punctuator => self.parse_punctuator_infix(left),
_ => {
self.add_error(format!(
"unexpected infix token: {:?} at {}",
self.cur_token.token_type, self.cur_token.position
));
None
}
}
}
fn parse_binary_expression(&mut self, left: Expression) -> Option<Expression> {
let token = self.cur_token.clone();
let operator = self.cur_token.literal.clone();
let precedence = self.cur_precedence();
self.next_token();
if self.cur_token_is_keyword("ALL")
|| self.cur_token_is_keyword("ANY")
|| self.cur_token_is_keyword("SOME")
{
if !matches!(
operator.as_str(),
"=" | "<>" | "!=" | "<" | "<=" | ">" | ">="
) {
self.add_error(format!(
"{} is not a valid comparison operator before {}",
operator, self.cur_token.literal
));
return None;
}
return self.parse_all_any_expression(left, token, operator);
}
let right = self.parse_expression(precedence)?;
Some(Expression::Infix(InfixExpression::new(
token,
Box::new(left),
operator,
Box::new(right),
)))
}
fn parse_all_any_expression(
&mut self,
left: Expression,
token: Token,
operator: SmartString,
) -> Option<Expression> {
use super::ast::{AllAnyExpression, AllAnyType};
let all_any_type = if self.cur_token_is_keyword("ALL") {
AllAnyType::All
} else {
AllAnyType::Any };
self.next_token();
if !self.cur_token_is_punctuator("(") {
self.add_error(format!(
"expected '(' after {} at {}",
all_any_type, self.cur_token.position
));
return None;
}
self.next_token();
if !self.cur_token_is_keyword("SELECT") {
self.add_error(format!(
"expected SELECT in {} subquery at {}",
all_any_type, self.cur_token.position
));
return None;
}
let subquery = self.parse_select_statement()?;
if !self.expect_peek(TokenType::Punctuator) || self.cur_token.literal != ")" {
self.add_error(format!(
"expected ')' after {} subquery at {}",
all_any_type, self.cur_token.position
));
return None;
}
Some(Expression::AllAny(AllAnyExpression {
token,
left: Box::new(left),
operator,
all_any_type,
subquery: Box::new(subquery),
}))
}
fn parse_keyword_infix(&mut self, left: Expression) -> Option<Expression> {
let keyword = self.cur_token.literal.to_uppercase();
match keyword.as_str() {
"AND" | "OR" | "XOR" => {
let token = self.cur_token.clone();
let operator = keyword.clone();
let precedence = self.cur_precedence();
self.next_token();
let right = self.parse_expression(precedence)?;
Some(Expression::Infix(InfixExpression::new(
token,
Box::new(left),
operator,
Box::new(right),
)))
}
"LIKE" | "ILIKE" | "GLOB" | "REGEXP" | "RLIKE" => {
self.parse_like_expression(left, keyword.clone(), false)
}
"IS" => self.parse_is_expression(left),
"IN" => self.parse_in_expression(left, false),
"BETWEEN" => self.parse_between_expression(left, false),
"NOT" => {
if self.peek_token_is_keyword("IN") {
self.next_token(); self.parse_in_expression(left, true)
} else if self.peek_token_is_keyword("BETWEEN") {
self.next_token(); self.parse_between_expression(left, true)
} else if self.peek_token_is_keyword("LIKE") {
self.next_token(); self.parse_like_expression(left, SmartString::const_new("LIKE"), true)
} else if self.peek_token_is_keyword("ILIKE") {
self.next_token(); self.parse_like_expression(left, SmartString::const_new("ILIKE"), true)
} else if self.peek_token_is_keyword("GLOB") {
self.next_token(); self.parse_like_expression(left, SmartString::const_new("GLOB"), true)
} else if self.peek_token_is_keyword("REGEXP") {
self.next_token(); self.parse_like_expression(left, SmartString::const_new("REGEXP"), true)
} else if self.peek_token_is_keyword("RLIKE") {
self.next_token(); self.parse_like_expression(left, SmartString::const_new("RLIKE"), true)
} else {
self.add_error(format!(
"NOT must be followed by IN, BETWEEN, LIKE, ILIKE, GLOB, REGEXP, or RLIKE at {}",
self.peek_token.position
));
None
}
}
_ => {
self.add_error(format!(
"unexpected infix keyword: {} at {}",
keyword, self.cur_token.position
));
None
}
}
}
fn parse_punctuator_infix(&mut self, left: Expression) -> Option<Expression> {
match self.cur_token.literal.as_str() {
"." => self.parse_qualified_identifier(left),
"(" => self.parse_function_call(left),
"[" => self.parse_index_expression(left),
"*" => {
let token = self.cur_token.clone();
let precedence = Precedence::Product;
self.next_token();
let right = self.parse_expression(precedence)?;
Some(Expression::Infix(InfixExpression::new(
token,
Box::new(left),
SmartString::const_new("*"),
Box::new(right),
)))
}
_ => {
self.add_error(format!(
"unexpected infix punctuator: {} at {}",
self.cur_token.literal, self.cur_token.position
));
None
}
}
}
fn parse_qualified_identifier(&mut self, left: Expression) -> Option<Expression> {
if let Expression::Parameter(mut parameter) = left {
if parameter.index != 0 || !parameter.name.starts_with(':') || parameter.field.is_some()
{
self.add_error(format!(
"only one field may follow a named parameter at {}",
self.cur_token.position
));
return None;
}
if !self.peek_token_is(TokenType::Identifier) && !self.peek_token_is(TokenType::Keyword)
{
self.peek_error(TokenType::Identifier);
return None;
}
self.next_token();
parameter.field = Some(Box::new(Identifier::new(
self.cur_token.clone(),
self.cur_token.literal.clone(),
)));
return Some(Expression::Parameter(parameter));
}
let mut qualified = match left {
Expression::Identifier(id) => QualifiedIdentifier {
token: id.token.clone(),
qualifier: Box::new(id),
intermediate: None,
name: Box::new(Identifier::new(
self.cur_token.clone(),
SmartString::const_new(""),
)),
},
Expression::QualifiedIdentifier(path) => path,
_ => {
self.add_error(format!(
"left side of '.' must be an identifier path at {}",
self.cur_token.position
));
return None;
}
};
if self.peek_token_is_operator("*") {
if qualified.is_multi_part_path() || !qualified.name.value.is_empty() {
self.add_error(format!(
"qualified star cannot follow a multi-part identifier path at {}",
self.cur_token.position
));
return None;
}
self.next_token(); return Some(Expression::QualifiedStar(QualifiedStarExpression {
token: qualified.qualifier.token.clone(),
qualifier: qualified.qualifier.value,
}));
}
if !self.peek_token_is(TokenType::Identifier) && !self.peek_token_is(TokenType::Keyword) {
self.peek_error(TokenType::Identifier);
return None;
}
self.next_token();
let next = Identifier::new(self.cur_token.clone(), self.cur_token.literal.clone());
if !qualified.name.value.is_empty() {
qualified
.intermediate
.get_or_insert_with(|| Box::new(Vec::new()))
.push(*qualified.name);
}
qualified.name = Box::new(next);
Some(Expression::QualifiedIdentifier(qualified))
}
fn parse_function_call(&mut self, left: Expression) -> Option<Expression> {
let left_ident = match left {
Expression::Identifier(id) => id,
_ => {
self.add_error(format!(
"left side of '(' must be an identifier at {}",
self.cur_token.position
));
return None;
}
};
let mut call = FunctionCall {
token: left_ident.token.clone(),
function: left_ident.value.to_uppercase(),
arguments: Vec::new(),
is_distinct: false,
order_by: Vec::new(),
filter: None,
};
if self.peek_token_is_punctuator(")") {
self.next_token();
if self.peek_token_is_keyword("FILTER") {
call.filter = self.parse_filter_clause();
}
if self.peek_token_is_keyword("OVER") {
return self.parse_window_expression(call);
}
return Some(Expression::FunctionCall(Box::new(call)));
}
if self.peek_token_is_operator("*") {
self.next_token();
call.arguments.push(Expression::Star(StarExpression {
token: self.cur_token.clone(),
}));
if self.peek_token_is_keyword("ORDER") {
self.parse_function_order_by(&mut call);
}
if !self.expect_peek(TokenType::Punctuator) || self.cur_token.literal != ")" {
self.add_error(format!("expected ')' at {}", self.cur_token.position));
return None;
}
if self.peek_token_is_keyword("FILTER") {
call.filter = self.parse_filter_clause();
}
if self.peek_token_is_keyword("OVER") {
return self.parse_window_expression(call);
}
return Some(Expression::FunctionCall(Box::new(call)));
}
if self.peek_token_is_keyword("DISTINCT") {
self.next_token();
call.is_distinct = true;
}
self.next_token();
let first_arg_precedence = if call.function == "POSITION" {
Precedence::LessGreater } else {
Precedence::Lowest
};
if let Some(arg) = self.parse_expression(first_arg_precedence) {
call.arguments.push(arg);
}
if call.function == "POSITION" && self.peek_token_is_keyword("IN") {
self.next_token(); self.next_token();
if let Some(string_arg) = self.parse_expression(Precedence::Lowest) {
call.arguments.push(string_arg);
} else {
self.add_error(format!(
"expected expression after IN in POSITION at {}",
self.cur_token.position
));
return None;
}
}
while self.peek_token_is_punctuator(",") {
self.next_token(); self.next_token();
if let Some(arg) = self.parse_expression(Precedence::Lowest) {
call.arguments.push(arg);
} else {
self.add_error(format!(
"expected expression after ',' at {}",
self.cur_token.position
));
return None;
}
}
if self.peek_token_is_keyword("ORDER") {
self.parse_function_order_by(&mut call);
}
if !self.expect_peek(TokenType::Punctuator) || self.cur_token.literal != ")" {
self.add_error(format!("expected ')' at {}", self.cur_token.position));
return None;
}
if self.peek_token_is_keyword("FILTER") {
call.filter = self.parse_filter_clause();
}
if self.peek_token_is_keyword("OVER") {
return self.parse_window_expression(call);
}
Some(Expression::FunctionCall(Box::new(call)))
}
fn parse_filter_clause(&mut self) -> Option<Box<Expression>> {
self.next_token();
if !self.expect_peek(TokenType::Punctuator) || self.cur_token.literal != "(" {
self.add_error(format!(
"expected '(' after FILTER at {}",
self.cur_token.position
));
return None;
}
if !self.expect_keyword("WHERE") {
self.add_error(format!(
"expected WHERE after FILTER( at {}",
self.cur_token.position
));
return None;
}
self.next_token();
let condition = self.parse_expression(Precedence::Lowest)?;
if !self.expect_peek(TokenType::Punctuator) || self.cur_token.literal != ")" {
self.add_error(format!(
"expected ')' after FILTER condition at {}",
self.cur_token.position
));
return None;
}
Some(Box::new(condition))
}
fn parse_function_order_by(&mut self, call: &mut FunctionCall) {
self.next_token();
if !self.expect_keyword("BY") {
return;
}
self.next_token();
if let Some(order_expr) = self.parse_order_by_expression() {
call.order_by.push(order_expr);
}
while self.peek_token_is_punctuator(",") {
self.next_token(); self.next_token();
if let Some(order_expr) = self.parse_order_by_expression() {
call.order_by.push(order_expr);
}
}
}
fn parse_window_expression(&mut self, function: FunctionCall) -> Option<Expression> {
self.next_token(); let token = self.cur_token.clone();
if matches!(
self.peek_token.token_type,
TokenType::Identifier | TokenType::Keyword
) {
self.next_token(); let window_ref = self.cur_token.literal.clone();
return Some(Expression::Window(Box::new(WindowExpression {
token,
function: Box::new(function),
window_ref: Some(window_ref),
partition_by: Vec::new(),
order_by: Vec::new(),
frame: None,
})));
}
if !self.expect_peek(TokenType::Punctuator) || self.cur_token.literal != "(" {
self.add_error(format!(
"expected '(' or window name after OVER at {}",
self.cur_token.position
));
return None;
}
let mut partition_by = Vec::new();
let mut order_by = Vec::new();
let mut frame = None;
if self.peek_token_is_keyword("PARTITION") {
self.next_token(); if !self.expect_keyword("BY") {
return None;
}
self.next_token();
if let Some(expr) = self.parse_expression(Precedence::Lowest) {
partition_by.push(expr);
}
while self.peek_token_is_punctuator(",") {
self.next_token(); self.next_token();
if let Some(expr) = self.parse_expression(Precedence::Lowest) {
partition_by.push(expr);
}
}
}
if self.peek_token_is_keyword("ORDER") {
self.next_token(); if !self.expect_keyword("BY") {
return None;
}
self.next_token();
if let Some(order_expr) = self.parse_order_by_expression() {
order_by.push(order_expr);
}
while self.peek_token_is_punctuator(",") {
self.next_token(); self.next_token();
if let Some(order_expr) = self.parse_order_by_expression() {
order_by.push(order_expr);
}
}
}
if self.peek_token_is_keyword("ROWS") || self.peek_token_is_keyword("RANGE") {
frame = self.parse_window_frame();
}
if !self.expect_peek(TokenType::Punctuator) || self.cur_token.literal != ")" {
self.add_error(format!(
"expected ')' after window specification at {}",
self.cur_token.position
));
return None;
}
Some(Expression::Window(Box::new(WindowExpression {
token,
function: Box::new(function),
window_ref: None,
partition_by,
order_by,
frame,
})))
}
fn parse_window_frame(&mut self) -> Option<WindowFrame> {
self.next_token();
let unit = if self.cur_token_is_keyword("ROWS") {
WindowFrameUnit::Rows
} else {
WindowFrameUnit::Range
};
self.next_token();
let (start, end) = if self.cur_token_is_keyword("BETWEEN") {
self.next_token();
let start = self.parse_window_frame_bound()?;
if !self.expect_keyword("AND") {
return None;
}
self.next_token();
let end = self.parse_window_frame_bound()?;
(start, Some(end))
} else {
let start = self.parse_window_frame_bound()?;
(start, None)
};
if !window_frame_is_ordered(&start, end.as_ref()) {
self.add_error("window frame start must not follow its end".to_string());
return None;
}
Some(WindowFrame { unit, start, end })
}
fn parse_window_frame_bound(&mut self) -> Option<WindowFrameBound> {
if self.cur_token_is_keyword("CURRENT") {
if !self.expect_keyword("ROW") {
return None;
}
Some(WindowFrameBound::CurrentRow)
} else if self.cur_token_is_keyword("UNBOUNDED") {
self.next_token();
if self.cur_token_is_keyword("PRECEDING") {
Some(WindowFrameBound::UnboundedPreceding)
} else if self.cur_token_is_keyword("FOLLOWING") {
Some(WindowFrameBound::UnboundedFollowing)
} else {
self.add_error(format!(
"expected PRECEDING or FOLLOWING after UNBOUNDED at {}",
self.cur_token.position
));
None
}
} else {
let expr = self.parse_expression(Precedence::Lowest)?;
if !matches!(&expr, Expression::IntegerLiteral(value) if value.value >= 0) {
self.add_error(
"window frame offset must be a non-negative integer literal".to_string(),
);
return None;
}
self.next_token();
if self.cur_token_is_keyword("PRECEDING") {
Some(WindowFrameBound::Preceding(Box::new(expr)))
} else if self.cur_token_is_keyword("FOLLOWING") {
Some(WindowFrameBound::Following(Box::new(expr)))
} else {
self.add_error(format!(
"expected PRECEDING or FOLLOWING at {}",
self.cur_token.position
));
None
}
}
}
fn parse_index_expression(&mut self, left: Expression) -> Option<Expression> {
let token = self.cur_token.clone();
self.next_token();
let index = self.parse_expression(Precedence::Lowest)?;
if !self.expect_peek(TokenType::Punctuator) || self.cur_token.literal != "]" {
self.add_error(format!("expected ']' at {}", self.cur_token.position));
return None;
}
Some(Expression::Infix(InfixExpression::new(
token,
Box::new(left),
"[]".to_string(),
Box::new(index),
)))
}
fn parse_is_expression(&mut self, left: Expression) -> Option<Expression> {
let token = self.cur_token.clone();
let mut operator = "IS".to_string();
if self.peek_token_is_keyword("NOT") {
self.next_token();
operator = "IS NOT".to_string();
}
self.next_token();
if self.cur_token_is_keyword("NULL") {
return Some(Expression::Infix(InfixExpression::new(
token,
Box::new(left),
operator,
Box::new(Expression::NullLiteral(NullLiteral {
token: self.cur_token.clone(),
})),
)));
}
if self.cur_token_is_keyword("TRUE") {
return Some(Expression::Infix(InfixExpression::new(
token,
Box::new(left),
operator,
Box::new(Expression::BooleanLiteral(BooleanLiteral {
token: self.cur_token.clone(),
value: true,
})),
)));
}
if self.cur_token_is_keyword("FALSE") {
return Some(Expression::Infix(InfixExpression::new(
token,
Box::new(left),
operator,
Box::new(Expression::BooleanLiteral(BooleanLiteral {
token: self.cur_token.clone(),
value: false,
})),
)));
}
if self.cur_token_is_keyword("DISTINCT") {
if !self.expect_keyword("FROM") {
return None;
}
self.next_token();
let right = self.parse_expression(Precedence::Equals)?;
let distinct_op = if operator == "IS" {
"IS DISTINCT FROM".to_string()
} else {
"IS NOT DISTINCT FROM".to_string()
};
return Some(Expression::Infix(InfixExpression::new(
token,
Box::new(left),
distinct_op,
Box::new(right),
)));
}
self.add_error(format!(
"expected NULL, TRUE, FALSE, or DISTINCT FROM after IS at {}",
self.cur_token.position
));
None
}
fn parse_in_expression(&mut self, left: Expression, not: bool) -> Option<Expression> {
let token = self.cur_token.clone();
if !self.peek_token_is_punctuator("(") {
self.add_error(format!(
"expected '(' after IN, got {}",
Self::format_token_for_error(&self.peek_token)
));
return None;
}
self.next_token();
if self.peek_token_is_keyword("SELECT") {
self.next_token();
let subquery = self.parse_select_statement()?;
if !self.expect_peek(TokenType::Punctuator) || self.cur_token.literal != ")" {
self.add_error(format!(
"expected ')' after IN subquery at {}",
self.cur_token.position
));
return None;
}
return Some(Expression::In(InExpression {
token,
left: Box::new(left),
right: Box::new(Expression::ScalarSubquery(ScalarSubquery {
token: self.cur_token.clone(),
subquery: Box::new(subquery),
})),
not,
}));
}
let list_token = self.cur_token.clone();
self.next_token();
let mut expressions = Vec::new();
if self.cur_token_is_punctuator(")") {
return Some(Expression::In(InExpression {
token,
left: Box::new(left),
right: Box::new(Expression::ExpressionList(Box::new(ExpressionList {
token: list_token,
expressions,
}))),
not,
}));
}
if let Some(expr) = self.parse_expression(Precedence::Lowest) {
expressions.push(expr);
}
while self.peek_token_is_punctuator(",") {
self.next_token(); self.next_token();
if let Some(expr) = self.parse_expression(Precedence::Lowest) {
expressions.push(expr);
}
}
if !self.expect_peek(TokenType::Punctuator) || self.cur_token.literal != ")" {
self.add_error(format!(
"expected ')' in IN expression at {}",
self.cur_token.position
));
return None;
}
Some(Expression::In(InExpression {
token,
left: Box::new(left),
right: Box::new(Expression::ExpressionList(Box::new(ExpressionList {
token: list_token,
expressions,
}))),
not,
}))
}
fn parse_between_expression(&mut self, left: Expression, not: bool) -> Option<Expression> {
let token = self.cur_token.clone();
self.next_token();
let lower = self.parse_expression(Precedence::Equals)?;
if !self.expect_keyword("AND") {
return None;
}
self.next_token();
let upper = self.parse_expression(Precedence::Equals)?;
Some(Expression::Between(BetweenExpression {
token,
expr: Box::new(left),
lower: Box::new(lower),
upper: Box::new(upper),
not,
}))
}
fn parse_like_expression(
&mut self,
left: Expression,
op: SmartString,
not: bool,
) -> Option<Expression> {
let token = self.cur_token.clone();
let operator = if not {
SmartString::from_string(format!("NOT {}", op))
} else {
op
};
let precedence = self.cur_precedence();
self.next_token();
let pattern = self.parse_expression(precedence)?;
let escape = if self.peek_token_is_keyword("ESCAPE") {
self.next_token(); self.next_token(); let escape = self.parse_expression(Precedence::Lowest)?;
match &escape {
Expression::StringLiteral(lit) if lit.value.chars().count() == 1 => {}
_ => {
self.add_error(
"LIKE ESCAPE must be a string literal containing exactly one character"
.to_string(),
);
return None;
}
}
Some(Box::new(escape))
} else {
None
};
Some(Expression::Like(LikeExpression {
token,
left: Box::new(left),
pattern: Box::new(pattern),
operator,
escape,
}))
}
fn parse_case_expression(&mut self) -> Option<Expression> {
let token = self.cur_token.clone();
let mut value = None;
let mut when_clauses = Vec::new();
let mut else_value = None;
if !self.peek_token_is_keyword("WHEN") {
self.next_token();
value = Some(Box::new(self.parse_expression(Precedence::Lowest)?));
}
while self.peek_token_is_keyword("WHEN") {
self.next_token(); let when_token = self.cur_token.clone();
self.next_token();
let condition = self.parse_expression(Precedence::Lowest)?;
if !self.expect_keyword("THEN") {
return None;
}
self.next_token();
let then_result = self.parse_expression(Precedence::Lowest)?;
when_clauses.push(WhenClause {
token: when_token,
condition,
then_result,
});
}
if when_clauses.is_empty() {
self.add_error(format!(
"expected at least one WHEN clause in CASE at {}",
self.cur_token.position
));
return None;
}
if self.peek_token_is_keyword("ELSE") {
self.next_token(); self.next_token();
else_value = Some(Box::new(self.parse_expression(Precedence::Lowest)?));
}
if !self.expect_keyword("END") {
return None;
}
Some(Expression::Case(Box::new(CaseExpression {
token,
value,
when_clauses,
else_value,
})))
}
fn parse_cast_expression(&mut self) -> Option<Expression> {
let token = self.cur_token.clone();
if !self.expect_peek(TokenType::Punctuator) || self.cur_token.literal != "(" {
self.add_error(format!(
"expected '(' after CAST at {}",
self.cur_token.position
));
return None;
}
self.next_token();
let expr = self.parse_expression(Precedence::Equals)?;
if !self.expect_keyword("AS") {
return None;
}
let type_name = self.parse_column_data_type()?;
if let Err(message) = validate_cast_type_name(&type_name) {
self.add_error(message);
return None;
}
if !self.expect_peek(TokenType::Punctuator) || self.cur_token.literal != ")" {
self.add_error(format!(
"expected ')' after type name in CAST at {}",
self.cur_token.position
));
return None;
}
Some(Expression::Cast(CastExpression {
token,
expr: Box::new(expr),
type_name,
}))
}
fn parse_extract_expression(&mut self) -> Option<Expression> {
let token = self.cur_token.clone();
if !self.expect_peek(TokenType::Punctuator) || self.cur_token.literal != "(" {
self.add_error(format!(
"expected '(' after EXTRACT at {}",
self.cur_token.position
));
return None;
}
if !self.peek_token_is(TokenType::Keyword) && !self.peek_token_is(TokenType::Identifier) {
self.add_error(format!(
"expected field name (YEAR, MONTH, DAY, etc.) in EXTRACT at {}",
self.peek_token.position
));
return None;
}
self.next_token();
let field = self.cur_token.literal.to_uppercase();
let valid_fields = [
"YEAR",
"MONTH",
"DAY",
"HOUR",
"MINUTE",
"SECOND",
"DOW",
"DAYOFWEEK",
"ISODOW",
"DOY",
"DAYOFYEAR",
"WEEK",
"ISOWEEK",
"QUARTER",
"EPOCH",
"MILLISECOND",
"MILLISECONDS",
"MICROSECOND",
"MICROSECONDS",
];
if !valid_fields.contains(&field.as_str()) {
self.add_error(format!(
"invalid EXTRACT field '{}'. Valid fields: YEAR, MONTH, DAY, HOUR, MINUTE, SECOND, DOW, DOY, WEEK, QUARTER, EPOCH, etc.",
field
));
return None;
}
if !self.expect_keyword("FROM") {
return None;
}
self.next_token();
let source = self.parse_expression(Precedence::Lowest)?;
if !self.expect_peek(TokenType::Punctuator) || self.cur_token.literal != ")" {
self.add_error(format!(
"expected ')' after source in EXTRACT at {}",
self.cur_token.position
));
return None;
}
let basic_fields = ["YEAR", "MONTH", "DAY", "HOUR", "MINUTE", "SECOND"];
if basic_fields.contains(&field.as_str()) {
Some(Expression::FunctionCall(Box::new(FunctionCall {
token,
function: field,
arguments: vec![source],
is_distinct: false,
order_by: Vec::new(),
filter: None,
})))
} else {
let field_literal = Expression::StringLiteral(StringLiteral {
token: Token {
token_type: TokenType::String,
literal: field.to_lowercase(),
position: token.position,
quoted: false,
},
value: field.to_lowercase(),
type_hint: None,
});
Some(Expression::FunctionCall(Box::new(FunctionCall {
token,
function: SmartString::const_new("EXTRACT"),
arguments: vec![field_literal, source],
is_distinct: false,
order_by: Vec::new(),
filter: None,
})))
}
}
fn parse_exists_expression(&mut self) -> Option<Expression> {
let token = self.cur_token.clone();
if !self.expect_peek(TokenType::Punctuator) || self.cur_token.literal != "(" {
self.add_error(format!(
"expected '(' after EXISTS at {}",
self.cur_token.position
));
return None;
}
self.next_token();
if !self.cur_token_is_keyword("SELECT") {
self.add_error(format!(
"expected SELECT in EXISTS subquery at {}",
self.cur_token.position
));
return None;
}
let subquery = self.parse_select_statement()?;
if !self.expect_peek(TokenType::Punctuator) || self.cur_token.literal != ")" {
self.add_error(format!(
"expected ')' after EXISTS subquery at {}",
self.cur_token.position
));
return None;
}
Some(Expression::Exists(ExistsExpression {
token,
subquery: Box::new(subquery),
}))
}
fn parse_not_expression(&mut self) -> Option<Expression> {
let token = self.cur_token.clone();
if self.peek_token_is_keyword("EXISTS") {
self.next_token();
let exists_expr = self.parse_exists_expression()?;
return Some(Expression::Prefix(PrefixExpression::new(
token,
"NOT".to_string(),
Box::new(exists_expr),
)));
}
self.next_token();
let right = self.parse_expression(Precedence::Not)?;
Some(Expression::Prefix(PrefixExpression::new(
token,
"NOT".to_string(),
Box::new(right),
)))
}
fn parse_interval_literal(&mut self) -> Option<Expression> {
let token = self.cur_token.clone();
if self.peek_token_is(TokenType::Integer) {
self.next_token();
let quantity = match self.cur_token.literal.parse::<i64>() {
Ok(q) => q,
Err(_) => {
self.add_error(format!(
"invalid interval quantity: {} at {}",
self.cur_token.literal, self.cur_token.position
));
return None;
}
};
self.next_token();
let unit_raw = self.cur_token.literal.to_uppercase();
let unit = match unit_raw.as_str() {
"SECOND" | "SECONDS" => "second",
"MINUTE" | "MINUTES" => "minute",
"HOUR" | "HOURS" => "hour",
"DAY" | "DAYS" => "day",
"WEEK" | "WEEKS" => "week",
"MONTH" | "MONTHS" => "month",
"YEAR" | "YEARS" => "year",
_ => {
self.add_error(format!(
"invalid interval unit: {} at {}. Expected SECOND, MINUTE, HOUR, DAY, WEEK, MONTH, or YEAR",
self.cur_token.literal, self.cur_token.position
));
return None;
}
};
let value = SmartString::from_string(format!("{} {}", quantity, unit));
Some(Expression::IntervalLiteral(IntervalLiteral {
token,
value,
quantity,
unit: SmartString::new(unit),
}))
} else if self.expect_peek(TokenType::String) {
let literal = &self.cur_token.literal;
let value_str = if literal.len() >= 2 {
&literal[1..literal.len() - 1]
} else {
literal.as_str()
};
let parts: Vec<&str> = value_str.split_whitespace().collect();
if parts.len() != 2 {
self.add_error(format!(
"invalid interval format: {} at {}",
value_str, self.cur_token.position
));
return None;
}
let quantity = match parts[0].parse::<i64>() {
Ok(q) => q,
Err(_) => {
self.add_error(format!(
"invalid interval quantity: {} at {}",
parts[0], self.cur_token.position
));
return None;
}
};
let unit_raw = parts[1].to_ascii_uppercase();
let unit = match unit_raw.as_str() {
"SECOND" | "SECONDS" => "second",
"MINUTE" | "MINUTES" => "minute",
"HOUR" | "HOURS" => "hour",
"DAY" | "DAYS" => "day",
"WEEK" | "WEEKS" => "week",
"MONTH" | "MONTHS" => "month",
"YEAR" | "YEARS" => "year",
_ => {
self.add_error(format!(
"invalid interval unit: {} at {}. Expected SECOND, MINUTE, HOUR, DAY, WEEK, MONTH, or YEAR",
parts[1], self.cur_token.position
));
return None;
}
};
Some(Expression::IntervalLiteral(IntervalLiteral {
token,
value: SmartString::from(value_str),
quantity,
unit: SmartString::new(unit),
}))
} else {
None
}
}
fn parse_typed_literal(&mut self) -> Option<Expression> {
let type_hint = self.cur_token.literal.clone();
if !self.peek_token_is(TokenType::String) {
self.add_error(format!(
"expected string literal after {} at {}",
type_hint, self.cur_token.position
));
return None;
}
self.next_token();
let literal = &self.cur_token.literal;
let value = if literal.len() >= 2 && literal.starts_with('\'') && literal.ends_with('\'') {
SmartString::from(&literal[1..literal.len() - 1])
} else {
literal.clone()
};
Some(Expression::StringLiteral(StringLiteral {
token: self.cur_token.clone(),
value,
type_hint: Some(type_hint),
}))
}
pub fn parse_expression_list(&mut self) -> Vec<Expression> {
let mut list = Vec::with_capacity(8);
self.next_token();
if let Some(expr) = self.parse_expression(Precedence::Lowest) {
list.push(expr);
}
while self.peek_token_is_punctuator(",") {
self.next_token(); self.next_token();
if let Some(expr) = self.parse_expression(Precedence::Lowest) {
list.push(expr);
}
}
list
}
pub fn parse_group_by_clause(&mut self) -> GroupByClause {
use crate::ast::{GroupByClause, GroupByModifier};
self.next_token();
let modifier = if self.cur_token.token_type == TokenType::Identifier
|| self.cur_token.token_type == TokenType::Keyword
{
let upper = self.cur_token.literal.to_uppercase();
if upper == "ROLLUP" {
self.next_token(); if self.cur_token.literal == "(" {
self.next_token(); let columns = self.parse_group_by_columns();
return GroupByClause {
columns,
modifier: GroupByModifier::Rollup,
};
}
GroupByModifier::None
} else if upper == "CUBE" {
self.next_token(); if self.cur_token.literal == "(" {
self.next_token(); let columns = self.parse_group_by_columns();
return GroupByClause {
columns,
modifier: GroupByModifier::Cube,
};
}
GroupByModifier::None
} else if upper == "GROUPING" {
if self.peek_token.literal.to_uppercase() == "SETS" {
self.next_token(); self.next_token(); if self.cur_token.literal == "(" {
self.next_token(); let sets = self.parse_grouping_sets();
return GroupByClause {
columns: Vec::new(),
modifier: GroupByModifier::GroupingSets(sets),
};
}
}
GroupByModifier::None
} else {
GroupByModifier::None
}
} else {
GroupByModifier::None
};
let mut columns = Vec::new();
if let Some(expr) = self.parse_expression(Precedence::Lowest) {
columns.push(expr);
}
while self.peek_token_is_punctuator(",") {
self.next_token(); self.next_token();
if let Some(expr) = self.parse_expression(Precedence::Lowest) {
columns.push(expr);
}
}
GroupByClause { columns, modifier }
}
fn parse_group_by_columns(&mut self) -> Vec<Expression> {
let mut columns = Vec::new();
if let Some(expr) = self.parse_expression(Precedence::Lowest) {
columns.push(expr);
}
while self.peek_token_is_punctuator(",") {
self.next_token(); self.next_token();
if let Some(expr) = self.parse_expression(Precedence::Lowest) {
columns.push(expr);
}
}
if self.peek_token_is_punctuator(")") {
self.next_token();
}
columns
}
fn parse_grouping_sets(&mut self) -> Vec<Vec<Expression>> {
let mut sets = Vec::new();
if self.cur_token.literal == "(" {
sets.push(self.parse_single_grouping_set());
}
while self.cur_token.literal == "," {
self.next_token(); if self.cur_token.literal == "(" {
sets.push(self.parse_single_grouping_set());
}
}
sets
}
fn parse_single_grouping_set(&mut self) -> Vec<Expression> {
let mut columns = Vec::new();
self.next_token();
if self.cur_token.literal == ")" {
self.next_token(); return columns;
}
if let Some(expr) = self.parse_expression(Precedence::Lowest) {
columns.push(expr);
}
while self.peek_token_is_punctuator(",") {
self.next_token(); self.next_token(); if let Some(expr) = self.parse_expression(Precedence::Lowest) {
columns.push(expr);
}
}
if self.peek_token_is_punctuator(")") {
self.next_token(); }
self.next_token();
columns
}
pub fn parse_window_definitions(&mut self) -> Vec<WindowDefinition> {
let mut defs = Vec::new();
if let Some(def) = self.parse_single_window_definition() {
defs.push(def);
}
while self.peek_token_is_punctuator(",") {
self.next_token(); self.next_token(); if let Some(def) = self.parse_single_window_definition() {
defs.push(def);
}
}
defs
}
fn parse_single_window_definition(&mut self) -> Option<WindowDefinition> {
self.next_token();
let name = if self.cur_token_is(TokenType::Identifier) {
self.cur_token.literal.clone()
} else if self.cur_token_is(TokenType::Keyword) {
self.cur_token.literal.clone()
} else {
self.add_error(format!(
"expected window name at {}",
self.cur_token.position
));
return None;
};
if !self.expect_keyword("AS") {
return None;
}
if !self.expect_peek(TokenType::Punctuator) || self.cur_token.literal != "(" {
self.add_error(format!(
"expected '(' after AS in window definition at {}",
self.cur_token.position
));
return None;
}
let mut partition_by = Vec::new();
let mut order_by = Vec::new();
let mut frame = None;
if self.peek_token_is_keyword("PARTITION") {
self.next_token(); if !self.expect_keyword("BY") {
return None;
}
self.next_token();
if let Some(expr) = self.parse_expression(Precedence::Lowest) {
partition_by.push(expr);
}
while self.peek_token_is_punctuator(",") {
self.next_token(); self.next_token();
if let Some(expr) = self.parse_expression(Precedence::Lowest) {
partition_by.push(expr);
}
}
}
if self.peek_token_is_keyword("ORDER") {
self.next_token(); if !self.expect_keyword("BY") {
return None;
}
self.next_token();
if let Some(order_expr) = self.parse_order_by_expression() {
order_by.push(order_expr);
}
while self.peek_token_is_punctuator(",") {
self.next_token(); self.next_token();
if let Some(order_expr) = self.parse_order_by_expression() {
order_by.push(order_expr);
}
}
}
if self.peek_token_is_keyword("ROWS") || self.peek_token_is_keyword("RANGE") {
frame = self.parse_window_frame();
}
if !self.expect_peek(TokenType::Punctuator) || self.cur_token.literal != ")" {
self.add_error(format!(
"expected ')' after window specification at {}",
self.cur_token.position
));
return None;
}
Some(WindowDefinition {
name,
partition_by,
order_by,
frame,
})
}
pub fn parse_order_by_expression(&mut self) -> Option<OrderByExpression> {
let expression = self.parse_expression(Precedence::Lowest)?;
let mut ascending = true;
if self.peek_token_is_keyword("ASC") {
self.next_token();
ascending = true;
} else if self.peek_token_is_keyword("DESC") {
self.next_token();
ascending = false;
}
let nulls_first = if self.peek_token_is_keyword("NULLS") {
self.next_token(); if self.peek_token_is_keyword("FIRST") {
self.next_token(); Some(true)
} else if self.peek_token_is_keyword("LAST") {
self.next_token(); Some(false)
} else {
self.add_error(format!(
"expected FIRST or LAST after NULLS at {}",
self.peek_token.position
));
return None;
}
} else {
None
};
Some(OrderByExpression {
expression,
ascending,
nulls_first,
})
}
pub fn parse_order_by_expressions(&mut self) -> Vec<OrderByExpression> {
let mut list = Vec::new();
self.next_token();
if let Some(expr) = self.parse_order_by_expression() {
list.push(expr);
}
while self.peek_token_is_punctuator(",") {
self.next_token(); self.next_token();
if let Some(expr) = self.parse_order_by_expression() {
list.push(expr);
}
}
list
}
}
#[cfg(test)]
mod tests {
use super::*;
fn parse_expr(input: &str) -> Option<Expression> {
let mut parser = Parser::new(input);
parser.parse_expression(Precedence::Lowest)
}
#[test]
fn test_parse_identifier() {
let expr = parse_expr("users").unwrap();
match expr {
Expression::Identifier(id) => assert_eq!(id.value, "users"),
_ => panic!("expected Identifier"),
}
}
#[test]
fn test_parse_integer() {
let expr = parse_expr("42").unwrap();
match expr {
Expression::IntegerLiteral(lit) => assert_eq!(lit.value, 42),
_ => panic!("expected IntegerLiteral"),
}
}
#[test]
fn test_parse_integer_i64_boundaries() {
let mut max_parser = Parser::new("9223372036854775807");
let max_expr = max_parser
.parse_expression(Precedence::Lowest)
.expect("i64::MAX should parse as an integer literal");
assert!(max_parser.errors().is_empty());
match max_expr {
Expression::IntegerLiteral(lit) => assert_eq!(lit.value, i64::MAX),
other => panic!("expected IntegerLiteral, got {other:?}"),
}
let mut min_parser = Parser::new("-9223372036854775808");
let min_expr = min_parser
.parse_expression(Precedence::Lowest)
.expect("i64::MIN should parse through the unary-minus special case");
assert!(min_parser.errors().is_empty());
match min_expr {
Expression::IntegerLiteral(lit) => assert_eq!(lit.value, i64::MIN),
other => panic!("expected IntegerLiteral, got {other:?}"),
}
}
#[test]
fn test_parse_integer_overflow_is_an_error() {
for literal in ["9223372036854775808", "-9223372036854775809"] {
let mut parser = Parser::new(literal);
let expression = parser.parse_expression(Precedence::Lowest);
assert!(expression.is_none(), "{literal} must not produce an AST");
assert_eq!(
parser.errors().len(),
1,
"{literal} must produce exactly one parser error"
);
assert!(
parser.errors()[0]
.message
.contains("is out of range for i64"),
"unexpected parser error for {literal}: {}",
parser.errors()[0].message
);
}
}
#[test]
fn test_parse_float() {
let expr = parse_expr("3.5").unwrap();
match expr {
Expression::FloatLiteral(lit) => assert!((lit.value - 3.5).abs() < 0.001),
_ => panic!("expected FloatLiteral"),
}
}
#[test]
fn test_parse_string() {
let expr = parse_expr("'hello'").unwrap();
match expr {
Expression::StringLiteral(lit) => assert_eq!(lit.value, "hello"),
_ => panic!("expected StringLiteral"),
}
}
#[test]
fn test_parse_boolean() {
let expr = parse_expr("TRUE").unwrap();
match expr {
Expression::BooleanLiteral(lit) => assert!(lit.value),
_ => panic!("expected BooleanLiteral"),
}
let expr = parse_expr("FALSE").unwrap();
match expr {
Expression::BooleanLiteral(lit) => assert!(!lit.value),
_ => panic!("expected BooleanLiteral"),
}
}
#[test]
fn test_parse_null() {
let expr = parse_expr("NULL").unwrap();
match expr {
Expression::NullLiteral(_) => {}
_ => panic!("expected NullLiteral"),
}
}
#[test]
fn test_parse_infix() {
let expr = parse_expr("1 + 2").unwrap();
match expr {
Expression::Infix(infix) => {
assert_eq!(infix.operator, "+");
}
_ => panic!("expected InfixExpression"),
}
}
#[test]
fn test_parse_precedence() {
let expr = parse_expr("1 + 2 * 3").unwrap();
match expr {
Expression::Infix(infix) => {
assert_eq!(infix.operator, "+");
match infix.right.as_ref() {
Expression::Infix(right) => {
assert_eq!(right.operator, "*");
}
_ => panic!("expected nested InfixExpression"),
}
}
_ => panic!("expected InfixExpression"),
}
}
#[test]
fn test_expression_stops_before_statement_owned_alias() {
let mut parser = Parser::new("1 AS answer");
let expression = parser
.parse_expression(Precedence::Lowest)
.expect("integer expression should parse");
assert!(matches!(expression, Expression::IntegerLiteral(_)));
assert!(parser.peek_token_is_keyword("AS"));
}
#[test]
fn test_parse_qualified_identifier() {
let expr = parse_expr("users.id").unwrap();
match expr {
Expression::QualifiedIdentifier(qi) => {
assert_eq!(qi.qualifier.value, "users");
assert!(qi.intermediate.is_none());
assert_eq!(qi.name.value, "id");
assert_eq!(qi.component_count(), 2);
}
_ => panic!("expected QualifiedIdentifier"),
}
}
#[test]
fn test_parse_multi_part_identifier_path_preserves_components_and_positions() {
let expr = parse_expr("m.sender_id.profile_id.display_name").unwrap();
let Expression::QualifiedIdentifier(path) = expr else {
panic!("expected unresolved multi-part QualifiedIdentifier");
};
let components: Vec<_> = path
.components()
.map(|component| {
(
component.value().to_string(),
component.token.position.column,
)
})
.collect();
assert_eq!(
components,
vec![
("m".to_string(), 1),
("sender_id".to_string(), 3),
("profile_id".to_string(), 13),
("display_name".to_string(), 24),
]
);
assert_eq!(path.component_count(), 4);
assert!(path.is_multi_part_path());
assert_eq!(path.to_string(), "m.sender_id.profile_id.display_name");
}
#[test]
fn test_parse_identifier_path_supports_eight_components_and_roundtrip() {
let sql = "a.b.c.d.e.f.g.h";
let expr = parse_expr(sql).unwrap();
let Expression::QualifiedIdentifier(path) = &expr else {
panic!("expected eight-component path");
};
assert_eq!(path.component_count(), 8);
assert_eq!(expr.to_string(), sql);
let reparsed = parse_expr(&expr.to_string()).unwrap();
let Expression::QualifiedIdentifier(reparsed) = reparsed else {
panic!("expected round-tripped path");
};
let values: Vec<_> = reparsed
.components()
.map(|component| component.value().to_string())
.collect();
assert_eq!(values, ["a", "b", "c", "d", "e", "f", "g", "h"]);
}
#[test]
fn test_parse_identifier_path_preserves_quoted_and_keyword_components() {
let expr = parse_expr("\"Root\".type.\"Target Field\"").unwrap();
let Expression::QualifiedIdentifier(path) = expr else {
panic!("expected quoted identifier path");
};
let values: Vec<_> = path
.components()
.map(|component| component.value().to_string())
.collect();
assert_eq!(values, ["Root", "type", "Target Field"]);
assert_eq!(path.to_string(), "\"Root\".type.\"Target Field\"");
}
#[test]
fn test_multi_part_qualified_star_is_rejected_without_breaking_table_star() {
assert!(matches!(
parse_expr("users.*").unwrap(),
Expression::QualifiedStar(_)
));
let mut parser = Parser::new("users.profile_id.*");
assert!(parser.parse_expression(Precedence::Lowest).is_none());
assert!(parser
.errors()
.iter()
.any(|error| error.message.contains("qualified star cannot follow")));
}
#[test]
fn test_parse_function_call() {
let expr = parse_expr("COUNT(*)").unwrap();
match expr {
Expression::FunctionCall(fc) => {
assert_eq!(fc.function, "COUNT");
assert_eq!(fc.arguments.len(), 1);
}
_ => panic!("expected FunctionCall"),
}
}
#[test]
fn test_parse_count_star_with_filter() {
let expr = parse_expr("COUNT(*) FILTER (WHERE x = 1)").unwrap();
match expr {
Expression::FunctionCall(fc) => {
assert_eq!(fc.function, "COUNT");
assert_eq!(fc.arguments.len(), 1);
assert!(
fc.filter.is_some(),
"FILTER clause should be parsed for COUNT(*)"
);
}
_ => panic!("expected FunctionCall"),
}
}
#[test]
fn test_parse_function_call_with_multiple_args() {
let expr = parse_expr("STRING_AGG(name, '; ')").unwrap();
match expr {
Expression::FunctionCall(fc) => {
assert_eq!(fc.function, "STRING_AGG");
assert_eq!(
fc.arguments.len(),
2,
"Expected 2 arguments, got: {:?}",
fc.arguments
);
match &fc.arguments[0] {
Expression::Identifier(id) => assert_eq!(id.value, "name"),
other => panic!("Expected Identifier, got: {:?}", other),
}
match &fc.arguments[1] {
Expression::StringLiteral(lit) => assert_eq!(lit.value, "; "),
other => panic!("Expected StringLiteral, got: {:?}", other),
}
}
_ => panic!("expected FunctionCall"),
}
}
#[test]
fn test_parse_is_null() {
let expr = parse_expr("x IS NULL").unwrap();
match expr {
Expression::Infix(infix) => {
assert_eq!(infix.operator, "IS");
}
_ => panic!("expected InfixExpression"),
}
}
#[test]
fn test_parse_is_not_null() {
let expr = parse_expr("x IS NOT NULL").unwrap();
match expr {
Expression::Infix(infix) => {
assert_eq!(infix.operator, "IS NOT");
}
_ => panic!("expected InfixExpression"),
}
}
#[test]
fn test_parse_in() {
let expr = parse_expr("x IN (1, 2, 3)").unwrap();
match expr {
Expression::In(in_expr) => {
assert!(!in_expr.not);
}
_ => panic!("expected InExpression"),
}
}
#[test]
fn test_parse_not_in() {
let expr = parse_expr("x NOT IN (1, 2, 3)").unwrap();
match expr {
Expression::In(in_expr) => {
assert!(in_expr.not);
}
_ => panic!("expected InExpression"),
}
}
#[test]
fn test_parse_between() {
let expr = parse_expr("x BETWEEN 1 AND 10").unwrap();
match expr {
Expression::Between(between) => {
assert!(!between.not);
}
_ => panic!("expected BetweenExpression"),
}
}
#[test]
fn test_parse_case() {
let expr = parse_expr("CASE WHEN x = 1 THEN 'one' ELSE 'other' END").unwrap();
match expr {
Expression::Case(case) => {
assert!(case.value.is_none());
assert_eq!(case.when_clauses.len(), 1);
assert!(case.else_value.is_some());
}
_ => panic!("expected CaseExpression"),
}
}
#[test]
fn test_parse_cast() {
let expr = parse_expr("CAST(x AS INTEGER)").unwrap();
match expr {
Expression::Cast(cast) => {
assert_eq!(cast.type_name, "INTEGER");
}
_ => panic!("expected CastExpression"),
}
}
}