use crate::sql::parser::ast::SqlExpression;
use crate::sql::parser::lexer::Token;
use tracing::debug;
use super::{log_parse_decision, trace_parse_entry, trace_parse_exit};
pub fn parse_comparison<P>(parser: &mut P) -> Result<SqlExpression, String>
where
P: ParseComparison + ?Sized,
{
trace_parse_entry("parse_comparison", parser.current_token());
let mut left = parser.parse_additive()?;
if matches!(parser.current_token(), Token::Between) {
debug!("BETWEEN operator detected");
log_parse_decision(
"parse_comparison",
parser.current_token(),
"BETWEEN operator - parsing range bounds",
);
parser.advance(); let lower = parser.parse_additive()?;
parser.consume(Token::And)?; let upper = parser.parse_additive()?;
let result = Ok(SqlExpression::Between {
expr: Box::new(left),
lower: Box::new(lower),
upper: Box::new(upper),
});
trace_parse_exit("parse_comparison", &result);
return result;
}
if matches!(parser.current_token(), Token::Not) {
parser.advance(); if matches!(parser.current_token(), Token::In) {
debug!("NOT IN operator detected");
log_parse_decision(
"parse_comparison",
parser.current_token(),
"NOT IN operator - parsing value list",
);
parser.advance(); parser.consume(Token::LeftParen)?;
if matches!(parser.current_token(), Token::Select) {
debug!("Detected NOT IN subquery");
let subquery = parser.parse_subquery()?;
parser.consume(Token::RightParen)?;
let result = Ok(SqlExpression::NotInSubquery {
expr: Box::new(left),
subquery: Box::new(subquery),
});
trace_parse_exit("parse_comparison", &result);
return result;
} else {
let values = parser.parse_expression_list()?;
parser.consume(Token::RightParen)?;
let result = Ok(SqlExpression::NotInList {
expr: Box::new(left),
values,
});
trace_parse_exit("parse_comparison", &result);
return result;
}
} else {
return Err("Expected IN after NOT".to_string());
}
}
if matches!(parser.current_token(), Token::Is) {
parser.advance();
if matches!(parser.current_token(), Token::Not) {
parser.advance(); if matches!(parser.current_token(), Token::Null) {
debug!("IS NOT NULL operator detected");
log_parse_decision(
"parse_comparison",
parser.current_token(),
"IS NOT NULL operator",
);
parser.advance(); left = SqlExpression::BinaryOp {
left: Box::new(left),
op: "IS NOT NULL".to_string(),
right: Box::new(SqlExpression::Null),
};
} else {
return Err("Expected NULL after IS NOT".to_string());
}
} else if matches!(parser.current_token(), Token::Null) {
debug!("IS NULL operator detected");
log_parse_decision(
"parse_comparison",
parser.current_token(),
"IS NULL operator",
);
parser.advance(); left = SqlExpression::BinaryOp {
left: Box::new(left),
op: "IS NULL".to_string(),
right: Box::new(SqlExpression::Null),
};
} else {
return Err("Expected NULL or NOT after IS".to_string());
}
}
else if let Some(op) = get_comparison_op(parser.current_token()) {
log_parse_decision(
"parse_comparison",
parser.current_token(),
&format!("Comparison operator '{}' found", op),
);
debug!(operator = %op, "Processing comparison operator");
parser.advance();
let right = parser.parse_additive()?;
left = SqlExpression::BinaryOp {
left: Box::new(left),
op,
right: Box::new(right),
};
}
let result = Ok(left);
trace_parse_exit("parse_comparison", &result);
result
}
pub fn parse_in_operator<P>(parser: &mut P, expr: SqlExpression) -> Result<SqlExpression, String>
where
P: ParseComparison + ?Sized,
{
trace_parse_entry("parse_in_operator", parser.current_token());
if matches!(parser.current_token(), Token::In) {
debug!("IN operator detected");
log_parse_decision(
"parse_in_operator",
parser.current_token(),
"IN operator - parsing value list",
);
parser.advance(); parser.consume(Token::LeftParen)?;
if matches!(parser.current_token(), Token::Select) {
debug!("Detected IN subquery");
let subquery = parser.parse_subquery()?;
parser.consume(Token::RightParen)?;
let result = Ok(SqlExpression::InSubquery {
expr: Box::new(expr),
subquery: Box::new(subquery),
});
trace_parse_exit("parse_in_operator", &result);
return result;
} else {
let values = parser.parse_expression_list()?;
parser.consume(Token::RightParen)?;
let result = Ok(SqlExpression::InList {
expr: Box::new(expr),
values,
});
trace_parse_exit("parse_in_operator", &result);
return result;
}
} else {
Ok(expr)
}
}
fn get_comparison_op(token: &Token) -> Option<String> {
match token {
Token::Equal => Some("=".to_string()),
Token::NotEqual => Some("!=".to_string()),
Token::LessThan => Some("<".to_string()),
Token::GreaterThan => Some(">".to_string()),
Token::LessThanOrEqual => Some("<=".to_string()),
Token::GreaterThanOrEqual => Some(">=".to_string()),
Token::Like => Some("LIKE".to_string()),
Token::ILike => Some("ILIKE".to_string()),
_ => None,
}
}
pub trait ParseComparison {
fn current_token(&self) -> &Token;
fn advance(&mut self);
fn consume(&mut self, expected: Token) -> Result<(), String>;
fn parse_primary(&mut self) -> Result<SqlExpression, String>;
fn parse_additive(&mut self) -> Result<SqlExpression, String>;
fn parse_expression_list(&mut self) -> Result<Vec<SqlExpression>, String>;
fn parse_subquery(&mut self) -> Result<crate::sql::parser::ast::SelectStatement, String>;
}