reddb_types/operator.rs
1//! Binary-operator vocabulary.
2//!
3//! `BinOp` is the syntactic binary-operator enum the query AST emits and
4//! the coercion spine keys overload resolution on. It is **coercion
5//! vocabulary** as much as it is parser vocabulary: the spine
6//! (`coercion_spine::resolve_binop`) cannot resolve an operator overload
7//! without it, and that spine lives in this keystone crate (ADR 0052).
8//!
9//! Re-homing only the spine while leaving `BinOp` in the server would force
10//! this crate to depend back on `reddb-server` — the exact cycle ADR 0052
11//! exists to prevent. So the operator vocabulary moves here and the query
12//! AST (`reddb-server`'s `storage::query::ast`) re-exports it, keeping every
13//! existing `ast::BinOp` call-site untouched.
14//!
15//! The move is byte-faithful: the enum, its variant set, and the
16//! `precedence()` table are relocated verbatim from `storage::query::ast`.
17
18/// Syntactic binary operators. Parsed precedence determines grouping;
19/// this enum only identifies the operator itself. Comparison and logical
20/// operators live alongside arithmetic so a single `Expr::BinaryOp`
21/// walker can cover every infix form the parser emits.
22#[derive(Debug, Clone, Copy, PartialEq, Eq)]
23pub enum BinOp {
24 // Arithmetic
25 Add,
26 Sub,
27 Mul,
28 Div,
29 Mod,
30 // String
31 Concat,
32 // Comparison
33 Eq,
34 Ne,
35 Lt,
36 Le,
37 Gt,
38 Ge,
39 // Logical
40 And,
41 Or,
42}
43
44impl BinOp {
45 /// Left-binding precedence for Pratt parsing. Higher = binds tighter.
46 /// Mirrors PG gram.y's precedence table for the operators we have.
47 pub fn precedence(self) -> u8 {
48 match self {
49 BinOp::Or => 10,
50 BinOp::And => 20,
51 BinOp::Eq | BinOp::Ne | BinOp::Lt | BinOp::Le | BinOp::Gt | BinOp::Ge => 30,
52 BinOp::Concat => 40,
53 BinOp::Add | BinOp::Sub => 50,
54 BinOp::Mul | BinOp::Div | BinOp::Mod => 60,
55 }
56 }
57}
58
59#[cfg(test)]
60mod tests {
61 use super::*;
62
63 #[test]
64 fn precedence_groups_match_parser_contract() {
65 assert_eq!(BinOp::Or.precedence(), 10);
66 assert_eq!(BinOp::And.precedence(), 20);
67 for op in [
68 BinOp::Eq,
69 BinOp::Ne,
70 BinOp::Lt,
71 BinOp::Le,
72 BinOp::Gt,
73 BinOp::Ge,
74 ] {
75 assert_eq!(op.precedence(), 30, "{op:?}");
76 }
77 assert_eq!(BinOp::Concat.precedence(), 40);
78 for op in [BinOp::Add, BinOp::Sub] {
79 assert_eq!(op.precedence(), 50, "{op:?}");
80 }
81 for op in [BinOp::Mul, BinOp::Div, BinOp::Mod] {
82 assert_eq!(op.precedence(), 60, "{op:?}");
83 }
84 }
85}