use crate::ast::{
BinaryOperator, EqualsSpelling, IsDistinctFromSpelling, IsNotDistinctFromSpelling, SetOperator,
UnaryOperator,
};
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum Assoc {
Left,
Right,
NonAssoc,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct BindingPower {
pub left: u8,
pub right: u8,
pub assoc: Assoc,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct BindingPowerTable {
pub or: BindingPower,
pub xor: BindingPower,
pub and: BindingPower,
pub comparison: BindingPower,
pub range_predicate_override: Option<BindingPower>,
pub is_predicate_override: Option<BindingPower>,
pub double_equals: BindingPower,
pub additive: BindingPower,
pub multiplicative: BindingPower,
pub exponent: BindingPower,
pub string_concat: BindingPower,
pub any_operator: BindingPower,
pub json_get: BindingPower,
pub bitwise_or: BindingPower,
pub bitwise_and: BindingPower,
pub bitwise_shift: BindingPower,
pub bitwise_xor: BindingPower,
pub prefix_not: u8,
pub prefix_sign: u8,
pub prefix_bitwise_not: u8,
pub at_time_zone: BindingPower,
pub collate: BindingPower,
pub subscript: BindingPower,
pub typecast: BindingPower,
pub field_selection: BindingPower,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct SetOperationBindingPowerTable {
pub union_except: BindingPower,
pub intersect: BindingPower,
}
impl SetOperationBindingPowerTable {
pub const STANDARD: Self = Self {
union_except: BindingPower {
left: 10,
right: 11,
assoc: Assoc::Left,
},
intersect: BindingPower {
left: 20,
right: 21,
assoc: Assoc::Left,
},
};
pub const fn set_operation(&self, op: &SetOperator) -> BindingPower {
match op {
SetOperator::Union | SetOperator::Except => self.union_except,
SetOperator::Intersect => self.intersect,
}
}
pub const fn with_set_operator(mut self, op: &SetOperator, bp: BindingPower) -> Self {
match op {
SetOperator::Union | SetOperator::Except => self.union_except = bp,
SetOperator::Intersect => self.intersect = bp,
}
self
}
pub const fn needs_parens(
&self,
parent: &SetOperator,
child: &SetOperator,
side: Side,
) -> bool {
needs_parens_between(self.set_operation(parent), self.set_operation(child), side)
}
}
impl BindingPowerTable {
pub const STANDARD: Self = Self {
or: BindingPower {
left: 10,
right: 11,
assoc: Assoc::Left,
},
xor: BindingPower {
left: 15,
right: 16,
assoc: Assoc::Left,
},
and: BindingPower {
left: 20,
right: 21,
assoc: Assoc::Left,
},
comparison: BindingPower {
left: 40,
right: 41,
assoc: Assoc::NonAssoc,
},
range_predicate_override: None,
is_predicate_override: None,
double_equals: BindingPower {
left: 40,
right: 41,
assoc: Assoc::NonAssoc,
},
additive: BindingPower {
left: 50,
right: 51,
assoc: Assoc::Left,
},
multiplicative: BindingPower {
left: 60,
right: 61,
assoc: Assoc::Left,
},
exponent: BindingPower {
left: 65,
right: 66,
assoc: Assoc::Left,
},
string_concat: BindingPower {
left: 45,
right: 46,
assoc: Assoc::Left,
},
any_operator: BindingPower {
left: 45,
right: 46,
assoc: Assoc::Left,
},
json_get: BindingPower {
left: 45,
right: 46,
assoc: Assoc::Left,
},
bitwise_or: BindingPower {
left: 45,
right: 46,
assoc: Assoc::Left,
},
bitwise_and: BindingPower {
left: 45,
right: 46,
assoc: Assoc::Left,
},
bitwise_shift: BindingPower {
left: 45,
right: 46,
assoc: Assoc::Left,
},
bitwise_xor: BindingPower {
left: 45,
right: 46,
assoc: Assoc::Left,
},
prefix_not: 30,
prefix_sign: 80,
prefix_bitwise_not: 46,
at_time_zone: BindingPower {
left: 70,
right: 71,
assoc: Assoc::Left,
},
collate: BindingPower {
left: 74,
right: 75,
assoc: Assoc::Left,
},
subscript: BindingPower {
left: 84,
right: 85,
assoc: Assoc::Left,
},
typecast: BindingPower {
left: 88,
right: 89,
assoc: Assoc::Left,
},
field_selection: BindingPower {
left: 92,
right: 93,
assoc: Assoc::Left,
},
};
pub const fn binary(&self, op: &BinaryOperator) -> BindingPower {
match op {
BinaryOperator::Or => self.or,
BinaryOperator::Xor => self.xor,
BinaryOperator::And => self.and,
BinaryOperator::IsDistinctFrom(IsDistinctFromSpelling::Keyword)
| BinaryOperator::IsNotDistinctFrom(IsNotDistinctFromSpelling::Keyword) => {
self.predicate()
}
BinaryOperator::IsDistinctFrom(IsDistinctFromSpelling::Is)
| BinaryOperator::IsNotDistinctFrom(
IsNotDistinctFromSpelling::Is | IsNotDistinctFromSpelling::NullSafeEq,
) => self.comparison,
BinaryOperator::Eq(EqualsSpelling::Double) => self.double_equals,
BinaryOperator::Eq(_)
| BinaryOperator::NotEq(_)
| BinaryOperator::Lt
| BinaryOperator::LtEq
| BinaryOperator::Gt
| BinaryOperator::GtEq
| BinaryOperator::Regexp(_)
| BinaryOperator::Glob
| BinaryOperator::Match => self.comparison,
BinaryOperator::Plus | BinaryOperator::Minus => self.additive,
BinaryOperator::Multiply
| BinaryOperator::Divide
| BinaryOperator::Modulo(_)
| BinaryOperator::IntegerDivide(_) => self.multiplicative,
BinaryOperator::Exponent => self.exponent,
BinaryOperator::StringConcat => self.string_concat,
BinaryOperator::Contains
| BinaryOperator::ContainedBy
| BinaryOperator::StartsWith
| BinaryOperator::JsonGetText
| BinaryOperator::JsonExists
| BinaryOperator::JsonExistsAny
| BinaryOperator::JsonExistsAll
| BinaryOperator::JsonPathExists
| BinaryOperator::JsonPathMatch
| BinaryOperator::JsonExtractPath
| BinaryOperator::JsonExtractPathText
| BinaryOperator::JsonDeletePath
| BinaryOperator::Overlap => self.any_operator,
BinaryOperator::JsonGet => self.json_get,
BinaryOperator::BitwiseOr => self.bitwise_or,
BinaryOperator::BitwiseAnd => self.bitwise_and,
BinaryOperator::BitwiseShiftLeft | BinaryOperator::BitwiseShiftRight => {
self.bitwise_shift
}
BinaryOperator::BitwiseXor(_) => self.bitwise_xor,
BinaryOperator::Overlaps => OVERLAPS_PREDICATE,
}
}
pub const fn prefix(&self, op: &UnaryOperator) -> u8 {
match op {
UnaryOperator::Not => self.prefix_not,
UnaryOperator::Minus | UnaryOperator::Plus | UnaryOperator::Prior => self.prefix_sign,
UnaryOperator::BitwiseNot => self.prefix_bitwise_not,
}
}
pub const fn predicate(&self) -> BindingPower {
match self.is_predicate_override {
Some(bp) => bp,
None => self.comparison,
}
}
pub const fn range_predicate(&self) -> BindingPower {
match self.range_predicate_override {
Some(bp) => bp,
None => self.comparison,
}
}
pub const fn with_binary(mut self, op: &BinaryOperator, bp: BindingPower) -> Self {
match op {
BinaryOperator::Or => self.or = bp,
BinaryOperator::Xor => self.xor = bp,
BinaryOperator::And => self.and = bp,
BinaryOperator::Eq(EqualsSpelling::Double) => self.double_equals = bp,
BinaryOperator::Eq(_)
| BinaryOperator::NotEq(_)
| BinaryOperator::Lt
| BinaryOperator::LtEq
| BinaryOperator::Gt
| BinaryOperator::GtEq
| BinaryOperator::IsDistinctFrom(_)
| BinaryOperator::IsNotDistinctFrom(_)
| BinaryOperator::Regexp(_)
| BinaryOperator::Glob
| BinaryOperator::Match => {
self.comparison = bp;
self.double_equals = bp;
}
BinaryOperator::Plus | BinaryOperator::Minus => self.additive = bp,
BinaryOperator::Multiply
| BinaryOperator::Divide
| BinaryOperator::Modulo(_)
| BinaryOperator::IntegerDivide(_) => {
self.multiplicative = bp;
}
BinaryOperator::Exponent => self.exponent = bp,
BinaryOperator::StringConcat => self.string_concat = bp,
BinaryOperator::Contains
| BinaryOperator::ContainedBy
| BinaryOperator::StartsWith
| BinaryOperator::JsonGetText
| BinaryOperator::JsonExists
| BinaryOperator::JsonExistsAny
| BinaryOperator::JsonExistsAll
| BinaryOperator::JsonPathExists
| BinaryOperator::JsonPathMatch
| BinaryOperator::JsonExtractPath
| BinaryOperator::JsonExtractPathText
| BinaryOperator::JsonDeletePath
| BinaryOperator::Overlap => self.any_operator = bp,
BinaryOperator::JsonGet => self.json_get = bp,
BinaryOperator::BitwiseOr => self.bitwise_or = bp,
BinaryOperator::BitwiseAnd => self.bitwise_and = bp,
BinaryOperator::BitwiseShiftLeft | BinaryOperator::BitwiseShiftRight => {
self.bitwise_shift = bp;
}
BinaryOperator::BitwiseXor(_) => self.bitwise_xor = bp,
BinaryOperator::Overlaps => {}
}
self
}
pub const fn needs_parens(
&self,
parent: &BinaryOperator,
child: &BinaryOperator,
side: Side,
) -> bool {
needs_parens_between(self.binary(parent), self.binary(child), side)
}
}
pub const STANDARD_BINDING_POWERS: BindingPowerTable = BindingPowerTable::STANDARD;
pub const UNPARENTHESIZED_IN_LIST: BindingPower = BindingPower {
left: 42,
right: 43,
assoc: Assoc::Left,
};
pub const OVERLAPS_PREDICATE: BindingPower = BindingPower {
left: 42,
right: 43,
assoc: Assoc::NonAssoc,
};
pub const RANGE_PREDICATE_ABOVE_COMPARISON: BindingPower = BindingPower {
left: 42,
right: 43,
assoc: Assoc::NonAssoc,
};
pub const IS_PREDICATE_BELOW_COMPARISON: BindingPower = BindingPower {
left: 35,
right: 36,
assoc: Assoc::NonAssoc,
};
pub const STANDARD_SET_OPERATION_BINDING_POWERS: SetOperationBindingPowerTable =
SetOperationBindingPowerTable::STANDARD;
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum Side {
Left,
Right,
}
pub const fn needs_parens_between(parent: BindingPower, child: BindingPower, side: Side) -> bool {
let same_precedence = parent.left == child.left && parent.right == child.right;
match side {
Side::Left => {
child.right < parent.left
|| (same_precedence
&& match parent.assoc {
Assoc::Left => false,
Assoc::Right | Assoc::NonAssoc => true,
})
}
Side::Right => {
child.left < parent.right
|| (same_precedence
&& match parent.assoc {
Assoc::Right => false,
Assoc::Left | Assoc::NonAssoc => true,
})
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::ast::{EqualsSpelling, NotEqSpelling};
#[test]
fn pratt_binding_powers_group_standard_sql_operators() {
let plus = STANDARD_BINDING_POWERS.binary(&BinaryOperator::Plus);
let multiply = STANDARD_BINDING_POWERS.binary(&BinaryOperator::Multiply);
let equals = STANDARD_BINDING_POWERS.binary(&BinaryOperator::Eq(EqualsSpelling::Single));
let and = STANDARD_BINDING_POWERS.binary(&BinaryOperator::And);
let concat = STANDARD_BINDING_POWERS.binary(&BinaryOperator::StringConcat);
assert!(plus.right < multiply.left);
assert!(plus.left < multiply.right);
assert!(and.right < equals.left);
assert!(concat.right < plus.left);
assert!(equals.right < concat.left);
}
#[test]
fn render_parentheses_are_derived_from_binding_powers() {
assert!(STANDARD_BINDING_POWERS.needs_parens(
&BinaryOperator::Multiply,
&BinaryOperator::Plus,
Side::Left
));
assert!(!STANDARD_BINDING_POWERS.needs_parens(
&BinaryOperator::Plus,
&BinaryOperator::Multiply,
Side::Right
));
}
#[test]
fn predicate_level_tracks_comparison_precedence() {
assert_eq!(
STANDARD_BINDING_POWERS.predicate(),
STANDARD_BINDING_POWERS.binary(&BinaryOperator::Eq(EqualsSpelling::Single)),
);
const TIGHT_COMPARISON: BindingPowerTable = STANDARD_BINDING_POWERS.with_binary(
&BinaryOperator::Eq(EqualsSpelling::Single),
BindingPower {
left: 70,
right: 71,
assoc: Assoc::NonAssoc,
},
);
assert_eq!(
TIGHT_COMPARISON.predicate(),
TIGHT_COMPARISON.binary(&BinaryOperator::Eq(EqualsSpelling::Single)),
);
assert_eq!(TIGHT_COMPARISON.predicate().left, 70);
}
#[test]
fn range_predicate_defaults_to_comparison_and_tracks_its_associativity() {
assert_eq!(STANDARD_BINDING_POWERS.range_predicate_override, None);
assert_eq!(
STANDARD_BINDING_POWERS.range_predicate(),
STANDARD_BINDING_POWERS.comparison,
);
const LEFT_COMPARISON: BindingPowerTable = STANDARD_BINDING_POWERS.with_binary(
&BinaryOperator::Eq(EqualsSpelling::Single),
BindingPower {
left: 40,
right: 41,
assoc: Assoc::Left,
},
);
assert_eq!(LEFT_COMPARISON.range_predicate().assoc, Assoc::Left);
}
#[test]
fn range_predicate_override_ranks_above_comparison_below_any_operator() {
const PG_RANGE: BindingPowerTable = BindingPowerTable {
range_predicate_override: Some(RANGE_PREDICATE_ABOVE_COMPARISON),
..STANDARD_BINDING_POWERS
};
let range = PG_RANGE.range_predicate();
let comparison = PG_RANGE.binary(&BinaryOperator::Eq(EqualsSpelling::Single));
assert!(comparison.left < range.left, "tighter than comparison");
assert!(
range.left < PG_RANGE.any_operator.left,
"looser than any-operator"
);
assert_eq!(range.assoc, Assoc::NonAssoc);
assert_eq!(PG_RANGE.predicate(), comparison);
}
#[test]
fn predicate_operands_parenthesize_like_comparisons() {
let predicate = STANDARD_BINDING_POWERS.predicate();
let additive = STANDARD_BINDING_POWERS.binary(&BinaryOperator::Plus);
let or = STANDARD_BINDING_POWERS.binary(&BinaryOperator::Or);
let comparison =
STANDARD_BINDING_POWERS.binary(&BinaryOperator::Eq(EqualsSpelling::Single));
assert!(!needs_parens_between(predicate, additive, Side::Left));
assert!(needs_parens_between(predicate, or, Side::Left));
assert!(needs_parens_between(predicate, comparison, Side::Left));
assert!(!needs_parens_between(predicate, additive, Side::Right));
assert!(needs_parens_between(predicate, comparison, Side::Right));
}
#[test]
fn associativity_controls_equal_precedence_parentheses() {
assert!(!STANDARD_BINDING_POWERS.needs_parens(
&BinaryOperator::Minus,
&BinaryOperator::Plus,
Side::Left
));
assert!(STANDARD_BINDING_POWERS.needs_parens(
&BinaryOperator::Minus,
&BinaryOperator::Plus,
Side::Right
));
assert!(STANDARD_BINDING_POWERS.needs_parens(
&BinaryOperator::Eq(EqualsSpelling::Single),
&BinaryOperator::Lt,
Side::Left
));
assert!(STANDARD_BINDING_POWERS.needs_parens(
&BinaryOperator::Eq(EqualsSpelling::Single),
&BinaryOperator::Lt,
Side::Right
));
}
#[test]
fn postgres_postfix_operators_rank_above_arithmetic_in_gram_y_order() {
let table = STANDARD_BINDING_POWERS;
let multiplicative = table.multiplicative.left;
assert!(multiplicative < table.at_time_zone.left);
assert!(table.at_time_zone.left < table.collate.left);
assert!(table.collate.left < table.prefix_sign);
assert!(table.prefix_sign < table.subscript.left);
assert!(table.subscript.left < table.typecast.left);
assert!(table.typecast.left < table.field_selection.left);
for bp in [
table.at_time_zone,
table.collate,
table.subscript,
table.typecast,
table.field_selection,
] {
assert_eq!(bp.assoc, Assoc::Left);
}
}
#[test]
fn precedence_values_match_m1_ordering() {
assert_eq!(STANDARD_BINDING_POWERS.binary(&BinaryOperator::Or).left, 10);
assert_eq!(
STANDARD_BINDING_POWERS.binary(&BinaryOperator::And).left,
20
);
assert_eq!(STANDARD_BINDING_POWERS.prefix(&UnaryOperator::Not), 30);
assert_eq!(
STANDARD_BINDING_POWERS
.binary(&BinaryOperator::Eq(EqualsSpelling::Single))
.left,
40
);
assert_eq!(
STANDARD_BINDING_POWERS.binary(&BinaryOperator::Plus).left,
50
);
assert_eq!(
STANDARD_BINDING_POWERS
.binary(&BinaryOperator::Multiply)
.left,
60
);
assert_eq!(
STANDARD_BINDING_POWERS
.binary(&BinaryOperator::StringConcat)
.left,
45
);
assert_eq!(STANDARD_BINDING_POWERS.prefix(&UnaryOperator::Plus), 80);
assert_eq!(STANDARD_BINDING_POWERS.prefix(&UnaryOperator::Minus), 80);
}
#[test]
fn mysql_keyword_operators_rank_at_their_documented_levels() {
use crate::ast::{IntegerDivideSpelling, ModuloSpelling, RegexpSpelling};
let or = STANDARD_BINDING_POWERS.binary(&BinaryOperator::Or);
let xor = STANDARD_BINDING_POWERS.binary(&BinaryOperator::Xor);
let and = STANDARD_BINDING_POWERS.binary(&BinaryOperator::And);
assert!(or.left < xor.left);
assert!(xor.left < and.left);
assert_eq!(xor.assoc, Assoc::Left);
let multiplicative = STANDARD_BINDING_POWERS.binary(&BinaryOperator::Multiply);
assert_eq!(
STANDARD_BINDING_POWERS
.binary(&BinaryOperator::IntegerDivide(IntegerDivideSpelling::Div)),
multiplicative
);
assert_eq!(
STANDARD_BINDING_POWERS.binary(&BinaryOperator::IntegerDivide(
IntegerDivideSpelling::SlashSlash
)),
multiplicative
);
assert_eq!(
STANDARD_BINDING_POWERS.binary(&BinaryOperator::Modulo(ModuloSpelling::Mod)),
multiplicative,
);
assert_eq!(
STANDARD_BINDING_POWERS.binary(&BinaryOperator::Modulo(ModuloSpelling::Percent)),
multiplicative,
);
let comparison =
STANDARD_BINDING_POWERS.binary(&BinaryOperator::Eq(EqualsSpelling::Single));
assert_eq!(
STANDARD_BINDING_POWERS.binary(&BinaryOperator::Regexp(RegexpSpelling::Rlike)),
comparison,
);
assert_eq!(
STANDARD_BINDING_POWERS.binary(&BinaryOperator::Regexp(RegexpSpelling::Regexp)),
comparison,
);
}
#[test]
fn postgres_at_family_operators_bind_at_the_any_operator_rank() {
let table = STANDARD_BINDING_POWERS;
let additive = STANDARD_BINDING_POWERS.binary(&BinaryOperator::Plus);
let comparison =
STANDARD_BINDING_POWERS.binary(&BinaryOperator::Eq(EqualsSpelling::Single));
for op in [
BinaryOperator::Contains,
BinaryOperator::ContainedBy,
BinaryOperator::JsonGet,
BinaryOperator::JsonGetText,
] {
let bp = STANDARD_BINDING_POWERS.binary(&op);
assert_eq!(bp, table.any_operator);
assert_eq!(
bp,
STANDARD_BINDING_POWERS.binary(&BinaryOperator::StringConcat)
);
assert_eq!(bp.assoc, Assoc::Left);
assert!(comparison.left < bp.left, "tighter than comparison");
assert!(bp.left < additive.left, "looser than additive");
}
}
#[test]
fn any_operator_field_is_independent_of_string_concat() {
const HIGH_CONCAT: BindingPowerTable = STANDARD_BINDING_POWERS.with_binary(
&BinaryOperator::StringConcat,
BindingPower {
left: 70,
right: 71,
assoc: Assoc::Left,
},
);
assert_eq!(HIGH_CONCAT.binary(&BinaryOperator::StringConcat).left, 70);
assert_eq!(
HIGH_CONCAT.binary(&BinaryOperator::Contains),
STANDARD_BINDING_POWERS.any_operator,
);
}
#[test]
fn json_get_field_is_independent_of_the_any_operator_rank() {
const LOOSE_ARROW: BindingPowerTable = STANDARD_BINDING_POWERS.with_binary(
&BinaryOperator::JsonGet,
BindingPower {
left: 4,
right: 5,
assoc: Assoc::Left,
},
);
assert_eq!(
STANDARD_BINDING_POWERS.binary(&BinaryOperator::JsonGet),
STANDARD_BINDING_POWERS.any_operator,
);
assert_eq!(LOOSE_ARROW.binary(&BinaryOperator::JsonGet).left, 4);
for untouched in [
BinaryOperator::JsonGetText,
BinaryOperator::Contains,
BinaryOperator::ContainedBy,
] {
assert_eq!(
LOOSE_ARROW.binary(&untouched),
STANDARD_BINDING_POWERS.any_operator,
);
}
}
#[test]
fn standard_bitwise_operators_rank_between_additive_and_comparison() {
use crate::ast::BitwiseXorSpelling;
let table = STANDARD_BINDING_POWERS;
let additive = table.binary(&BinaryOperator::Plus);
let comparison = table.binary(&BinaryOperator::Eq(EqualsSpelling::Single));
for op in [
BinaryOperator::BitwiseOr,
BinaryOperator::BitwiseAnd,
BinaryOperator::BitwiseShiftLeft,
BinaryOperator::BitwiseShiftRight,
BinaryOperator::BitwiseXor(BitwiseXorSpelling::Hash),
] {
let bp = table.binary(&op);
assert_eq!(bp.assoc, Assoc::Left, "{op:?} is left-associative");
assert!(comparison.left < bp.left, "{op:?} tighter than comparison");
assert!(bp.left < additive.left, "{op:?} looser than additive");
}
assert_eq!(
table.binary(&BinaryOperator::BitwiseShiftLeft),
table.binary(&BinaryOperator::BitwiseShiftRight),
);
assert_eq!(
table.binary(&BinaryOperator::BitwiseXor(BitwiseXorSpelling::Hash)),
table.binary(&BinaryOperator::BitwiseXor(BitwiseXorSpelling::Caret)),
);
}
#[test]
fn standard_prefix_bitwise_not_sits_between_arithmetic_and_the_bitwise_binaries() {
let table = STANDARD_BINDING_POWERS;
let rbp = table.prefix(&UnaryOperator::BitwiseNot);
assert!(
table.binary(&BinaryOperator::BitwiseOr).left <= rbp,
"`&`/`|` do not fold into `~`'s operand"
);
assert!(
rbp < table.binary(&BinaryOperator::Plus).left,
"additive folds into `~`'s operand"
);
assert!(rbp > table.binary(&BinaryOperator::BitwiseAnd).left);
}
#[test]
fn bitwise_fields_move_independently() {
use crate::ast::BitwiseXorSpelling;
const TIGHT_AND: BindingPowerTable = STANDARD_BINDING_POWERS.with_binary(
&BinaryOperator::BitwiseAnd,
BindingPower {
left: 48,
right: 49,
assoc: Assoc::Left,
},
);
assert_eq!(TIGHT_AND.binary(&BinaryOperator::BitwiseAnd).left, 48);
assert_eq!(
TIGHT_AND.binary(&BinaryOperator::BitwiseOr),
STANDARD_BINDING_POWERS.binary(&BinaryOperator::BitwiseOr),
);
assert_eq!(
TIGHT_AND.binary(&BinaryOperator::BitwiseShiftLeft),
STANDARD_BINDING_POWERS.binary(&BinaryOperator::BitwiseShiftLeft),
);
assert_eq!(
TIGHT_AND.binary(&BinaryOperator::BitwiseXor(BitwiseXorSpelling::Caret)),
STANDARD_BINDING_POWERS.binary(&BinaryOperator::BitwiseXor(BitwiseXorSpelling::Caret)),
);
}
#[test]
fn comparisons_are_non_associative_for_m1() {
assert_eq!(
STANDARD_BINDING_POWERS
.binary(&BinaryOperator::Eq(EqualsSpelling::Single))
.assoc,
Assoc::NonAssoc
);
assert_eq!(
STANDARD_BINDING_POWERS
.binary(&BinaryOperator::NotEq(NotEqSpelling::AngleBracket))
.assoc,
Assoc::NonAssoc
);
assert_eq!(
STANDARD_BINDING_POWERS.binary(&BinaryOperator::Lt).assoc,
Assoc::NonAssoc
);
assert_eq!(
STANDARD_BINDING_POWERS.binary(&BinaryOperator::LtEq).assoc,
Assoc::NonAssoc
);
assert_eq!(
STANDARD_BINDING_POWERS.binary(&BinaryOperator::Gt).assoc,
Assoc::NonAssoc
);
assert_eq!(
STANDARD_BINDING_POWERS.binary(&BinaryOperator::GtEq).assoc,
Assoc::NonAssoc
);
}
#[test]
fn binding_power_table_supports_const_deltas() {
const SQLITE_LIKE: BindingPowerTable = STANDARD_BINDING_POWERS.with_binary(
&BinaryOperator::StringConcat,
BindingPower {
left: 70,
right: 71,
assoc: Assoc::Left,
},
);
assert_eq!(SQLITE_LIKE.binary(&BinaryOperator::StringConcat).left, 70);
assert_eq!(
SQLITE_LIKE.binary(&BinaryOperator::Plus),
STANDARD_BINDING_POWERS.binary(&BinaryOperator::Plus),
);
const LEFT_ASSOC_COMPARISON: BindingPowerTable = STANDARD_BINDING_POWERS.with_binary(
&BinaryOperator::Lt,
BindingPower {
left: 40,
right: 41,
assoc: Assoc::Left,
},
);
assert_eq!(
LEFT_ASSOC_COMPARISON
.binary(&BinaryOperator::Eq(EqualsSpelling::Single))
.assoc,
Assoc::Left,
);
assert!(!LEFT_ASSOC_COMPARISON.needs_parens(
&BinaryOperator::Lt,
&BinaryOperator::Eq(EqualsSpelling::Single),
Side::Left,
));
assert!(LEFT_ASSOC_COMPARISON.needs_parens(
&BinaryOperator::Lt,
&BinaryOperator::Eq(EqualsSpelling::Single),
Side::Right,
));
}
#[test]
fn set_operation_binding_powers_rank_intersect_above_union_except() {
let union = STANDARD_SET_OPERATION_BINDING_POWERS.set_operation(&SetOperator::Union);
let except = STANDARD_SET_OPERATION_BINDING_POWERS.set_operation(&SetOperator::Except);
let intersect =
STANDARD_SET_OPERATION_BINDING_POWERS.set_operation(&SetOperator::Intersect);
assert_eq!(union, except);
assert!(union.right < intersect.left);
assert!(intersect.left > except.right);
}
#[test]
fn set_operation_parentheses_are_derived_from_binding_powers() {
assert!(!STANDARD_SET_OPERATION_BINDING_POWERS.needs_parens(
&SetOperator::Union,
&SetOperator::Intersect,
Side::Right,
));
assert!(STANDARD_SET_OPERATION_BINDING_POWERS.needs_parens(
&SetOperator::Intersect,
&SetOperator::Union,
Side::Left,
));
assert!(STANDARD_SET_OPERATION_BINDING_POWERS.needs_parens(
&SetOperator::Union,
&SetOperator::Except,
Side::Right,
));
assert!(!STANDARD_SET_OPERATION_BINDING_POWERS.needs_parens(
&SetOperator::Except,
&SetOperator::Union,
Side::Left,
));
}
#[test]
fn set_operation_binding_power_table_supports_const_deltas() {
const UNION_IS_HIGHER: SetOperationBindingPowerTable =
STANDARD_SET_OPERATION_BINDING_POWERS.with_set_operator(
&SetOperator::Union,
BindingPower {
left: 30,
right: 31,
assoc: Assoc::Left,
},
);
assert_eq!(UNION_IS_HIGHER.set_operation(&SetOperator::Except).left, 30,);
assert_eq!(
UNION_IS_HIGHER.set_operation(&SetOperator::Intersect).left,
STANDARD_SET_OPERATION_BINDING_POWERS
.set_operation(&SetOperator::Intersect)
.left,
);
}
}