#[path = "builders.rs"]
mod builders;
#[path = "core.rs"]
mod core;
pub use builders::*;
pub use core::*;
#[cfg(test)]
#[path = "tests.rs"]
mod tests;
use crate::storage::query::lexer::Position;
use crate::storage::schema::{DataType, Value};
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
pub struct Span {
pub start: Position,
pub end: Position,
}
impl Span {
pub fn new(start: Position, end: Position) -> Self {
Self { start, end }
}
pub fn synthetic() -> Self {
Self::default()
}
pub fn is_synthetic(&self) -> bool {
self.start == Position::default() && self.end == Position::default()
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum BinOp {
Add,
Sub,
Mul,
Div,
Mod,
Concat,
Eq,
Ne,
Lt,
Le,
Gt,
Ge,
And,
Or,
}
impl BinOp {
pub fn precedence(self) -> u8 {
match self {
BinOp::Or => 10,
BinOp::And => 20,
BinOp::Eq | BinOp::Ne | BinOp::Lt | BinOp::Le | BinOp::Gt | BinOp::Ge => 30,
BinOp::Concat => 40,
BinOp::Add | BinOp::Sub => 50,
BinOp::Mul | BinOp::Div | BinOp::Mod => 60,
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum UnaryOp {
Neg,
Not,
}
#[derive(Debug, Clone, PartialEq)]
pub enum Expr {
Literal { value: Value, span: Span },
Column { field: FieldRef, span: Span },
Parameter { index: usize, span: Span },
BinaryOp {
op: BinOp,
lhs: Box<Expr>,
rhs: Box<Expr>,
span: Span,
},
UnaryOp {
op: UnaryOp,
operand: Box<Expr>,
span: Span,
},
Cast {
inner: Box<Expr>,
target: DataType,
span: Span,
},
FunctionCall {
name: String,
args: Vec<Expr>,
span: Span,
},
Case {
branches: Vec<(Expr, Expr)>,
else_: Option<Box<Expr>>,
span: Span,
},
IsNull {
operand: Box<Expr>,
negated: bool,
span: Span,
},
InList {
target: Box<Expr>,
values: Vec<Expr>,
negated: bool,
span: Span,
},
Between {
target: Box<Expr>,
low: Box<Expr>,
high: Box<Expr>,
negated: bool,
span: Span,
},
}
impl Expr {
pub fn span(&self) -> Span {
match self {
Expr::Literal { span, .. }
| Expr::Column { span, .. }
| Expr::Parameter { span, .. }
| Expr::BinaryOp { span, .. }
| Expr::UnaryOp { span, .. }
| Expr::Cast { span, .. }
| Expr::FunctionCall { span, .. }
| Expr::Case { span, .. }
| Expr::IsNull { span, .. }
| Expr::InList { span, .. }
| Expr::Between { span, .. } => *span,
}
}
pub fn lit(value: Value) -> Self {
Expr::Literal {
value,
span: Span::synthetic(),
}
}
pub fn col(field: FieldRef) -> Self {
Expr::Column {
field,
span: Span::synthetic(),
}
}
pub fn binop(op: BinOp, lhs: Expr, rhs: Expr) -> Self {
Expr::BinaryOp {
op,
lhs: Box::new(lhs),
rhs: Box::new(rhs),
span: Span::synthetic(),
}
}
}
#[cfg(test)]
mod expr_tests {
use super::*;
#[test]
fn precedence_orders_mul_over_add_and_and_over_or() {
assert!(BinOp::Mul.precedence() > BinOp::Add.precedence());
assert!(BinOp::Add.precedence() > BinOp::Eq.precedence());
assert!(BinOp::Eq.precedence() > BinOp::And.precedence());
assert!(BinOp::And.precedence() > BinOp::Or.precedence());
}
#[test]
fn span_synthetic_round_trip() {
let s = Span::synthetic();
assert!(s.is_synthetic());
let real = Span::new(Position::new(1, 1, 0), Position::new(1, 5, 4));
assert!(!real.is_synthetic());
}
#[test]
fn expr_constructors_carry_synthetic_span() {
let lit = Expr::lit(Value::Integer(42));
assert!(lit.span().is_synthetic());
assert_eq!(
lit,
Expr::Literal {
value: Value::Integer(42),
span: Span::synthetic(),
}
);
}
#[test]
fn binop_shortcut_nests() {
let expr = Expr::binop(
BinOp::Add,
Expr::col(FieldRef::column("", "a")),
Expr::binop(
BinOp::Mul,
Expr::col(FieldRef::column("", "b")),
Expr::col(FieldRef::column("", "c")),
),
);
match expr {
Expr::BinaryOp {
op: BinOp::Add,
rhs,
..
} => match *rhs {
Expr::BinaryOp { op: BinOp::Mul, .. } => {}
other => panic!("expected Mul on rhs, got {:?}", other),
},
other => panic!("expected Add at root, got {:?}", other),
}
}
}