Skip to main content

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    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::Constant(_)
73            | ExprRef::BooleanLiteral(_)
74            | ExprRef::NoneLiteral(_)
75            | ExprRef::EllipsisLiteral(_)
76            | ExprRef::FString(_)
77            | ExprRef::TString(_) => Self::Atomic,
78            // Subscription, slicing, call, attribute reference
79            ExprRef::Attribute(_)
80            | ExprRef::Subscript(_)
81            | ExprRef::Call(_)
82            | ExprRef::Slice(_) => Self::CallAttribute,
83
84            // Await expression
85            ExprRef::Await(_) => Self::Await,
86
87            // Exponentiation **
88            // Handled below along with other binary operators
89
90            // Unary operators: +x, -x, ~x (except boolean not)
91            ExprRef::UnaryOp(operator) => match operator.op {
92                UnaryOp::UAdd | UnaryOp::USub | UnaryOp::Invert => Self::PosNegBitNot,
93                UnaryOp::Not => Self::Not,
94            },
95
96            // Math binary ops
97            ExprRef::BinOp(binary_operation) => Self::from(binary_operation.op),
98
99            // Comparisons: <, <=, >, >=, ==, !=, in, not in, is, is not
100            ExprRef::Compare(_) => Self::ComparisonsMembershipIdentity,
101
102            // Boolean not
103            // Handled above in unary operators
104
105            // Boolean operations: and, or
106            ExprRef::BoolOp(bool_op) => Self::from(bool_op.op),
107
108            // Conditional expressions: x if y else z
109            ExprRef::If(_) => Self::IfElse,
110
111            // Lambda expressions
112            ExprRef::Lambda(_) => Self::Lambda,
113
114            // Unpacking also omitted in the docs, but has almost the lowest precedence,
115            // except for assignment & yield expressions. E.g. `[*(v := [1,2])]` is valid
116            // but `[*v := [1,2]] would fail on incorrect syntax because * will associate
117            // `v` before the assignment.
118            ExprRef::Starred(_) => Self::Starred,
119
120            // Assignment expressions (aka named)
121            ExprRef::Named(_) => Self::Assign,
122
123            // Although omitted in docs, yield expressions may be used inside an expression
124            // but must be parenthesized. So for our purposes we assume they just have
125            // the lowest "real" precedence.
126            ExprRef::Yield(_) | ExprRef::YieldFrom(_) => Self::Yield,
127
128            // Not a real python expression, so treat as lowest as well
129            ExprRef::IpyEscapeCommand(_) => Self::None,
130        }
131    }
132
133    pub fn from_expr(expr: &Expr) -> Self {
134        Self::from(ExprRef::from(expr))
135    }
136
137    /// Returns `true` if the precedence is right-associative i.e., the operations are evaluated
138    /// from right to left.
139    pub fn is_right_associative(self) -> bool {
140        matches!(self, OperatorPrecedence::Exponent)
141    }
142}
143
144impl From<&Expr> for OperatorPrecedence {
145    fn from(expr: &Expr) -> Self {
146        Self::from_expr(expr)
147    }
148}
149
150impl<'a> From<ExprRef<'a>> for OperatorPrecedence {
151    fn from(expr_ref: ExprRef<'a>) -> Self {
152        Self::from_expr_ref(expr_ref)
153    }
154}
155
156impl From<Operator> for OperatorPrecedence {
157    fn from(operator: Operator) -> Self {
158        match operator {
159            // Multiplication, matrix multiplication, division, floor division, remainder:
160            // *, @, /, //, %
161            Operator::Mult
162            | Operator::MatMult
163            | Operator::Div
164            | Operator::Mod
165            | Operator::FloorDiv => Self::MulDivRemain,
166            // Addition, subtraction
167            Operator::Add | Operator::Sub => Self::AddSub,
168            // Bitwise shifts: <<, >>
169            Operator::LShift | Operator::RShift => Self::LeftRightShift,
170            // Bitwise operations: &, ^, |
171            Operator::BitAnd => Self::BitAnd,
172            Operator::BitXor => Self::BitXor,
173            Operator::BitOr => Self::BitOr,
174            // Exponentiation **
175            Operator::Pow => Self::Exponent,
176        }
177    }
178}
179
180impl From<BoolOp> for OperatorPrecedence {
181    fn from(operator: BoolOp) -> Self {
182        match operator {
183            BoolOp::And => Self::And,
184            BoolOp::Or => Self::Or,
185        }
186    }
187}
188
189impl From<UnaryOp> for OperatorPrecedence {
190    fn from(unary_op: UnaryOp) -> Self {
191        match unary_op {
192            UnaryOp::UAdd | UnaryOp::USub | UnaryOp::Invert => Self::PosNegBitNot,
193            UnaryOp::Not => Self::Not,
194        }
195    }
196}