surrealpatch/lib/src/sql/operator.rs

294 lines
7.7 KiB
Rust
Raw Normal View History

2023-06-21 18:31:15 +00:00
use crate::idx::ft::MatchRef;
use crate::sql::comment::mightbespace;
use crate::sql::comment::shouldbespace;
2022-01-16 20:31:50 +00:00
use crate::sql::error::IResult;
2020-06-29 15:36:01 +00:00
use nom::branch::alt;
use nom::bytes::complete::tag;
use nom::bytes::complete::tag_no_case;
use nom::character::complete::char;
use nom::character::complete::u8 as uint8;
use nom::combinator::cut;
use nom::combinator::opt;
use nom::combinator::value;
use revision::revisioned;
2020-06-29 15:36:01 +00:00
use serde::{Deserialize, Serialize};
use std::fmt;
use std::fmt::Write;
2020-06-29 15:36:01 +00:00
2023-06-20 23:31:23 +00:00
/// Binary operators.
#[derive(Clone, Debug, Eq, PartialEq, PartialOrd, Serialize, Deserialize, Hash)]
#[revisioned(revision = 1)]
2020-06-29 15:36:01 +00:00
pub enum Operator {
2023-06-20 23:31:23 +00:00
//
Neg, // -
Not, // !
//
2020-06-29 15:36:01 +00:00
Or, // ||
2021-03-29 15:43:37 +00:00
And, // &&
Tco, // ?: Ternary conditional operator
Nco, // ?? Null coalescing operator
2020-06-29 15:36:01 +00:00
//
Add, // +
Sub, // -
Mul, // *
Div, // /
Pow, // **
2020-06-29 15:36:01 +00:00
Inc, // +=
Dec, // -=
Ext, // +?=
2020-06-29 15:36:01 +00:00
//
Equal, // =
Exact, // ==
2020-06-29 15:36:01 +00:00
NotEqual, // !=
AllEqual, // *=
AnyEqual, // ?=
//
2023-06-21 18:31:15 +00:00
Like, // ~
NotLike, // !~
AllLike, // *~
AnyLike, // ?~
Matches(Option<MatchRef>), // @{ref}@
2020-06-29 15:36:01 +00:00
//
LessThan, // <
LessThanOrEqual, // <=
MoreThan, // >
MoreThanOrEqual, // >=
//
Contain, // ∋
NotContain, // ∌
ContainAll, // ⊇
ContainAny, // ⊃
2020-06-29 15:36:01 +00:00
ContainNone, // ⊅
Inside, // ∈
NotInside, // ∉
AllInside, // ⊆
AnyInside, // ⊂
2020-06-29 15:36:01 +00:00
NoneInside, // ⊄
//
Outside,
Intersects,
2020-06-29 15:36:01 +00:00
}
impl Default for Operator {
fn default() -> Self {
Self::Equal
2020-06-29 15:36:01 +00:00
}
}
impl Operator {
#[inline]
pub fn precedence(&self) -> u8 {
match self {
2023-06-20 23:31:23 +00:00
Self::Or => 1,
Self::And => 2,
Self::Tco => 3,
Self::Nco => 4,
Self::Sub => 6,
Self::Add => 7,
Self::Mul => 8,
Self::Div => 9,
_ => 5,
}
}
}
2020-06-29 15:36:01 +00:00
impl fmt::Display for Operator {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
2023-06-20 23:31:23 +00:00
Self::Neg => f.write_str("-"),
Self::Not => f.write_str("!"),
Self::Or => f.write_str("OR"),
Self::And => f.write_str("AND"),
Self::Tco => f.write_str("?:"),
Self::Nco => f.write_str("??"),
Self::Add => f.write_str("+"),
Self::Sub => f.write_char('-'),
Self::Mul => f.write_char('*'),
Self::Div => f.write_char('/'),
Self::Pow => f.write_str("**"),
Self::Inc => f.write_str("+="),
Self::Dec => f.write_str("-="),
Self::Ext => f.write_str("+?="),
Self::Equal => f.write_char('='),
Self::Exact => f.write_str("=="),
Self::NotEqual => f.write_str("!="),
Self::AllEqual => f.write_str("*="),
Self::AnyEqual => f.write_str("?="),
Self::Like => f.write_char('~'),
Self::NotLike => f.write_str("!~"),
Self::AllLike => f.write_str("*~"),
Self::AnyLike => f.write_str("?~"),
Self::LessThan => f.write_char('<'),
Self::LessThanOrEqual => f.write_str("<="),
Self::MoreThan => f.write_char('>'),
Self::MoreThanOrEqual => f.write_str(">="),
Self::Contain => f.write_str("CONTAINS"),
Self::NotContain => f.write_str("CONTAINSNOT"),
Self::ContainAll => f.write_str("CONTAINSALL"),
Self::ContainAny => f.write_str("CONTAINSANY"),
Self::ContainNone => f.write_str("CONTAINSNONE"),
Self::Inside => f.write_str("INSIDE"),
Self::NotInside => f.write_str("NOTINSIDE"),
Self::AllInside => f.write_str("ALLINSIDE"),
Self::AnyInside => f.write_str("ANYINSIDE"),
Self::NoneInside => f.write_str("NONEINSIDE"),
Self::Outside => f.write_str("OUTSIDE"),
Self::Intersects => f.write_str("INTERSECTS"),
Self::Matches(reference) => {
if let Some(r) = reference {
write!(f, "@{}@", r)
} else {
f.write_str("@@")
}
}
}
2020-06-29 15:36:01 +00:00
}
}
pub fn assigner(i: &str) -> IResult<&str, Operator> {
alt((
value(Operator::Equal, char('=')),
value(Operator::Inc, tag("+=")),
value(Operator::Dec, tag("-=")),
value(Operator::Ext, tag("+?=")),
2020-06-29 15:36:01 +00:00
))(i)
}
2023-06-20 23:31:23 +00:00
pub fn unary(i: &str) -> IResult<&str, Operator> {
unary_symbols(i)
}
pub fn unary_symbols(i: &str) -> IResult<&str, Operator> {
let (i, _) = mightbespace(i)?;
let (i, v) = alt((value(Operator::Neg, tag("-")), value(Operator::Not, tag("!"))))(i)?;
2023-06-20 23:31:23 +00:00
let (i, _) = mightbespace(i)?;
Ok((i, v))
}
pub fn binary(i: &str) -> IResult<&str, Operator> {
alt((binary_symbols, binary_phrases))(i)
}
2023-06-20 23:31:23 +00:00
pub fn binary_symbols(i: &str) -> IResult<&str, Operator> {
let (i, _) = mightbespace(i)?;
let (i, v) = alt((
alt((
value(Operator::Or, tag("||")),
value(Operator::And, tag("&&")),
value(Operator::Tco, tag("?:")),
value(Operator::Nco, tag("??")),
)),
2020-06-29 15:36:01 +00:00
alt((
value(Operator::Exact, tag("==")),
value(Operator::NotEqual, tag("!=")),
value(Operator::AllEqual, tag("*=")),
value(Operator::AnyEqual, tag("?=")),
value(Operator::Equal, char('=')),
2020-06-29 15:36:01 +00:00
)),
alt((
value(Operator::NotLike, tag("!~")),
value(Operator::AllLike, tag("*~")),
value(Operator::AnyLike, tag("?~")),
value(Operator::Like, char('~')),
matches,
2020-06-29 15:36:01 +00:00
)),
alt((
value(Operator::LessThanOrEqual, tag("<=")),
value(Operator::LessThan, char('<')),
value(Operator::MoreThanOrEqual, tag(">=")),
value(Operator::MoreThan, char('>')),
2020-06-29 15:36:01 +00:00
)),
alt((
value(Operator::Pow, tag("**")),
value(Operator::Add, char('+')),
value(Operator::Sub, char('-')),
value(Operator::Mul, char('*')),
value(Operator::Mul, char('×')),
value(Operator::Mul, char('∙')),
value(Operator::Div, char('/')),
value(Operator::Div, char('÷')),
)),
2020-06-29 15:36:01 +00:00
alt((
value(Operator::Contain, char('∋')),
value(Operator::NotContain, char('∌')),
value(Operator::Inside, char('∈')),
value(Operator::NotInside, char('∉')),
value(Operator::ContainAll, char('⊇')),
value(Operator::ContainAny, char('⊃')),
value(Operator::ContainNone, char('⊅')),
value(Operator::AllInside, char('⊆')),
value(Operator::AnyInside, char('⊂')),
value(Operator::NoneInside, char('⊄')),
2020-06-29 15:36:01 +00:00
)),
))(i)?;
let (i, _) = mightbespace(i)?;
Ok((i, v))
}
2023-06-20 23:31:23 +00:00
pub fn binary_phrases(i: &str) -> IResult<&str, Operator> {
let (i, _) = shouldbespace(i)?;
let (i, v) = alt((
2020-06-29 15:36:01 +00:00
alt((
value(Operator::Or, tag_no_case("OR")),
value(Operator::And, tag_no_case("AND")),
value(Operator::NotEqual, tag_no_case("IS NOT")),
value(Operator::Equal, tag_no_case("IS")),
2020-06-29 15:36:01 +00:00
)),
alt((
value(Operator::ContainAll, tag_no_case("CONTAINSALL")),
value(Operator::ContainAny, tag_no_case("CONTAINSANY")),
value(Operator::ContainNone, tag_no_case("CONTAINSNONE")),
value(Operator::NotContain, tag_no_case("CONTAINSNOT")),
value(Operator::Contain, tag_no_case("CONTAINS")),
value(Operator::AllInside, tag_no_case("ALLINSIDE")),
value(Operator::AnyInside, tag_no_case("ANYINSIDE")),
value(Operator::NoneInside, tag_no_case("NONEINSIDE")),
value(Operator::NotInside, tag_no_case("NOTINSIDE")),
value(Operator::Inside, tag_no_case("INSIDE")),
value(Operator::Outside, tag_no_case("OUTSIDE")),
value(Operator::Intersects, tag_no_case("INTERSECTS")),
value(Operator::NotInside, tag_no_case("NOT IN")),
value(Operator::Inside, tag_no_case("IN")),
2020-06-29 15:36:01 +00:00
)),
))(i)?;
let (i, _) = shouldbespace(i)?;
Ok((i, v))
2020-06-29 15:36:01 +00:00
}
pub fn matches(i: &str) -> IResult<&str, Operator> {
let (i, _) = char('@')(i)?;
2023-06-20 11:48:20 +00:00
// let (i, reference) = opt(|i| uint8(i))(i)?;
cut(|i| {
let (i, reference) = opt(uint8)(i)?;
let (i, _) = char('@')(i)?;
Ok((i, Operator::Matches(reference)))
})(i)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn matches_without_reference() {
let res = matches("@@");
let out = res.unwrap().1;
assert_eq!("@@", format!("{}", out));
assert_eq!(out, Operator::Matches(None));
}
#[test]
fn matches_with_reference() {
let res = matches("@12@");
let out = res.unwrap().1;
assert_eq!("@12@", format!("{}", out));
assert_eq!(out, Operator::Matches(Some(12u8)));
}
#[test]
fn matches_with_invalid_reference() {
let res = matches("@256@");
res.unwrap_err();
}
}