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}