use super::{
Location, MetaTracker, ParserInner, Result,
expression::{ExprParser, ParseExprContext},
precedence::{self, MIN_BINDING_POWER, Prec},
};
use crate::{
ast::{
AndCondition, BetweenCondition, CompareCondition, CompareExpr, CompareKind, CompareOp,
CompareQuantifier, Condition, ExistsCondition, Expr, ExprList, FloatType, InCondition,
IsFloatCondition, IsNullCondition, LikeCondition, LikeEscape, LikeVariant, Node,
NotCondition, NotEqSymbol, OrCondition, RegexpLikeCondition, RegexpLikeParams, UnaryExprOp,
Value,
},
parser::{Error, expression::ParseCaseIdent, parse_opened_parens, parse_parens},
scanner::{Keyword, Reserved, Token, TokenType},
};
impl<'s, M> ParserInner<'s, M>
where
M: MetaTracker<'s>,
{
pub(super) fn condition_parser(&mut self) -> ConditionParser<'_, 's, M> {
ConditionParser {
inner: self,
context: ParseConditionContext::Default,
}
}
}
#[derive(Debug, Copy, Clone)]
pub(super) enum ParseConditionContext {
Default,
ForProjectionItem,
ForOrderBy,
ForHaving,
}
impl ParseConditionContext {
fn parse_expr_context<'s, M>(&self) -> ParseExprContext<'s, M> {
match self {
ParseConditionContext::Default => ParseExprContext::default(),
ParseConditionContext::ForProjectionItem => ParseExprContext::for_projection_item(),
ParseConditionContext::ForOrderBy => ParseExprContext::for_order_by(),
ParseConditionContext::ForHaving => ParseExprContext::for_having(),
}
}
}
pub(super) struct ConditionParser<'p, 's, M> {
inner: &'p mut ParserInner<'s, M>,
context: ParseConditionContext,
}
impl<'p, 's, M> AsMut<ParserInner<'s, M>> for ConditionParser<'p, 's, M> {
fn as_mut(&mut self) -> &mut ParserInner<'s, M> {
self.inner
}
}
impl<'p, 's, M> ConditionParser<'p, 's, M> {
pub(super) fn with_context(mut self, context: ParseConditionContext) -> Self {
self.context = context;
self
}
}
enum CondOrValue<'s, ID> {
Cond(Condition<'s, ID>),
Value(CompareExpr<'s, ID>),
}
impl<'s, ID> From<CompareExpr<'s, ID>> for CondOrValue<'s, ID> {
fn from(value: CompareExpr<'s, ID>) -> Self {
Self::Value(value)
}
}
impl<'s, ID> From<Condition<'s, ID>> for CondOrValue<'s, ID> {
fn from(value: Condition<'s, ID>) -> Self {
Self::Cond(value)
}
}
#[derive(Copy, Clone)]
enum ParseState {
CondOrValue,
ValueOnly,
}
impl<'p, 's, M> ConditionParser<'p, 's, M>
where
M: MetaTracker<'s>,
{
#[inline]
pub(super) fn parse_condition(&mut self) -> Result<Condition<'s, M::NodeId>> {
self.parse_condition_(MIN_BINDING_POWER)
}
fn parse_condition_(&mut self, min_bp: Prec) -> Result<Condition<'s, M::NodeId>> {
let left = self.parse_single_condition()?;
self.parse_condition_rest(left, min_bp)
}
fn parse_single_condition(&mut self) -> Result<Condition<'s, M::NodeId>> {
let left = match self.parse_cond_or_value(MIN_BINDING_POWER, ParseState::CondOrValue)? {
CondOrValue::Cond(cond) => return Ok(cond),
CondOrValue::Value(expr) => expr,
};
self.parse_single_condition_rest(left)
}
fn parse_condition_rest(
&mut self,
mut left: Condition<'s, M::NodeId>,
min_bp: Prec,
) -> Result<Condition<'s, M::NodeId>> {
enum Op {
And,
Or,
}
impl Op {
fn precedence(&self) -> (Prec, Prec) {
precedence::binary(match self {
Op::And => precedence::BinaryOp::And,
Op::Or => precedence::BinaryOp::Or,
})
}
fn combine<'s, ID>(
&self,
left: Condition<'s, ID>,
op_id: ID,
right: Condition<'s, ID>,
) -> Condition<'s, ID> {
match self {
Op::And => Condition::And(AndCondition {
left: left.into(),
and_token: Node((), op_id),
right: right.into(),
}),
Op::Or => Condition::Or(OrCondition {
left: left.into(),
or_token: Node((), op_id),
right: right.into(),
}),
}
}
}
while let Some(t) = self.inner.peek_token()? {
let op = expect_token!(|t| "AND or OR" match {
TokenType::Keyword(Keyword::AND) => Op::And,
TokenType::Keyword(Keyword::OR) => Op::Or,
TokenType::RightParen => {
let loc = t.loc;
if self.inner.nest_level == 0 {
return Err(Error::Unbalanced { loc });
}
break;
}
_ => break,
});
let (l_bp, r_bp) = op.precedence();
if l_bp < min_bp {
break;
}
let op_id = {
let loc = t.loc;
self.inner.consume_token()?;
self.inner.meta_tracker.on_node_start(loc)
};
let right = self.parse_condition_(r_bp)?;
left = op.combine(left, op_id, right);
}
Ok(left)
}
fn is_condition_operator(t: &Token<'_>) -> bool {
matches!(
t.ttype,
TokenType::Equal
| TokenType::LessGreater
| TokenType::BangEqual
| TokenType::CaretEqual
| TokenType::Less
| TokenType::LessEqual
| TokenType::Greater
| TokenType::GreaterEqual
| TokenType::Keyword(Keyword::IS)
| TokenType::Keyword(Keyword::IN)
| TokenType::Keyword(Keyword::NOT)
| TokenType::Keyword(Keyword::BETWEEN)
| TokenType::Keyword(Keyword::LIKE)
| TokenType::Identifier(_, Some(Reserved::LIKE2))
| TokenType::Identifier(_, Some(Reserved::LIKE4))
| TokenType::Identifier(_, Some(Reserved::LIKEC))
)
}
fn parse_single_condition_rest(
&mut self,
left: CompareExpr<'s, M::NodeId>,
) -> Result<Condition<'s, M::NodeId>> {
enum Dispatched<'s, R> {
Ok(R),
Unexpected(Option<Token<'s>>, &'static str),
}
#[rustfmt::skip]
fn dispatch<'p, 's, M: MetaTracker<'s>>(
p: &mut ConditionParser<'p, 's, M>,
left: CompareExpr<'s, M::NodeId>,
) -> Result<Dispatched<'s, Condition<'s, M::NodeId>>> {
const EXPECTED_AFTER_EXPR: &str = "the IS, IN, LIKE keyword or a comparison operator";
const EXPECTED_AFTER_NOT: &str = "the LIKE or IN keyword";
match p.inner.next_token()? {
None => Ok(Dispatched::Unexpected(None, EXPECTED_AFTER_EXPR)),
Some(t) => {
match t.ttype {
TokenType::Equal => return p.parse_compare_condition_right_(left, CompareKind::Eq, t.loc).map(Dispatched::Ok),
TokenType::LessGreater => return p.parse_compare_condition_right_(left, CompareKind::NotEq(NotEqSymbol::Diamond), t.loc).map(Dispatched::Ok),
TokenType::BangEqual => return p.parse_compare_condition_right_(left, CompareKind::NotEq(NotEqSymbol::Logical), t.loc).map(Dispatched::Ok),
TokenType::CaretEqual => return p.parse_compare_condition_right_(left, CompareKind::NotEq(NotEqSymbol::Bitwise), t.loc).map(Dispatched::Ok),
TokenType::Less => return p.parse_compare_condition_right_(left, CompareKind::Lt, t.loc).map(Dispatched::Ok),
TokenType::LessEqual => return p.parse_compare_condition_right_(left, CompareKind::LtEq, t.loc).map(Dispatched::Ok),
TokenType::Greater => return p.parse_compare_condition_right_(left, CompareKind::Gt, t.loc).map(Dispatched::Ok),
TokenType::GreaterEqual => return p.parse_compare_condition_right_(left, CompareKind::GtEq, t.loc).map(Dispatched::Ok),
TokenType::Keyword(Keyword::NOT) => {
let not_token = Some(Node((), p.inner.meta_tracker.on_node_start(t.loc)));
match p.inner.next_token()? {
None => return Ok(Dispatched::Unexpected(None, EXPECTED_AFTER_NOT)),
Some(t) => {
if matches!(t.ttype, TokenType::Keyword(Keyword::IN)) {
let in_token = Node((), p.inner.meta_tracker.on_node_start(t.loc));
return p.parse_in_condition_right_(left, not_token, in_token).map(Dispatched::Ok);
} else if let CompareExpr::Expr(left) = left {
match t.ttype {
TokenType::Keyword(Keyword::BETWEEN) => return p.parse_expr_between_condition_right_(left, not_token, t).map(Dispatched::Ok),
TokenType::Keyword(Keyword::LIKE) => return p.parse_expr_like_condition_right_(left, not_token, (LikeVariant::Like, t.loc)).map(Dispatched::Ok),
TokenType::Identifier(_, Some(Reserved::LIKE2)) => return p.parse_expr_like_condition_right_(left, not_token, (LikeVariant::Like2, t.loc)).map(Dispatched::Ok),
TokenType::Identifier(_, Some(Reserved::LIKE4)) => return p.parse_expr_like_condition_right_(left, not_token, (LikeVariant::Like4, t.loc)).map(Dispatched::Ok),
TokenType::Identifier(_, Some(Reserved::LIKEC)) => return p.parse_expr_like_condition_right_(left, not_token, (LikeVariant::LikeC, t.loc)).map(Dispatched::Ok),
_ => {}
}
}
return Ok(Dispatched::Unexpected(Some(t), EXPECTED_AFTER_NOT));
}
}
}
TokenType::Keyword(Keyword::IN) => {
let in_token = Node((), p.inner.meta_tracker.on_node_start(t.loc));
return p.parse_in_condition_right_(left, None, in_token).map(Dispatched::Ok);
}
_ => if let CompareExpr::Expr(left) = left {
match t.ttype {
TokenType::Keyword(Keyword::IS) => return p.parse_expr_is_condition_right_(left, t).map(Dispatched::Ok),
TokenType::Keyword(Keyword::BETWEEN) => return p.parse_expr_between_condition_right_(left, None, t).map(Dispatched::Ok),
TokenType::Keyword(Keyword::LIKE) => return p.parse_expr_like_condition_right_(left, None, (LikeVariant::Like, t.loc)).map(Dispatched::Ok),
TokenType::Identifier(_, Some(reserved)) => match reserved {
Reserved::LIKE2 => return p.parse_expr_like_condition_right_(left, None, (LikeVariant::Like2, t.loc)).map(Dispatched::Ok),
Reserved::LIKE4 => return p.parse_expr_like_condition_right_(left, None, (LikeVariant::Like4, t.loc)).map(Dispatched::Ok),
Reserved::LIKEC => return p.parse_expr_like_condition_right_(left, None, (LikeVariant::LikeC, t.loc)).map(Dispatched::Ok),
_ => {},
}
_ => {},
}
}
}
Ok(Dispatched::Unexpected(Some(t), EXPECTED_AFTER_EXPR))
}
}
}
match dispatch(self, left)? {
Dispatched::Ok(r) => Ok(r),
Dispatched::Unexpected(Some(t), expected) => Err(Error::unexpected_token(t, expected)),
Dispatched::Unexpected(None, expected) => unexpected_eof_err!(self.inner, expected),
}
}
fn parse_compare_condition_right_(
&mut self,
left: CompareExpr<'s, M::NodeId>,
op: CompareKind,
op_loc: Location,
) -> Result<Condition<'s, M::NodeId>> {
let op_id = self.inner.meta_tracker.on_node_start(op_loc);
let quantifier = if let Some(t) = self.inner.peek_token()? {
let q = match &t.ttype {
TokenType::Keyword(Keyword::ALL) => Some(CompareQuantifier::All),
TokenType::Keyword(Keyword::ANY) => Some(CompareQuantifier::Any),
TokenType::Identifier(_, Some(Reserved::SOME)) => Some(CompareQuantifier::Some),
_ => None,
};
if let Some(q) = q {
let loc = t.loc;
self.inner.consume_token()?;
Some(Node(q, self.inner.meta_tracker.on_node_start(loc)))
} else {
None
}
} else {
None
};
let op = CompareOp {
kind: Node(op, op_id),
quantifier,
};
let error_loc = self
.inner
.peek_token()?
.map(|t| t.loc)
.unwrap_or(Location { line: 0, col: 0 });
let right =
match self.parse_cond_or_value(precedence::MIN_BINDING_POWER, ParseState::ValueOnly)? {
CondOrValue::Cond(_) => {
return Err(Error::Unexpected {
unexpected: "condition".into(),
expected: "an expression, an expression list, or an expressing list group",
loc: error_loc,
});
}
CondOrValue::Value(expr) => expr,
};
Ok(Condition::Compare(CompareCondition {
left: left.into(),
op,
right: right.into(),
}))
}
fn parse_expr_is_condition_right_(
&mut self,
left: Expr<'s, M::NodeId>,
is_token: Token<'s>,
) -> Result<Condition<'s, M::NodeId>> {
let inner = &mut *self.inner;
let is_token = Node((), inner.meta_tracker.on_node_start(is_token.loc));
let not_token = expect_token!(|t = inner.peek_token()| "the NOT, NULL, NAN, or INFINITE keyword" match {
TokenType::Keyword(Keyword::NOT) => {
let loc = t.loc;
inner.consume_token()?;
Some(Node((), inner.meta_tracker.on_node_start(loc)))
}
_ => None
});
let condition = expect_token!(|t = inner.next_token()| "the NULL, NAN, or INFINITE keyword" match {
TokenType::Keyword(Keyword::NULL) => Condition::IsNull(IsNullCondition {
expr: left.into(),
is_token,
not_token,
null_token: Node((), inner.meta_tracker.on_node_start(t.loc))
}),
TokenType::Identifier(_, Some(Reserved::NAN)) => Condition::IsFloat(IsFloatCondition {
expr: left.into(),
is_token,
not_token,
float_type: Node(FloatType::Nan, inner.meta_tracker.on_node_start(t.loc)),
}),
TokenType::Identifier(_, Some(Reserved::INFINITE)) => Condition::IsFloat(IsFloatCondition {
expr: left.into(),
is_token,
not_token,
float_type: Node(FloatType::Infinite, inner.meta_tracker.on_node_start(t.loc)),
}),
});
Ok(condition)
}
fn parse_expr_between_condition_right_(
&mut self,
expr: Expr<'s, M::NodeId>,
not_token: Option<Node<(), M::NodeId>>,
between_token: Token<'s>,
) -> Result<Condition<'s, M::NodeId>> {
let between_token = Node((), self.inner.meta_tracker.on_node_start(between_token.loc));
let range_from = self.parse_expr()?;
let and_token = expect_token!(|t = (self.inner).next_token()| "the AND keyword" match {
TokenType::Keyword(Keyword::AND) => Node((), self.inner.meta_tracker.on_node_start(t.loc)),
});
let range_upto = self.parse_expr()?;
Ok(Condition::Between(
BetweenCondition {
expr,
not_token,
between_token,
range_from,
and_token,
range_upto,
}
.into(),
))
}
fn parse_expr_like_condition_right_(
&mut self,
left: Expr<'s, M::NodeId>,
not_token: Option<Node<(), M::NodeId>>,
(variant, variant_loc): (LikeVariant, Location),
) -> Result<Condition<'s, M::NodeId>> {
let like_token = Node(variant, self.inner.meta_tracker.on_node_start(variant_loc));
let pattern = self.parse_expr()?;
let escape = if let Some(Token {
ttype: TokenType::Identifier(_, Some(Reserved::ESCAPE)),
loc,
}) = self.inner.peek_token()?
{
let loc = *loc;
self.inner.consume_token()?;
let escape_token = Node((), self.inner.meta_tracker.on_node_start(loc));
Some(LikeEscape {
escape_token,
escape_char: self.parse_expr()?,
})
} else {
None
};
Ok(Condition::Like(
LikeCondition {
source: left,
not_token,
like_token,
pattern,
escape,
}
.into(),
))
}
fn parse_in_condition_right_(
&mut self,
expr: CompareExpr<'s, M::NodeId>,
not_token: Option<Node<(), M::NodeId>>,
in_token: Node<(), M::NodeId>,
) -> Result<Condition<'s, M::NodeId>> {
let error_loc = expect_token!(
|t = (self.inner).peek_token()| "an opening parenthesis (starting an expression list or a sub-query)" match {
TokenType::LeftParen => t.loc,
});
let values = match self.parse_cond_or_value(MIN_BINDING_POWER, ParseState::ValueOnly)? {
CondOrValue::Cond(_) => {
return Err(Error::Unexpected {
unexpected: "condition".into(),
expected: "an expression",
loc: error_loc,
});
}
CondOrValue::Value(expr) => expr,
};
Ok(Condition::In(
InCondition {
expr,
not_token,
in_token,
values,
}
.into(),
))
}
fn parse_cond_or_value(
&mut self,
min_bp: Prec,
p_state: ParseState,
) -> Result<CondOrValue<'s, M::NodeId>> {
let left = expect_token!(|t = (self.inner).next_token()| "a value or an expression" match {
TokenType::Keyword(Keyword::NULL) => {
CompareExpr::Expr(Expr::Value(Node(Value::Null, self.inner.meta_tracker.on_node_start(t.loc)))).into()
}
TokenType::QuestionMark => {
CompareExpr::Expr(Expr::Value(Node(Value::Placeholder(None), self.inner.meta_tracker.on_node_start(t.loc)))).into()
}
TokenType::Integer(lit) => {
CompareExpr::Expr(Expr::Value(Node(Value::Integer(lit), self.inner.meta_tracker.on_node_start(t.loc)))).into()
}
TokenType::Float(lit) => {
CompareExpr::Expr(Expr::Value(Node(Value::Float(lit), self.inner.meta_tracker.on_node_start(t.loc)))).into()
}
TokenType::Text(text, national) => {
CompareExpr::Expr(Expr::Value(Node(Value::Text(text, national), self.inner.meta_tracker.on_node_start(t.loc)))).into()
}
TokenType::Placeholder(ident) => {
CompareExpr::Expr(Expr::Value(Node(Value::Placeholder(Some(ident)), self.inner.meta_tracker.on_node_start(t.loc)))).into()
}
TokenType::Identifier(ident, reserved) => {
match reserved {
Some(Reserved::CASE) => {
let ident = Node(ident, self.inner.meta_tracker.on_node_start(t.loc));
let mut parser = self.expr_parser();
CompareExpr::Expr(
match parser.parse_case_ident(ident)? {
ParseCaseIdent::Ident(ident) => parser.parse_with_ident(ident, t.loc)?,
ParseCaseIdent::Expr(expr) => expr,
}).into()
}
Some(Reserved::REGEXP_LIKE) => {
let regexp_like_token = Node((), self.inner.meta_tracker.on_node_start(t.loc));
parse_parens(self, |p, node_id| {
let source = p.parse_expr()?;
let pattern = expect_token!(|t = (p.inner).next_token()| "a comma (followed by a pattern)" match {
TokenType::Comma => {
p.inner.meta_tracker.on_node_end();
p.parse_expr()?
}
});
let options = if let Some(Token { ttype: TokenType::Comma, .. }) = p.inner.peek_token()? {
p.inner.consume_token()?;
p.inner.meta_tracker.on_node_end();
Some(p.parse_expr()?)
} else {
None
};
Ok(CondOrValue::Cond(Condition::RegexpLike(RegexpLikeCondition {
regexp_like_token,
params: Node(RegexpLikeParams { source, pattern, options }, node_id)
}.into())))
})?
}
_ => {
let id = self.inner.meta_tracker.on_node_start(t.loc);
let ident = self.inner.parse_identifier_(Node(ident, id))?;
CompareExpr::Expr(self.expr_parser().parse_with_identifier(ident, t.loc)?).into()
}
}
}
TokenType::Plus => CompareExpr::Expr(self.expr_parser().parse_unary(UnaryExprOp::Add, t.loc)?).into(),
TokenType::Minus => CompareExpr::Expr(self.expr_parser().parse_unary(UnaryExprOp::Sub, t.loc)?).into(),
TokenType::Keyword(Keyword::NOT) if matches!(p_state, ParseState::CondOrValue) => {
let not_token = Node((), self.inner.meta_tracker.on_node_start(t.loc));
let condition = self.parse_condition_(precedence::unary(precedence::UnaryOp::Not).1)?.into();
return Ok(Condition::Not(NotCondition { not_token, condition }).into());
}
TokenType::Keyword(Keyword::EXISTS) if matches!(p_state, ParseState::CondOrValue) => {
let exists_token = Node((), self.inner.meta_tracker.on_node_start(t.loc));
let query = parse_parens(self, |p, node_id| {
Ok(Node(p.inner.parse_query()?.into(), node_id))
})?;
return Ok(Condition::Exists(ExistsCondition { exists_token, query }).into());
}
TokenType::LeftParen => parse_opened_parens(self, t.into(), |p, node_id| {
if p.inner.can_parse_query()? {
Ok(CondOrValue::Value(CompareExpr::Expr(Expr::SubQuery(Node(p.inner.parse_query()?.into(), node_id)))))
} else {
let error_loc = p.inner.peek_token()?.map(|t| t.loc).unwrap_or(Location { line: 0, col: 0 });
Ok(match p.parse_cond_or_value(MIN_BINDING_POWER, p_state)? {
CondOrValue::Cond(cond) => {
let cond = p.parse_condition_rest(cond, MIN_BINDING_POWER)?;
return Ok(CondOrValue::Cond(Condition::Nested(Node(cond.into(), node_id))));
}
CondOrValue::Value(CompareExpr::Expr(expr)) => {
if let Some(Token { ttype: TokenType::Comma, .. }) = p.inner.peek_token()? {
let mut exprs = Vec::new();
exprs.push(expr);
loop {
p.inner.consume_token()?;
p.inner.meta_tracker.on_node_end();
exprs.push(p.parse_expr()?);
if let Some(Token { ttype: TokenType::Comma, .. }) = p.inner.peek_token()? {
} else {
break;
}
}
CompareExpr::List(ExprList(Node(exprs, node_id))).into()
} else {
if let Some(t) = p.inner.peek_token()? && Self::is_condition_operator(t) {
let cond = p.parse_single_condition_rest(CompareExpr::Expr(expr))?;
let cond = p.parse_condition_rest(cond, MIN_BINDING_POWER)?;
Condition::Nested(Node(cond.into(), node_id)).into()
} else {
CompareExpr::Expr(Expr::Nested(Node(expr.into(), node_id))).into()
}
}
}
CondOrValue::Value(CompareExpr::List(tuple)) if matches!(p_state, ParseState::ValueOnly) => {
let mut tuples = Vec::new();
tuples.push(tuple);
if let Some(Token { ttype: TokenType::Comma, .. }) = p.inner.peek_token()? {
loop {
p.inner.consume_token()?;
p.inner.meta_tracker.on_node_end();
let error_loc = p.inner.peek_token()?.map(|t| t.loc).unwrap_or(Location { line: 0, col: 0 });
match p.parse_cond_or_value(MIN_BINDING_POWER, ParseState::ValueOnly)? {
CondOrValue::Value(CompareExpr::List(tuple)) => {
tuples.push(tuple);
}
_ => return Err(Error::Unexpected {
unexpected: "condition, expression, or expression list group".into(),
expected: "an expression list",
loc: error_loc,
}),
};
if let Some(Token { ttype: TokenType::Comma, .. }) = p.inner.peek_token()? {
} else {
break;
}
}
}
CompareExpr::Lists(Node(tuples, node_id)).into()
}
_ => {
return Err(Error::Unexpected {
unexpected: "nested expression list group".into(),
expected: "expression or expression list",
loc: error_loc,
});
}
})
}
})?
});
match left {
left @ CondOrValue::Cond(_) => Ok(left),
CondOrValue::Value(left) => {
let left = match left {
CompareExpr::Expr(left) => {
CompareExpr::Expr(self.expr_parser().parse_right(left, min_bp)?)
}
expr @ CompareExpr::List(_) | expr @ CompareExpr::Lists(_) => expr,
};
Ok(left.into())
}
}
}
fn expr_parser<'c>(&'c mut self) -> ExprParser<'c, 's, M> {
self.inner
.expr_parser()
.with_context(self.context.parse_expr_context())
}
fn parse_expr(&mut self) -> Result<Expr<'s, M::NodeId>> {
self.expr_parser().parse()
}
}