use crate::compiler::syntax::SyntaxKind;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct BindingPower {
pub left: u8,
pub right: u8,
}
impl BindingPower {
#[inline]
pub const fn left(power: u8) -> Self {
Self {
left: power,
right: power + 1,
}
}
#[inline]
pub const fn right(power: u8) -> Self {
Self {
left: power + 1,
right: power,
}
}
#[inline]
pub const fn none(power: u8) -> Self {
Self {
left: power,
right: power,
}
}
}
pub mod prec {
use super::BindingPower;
pub const COMMA: BindingPower = BindingPower::left(2);
pub const ASSIGN: BindingPower = BindingPower::right(4);
pub const CONDITIONAL: BindingPower = BindingPower::right(6);
pub const NULLISH: BindingPower = BindingPower::left(8);
pub const LOGICAL_OR: BindingPower = BindingPower::left(10);
pub const LOGICAL_AND: BindingPower = BindingPower::left(12);
pub const BITWISE_OR: BindingPower = BindingPower::left(14);
pub const BITWISE_XOR: BindingPower = BindingPower::left(16);
pub const BITWISE_AND: BindingPower = BindingPower::left(18);
pub const EQUALITY: BindingPower = BindingPower::left(20);
pub const RELATIONAL: BindingPower = BindingPower::left(22);
pub const SHIFT: BindingPower = BindingPower::left(24);
pub const ADDITIVE: BindingPower = BindingPower::left(26);
pub const MULTIPLICATIVE: BindingPower = BindingPower::left(28);
pub const EXPONENT: BindingPower = BindingPower::right(30);
pub const PREFIX: u8 = 32;
pub const POSTFIX: u8 = 34;
pub const CALL: BindingPower = BindingPower::left(36);
pub const NEW_WITH_ARGS: u8 = 38;
pub const TS_AS: BindingPower = BindingPower::left(21);
pub const TS_NON_NULL: u8 = 35;
pub const TS_TYPE_INSTANTIATION: BindingPower = BindingPower::left(36);
}
pub fn infix_binding_power(kind: SyntaxKind, text: &str) -> Option<BindingPower> {
match kind {
SyntaxKind::Comma => return Some(prec::COMMA),
SyntaxKind::InKw => return Some(prec::RELATIONAL),
SyntaxKind::AsKw => return Some(prec::TS_AS),
SyntaxKind::SatisfiesKw => return Some(prec::TS_AS),
SyntaxKind::Lt => return Some(prec::RELATIONAL),
SyntaxKind::Gt => return Some(prec::RELATIONAL),
SyntaxKind::EqEq => return Some(prec::EQUALITY),
SyntaxKind::EqEqEq => return Some(prec::EQUALITY),
SyntaxKind::NotEq => return Some(prec::EQUALITY),
SyntaxKind::NotEqEq => return Some(prec::EQUALITY),
SyntaxKind::AmpersandAmpersand => return Some(prec::LOGICAL_AND),
SyntaxKind::Ampersand => return Some(prec::BITWISE_AND),
SyntaxKind::Star => {
if text == "**" {
return Some(prec::EXPONENT);
}
return Some(prec::MULTIPLICATIVE);
}
SyntaxKind::Question => return None,
SyntaxKind::Eq => {
return None;
}
_ => {}
}
match text {
"=" => None,
"+=" | "-=" | "*=" | "/=" | "%=" | "**=" | "<<=" | ">>=" | ">>>=" | "&=" | "|=" | "^="
| "&&=" | "||=" | "??=" => None,
"==" | "===" => Some(prec::EQUALITY),
"!=" | "!==" => Some(prec::EQUALITY),
"<=" | ">=" => Some(prec::RELATIONAL),
"&&" => Some(prec::LOGICAL_AND),
"||" => Some(prec::LOGICAL_OR),
"??" => Some(prec::NULLISH),
"|" => Some(prec::BITWISE_OR),
"^" => Some(prec::BITWISE_XOR),
"&" => Some(prec::BITWISE_AND),
"<<" => Some(prec::SHIFT),
">>" | ">>>" => Some(prec::SHIFT),
"+" | "-" => Some(prec::ADDITIVE),
"*" | "/" | "%" => Some(prec::MULTIPLICATIVE),
"**" => Some(prec::EXPONENT),
"instanceof" => Some(prec::RELATIONAL),
_ => None,
}
}
pub fn is_assignment_operator(text: &str) -> bool {
matches!(
text,
"=" | "+="
| "-="
| "*="
| "/="
| "%="
| "**="
| "<<="
| ">>="
| ">>>="
| "&="
| "|="
| "^="
| "&&="
| "||="
| "??="
)
}
pub fn is_prefix_operator(kind: SyntaxKind, text: &str) -> bool {
match kind {
SyntaxKind::PlusPlus | SyntaxKind::MinusMinus => true,
SyntaxKind::TypeofKw => true,
SyntaxKind::VoidKw => true,
SyntaxKind::DeleteKw => true,
SyntaxKind::AwaitKw => true,
SyntaxKind::YieldKw => true,
SyntaxKind::NewKw => true,
_ => matches!(text, "-" | "+" | "!" | "~"),
}
}
pub fn is_postfix_operator(kind: SyntaxKind) -> bool {
matches!(kind, SyntaxKind::PlusPlus | SyntaxKind::MinusMinus)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_binding_power_left_associative() {
let bp = BindingPower::left(10);
assert_eq!(bp.left, 10);
assert_eq!(bp.right, 11);
}
#[test]
fn test_binding_power_right_associative() {
let bp = BindingPower::right(10);
assert_eq!(bp.left, 11);
assert_eq!(bp.right, 10);
}
#[test]
fn test_precedence_ordering() {
assert!(prec::COMMA.left < prec::ASSIGN.right);
assert!(prec::ASSIGN.right < prec::CONDITIONAL.right);
assert!(prec::LOGICAL_OR.left < prec::LOGICAL_AND.left);
assert!(prec::LOGICAL_AND.left < prec::BITWISE_OR.left);
assert!(prec::EQUALITY.left < prec::RELATIONAL.left);
assert!(prec::ADDITIVE.left < prec::MULTIPLICATIVE.left);
assert!(prec::MULTIPLICATIVE.left < prec::EXPONENT.left);
}
#[test]
fn test_infix_binding_power_arithmetic() {
assert_eq!(
infix_binding_power(SyntaxKind::Text, "+"),
Some(prec::ADDITIVE)
);
assert_eq!(
infix_binding_power(SyntaxKind::Text, "-"),
Some(prec::ADDITIVE)
);
assert_eq!(
infix_binding_power(SyntaxKind::Text, "*"),
Some(prec::MULTIPLICATIVE)
);
assert_eq!(
infix_binding_power(SyntaxKind::Text, "/"),
Some(prec::MULTIPLICATIVE)
);
assert_eq!(
infix_binding_power(SyntaxKind::Text, "%"),
Some(prec::MULTIPLICATIVE)
);
assert_eq!(
infix_binding_power(SyntaxKind::Text, "**"),
Some(prec::EXPONENT)
);
}
#[test]
fn test_infix_binding_power_comparison() {
assert_eq!(
infix_binding_power(SyntaxKind::Text, "=="),
Some(prec::EQUALITY)
);
assert_eq!(
infix_binding_power(SyntaxKind::Text, "==="),
Some(prec::EQUALITY)
);
assert_eq!(
infix_binding_power(SyntaxKind::Text, "!="),
Some(prec::EQUALITY)
);
assert_eq!(
infix_binding_power(SyntaxKind::Text, "!=="),
Some(prec::EQUALITY)
);
assert_eq!(
infix_binding_power(SyntaxKind::Lt, "<"),
Some(prec::RELATIONAL)
);
assert_eq!(
infix_binding_power(SyntaxKind::Gt, ">"),
Some(prec::RELATIONAL)
);
assert_eq!(
infix_binding_power(SyntaxKind::Text, "<="),
Some(prec::RELATIONAL)
);
assert_eq!(
infix_binding_power(SyntaxKind::Text, ">="),
Some(prec::RELATIONAL)
);
}
#[test]
fn test_infix_binding_power_logical() {
assert_eq!(
infix_binding_power(SyntaxKind::Text, "&&"),
Some(prec::LOGICAL_AND)
);
assert_eq!(
infix_binding_power(SyntaxKind::Text, "||"),
Some(prec::LOGICAL_OR)
);
assert_eq!(
infix_binding_power(SyntaxKind::Text, "??"),
Some(prec::NULLISH)
);
}
#[test]
fn test_infix_binding_power_bitwise() {
assert_eq!(
infix_binding_power(SyntaxKind::Text, "&"),
Some(prec::BITWISE_AND)
);
assert_eq!(
infix_binding_power(SyntaxKind::Text, "|"),
Some(prec::BITWISE_OR)
);
assert_eq!(
infix_binding_power(SyntaxKind::Text, "^"),
Some(prec::BITWISE_XOR)
);
assert_eq!(
infix_binding_power(SyntaxKind::Text, "<<"),
Some(prec::SHIFT)
);
assert_eq!(
infix_binding_power(SyntaxKind::Text, ">>"),
Some(prec::SHIFT)
);
assert_eq!(
infix_binding_power(SyntaxKind::Text, ">>>"),
Some(prec::SHIFT)
);
}
#[test]
fn test_is_assignment_operator() {
assert!(is_assignment_operator("="));
assert!(is_assignment_operator("+="));
assert!(is_assignment_operator("-="));
assert!(is_assignment_operator("*="));
assert!(is_assignment_operator("/="));
assert!(is_assignment_operator("**="));
assert!(is_assignment_operator("&&="));
assert!(is_assignment_operator("||="));
assert!(is_assignment_operator("??="));
assert!(!is_assignment_operator("+"));
assert!(!is_assignment_operator("=="));
assert!(!is_assignment_operator("==="));
}
#[test]
fn test_is_prefix_operator() {
assert!(is_prefix_operator(SyntaxKind::PlusPlus, "++"));
assert!(is_prefix_operator(SyntaxKind::MinusMinus, "--"));
assert!(is_prefix_operator(SyntaxKind::TypeofKw, "typeof"));
assert!(is_prefix_operator(SyntaxKind::Text, "-"));
assert!(is_prefix_operator(SyntaxKind::Text, "+"));
assert!(is_prefix_operator(SyntaxKind::Text, "!"));
assert!(is_prefix_operator(SyntaxKind::Text, "~"));
assert!(!is_prefix_operator(SyntaxKind::Text, "*"));
assert!(!is_prefix_operator(SyntaxKind::Text, "/"));
}
#[test]
fn test_typescript_as_precedence() {
let as_bp = prec::TS_AS;
assert!(as_bp.left > prec::EQUALITY.left);
assert!(as_bp.left < prec::RELATIONAL.left);
}
}