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}