use reblessive::Stk;
use surrealdb_types::ToSql;
use super::enter_query_recursion;
use super::mac::unexpected;
use crate::sql::operator::{BindingPower, BooleanOperator, MatchesOperator, NearestNeighbor};
use crate::sql::{BinaryOperator, Expr, Literal, Part, PostfixOperator, PrefixOperator};
use crate::syn::error::bail;
use crate::syn::lexer::compound::Numeric;
use crate::syn::parser::mac::expected;
use crate::syn::parser::{ParseResult, Parser};
use crate::syn::token::{Span, Token, TokenKind, t};
use crate::types::PublicDuration;
impl Parser<'_> {
pub(crate) async fn parse_expr_start(&mut self, stk: &mut Stk) -> ParseResult<Expr> {
self.table_as_field = true;
self.pratt_parse_expr(stk, BindingPower::Base).await
}
pub(crate) async fn parse_expr_table(&mut self, stk: &mut Stk) -> ParseResult<Expr> {
let old = self.table_as_field;
self.table_as_field = false;
let res = enter_query_recursion!(this = self => {
this.pratt_parse_expr(stk, BindingPower::Base).await
});
self.table_as_field = old;
res
}
pub(crate) async fn parse_expr_field(&mut self, stk: &mut Stk) -> ParseResult<Expr> {
let old = self.table_as_field;
self.table_as_field = true;
let res = enter_query_recursion!(this = self => {
this.pratt_parse_expr(stk, BindingPower::Base).await
});
self.table_as_field = old;
res
}
pub(super) async fn parse_expr_inherit(&mut self, stk: &mut Stk) -> ParseResult<Expr> {
enter_query_recursion!(this = self => {
this.pratt_parse_expr(stk, BindingPower::Base).await
})
}
fn infix_binding_power(&mut self, token: TokenKind) -> Option<BindingPower> {
match token {
t!("||") | t!("OR") => Some(BindingPower::Or),
t!("&&") | t!("AND") => Some(BindingPower::And),
t!("=") | t!("IS") | t!("==") | t!("!=") | t!("*=") | t!("?=") | t!("@") => {
Some(BindingPower::Equality)
}
t!("<") => {
if let Some(peek) = self.peek_whitespace1()
&& let t!("-") | t!("~") | t!("->") | t!("..") = peek.kind
{
return None;
}
Some(BindingPower::Relation)
}
t!(">") => {
if let Some(t!("..")) = self.peek_whitespace1().map(|x| x.kind) {
return Some(BindingPower::Range);
}
Some(BindingPower::Relation)
}
t!("..") => Some(BindingPower::Range),
t!("<=")
| t!(">=")
| t!("∋")
| t!("CONTAINS")
| t!("∌")
| t!("CONTAINSNOT")
| t!("∈")
| t!("INSIDE")
| t!("∉")
| t!("NOTINSIDE")
| t!("⊇")
| t!("CONTAINSALL")
| t!("⊃")
| t!("CONTAINSANY")
| t!("⊅")
| t!("CONTAINSNONE")
| t!("⊆")
| t!("ALLINSIDE")
| t!("⊂")
| t!("ANYINSIDE")
| t!("⊄")
| t!("NONEINSIDE")
| t!("OUTSIDE")
| t!("INTERSECTS")
| t!("NOT")
| t!("IN")
| t!("<|") => Some(BindingPower::Relation),
t!("+") | t!("-") => Some(BindingPower::AddSub),
t!("*") | t!("×") | t!("/") | t!("÷") | t!("%") => Some(BindingPower::MulDiv),
t!("**") => Some(BindingPower::Power),
t!("?:") | t!("?") => Some(BindingPower::Nullish),
_ => None,
}
}
fn prefix_binding_power(&mut self, token: TokenKind) -> Option<BindingPower> {
match token {
t!("!") | t!("+") | t!("-") => Some(BindingPower::Prefix),
t!("..") => Some(BindingPower::Range),
t!("<") => {
if let Some(peek) = self.peek_whitespace1() {
if peek.kind == t!("-") {
let recover = self.last_span();
if self.peek2().kind == TokenKind::Digits {
self.backup_after(recover);
return Some(BindingPower::Prefix);
}
return None;
}
if let t!("~") | t!("->") = peek.kind {
return None;
}
}
Some(BindingPower::Prefix)
}
_ => None,
}
}
fn postfix_binding_power(&mut self, token: TokenKind) -> Option<BindingPower> {
match token {
t!(">") => {
if let Some(peek) = self.peek_whitespace1()
&& let t!("..") = peek.kind
{
if let Some(peek) = self.peek_whitespace2()
&& (t!("=") == peek.kind || Self::kind_starts_expression(peek.kind))
{
return None;
} else {
return Some(BindingPower::Range);
}
}
None
}
t!("..") => match self.peek_whitespace1().map(|x| x.kind) {
Some(t!("=")) => None,
Some(x) if Self::kind_starts_expression(x) => None,
_ => Some(BindingPower::Range),
},
t!("(") => Some(BindingPower::Call),
_ => None,
}
}
async fn parse_prefix_op(&mut self, stk: &mut Stk, min_bp: BindingPower) -> ParseResult<Expr> {
let token = self.peek();
let operator = match token.kind {
t!("+") => {
if let Some(TokenKind::Digits) = self.peek_whitespace1().map(|x| x.kind) {
self.pop_peek();
let expr = match self.next_token_value::<Numeric>()? {
Numeric::Float(f) => Expr::Literal(Literal::Float(f)),
Numeric::Integer(i) => {
Expr::Literal(Literal::Integer(i.into_int(self.recent_span())?))
}
Numeric::Decimal(d) => Expr::Literal(Literal::Decimal(d)),
Numeric::Duration(d) => Expr::Prefix {
op: PrefixOperator::Positive,
expr: Box::new(Expr::Literal(Literal::Duration(PublicDuration::from(
d,
)))),
},
};
if self.peek_continues_idiom() {
return self
.parse_remaining_value_idiom(stk, vec![Part::Start(expr)])
.await;
} else {
return Ok(expr);
}
}
self.pop_peek();
PrefixOperator::Positive
}
t!("-") => {
if let Some(TokenKind::Digits) = self.peek_whitespace1().map(|x| x.kind) {
self.pop_peek();
let expr = match self.next_token_value::<Numeric>()? {
Numeric::Float(f) => Expr::Literal(Literal::Float(-f)),
Numeric::Integer(i) => {
Expr::Literal(Literal::Integer(i.into_neg_int(self.recent_span())?))
}
Numeric::Decimal(d) => Expr::Literal(Literal::Decimal(-d)),
Numeric::Duration(d) => Expr::Prefix {
op: PrefixOperator::Negate,
expr: Box::new(Expr::Literal(Literal::Duration(PublicDuration::from(
d,
)))),
},
};
if self.peek_continues_idiom() {
return self
.parse_remaining_value_idiom(stk, vec![Part::Start(expr)])
.await;
} else {
return Ok(expr);
}
}
self.pop_peek();
PrefixOperator::Negate
}
t!("!") => {
self.pop_peek();
PrefixOperator::Not
}
t!("<") => {
self.pop_peek();
let kind = self.parse_kind(stk, token.span).await?;
PrefixOperator::Cast(kind)
}
t!("..") => {
self.pop_peek();
if let Some(x) = self.peek_whitespace() {
if let t!("=") = x.kind {
self.pop_peek();
PrefixOperator::RangeInclusive
} else if !Self::kind_starts_prime_value(x.kind) {
return Ok(Expr::Literal(Literal::UnboundedRange));
} else {
PrefixOperator::Range
}
} else {
return Ok(Expr::Literal(Literal::UnboundedRange));
}
}
_ => unreachable!(),
};
let v = stk.run(|stk| self.pratt_parse_expr(stk, min_bp)).await?;
Ok(Expr::Prefix {
op: operator,
expr: Box::new(v),
})
}
pub(super) fn parse_nearest_neighbor(&mut self, token: Token) -> ParseResult<NearestNeighbor> {
let amount = self.next_token_value()?;
let res = if self.eat(t!(",")) {
let token = self.peek();
match token.kind {
TokenKind::Distance(_) => {
let d = self.parse_distance()?;
NearestNeighbor::K(amount, d)
}
TokenKind::Digits => {
let ef = self.next_token_value()?;
NearestNeighbor::Approximate(amount, ef)
}
_ => {
bail!("Unexpected token {} expected a distance of an integer", token.kind,
@token.span => "The NN operator accepts either a distance or an EF value (integer)")
}
}
} else {
NearestNeighbor::KTree(amount)
};
if !self.eat(t!("|")) || !self.eat_whitespace(t!(">")) {
bail!("Unexpected token `{}` expected delimiter `|>`",
self.peek().kind,
@self.recent_span(),
@token.span=> "expected this delimiter to close"
);
}
Ok(res)
}
fn operator_has_associativity(operator: &BinaryOperator) -> bool {
!matches!(
operator,
BinaryOperator::Equal
| BinaryOperator::NotEqual
| BinaryOperator::AllEqual
| BinaryOperator::AnyEqual
| BinaryOperator::LessThan
| BinaryOperator::LessThanEqual
| BinaryOperator::MoreThan
| BinaryOperator::MoreThanEqual
| BinaryOperator::Matches(_)
| BinaryOperator::Contain
| BinaryOperator::NotContain
| BinaryOperator::ContainAll
| BinaryOperator::ContainAny
| BinaryOperator::ContainNone
| BinaryOperator::Inside
| BinaryOperator::NotInside
| BinaryOperator::AllInside
| BinaryOperator::AnyInside
| BinaryOperator::NoneInside
| BinaryOperator::Outside
| BinaryOperator::Intersects
| BinaryOperator::NearestNeighbor(_)
)
}
fn expr_is_range(expr: &Expr) -> bool {
match expr {
Expr::Binary {
op,
..
} => matches!(
op,
BinaryOperator::Range
| BinaryOperator::RangeSkipInclusive
| BinaryOperator::RangeSkip
| BinaryOperator::RangeInclusive
),
Expr::Prefix {
op,
..
} => matches!(op, PrefixOperator::Range | PrefixOperator::RangeInclusive),
Expr::Postfix {
op,
..
} => matches!(op, PostfixOperator::Range | PostfixOperator::RangeSkip),
_ => false,
}
}
async fn parse_infix_op(
&mut self,
stk: &mut Stk,
min_bp: BindingPower,
lhs: Expr,
lhs_prime: bool,
) -> ParseResult<Expr> {
let token = self.next();
let operator = match token.kind {
t!("||") | t!("OR") => BinaryOperator::Or,
t!("&&") | t!("AND") => BinaryOperator::And,
t!("?:") => BinaryOperator::TenaryCondition,
t!("?") => {
if !self.eat_whitespace(t!("?")) {
unexpected!(self, token, "`??`")
}
BinaryOperator::NullCoalescing
}
t!("==") => BinaryOperator::ExactEqual,
t!("!=") => BinaryOperator::NotEqual,
t!("*=") => BinaryOperator::AllEqual,
t!("?=") => BinaryOperator::AnyEqual,
t!("=") => BinaryOperator::Equal,
t!("@") => {
let op = self.parse_matches()?;
BinaryOperator::Matches(op)
}
t!("<=") => BinaryOperator::LessThanEqual,
t!("<") => BinaryOperator::LessThan,
t!(">=") => BinaryOperator::MoreThanEqual,
t!("**") => BinaryOperator::Power,
t!("+") => BinaryOperator::Add,
t!("-") => BinaryOperator::Subtract,
t!("*") | t!("×") => BinaryOperator::Multiply,
t!("/") | t!("÷") => BinaryOperator::Divide,
t!("%") => BinaryOperator::Remainder,
t!("∋") | t!("CONTAINS") => BinaryOperator::Contain,
t!("∌") | t!("CONTAINSNOT") => BinaryOperator::NotContain,
t!("∈") | t!("INSIDE") => BinaryOperator::Inside,
t!("∉") | t!("NOTINSIDE") => BinaryOperator::NotInside,
t!("⊇") | t!("CONTAINSALL") => BinaryOperator::ContainAll,
t!("⊃") | t!("CONTAINSANY") => BinaryOperator::ContainAny,
t!("⊅") | t!("CONTAINSNONE") => BinaryOperator::ContainNone,
t!("⊆") | t!("ALLINSIDE") => BinaryOperator::AllInside,
t!("⊂") | t!("ANYINSIDE") => BinaryOperator::AnyInside,
t!("⊄") | t!("NONEINSIDE") => BinaryOperator::NoneInside,
t!("IS") => {
if self.eat(t!("NOT")) {
BinaryOperator::NotEqual
} else {
BinaryOperator::Equal
}
}
t!("OUTSIDE") => BinaryOperator::Outside,
t!("INTERSECTS") => BinaryOperator::Intersects,
t!("NOT") => {
expected!(self, t!("IN"));
BinaryOperator::NotInside
}
t!("IN") => BinaryOperator::Inside,
t!("<|") => {
BinaryOperator::NearestNeighbor(Box::new(self.parse_nearest_neighbor(token)?))
}
t!(">") => {
if let Some(t!("..")) = self.peek_whitespace().map(|x| x.kind) {
self.pop_peek();
if let Some(t!("=")) = self.peek_whitespace().map(|x| x.kind) {
self.pop_peek();
BinaryOperator::RangeSkipInclusive
} else {
BinaryOperator::RangeSkip
}
} else {
BinaryOperator::MoreThan
}
}
t!("..") => {
if let Some(t!("=")) = self.peek_whitespace().map(|x| x.kind) {
self.pop_peek();
BinaryOperator::RangeInclusive
} else {
BinaryOperator::Range
}
}
x => unreachable!("found non-operator token {x:?}"),
};
let rhs_covered = self.peek().kind == t!("(");
let rhs = stk.run(|ctx| self.pratt_parse_expr(ctx, min_bp)).await?;
let has_associatitivity = Self::operator_has_associativity(&operator);
if !lhs_prime
&& !has_associatitivity
&& BindingPower::for_expr(&lhs) == BindingPower::for_binary_operator(&operator)
{
let span = token.span.covers(self.recent_span());
if matches!(
operator,
BinaryOperator::Range
| BinaryOperator::RangeSkipInclusive
| BinaryOperator::RangeSkip
| BinaryOperator::RangeInclusive
) {
bail!("Chained range operators has no specified associativity",
@span => "use parens, '()', to specify which operator must be evaluated first")
} else {
bail!("Chained relational operators have no defined associativity.",
@span => "Use parens, '()', to specify which operator must be evaluated first")
}
}
if !rhs_covered
&& !has_associatitivity
&& BindingPower::for_expr(&rhs) == BindingPower::for_binary_operator(&operator)
{
let span = token.span.covers(self.recent_span());
if matches!(
operator,
BinaryOperator::Range
| BinaryOperator::RangeSkipInclusive
| BinaryOperator::RangeSkip
| BinaryOperator::RangeInclusive
) {
bail!("Chained range operators have no defined associativity.",
@span => "Use parens, '()', to specify which operator must be evaluated first")
} else {
bail!("Chained relational operators have no defined associativity.",
@span => "Use parens, '()', to specify which operator must be evaluated first")
}
}
Ok(Expr::Binary {
left: Box::new(lhs),
op: operator,
right: Box::new(rhs),
})
}
fn parse_matches(&mut self) -> ParseResult<MatchesOperator> {
let peek = self.peek();
match peek.kind {
TokenKind::Digits => {
let number = self.next_token_value()?;
let op = if self.eat(t!(",")) {
let peek = self.next();
let op = match peek.kind {
t!("AND") => BooleanOperator::And,
t!("OR") => BooleanOperator::Or,
_ => unexpected!(self, peek, "either `AND` or `OR`"),
};
Some(op)
} else {
None
};
expected!(self, t!("@"));
Ok(MatchesOperator {
operator: op,
rf: Some(number),
})
}
t!("AND") => {
self.pop_peek();
expected!(self, t!("@"));
Ok(MatchesOperator {
operator: Some(BooleanOperator::And),
rf: None,
})
}
t!("OR") => {
self.pop_peek();
expected!(self, t!("@"));
Ok(MatchesOperator {
operator: Some(BooleanOperator::Or),
rf: None,
})
}
t!("@") => {
self.pop_peek();
Ok(MatchesOperator {
operator: None,
rf: None,
})
}
_ => unexpected!(self, peek, "a match reference, operator or `@`"),
}
}
async fn parse_postfix(
&mut self,
stk: &mut Stk,
lhs: Expr,
lhs_prime: bool,
) -> ParseResult<Expr> {
let token = self.next();
let op = match token.kind {
t!(">") => {
assert!(self.eat_whitespace(t!("..")));
if !lhs_prime && Self::expr_is_range(&lhs) {
bail!("Chaining range operators has no specified associativity",
@token.span => "use parens, '()', to specify which operator must be evaluated first")
}
PostfixOperator::RangeSkip
}
t!("..") => {
if !lhs_prime && Self::expr_is_range(&lhs) {
bail!("Chaining range operators has no specified associativity",
@token.span => "use parens, '()', to specify which operator must be evaluated first")
}
PostfixOperator::Range
}
t!("(") => {
let mut args = Vec::new();
loop {
if self.eat(t!(")")) {
break;
}
let arg = stk.run(|ctx| self.parse_expr_inherit(ctx)).await?;
args.push(arg);
if !self.eat(t!(",")) {
self.expect_closing_delimiter(t!(")"), token.span)?;
break;
}
}
PostfixOperator::Call(args)
}
t!(".") => {
let name = self.parse_ident()?;
expected!(self, t!("("));
let mut args = Vec::new();
loop {
if self.eat(t!(")")) {
break;
}
let arg = stk.run(|ctx| self.parse_expr_inherit(ctx)).await?;
args.push(arg);
if !self.eat(t!(",")) {
self.expect_closing_delimiter(t!(")"), token.span)?;
break;
}
}
PostfixOperator::MethodCall(name.into_string(), args)
}
x => unreachable!("found non-operator token {x:?}"),
};
Ok(Expr::Postfix {
expr: Box::new(lhs),
op,
})
}
fn enter_expr_depth(&mut self) -> ParseResult<()> {
if self.settings.expr_recursion_limit == 0 {
bail!("Exceeded expression recursion depth limit",
@self.last_span() => "this expression nests or chains operators too deeply");
}
self.settings.expr_recursion_limit -= 1;
Ok(())
}
async fn pratt_parse_expr(&mut self, stk: &mut Stk, min_bp: BindingPower) -> ParseResult<Expr> {
let restore_to = self.settings.expr_recursion_limit;
self.enter_expr_depth()?;
let res = self.pratt_parse_expr_inner(stk, min_bp).await;
self.settings.expr_recursion_limit = restore_to;
res
}
async fn pratt_parse_expr_inner(
&mut self,
stk: &mut Stk,
min_bp: BindingPower,
) -> ParseResult<Expr> {
let peek = self.peek();
let (mut lhs, mut lhs_prime) = if let Some(bp) = self.prefix_binding_power(peek.kind) {
(self.parse_prefix_op(stk, bp).await?, false)
} else {
(self.parse_prime_expr(stk).await?, true)
};
loop {
let token = self.peek();
if let Some(bp) = self.postfix_binding_power(token.kind) {
if bp <= min_bp {
break;
}
self.enter_expr_depth()?;
lhs = self.parse_postfix(stk, lhs, lhs_prime).await?;
lhs_prime = false;
continue;
}
if let t!("+=") | t!("-=") | t!("+?=") = token.kind {
unexpected!(self,token,"an operator",
=> "assignment operators are only allowed in SET and DUPLICATE KEY UPDATE clauses")
}
let Some(bp) = self.infix_binding_power(token.kind) else {
break;
};
if bp <= min_bp {
break;
}
self.enter_expr_depth()?;
lhs = self.parse_infix_op(stk, bp, lhs, lhs_prime).await?;
lhs_prime = false;
}
Ok(lhs)
}
pub(crate) fn reject_letless_let(expr: &Expr, span: Span) -> ParseResult<()> {
let Expr::Binary {
left,
op,
..
} = expr
else {
return Ok(());
};
let Expr::Param(p) = &**left else {
return Ok(());
};
let BinaryOperator::Equal = op else {
return Ok(());
};
bail!("Parameter declarations without `let` are deprecated.",
@span => "Replace with `let {} = ...` to keep the previous behavior.", p.to_sql())
}
}
#[cfg(test)]
mod test {
use surrealdb_types::ToSql;
use crate::sql::{BinaryOperator, Expr, Kind, Literal, PrefixOperator};
use crate::syn;
#[test]
fn cast_int() {
let sql = "<int>1.2345";
let out = syn::expr(sql).unwrap();
assert_eq!("<int> 1.2345f", out.to_sql());
assert_eq!(
out,
Expr::Prefix {
op: PrefixOperator::Cast(Kind::Int),
expr: Box::new(Expr::Literal(Literal::Float(1.2345)))
}
)
}
#[test]
fn cast_string() {
let sql = "<string>1.2345";
let out = syn::expr(sql).unwrap();
assert_eq!("<string> 1.2345f", out.to_sql());
assert_eq!(
out,
Expr::Prefix {
op: PrefixOperator::Cast(Kind::String),
expr: Box::new(Expr::Literal(Literal::Float(1.2345)))
}
)
}
#[test]
fn expression_statement() {
let sql = "true AND false";
let out = syn::expr(sql).unwrap();
assert_eq!("true AND false", out.to_sql());
}
#[test]
fn expression_left_opened() {
let sql = "3 * 3 * 3 = 27";
let out = syn::expr(sql).unwrap();
assert_eq!("3 * 3 * 3 = 27", out.to_sql());
}
#[test]
fn expression_left_closed() {
let sql = "(3 * 3 * 3) = 27";
let out = syn::expr(sql).unwrap();
assert_eq!("3 * 3 * 3 = 27", out.to_sql());
}
#[test]
fn expression_right_opened() {
let sql = "27 = 3 * 3 * 3";
let out = syn::expr(sql).unwrap();
assert_eq!("27 = 3 * 3 * 3", out.to_sql());
}
#[test]
fn expression_right_closed() {
let sql = "27 = (3 * 3 * 3)";
let out = syn::expr(sql).unwrap();
assert_eq!("27 = 3 * 3 * 3", out.to_sql());
}
#[test]
fn expression_both_opened() {
let sql = "3 * 3 * 3 = 3 * 3 * 3";
let out = syn::expr(sql).unwrap();
assert_eq!("3 * 3 * 3 = 3 * 3 * 3", out.to_sql());
}
#[test]
fn expression_both_closed() {
let sql = "(3 * 3 * 3) = (3 * 3 * 3)";
let out = syn::expr(sql).unwrap();
assert_eq!("3 * 3 * 3 = 3 * 3 * 3", out.to_sql());
}
#[test]
fn expression_closed_required() {
let sql = "(3 + 3) * 3";
let out = syn::expr(sql).unwrap();
assert_eq!("(3 + 3) * 3", out.to_sql());
}
#[test]
fn range_closed_required() {
let sql = "(1..2)..3";
let out = syn::expr(sql).unwrap();
assert_eq!("(1..2)..3", out.to_sql());
}
#[test]
fn expression_unary() {
let sql = "-a";
let out = syn::expr(sql).unwrap();
assert_eq!(sql, out.to_sql());
}
#[test]
fn expression_with_unary() {
let sql = "-(5) + 5";
let out = syn::expr(sql).unwrap();
assert_eq!("-5 + 5", out.to_sql());
}
#[test]
fn expression_left_associative() {
let sql = "1 - 1 - 1";
let out = syn::expr(sql).unwrap();
let one = Expr::Literal(Literal::Integer(1));
let expected = Expr::Binary {
left: Box::new(Expr::Binary {
left: Box::new(one.clone()),
op: BinaryOperator::Subtract,
right: Box::new(one.clone()),
}),
op: BinaryOperator::Subtract,
right: Box::new(one),
};
assert_eq!(expected, out);
}
}