Coverage Report

Created: 2025-09-05 15: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
3
pub fn get_operator_info(token: &Token) -> Option<(Precedence, Associativity)> {
45
    use Associativity::{Left, Right};
46
47
3
    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
1
        | 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
1
        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
1
        Token::Power => Option::Some((Precedence::POWER, Right)),
92
93
0
        _ => Option::None,
94
    }
95
3
}
96
97
/// Check if token is a postfix operator
98
4
pub fn is_postfix_operator(token: &Token) -> bool {
99
1
    matches!(
100
4
        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
4
}
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
1
    fn test_operator_precedence_ordering() {
150
        // Verify precedence ordering
151
1
        assert!(Precedence::ASSIGNMENT < Precedence::PIPELINE);
152
1
        assert!(Precedence::PIPELINE < Precedence::LOGICAL_OR);
153
1
        assert!(Precedence::LOGICAL_OR < Precedence::LOGICAL_AND);
154
1
        assert!(Precedence::ADDITIVE < Precedence::MULTIPLICATIVE);
155
1
        assert!(Precedence::MULTIPLICATIVE < Precedence::POWER);
156
1
        assert!(Precedence::POWER < Precedence::POSTFIX);
157
1
        assert!(Precedence::POSTFIX < Precedence::CALL);
158
1
    }
159
160
    #[test]
161
1
    fn test_postfix_operators() {
162
1
        assert!(is_postfix_operator(&Token::Question));
163
1
        assert!(is_postfix_operator(&Token::Dot));
164
1
        assert!(is_postfix_operator(&Token::LeftParen));
165
1
        assert!(!is_postfix_operator(&Token::Plus));
166
1
    }
167
168
    #[test]
169
1
    fn test_operator_associativity() {
170
        // Assignment is right-associative
171
1
        let (_, assoc) = get_operator_info(&Token::Equal).expect("Equal should have operator info");
172
1
        assert_eq!(assoc, Associativity::Right);
173
174
        // Addition is left-associative
175
1
        let (_, assoc) = get_operator_info(&Token::Plus).expect("Plus should have operator info");
176
1
        assert_eq!(assoc, Associativity::Left);
177
178
        // Power is right-associative
179
1
        let (_, assoc) = get_operator_info(&Token::Power).expect("Power should have operator info");
180
1
        assert_eq!(assoc, Associativity::Right);
181
1
    }
182
}