Skip to main content

rustpython_ruff_python_ast/
operator_precedence.rs

1use crate::{BoolOp, Expr, ExprRef, Operator, UnaryOp};
2
3/// Represents the precedence levels for Python expressions.
4/// Variants at the top have lower precedence and variants at the bottom have
5/// higher precedence.
6///
7/// See: <https://docs.python.org/3/reference/expressions.html#operator-precedence>
8#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord)]
9pub enum OperatorPrecedence {
10    /// The lowest (virtual) precedence level
11    None,
12    /// Precedence of `yield` and `yield from` expressions.
13    Yield,
14    /// Precedence of assignment expressions (`name := expr`).
15    Assign,
16    /// Precedence of starred expressions (`*expr`).
17    Starred,
18    /// Precedence of lambda expressions (`lambda args: expr`).
19    Lambda,
20    /// Precedence of if/else expressions (`expr if cond else expr`).
21    IfElse,
22    /// Precedence of boolean `or` expressions.
23    Or,
24    /// Precedence of boolean `and` expressions.
25    And,
26    /// Precedence of boolean `not` expressions.
27    Not,
28    /// Precedence of comparisons (`<`, `<=`, `>`, `>=`, `!=`, `==`),
29    /// memberships (`in`, `not in`) and identity tests (`is`, `is not`).
30    ComparisonsMembershipIdentity,
31    /// Precedence of bitwise `|` operator.
32    BitOr,
33    /// Precedence of bitwise `^` operator.
34    BitXor,
35    /// Precedence of bitwise `&` operator.
36    BitAnd,
37    /// Precedence of left and right shift expressions (`<<`, `>>`).
38    LeftRightShift,
39    /// Precedence of addition and subtraction expressions (`+`, `-`).
40    AddSub,
41    /// Precedence of multiplication (`*`), matrix multiplication (`@`), division (`/`),
42    /// floor division (`//`) and remainder (`%`) expressions.
43    MulDivRemain,
44    /// Precedence of unary positive (`+`), negative (`-`), and bitwise NOT (`~`) expressions.
45    PosNegBitNot,
46    /// Precedence of exponentiation expressions (`**`).
47    Exponent,
48    /// Precedence of `await` expressions.
49    Await,
50    /// Precedence of call expressions (`()`), attribute access (`.`), and subscript (`[]`) expressions.
51    CallAttribute,
52    /// Precedence of atomic expressions (literals, names, containers).
53    Atomic,
54}
55
56impl OperatorPrecedence {
57    pub fn from_expr_ref(expr: &ExprRef) -> Self {
58        match expr {
59            // Binding or parenthesized expression, list display, dictionary display, set display
60            ExprRef::Tuple(_)
61            | ExprRef::Dict(_)
62            | ExprRef::Set(_)
63            | ExprRef::ListComp(_)
64            | ExprRef::List(_)
65            | ExprRef::SetComp(_)
66            | ExprRef::DictComp(_)
67            | ExprRef::Generator(_)
68            | ExprRef::Name(_)
69            | ExprRef::StringLiteral(_)
70            | ExprRef::BytesLiteral(_)
71            | ExprRef::NumberLiteral(_)
72            | ExprRef::BooleanLiteral(_)
73            | ExprRef::NoneLiteral(_)
74            | ExprRef::EllipsisLiteral(_)
75            | ExprRef::FString(_)
76            | ExprRef::TString(_) => Self::Atomic,
77            // Subscription, slicing, call, attribute reference
78            ExprRef::Attribute(_)
79            | ExprRef::Subscript(_)
80            | ExprRef::Call(_)
81            | ExprRef::Slice(_) => Self::CallAttribute,
82
83            // Await expression
84            ExprRef::Await(_) => Self::Await,
85
86            // Exponentiation **
87            // Handled below along with other binary operators
88
89            // Unary operators: +x, -x, ~x (except boolean not)
90            ExprRef::UnaryOp(operator) => match operator.op {
91                UnaryOp::UAdd | UnaryOp::USub | UnaryOp::Invert => Self::PosNegBitNot,
92                UnaryOp::Not => Self::Not,
93            },
94
95            // Math binary ops
96            ExprRef::BinOp(binary_operation) => Self::from(binary_operation.op),
97
98            // Comparisons: <, <=, >, >=, ==, !=, in, not in, is, is not
99            ExprRef::Compare(_) => Self::ComparisonsMembershipIdentity,
100
101            // Boolean not
102            // Handled above in unary operators
103
104            // Boolean operations: and, or
105            ExprRef::BoolOp(bool_op) => Self::from(bool_op.op),
106
107            // Conditional expressions: x if y else z
108            ExprRef::If(_) => Self::IfElse,
109
110            // Lambda expressions
111            ExprRef::Lambda(_) => Self::Lambda,
112
113            // Unpacking also omitted in the docs, but has almost the lowest precedence,
114            // except for assignment & yield expressions. E.g. `[*(v := [1,2])]` is valid
115            // but `[*v := [1,2]] would fail on incorrect syntax because * will associate
116            // `v` before the assignment.
117            ExprRef::Starred(_) => Self::Starred,
118
119            // Assignment expressions (aka named)
120            ExprRef::Named(_) => Self::Assign,
121
122            // Although omitted in docs, yield expressions may be used inside an expression
123            // but must be parenthesized. So for our purposes we assume they just have
124            // the lowest "real" precedence.
125            ExprRef::Yield(_) | ExprRef::YieldFrom(_) => Self::Yield,
126
127            // Not a real python expression, so treat as lowest as well
128            ExprRef::IpyEscapeCommand(_) => Self::None,
129        }
130    }
131
132    pub fn from_expr(expr: &Expr) -> Self {
133        Self::from(&ExprRef::from(expr))
134    }
135
136    /// Returns `true` if the precedence is right-associative i.e., the operations are evaluated
137    /// from right to left.
138    pub fn is_right_associative(self) -> bool {
139        matches!(self, OperatorPrecedence::Exponent)
140    }
141}
142
143impl From<&Expr> for OperatorPrecedence {
144    fn from(expr: &Expr) -> Self {
145        Self::from_expr(expr)
146    }
147}
148
149impl<'a> From<&ExprRef<'a>> for OperatorPrecedence {
150    fn from(expr_ref: &ExprRef<'a>) -> Self {
151        Self::from_expr_ref(expr_ref)
152    }
153}
154
155impl From<Operator> for OperatorPrecedence {
156    fn from(operator: Operator) -> Self {
157        match operator {
158            // Multiplication, matrix multiplication, division, floor division, remainder:
159            // *, @, /, //, %
160            Operator::Mult
161            | Operator::MatMult
162            | Operator::Div
163            | Operator::Mod
164            | Operator::FloorDiv => Self::MulDivRemain,
165            // Addition, subtraction
166            Operator::Add | Operator::Sub => Self::AddSub,
167            // Bitwise shifts: <<, >>
168            Operator::LShift | Operator::RShift => Self::LeftRightShift,
169            // Bitwise operations: &, ^, |
170            Operator::BitAnd => Self::BitAnd,
171            Operator::BitXor => Self::BitXor,
172            Operator::BitOr => Self::BitOr,
173            // Exponentiation **
174            Operator::Pow => Self::Exponent,
175        }
176    }
177}
178
179impl From<BoolOp> for OperatorPrecedence {
180    fn from(operator: BoolOp) -> Self {
181        match operator {
182            BoolOp::And => Self::And,
183            BoolOp::Or => Self::Or,
184        }
185    }
186}
187
188impl From<UnaryOp> for OperatorPrecedence {
189    fn from(unary_op: UnaryOp) -> Self {
190        match unary_op {
191            UnaryOp::UAdd | UnaryOp::USub | UnaryOp::Invert => Self::PosNegBitNot,
192            UnaryOp::Not => Self::Not,
193        }
194    }
195}