use crate::sql::parser::ast::{ColumnRef, SqlExpression};
use crate::sql::parser::lexer::Token;
use tracing::debug;
use super::{log_parse_decision, trace_parse_entry, trace_parse_exit};
pub fn parse_additive<P>(parser: &mut P) -> Result<SqlExpression, String>
where
P: ParseArithmetic + ?Sized,
{
trace_parse_entry("parse_additive", parser.current_token());
let mut left = parser.parse_multiplicative()?;
while matches!(
parser.current_token(),
Token::Plus | Token::Minus | Token::Concat
) {
let op = match parser.current_token() {
Token::Plus => "+",
Token::Minus => "-",
Token::Concat => {
log_parse_decision(
"parse_additive",
parser.current_token(),
"String concatenation '||' found, converting to TEXTJOIN",
);
parser.advance();
let right = parser.parse_multiplicative()?;
left = SqlExpression::FunctionCall {
name: "TEXTJOIN".to_string(),
args: vec![
SqlExpression::StringLiteral("".to_string()), SqlExpression::NumberLiteral("1".to_string()), left,
right,
],
distinct: false,
};
continue; }
_ => unreachable!(),
};
log_parse_decision(
"parse_additive",
parser.current_token(),
&format!("Binary operator '{}' found, parsing right operand", op),
);
parser.advance();
let right = parser.parse_multiplicative()?;
debug!(operator = op, "Creating additive binary operation");
left = SqlExpression::BinaryOp {
left: Box::new(left),
op: op.to_string(),
right: Box::new(right),
};
}
let result = Ok(left);
trace_parse_exit("parse_additive", &result);
result
}
pub fn parse_multiplicative<P>(parser: &mut P) -> Result<SqlExpression, String>
where
P: ParseArithmetic + ?Sized,
{
trace_parse_entry("parse_multiplicative", parser.current_token());
let mut left = parser.parse_primary()?;
while matches!(parser.current_token(), Token::Dot) {
debug!("Found dot operator");
parser.advance();
if let Token::Identifier(name) = parser.current_token() {
let name_str = name.clone();
parser.advance();
if matches!(parser.current_token(), Token::LeftParen) {
log_parse_decision(
"parse_multiplicative",
parser.current_token(),
&format!("Method call '{}' detected", name_str),
);
parser.advance();
let args = parser.parse_method_args()?;
parser.consume(Token::RightParen)?;
left = match left {
SqlExpression::Column(obj) => {
debug!(
column = %obj,
method = %name_str,
"Creating method call on column"
);
SqlExpression::MethodCall {
object: obj.name,
method: name_str,
args,
}
}
SqlExpression::MethodCall { .. } | SqlExpression::ChainedMethodCall { .. } => {
debug!(
method = %name_str,
"Creating chained method call"
);
SqlExpression::ChainedMethodCall {
base: Box::new(left),
method: name_str,
args,
}
}
_ => {
debug!(
method = %name_str,
"Creating method call on expression"
);
SqlExpression::ChainedMethodCall {
base: Box::new(left),
method: name_str,
args,
}
}
};
} else {
left = match left {
SqlExpression::Column(table_or_alias) => {
debug!(
table = %table_or_alias,
column = %name_str,
"Creating qualified column reference"
);
SqlExpression::Column(ColumnRef::unquoted(format!(
"{}.{}",
table_or_alias, name_str
)))
}
_ => {
return Err(format!(
"Invalid qualified column reference with expression"
));
}
};
}
} else {
return Err("Expected identifier after '.'".to_string());
}
}
while matches!(
parser.current_token(),
Token::Star | Token::Divide | Token::Modulo
) {
let op = match parser.current_token() {
Token::Star => "*",
Token::Divide => "/",
Token::Modulo => "%",
_ => unreachable!(),
};
log_parse_decision(
"parse_multiplicative",
parser.current_token(),
&format!("Binary operator '{}' found, parsing right operand", op),
);
parser.advance();
let right = parser.parse_primary()?;
debug!(operator = op, "Creating multiplicative binary operation");
left = SqlExpression::BinaryOp {
left: Box::new(left),
op: op.to_string(),
right: Box::new(right),
};
}
let result = Ok(left);
trace_parse_exit("parse_multiplicative", &result);
result
}
pub trait ParseArithmetic {
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_multiplicative(&mut self) -> Result<SqlExpression, String>;
fn parse_method_args(&mut self) -> Result<Vec<SqlExpression>, String>;
}