## Coverage Report

#### Created: 2025-09-08 21:26

next uncovered line (L), next uncovered region (R), next uncovered branch (B)

    /home/noah/src/ruchy/src/frontend/parser/operator_precedence.rs

    Line
    Count
    Source
    1
    //! Operator precedence handling for the Ruchy parser
    2
    //!
    3
    //! This module defines operator precedence and associativity rules
    4
    //! ensuring proper parsing of complex expressions.
    5
    6
    #![allow(dead_code)]
    7
    8
    use crate::frontend::lexer::Token;
    9
    10
    /// Operator associativity
    11
    #[derive(Debug, Clone, Copy, PartialEq, Eq)]
    12
    pub enum Associativity {
    13
        Left,
    14
        Right,
    15
    }
    16
    17
    /// Operator precedence levels (higher = tighter binding)
    18
    #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
    19
    pub struct Precedence(pub i32);
    20
    21
    impl Precedence {
    22
        // Precedence levels from lowest to highest
    23
        pub const ASSIGNMENT: Precedence = Precedence(10);
    24
        pub const PIPELINE: Precedence = Precedence(20);
    25
        pub const LOGICAL_OR: Precedence = Precedence(30);
    26
        pub const LOGICAL_AND: Precedence = Precedence(40);
    27
        pub const EQUALITY: Precedence = Precedence(50);
    28
        pub const COMPARISON: Precedence = Precedence(60);
    29
        pub const BITWISE_OR: Precedence = Precedence(70);
    30
        pub const BITWISE_XOR: Precedence = Precedence(80);
    31
        pub const BITWISE_AND: Precedence = Precedence(90);
    32
        pub const SHIFT: Precedence = Precedence(100);
    33
        pub const RANGE: Precedence = Precedence(110);
    34
        pub const ADDITIVE: Precedence = Precedence(120);
    35
        pub const MULTIPLICATIVE: Precedence = Precedence(130);
    36
        pub const POWER: Precedence = Precedence(140);
    37
        pub const UNARY: Precedence = Precedence(150);
    38
        pub const POSTFIX: Precedence = Precedence(160);
    39
        pub const CALL: Precedence = Precedence(170);
    40
        pub const MEMBER: Precedence = Precedence(180);
    41
    }
    42
    43
    /// Get operator precedence and associativity
    44
    0
    pub fn get_operator_info(token: &Token) -> Option<(Precedence, Associativity)> {
    45
        use Associativity::{Left, Right};
    46
    47
    0
        match token {
    48
            // Assignment and actor operators (right-associative)
    49
            Token::Equal
    50
            | Token::PlusEqual
    51
            | Token::MinusEqual
    52
            | Token::StarEqual
    53
            | Token::SlashEqual
    54
            | Token::PercentEqual
    55
            | Token::AmpersandEqual
    56
            | Token::PipeEqual
    57
            | Token::CaretEqual
    58
            | Token::LeftShiftEqual
    59
            | Token::LeftArrow
    60
    0
            | Token::ActorQuery => Option::Some((Precedence::ASSIGNMENT, Right)),
    61
    62
            // Pipeline operator (left-associative)
    63
    0
            Token::Pipeline => Option::Some((Precedence::PIPELINE, Left)),
    64
    65
            // Logical operators
    66
    0
            Token::OrOr => Option::Some((Precedence::LOGICAL_OR, Left)),
    67
    0
            Token::AndAnd => Option::Some((Precedence::LOGICAL_AND, Left)),
    68
    69
            // Equality operators
    70
    0
            Token::EqualEqual | Token::NotEqual => Option::Some((Precedence::EQUALITY, Left)),
    71
    72
            // Comparison operators
    73
            Token::Less | Token::Greater | Token::LessEqual | Token::GreaterEqual => {
    74
    0
                Option::Some((Precedence::COMPARISON, Left))
    75
            }
    76
    77
            // Bitwise operators
    78
    0
            Token::Pipe => Option::Some((Precedence::BITWISE_OR, Left)),
    79
    0
            Token::Caret => Option::Some((Precedence::BITWISE_XOR, Left)),
    80
    0
            Token::Ampersand => Option::Some((Precedence::BITWISE_AND, Left)),
    81
    0
            Token::LeftShift => Option::Some((Precedence::SHIFT, Left)),
    82
    83
            // Range operators
    84
    0
            Token::DotDot | Token::DotDotEqual => Option::Some((Precedence::RANGE, Left)),
    85
    86
            // Arithmetic operators
    87
    0
            Token::Plus | Token::Minus => Option::Some((Precedence::ADDITIVE, Left)),
    88
            Token::Star | Token::Slash | Token::Percent => {
    89
    0
                Option::Some((Precedence::MULTIPLICATIVE, Left))
    90
            }
    91
    0
            Token::Power => Option::Some((Precedence::POWER, Right)),
    92
    93
    0
            _ => Option::None,
    94
        }
    95
    0
    }
    96
    97
    /// Check if token is a postfix operator
    98
    0
    pub fn is_postfix_operator(token: &Token) -> bool {
    99
    0
        matches!(
    100
    0
            token,
    101
            Token::Question      // ? (try operator)
    102
            | Token::Increment   // ++
    103
            | Token::Decrement   // --
    104
            | Token::Dot         // . (member access)
    105
            | Token::SafeNav     // ?. (optional chaining)
    106
            | Token::LeftParen   // ( (function call)
    107
            | Token::LeftBracket // [ (indexing)
    108
        )
    109
    0
    }
    110
    111
    /// Check if token is a prefix operator
    112
    0
    pub fn is_prefix_operator(token: &Token) -> bool {
    113
    0
        matches!(
    114
    0
            token,
    115
            Token::Bang          // ! (logical not)
    116
            | Token::Minus       // - (negation)
    117
            | Token::Tilde       // ~ (bitwise not)
    118
            | Token::Ampersand   // & (reference)
    119
            | Token::Star        // * (dereference)
    120
            | Token::Increment   // ++ (pre-increment)
    121
            | Token::Decrement // -- (pre-decrement)
    122
        )
    123
    0
    }
    124
    125
    /// Get postfix operator precedence
    126
    0
    pub fn get_postfix_precedence(token: &Token) -> Precedence {
    127
    0
        match token {
    128
    0
            Token::Dot | Token::SafeNav => Precedence::MEMBER,
    129
    0
            Token::LeftParen | Token::LeftBracket => Precedence::CALL,
    130
    0
            Token::Question | Token::Increment | Token::Decrement => Precedence::POSTFIX,
    131
    0
            _ => Precedence(0),
    132
        }
    133
    0
    }
    134
    135
    /// Check if we should continue parsing based on precedence
    136
    0
    pub fn should_continue_parsing(current_token: &Token, min_precedence: Precedence) -> bool {
    137
    0
        if let Option::Some((prec, _)) = get_operator_info(current_token) {
    138
    0
            prec.0 >= min_precedence.0
    139
        } else {
    140
    0
            false
    141
        }
    142
    0
    }
    143
    144
    #[cfg(test)]
    145
    mod tests {
    146
        use super::*;
    147
    148
        #[test]
    149
        fn test_operator_precedence_ordering() {
    150
            // Verify precedence ordering
    151
            assert!(Precedence::ASSIGNMENT < Precedence::PIPELINE);
    152
            assert!(Precedence::PIPELINE < Precedence::LOGICAL_OR);
    153
            assert!(Precedence::LOGICAL_OR < Precedence::LOGICAL_AND);
    154
            assert!(Precedence::ADDITIVE < Precedence::MULTIPLICATIVE);
    155
            assert!(Precedence::MULTIPLICATIVE < Precedence::POWER);
    156
            assert!(Precedence::POWER < Precedence::POSTFIX);
    157
            assert!(Precedence::POSTFIX < Precedence::CALL);
    158
        }
    159
    160
        #[test]
    161
        fn test_postfix_operators() {
    162
            assert!(is_postfix_operator(&Token::Question));
    163
            assert!(is_postfix_operator(&Token::Dot));
    164
            assert!(is_postfix_operator(&Token::LeftParen));
    165
            assert!(!is_postfix_operator(&Token::Plus));
    166
        }
    167
    168
        #[test]
    169
        fn test_operator_associativity() {
    170
            // Assignment is right-associative
    171
            let (_, assoc) = get_operator_info(&Token::Equal).expect("Equal should have operator info");
    172
            assert_eq!(assoc, Associativity::Right);
    173
    174
            // Addition is left-associative
    175
            let (_, assoc) = get_operator_info(&Token::Plus).expect("Plus should have operator info");
    176
            assert_eq!(assoc, Associativity::Left);
    177
    178
            // Power is right-associative
    179
            let (_, assoc) = get_operator_info(&Token::Power).expect("Power should have operator info");
    180
            assert_eq!(assoc, Associativity::Right);
    181
        }
    182
    }