#![allow(dead_code)]
use crate::frontend::lexer::Token;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Associativity {
Left,
Right,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
pub struct Precedence(pub i32);
impl Precedence {
pub const ASSIGNMENT: Precedence = Precedence(10);
pub const MESSAGE_SEND: Precedence = Precedence(15); pub const PIPELINE: Precedence = Precedence(20);
pub const LOGICAL_OR: Precedence = Precedence(30);
pub const LOGICAL_AND: Precedence = Precedence(40);
pub const EQUALITY: Precedence = Precedence(50);
pub const COMPARISON: Precedence = Precedence(60);
pub const BITWISE_OR: Precedence = Precedence(70);
pub const BITWISE_XOR: Precedence = Precedence(80);
pub const BITWISE_AND: Precedence = Precedence(90);
pub const SHIFT: Precedence = Precedence(100);
pub const RANGE: Precedence = Precedence(110);
pub const ADDITIVE: Precedence = Precedence(120);
pub const MULTIPLICATIVE: Precedence = Precedence(130);
pub const POWER: Precedence = Precedence(140);
pub const UNARY: Precedence = Precedence(150);
pub const POSTFIX: Precedence = Precedence(160);
pub const CALL: Precedence = Precedence(170);
pub const MEMBER: Precedence = Precedence(180);
}
pub fn get_operator_info(token: &Token) -> Option<(Precedence, Associativity)> {
use Associativity::{Left, Right};
match token {
Token::Equal
| Token::PlusEqual
| Token::MinusEqual
| Token::StarEqual
| Token::SlashEqual
| Token::PercentEqual
| Token::AmpersandEqual
| Token::PipeEqual
| Token::CaretEqual
| Token::LeftShiftEqual
| Token::LeftArrow
| Token::ActorQuery => Option::Some((Precedence::ASSIGNMENT, Right)),
Token::Bang => Option::Some((Precedence::MESSAGE_SEND, Left)),
Token::Pipeline => Option::Some((Precedence::PIPELINE, Left)),
Token::OrOr => Option::Some((Precedence::LOGICAL_OR, Left)),
Token::AndAnd => Option::Some((Precedence::LOGICAL_AND, Left)),
Token::EqualEqual | Token::NotEqual => Option::Some((Precedence::EQUALITY, Left)),
Token::Less | Token::Greater | Token::LessEqual | Token::GreaterEqual => {
Option::Some((Precedence::COMPARISON, Left))
}
Token::Pipe => Option::Some((Precedence::BITWISE_OR, Left)),
Token::Caret => Option::Some((Precedence::BITWISE_XOR, Left)),
Token::Ampersand => Option::Some((Precedence::BITWISE_AND, Left)),
Token::LeftShift => Option::Some((Precedence::SHIFT, Left)),
Token::RightShift => Option::Some((Precedence::SHIFT, Left)),
Token::DotDot | Token::DotDotEqual => Option::Some((Precedence::RANGE, Left)),
Token::Plus | Token::Minus => Option::Some((Precedence::ADDITIVE, Left)),
Token::Star | Token::Slash | Token::Percent => {
Option::Some((Precedence::MULTIPLICATIVE, Left))
}
Token::Power => Option::Some((Precedence::POWER, Right)),
_ => Option::None,
}
}
pub fn is_postfix_operator(token: &Token) -> bool {
matches!(
token,
Token::Question | Token::Increment | Token::Decrement | Token::Dot | Token::SafeNav | Token::LeftParen | Token::LeftBracket )
}
pub fn is_prefix_operator(token: &Token) -> bool {
matches!(
token,
Token::Bang | Token::Minus | Token::Plus | Token::Tilde | Token::Ampersand | Token::Star | Token::Increment | Token::Decrement )
}
pub fn get_postfix_precedence(token: &Token) -> Precedence {
match token {
Token::Dot | Token::SafeNav => Precedence::MEMBER,
Token::LeftParen | Token::LeftBracket => Precedence::CALL,
Token::Question | Token::Increment | Token::Decrement => Precedence::POSTFIX,
_ => Precedence(0),
}
}
pub fn should_continue_parsing(current_token: &Token, min_precedence: Precedence) -> bool {
if let Option::Some((prec, _)) = get_operator_info(current_token) {
prec.0 >= min_precedence.0
} else {
false
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_operator_precedence_ordering() {
assert!(Precedence::ASSIGNMENT < Precedence::PIPELINE);
assert!(Precedence::PIPELINE < Precedence::LOGICAL_OR);
assert!(Precedence::LOGICAL_OR < Precedence::LOGICAL_AND);
assert!(Precedence::ADDITIVE < Precedence::MULTIPLICATIVE);
assert!(Precedence::MULTIPLICATIVE < Precedence::POWER);
assert!(Precedence::POWER < Precedence::POSTFIX);
assert!(Precedence::POSTFIX < Precedence::CALL);
}
#[test]
fn test_postfix_operators() {
assert!(is_postfix_operator(&Token::Question));
assert!(is_postfix_operator(&Token::Dot));
assert!(is_postfix_operator(&Token::LeftParen));
assert!(!is_postfix_operator(&Token::Plus));
}
#[test]
fn test_operator_associativity() {
let (_, assoc) = get_operator_info(&Token::Equal).expect("Equal should have operator info");
assert_eq!(assoc, Associativity::Right);
let (_, assoc) = get_operator_info(&Token::Plus).expect("Plus should have operator info");
assert_eq!(assoc, Associativity::Left);
let (_, assoc) = get_operator_info(&Token::Power).expect("Power should have operator info");
assert_eq!(assoc, Associativity::Right);
}
#[test]
fn test_all_operator_match_arms() {
assert!(get_operator_info(&Token::Pipeline).is_some(), "Pipeline");
assert!(get_operator_info(&Token::OrOr).is_some(), "OrOr");
assert!(get_operator_info(&Token::AndAnd).is_some(), "AndAnd");
assert!(
get_operator_info(&Token::EqualEqual).is_some(),
"EqualEqual"
);
assert!(get_operator_info(&Token::NotEqual).is_some(), "NotEqual");
assert!(get_operator_info(&Token::Less).is_some(), "Less");
assert!(get_operator_info(&Token::Greater).is_some(), "Greater");
assert!(get_operator_info(&Token::LessEqual).is_some(), "LessEqual");
assert!(
get_operator_info(&Token::GreaterEqual).is_some(),
"GreaterEqual"
);
assert!(
get_operator_info(&Token::Pipe).is_some(),
"Pipe (bitwise OR)"
);
assert!(get_operator_info(&Token::Caret).is_some(), "Caret (XOR)");
assert!(
get_operator_info(&Token::Ampersand).is_some(),
"Ampersand (bitwise AND)"
);
assert!(get_operator_info(&Token::LeftShift).is_some(), "LeftShift");
assert!(
get_operator_info(&Token::RightShift).is_some(),
"RightShift"
);
assert!(get_operator_info(&Token::DotDot).is_some(), "DotDot");
assert!(
get_operator_info(&Token::DotDotEqual).is_some(),
"DotDotEqual"
);
assert!(get_operator_info(&Token::Star).is_some(), "Star");
assert!(get_operator_info(&Token::Slash).is_some(), "Slash");
assert!(get_operator_info(&Token::Percent).is_some(), "Percent");
assert!(get_operator_info(&Token::Bang).is_some(), "Bang");
}
#[test]
fn test_all_postfix_match_arms() {
assert_eq!(
get_postfix_precedence(&Token::Dot),
Precedence::MEMBER,
"Dot"
);
assert_eq!(
get_postfix_precedence(&Token::SafeNav),
Precedence::MEMBER,
"SafeNav"
);
assert_eq!(
get_postfix_precedence(&Token::LeftParen),
Precedence::CALL,
"LeftParen"
);
assert_eq!(
get_postfix_precedence(&Token::LeftBracket),
Precedence::CALL,
"LeftBracket"
);
assert_eq!(
get_postfix_precedence(&Token::Question),
Precedence::POSTFIX,
"Question"
);
assert_eq!(
get_postfix_precedence(&Token::Increment),
Precedence::POSTFIX,
"Increment"
);
assert_eq!(
get_postfix_precedence(&Token::Decrement),
Precedence::POSTFIX,
"Decrement"
);
}
#[test]
fn test_is_prefix_operator_returns_false_for_non_prefix() {
assert!(!is_prefix_operator(&Token::Slash), "Slash is not prefix");
assert!(!is_prefix_operator(&Token::Dot), "Dot is not prefix");
assert!(
!is_prefix_operator(&Token::EqualEqual),
"EqualEqual is not prefix"
);
}
#[test]
fn test_is_prefix_operator_returns_true_for_prefix() {
assert!(is_prefix_operator(&Token::Bang), "Bang is prefix");
assert!(
is_prefix_operator(&Token::Minus),
"Minus (negation) is prefix"
);
assert!(
is_prefix_operator(&Token::Plus),
"Plus (identity) is prefix"
);
assert!(is_prefix_operator(&Token::Tilde), "Tilde is prefix");
assert!(
is_prefix_operator(&Token::Ampersand),
"Ampersand (ref) is prefix"
);
assert!(is_prefix_operator(&Token::Star), "Star (deref) is prefix");
assert!(is_prefix_operator(&Token::Increment), "Increment is prefix");
assert!(is_prefix_operator(&Token::Decrement), "Decrement is prefix");
}
#[test]
fn test_should_continue_parsing_precedence_comparison() {
assert!(
should_continue_parsing(&Token::Plus, Precedence(100)),
"Should continue when current > min"
);
assert!(
should_continue_parsing(&Token::Plus, Precedence(120)),
"Should continue when current == min"
);
assert!(
!should_continue_parsing(&Token::Plus, Precedence(130)),
"Should stop when current < min"
);
}
#[test]
fn test_should_continue_parsing_returns_false_for_non_operators() {
assert!(
!should_continue_parsing(&Token::Semicolon, Precedence(0)),
"Non-operator should return false"
);
assert!(
!should_continue_parsing(&Token::Comma, Precedence(0)),
"Non-operator should return false"
);
}
}
#[cfg(test)]
mod mutation_tests {
use super::*;
#[test]
fn test_is_prefix_operator_not_stub() {
assert!(is_prefix_operator(&Token::Bang), "Bang should be prefix");
assert!(is_prefix_operator(&Token::Minus), "Minus should be prefix");
assert!(
is_prefix_operator(&Token::Plus),
"Plus should be prefix (identity)"
);
assert!(is_prefix_operator(&Token::Tilde), "Tilde should be prefix");
assert!(
!is_prefix_operator(&Token::Slash),
"Slash should NOT be prefix"
);
assert!(!is_prefix_operator(&Token::Dot), "Dot should NOT be prefix");
assert!(
!is_prefix_operator(&Token::Equal),
"Equal should NOT be prefix"
);
}
#[test]
fn test_get_operator_info_ampersand_match_arm() {
let result = get_operator_info(&Token::Ampersand);
assert!(result.is_some(), "Ampersand should have operator info");
let (prec, assoc) = result.unwrap();
assert_eq!(
prec,
Precedence::BITWISE_AND,
"Ampersand should have BITWISE_AND precedence"
);
assert_eq!(
assoc,
Associativity::Left,
"Ampersand should be left-associative"
);
}
#[test]
fn test_get_operator_info_left_shift_match_arm() {
let result = get_operator_info(&Token::LeftShift);
assert!(result.is_some(), "LeftShift should have operator info");
let (prec, assoc) = result.unwrap();
assert_eq!(
prec,
Precedence::SHIFT,
"LeftShift should have SHIFT precedence"
);
assert_eq!(
assoc,
Associativity::Left,
"LeftShift should be left-associative"
);
}
#[test]
fn test_get_operator_info_right_shift_match_arm() {
let result = get_operator_info(&Token::RightShift);
assert!(result.is_some(), "RightShift should have operator info");
let (prec, assoc) = result.unwrap();
assert_eq!(
prec,
Precedence::SHIFT,
"RightShift should have SHIFT precedence"
);
assert_eq!(
assoc,
Associativity::Left,
"RightShift should be left-associative"
);
}
}