]>
Commit | Line | Data |
---|---|---|
60c5eb7d | 1 | use crate::token::{self, Token, BinOpToken}; |
dc9dc135 | 2 | use crate::symbol::kw; |
9fa01778 | 3 | use crate::ast::{self, BinOpKind}; |
2c00a5a8 | 4 | |
92a42be0 SL |
5 | /// Associative operator with precedence. |
6 | /// | |
7 | /// This is the enum which specifies operator precedence and fixity to the parser. | |
8faf50e0 | 8 | #[derive(PartialEq, Debug)] |
92a42be0 SL |
9 | pub enum AssocOp { |
10 | /// `+` | |
11 | Add, | |
12 | /// `-` | |
13 | Subtract, | |
14 | /// `*` | |
15 | Multiply, | |
16 | /// `/` | |
17 | Divide, | |
18 | /// `%` | |
19 | Modulus, | |
20 | /// `&&` | |
21 | LAnd, | |
22 | /// `||` | |
23 | LOr, | |
24 | /// `^` | |
25 | BitXor, | |
26 | /// `&` | |
27 | BitAnd, | |
28 | /// `|` | |
29 | BitOr, | |
30 | /// `<<` | |
31 | ShiftLeft, | |
32 | /// `>>` | |
33 | ShiftRight, | |
34 | /// `==` | |
35 | Equal, | |
36 | /// `<` | |
37 | Less, | |
38 | /// `<=` | |
39 | LessEqual, | |
40 | /// `!=` | |
41 | NotEqual, | |
42 | /// `>` | |
43 | Greater, | |
44 | /// `>=` | |
45 | GreaterEqual, | |
46 | /// `=` | |
47 | Assign, | |
92a42be0 SL |
48 | /// `?=` where ? is one of the BinOpToken |
49 | AssignOp(BinOpToken), | |
50 | /// `as` | |
51 | As, | |
52 | /// `..` range | |
9cc50fc6 | 53 | DotDot, |
ea8adc8c XL |
54 | /// `..=` range |
55 | DotDotEq, | |
9cc50fc6 SL |
56 | /// `:` |
57 | Colon, | |
92a42be0 SL |
58 | } |
59 | ||
8faf50e0 | 60 | #[derive(PartialEq, Debug)] |
92a42be0 SL |
61 | pub enum Fixity { |
62 | /// The operator is left-associative | |
63 | Left, | |
64 | /// The operator is right-associative | |
65 | Right, | |
66 | /// The operator is not associative | |
67 | None | |
68 | } | |
69 | ||
70 | impl AssocOp { | |
9fa01778 | 71 | /// Creates a new AssocOP from a token |
60c5eb7d | 72 | pub fn from_token(t: &Token) -> Option<AssocOp> { |
9fa01778 | 73 | use AssocOp::*; |
dc9dc135 XL |
74 | match t.kind { |
75 | token::BinOpEq(k) => Some(AssignOp(k)), | |
76 | token::Eq => Some(Assign), | |
77 | token::BinOp(BinOpToken::Star) => Some(Multiply), | |
78 | token::BinOp(BinOpToken::Slash) => Some(Divide), | |
79 | token::BinOp(BinOpToken::Percent) => Some(Modulus), | |
80 | token::BinOp(BinOpToken::Plus) => Some(Add), | |
81 | token::BinOp(BinOpToken::Minus) => Some(Subtract), | |
82 | token::BinOp(BinOpToken::Shl) => Some(ShiftLeft), | |
83 | token::BinOp(BinOpToken::Shr) => Some(ShiftRight), | |
84 | token::BinOp(BinOpToken::And) => Some(BitAnd), | |
85 | token::BinOp(BinOpToken::Caret) => Some(BitXor), | |
86 | token::BinOp(BinOpToken::Or) => Some(BitOr), | |
87 | token::Lt => Some(Less), | |
88 | token::Le => Some(LessEqual), | |
89 | token::Ge => Some(GreaterEqual), | |
90 | token::Gt => Some(Greater), | |
91 | token::EqEq => Some(Equal), | |
92 | token::Ne => Some(NotEqual), | |
93 | token::AndAnd => Some(LAnd), | |
94 | token::OrOr => Some(LOr), | |
95 | token::DotDot => Some(DotDot), | |
96 | token::DotDotEq => Some(DotDotEq), | |
abe05a73 | 97 | // DotDotDot is no longer supported, but we need some way to display the error |
dc9dc135 XL |
98 | token::DotDotDot => Some(DotDotEq), |
99 | token::Colon => Some(Colon), | |
e1599b0c XL |
100 | // `<-` should probably be `< -` |
101 | token::LArrow => Some(Less), | |
dc9dc135 | 102 | _ if t.is_keyword(kw::As) => Some(As), |
92a42be0 SL |
103 | _ => None |
104 | } | |
105 | } | |
106 | ||
9fa01778 | 107 | /// Creates a new AssocOp from ast::BinOpKind. |
7453a54e | 108 | pub fn from_ast_binop(op: BinOpKind) -> Self { |
9fa01778 | 109 | use AssocOp::*; |
92a42be0 | 110 | match op { |
7453a54e SL |
111 | BinOpKind::Lt => Less, |
112 | BinOpKind::Gt => Greater, | |
113 | BinOpKind::Le => LessEqual, | |
114 | BinOpKind::Ge => GreaterEqual, | |
115 | BinOpKind::Eq => Equal, | |
116 | BinOpKind::Ne => NotEqual, | |
117 | BinOpKind::Mul => Multiply, | |
118 | BinOpKind::Div => Divide, | |
119 | BinOpKind::Rem => Modulus, | |
120 | BinOpKind::Add => Add, | |
121 | BinOpKind::Sub => Subtract, | |
122 | BinOpKind::Shl => ShiftLeft, | |
123 | BinOpKind::Shr => ShiftRight, | |
124 | BinOpKind::BitAnd => BitAnd, | |
125 | BinOpKind::BitXor => BitXor, | |
126 | BinOpKind::BitOr => BitOr, | |
127 | BinOpKind::And => LAnd, | |
128 | BinOpKind::Or => LOr | |
92a42be0 SL |
129 | } |
130 | } | |
131 | ||
132 | /// Gets the precedence of this operator | |
133 | pub fn precedence(&self) -> usize { | |
9fa01778 | 134 | use AssocOp::*; |
92a42be0 | 135 | match *self { |
9cc50fc6 | 136 | As | Colon => 14, |
92a42be0 SL |
137 | Multiply | Divide | Modulus => 13, |
138 | Add | Subtract => 12, | |
139 | ShiftLeft | ShiftRight => 11, | |
140 | BitAnd => 10, | |
141 | BitXor => 9, | |
142 | BitOr => 8, | |
143 | Less | Greater | LessEqual | GreaterEqual | Equal | NotEqual => 7, | |
144 | LAnd => 6, | |
145 | LOr => 5, | |
ea8adc8c | 146 | DotDot | DotDotEq => 4, |
92a42be0 SL |
147 | Assign | AssignOp(_) => 2, |
148 | } | |
149 | } | |
150 | ||
151 | /// Gets the fixity of this operator | |
152 | pub fn fixity(&self) -> Fixity { | |
9fa01778 | 153 | use AssocOp::*; |
92a42be0 SL |
154 | // NOTE: it is a bug to have an operators that has same precedence but different fixities! |
155 | match *self { | |
dc9dc135 | 156 | Assign | AssignOp(_) => Fixity::Right, |
92a42be0 SL |
157 | As | Multiply | Divide | Modulus | Add | Subtract | ShiftLeft | ShiftRight | BitAnd | |
158 | BitXor | BitOr | Less | Greater | LessEqual | GreaterEqual | Equal | NotEqual | | |
9cc50fc6 | 159 | LAnd | LOr | Colon => Fixity::Left, |
ea8adc8c | 160 | DotDot | DotDotEq => Fixity::None |
92a42be0 SL |
161 | } |
162 | } | |
163 | ||
164 | pub fn is_comparison(&self) -> bool { | |
9fa01778 | 165 | use AssocOp::*; |
92a42be0 SL |
166 | match *self { |
167 | Less | Greater | LessEqual | GreaterEqual | Equal | NotEqual => true, | |
dc9dc135 | 168 | Assign | AssignOp(_) | As | Multiply | Divide | Modulus | Add | |
83c7162d | 169 | Subtract | ShiftLeft | ShiftRight | BitAnd | BitXor | BitOr | LAnd | LOr | |
ea8adc8c | 170 | DotDot | DotDotEq | Colon => false |
92a42be0 SL |
171 | } |
172 | } | |
173 | ||
174 | pub fn is_assign_like(&self) -> bool { | |
9fa01778 | 175 | use AssocOp::*; |
92a42be0 | 176 | match *self { |
dc9dc135 | 177 | Assign | AssignOp(_) => true, |
92a42be0 SL |
178 | Less | Greater | LessEqual | GreaterEqual | Equal | NotEqual | As | Multiply | Divide | |
179 | Modulus | Add | Subtract | ShiftLeft | ShiftRight | BitAnd | BitXor | BitOr | LAnd | | |
ea8adc8c | 180 | LOr | DotDot | DotDotEq | Colon => false |
92a42be0 SL |
181 | } |
182 | } | |
183 | ||
7453a54e | 184 | pub fn to_ast_binop(&self) -> Option<BinOpKind> { |
9fa01778 | 185 | use AssocOp::*; |
92a42be0 | 186 | match *self { |
7453a54e SL |
187 | Less => Some(BinOpKind::Lt), |
188 | Greater => Some(BinOpKind::Gt), | |
189 | LessEqual => Some(BinOpKind::Le), | |
190 | GreaterEqual => Some(BinOpKind::Ge), | |
191 | Equal => Some(BinOpKind::Eq), | |
192 | NotEqual => Some(BinOpKind::Ne), | |
193 | Multiply => Some(BinOpKind::Mul), | |
194 | Divide => Some(BinOpKind::Div), | |
195 | Modulus => Some(BinOpKind::Rem), | |
196 | Add => Some(BinOpKind::Add), | |
197 | Subtract => Some(BinOpKind::Sub), | |
198 | ShiftLeft => Some(BinOpKind::Shl), | |
199 | ShiftRight => Some(BinOpKind::Shr), | |
200 | BitAnd => Some(BinOpKind::BitAnd), | |
201 | BitXor => Some(BinOpKind::BitXor), | |
202 | BitOr => Some(BinOpKind::BitOr), | |
203 | LAnd => Some(BinOpKind::And), | |
204 | LOr => Some(BinOpKind::Or), | |
dc9dc135 | 205 | Assign | AssignOp(_) | As | DotDot | DotDotEq | Colon => None |
92a42be0 SL |
206 | } |
207 | } | |
48663c56 XL |
208 | |
209 | /// This operator could be used to follow a block unambiguously. | |
210 | /// | |
211 | /// This is used for error recovery at the moment, providing a suggestion to wrap blocks with | |
212 | /// parentheses while having a high degree of confidence on the correctness of the suggestion. | |
213 | pub fn can_continue_expr_unambiguously(&self) -> bool { | |
214 | use AssocOp::*; | |
215 | match self { | |
216 | BitXor | // `{ 42 } ^ 3` | |
217 | Assign | // `{ 42 } = { 42 }` | |
218 | Divide | // `{ 42 } / 42` | |
219 | Modulus | // `{ 42 } % 2` | |
220 | ShiftRight | // `{ 42 } >> 2` | |
221 | LessEqual | // `{ 42 } <= 3` | |
222 | Greater | // `{ 42 } > 3` | |
223 | GreaterEqual | // `{ 42 } >= 3` | |
224 | AssignOp(_) | // `{ 42 } +=` | |
225 | LAnd | // `{ 42 } &&foo` | |
226 | As | // `{ 42 } as usize` | |
227 | // Equal | // `{ 42 } == { 42 }` Accepting these here would regress incorrect | |
228 | // NotEqual | // `{ 42 } != { 42 } struct literals parser recovery. | |
229 | Colon => true, // `{ 42 }: usize` | |
230 | _ => false, | |
231 | } | |
232 | } | |
92a42be0 | 233 | } |
ea8adc8c XL |
234 | |
235 | pub const PREC_RESET: i8 = -100; | |
236 | pub const PREC_CLOSURE: i8 = -40; | |
237 | pub const PREC_JUMP: i8 = -30; | |
238 | pub const PREC_RANGE: i8 = -10; | |
dc9dc135 | 239 | // The range 2..=14 is reserved for AssocOp binary operator precedences. |
ea8adc8c XL |
240 | pub const PREC_PREFIX: i8 = 50; |
241 | pub const PREC_POSTFIX: i8 = 60; | |
242 | pub const PREC_PAREN: i8 = 99; | |
243 | pub const PREC_FORCE_PAREN: i8 = 100; | |
244 | ||
8faf50e0 | 245 | #[derive(Debug, Clone, Copy)] |
2c00a5a8 XL |
246 | pub enum ExprPrecedence { |
247 | Closure, | |
248 | Break, | |
249 | Continue, | |
250 | Ret, | |
251 | Yield, | |
252 | ||
253 | Range, | |
254 | ||
255 | Binary(BinOpKind), | |
256 | ||
2c00a5a8 XL |
257 | Cast, |
258 | Type, | |
259 | ||
260 | Assign, | |
261 | AssignOp, | |
262 | ||
263 | Box, | |
264 | AddrOf, | |
dc9dc135 | 265 | Let, |
2c00a5a8 XL |
266 | Unary, |
267 | ||
268 | Call, | |
269 | MethodCall, | |
270 | Field, | |
2c00a5a8 XL |
271 | Index, |
272 | Try, | |
273 | InlineAsm, | |
274 | Mac, | |
275 | ||
276 | Array, | |
277 | Repeat, | |
278 | Tup, | |
279 | Lit, | |
280 | Path, | |
281 | Paren, | |
282 | If, | |
2c00a5a8 | 283 | While, |
2c00a5a8 XL |
284 | ForLoop, |
285 | Loop, | |
286 | Match, | |
287 | Block, | |
b7449926 | 288 | TryBlock, |
2c00a5a8 | 289 | Struct, |
8faf50e0 | 290 | Async, |
48663c56 | 291 | Await, |
0731742a | 292 | Err, |
2c00a5a8 | 293 | } |
ea8adc8c | 294 | |
2c00a5a8 XL |
295 | impl ExprPrecedence { |
296 | pub fn order(self) -> i8 { | |
297 | match self { | |
298 | ExprPrecedence::Closure => PREC_CLOSURE, | |
ea8adc8c | 299 | |
2c00a5a8 XL |
300 | ExprPrecedence::Break | |
301 | ExprPrecedence::Continue | | |
302 | ExprPrecedence::Ret | | |
303 | ExprPrecedence::Yield => PREC_JUMP, | |
ea8adc8c | 304 | |
2c00a5a8 XL |
305 | // `Range` claims to have higher precedence than `Assign`, but `x .. x = x` fails to |
306 | // parse, instead of parsing as `(x .. x) = x`. Giving `Range` a lower precedence | |
307 | // ensures that `pprust` will add parentheses in the right places to get the desired | |
308 | // parse. | |
309 | ExprPrecedence::Range => PREC_RANGE, | |
ea8adc8c | 310 | |
2c00a5a8 XL |
311 | // Binop-like expr kinds, handled by `AssocOp`. |
312 | ExprPrecedence::Binary(op) => AssocOp::from_ast_binop(op).precedence() as i8, | |
2c00a5a8 XL |
313 | ExprPrecedence::Cast => AssocOp::As.precedence() as i8, |
314 | ExprPrecedence::Type => AssocOp::Colon.precedence() as i8, | |
ea8adc8c | 315 | |
2c00a5a8 XL |
316 | ExprPrecedence::Assign | |
317 | ExprPrecedence::AssignOp => AssocOp::Assign.precedence() as i8, | |
ea8adc8c | 318 | |
2c00a5a8 XL |
319 | // Unary, prefix |
320 | ExprPrecedence::Box | | |
321 | ExprPrecedence::AddrOf | | |
dc9dc135 XL |
322 | // Here `let pats = expr` has `let pats =` as a "unary" prefix of `expr`. |
323 | // However, this is not exactly right. When `let _ = a` is the LHS of a binop we | |
324 | // need parens sometimes. E.g. we can print `(let _ = a) && b` as `let _ = a && b` | |
325 | // but we need to print `(let _ = a) < b` as-is with parens. | |
326 | ExprPrecedence::Let | | |
2c00a5a8 XL |
327 | ExprPrecedence::Unary => PREC_PREFIX, |
328 | ||
329 | // Unary, postfix | |
48663c56 | 330 | ExprPrecedence::Await | |
2c00a5a8 XL |
331 | ExprPrecedence::Call | |
332 | ExprPrecedence::MethodCall | | |
333 | ExprPrecedence::Field | | |
2c00a5a8 XL |
334 | ExprPrecedence::Index | |
335 | ExprPrecedence::Try | | |
336 | ExprPrecedence::InlineAsm | | |
337 | ExprPrecedence::Mac => PREC_POSTFIX, | |
338 | ||
339 | // Never need parens | |
340 | ExprPrecedence::Array | | |
341 | ExprPrecedence::Repeat | | |
342 | ExprPrecedence::Tup | | |
343 | ExprPrecedence::Lit | | |
344 | ExprPrecedence::Path | | |
345 | ExprPrecedence::Paren | | |
346 | ExprPrecedence::If | | |
2c00a5a8 | 347 | ExprPrecedence::While | |
2c00a5a8 XL |
348 | ExprPrecedence::ForLoop | |
349 | ExprPrecedence::Loop | | |
350 | ExprPrecedence::Match | | |
351 | ExprPrecedence::Block | | |
b7449926 | 352 | ExprPrecedence::TryBlock | |
8faf50e0 | 353 | ExprPrecedence::Async | |
0731742a XL |
354 | ExprPrecedence::Struct | |
355 | ExprPrecedence::Err => PREC_PAREN, | |
2c00a5a8 | 356 | } |
ea8adc8c XL |
357 | } |
358 | } | |
359 | ||
dc9dc135 | 360 | /// In `let p = e`, operators with precedence `<=` this one requires parenthesis in `e`. |
60c5eb7d | 361 | pub fn prec_let_scrutinee_needs_par() -> usize { |
dc9dc135 XL |
362 | AssocOp::LAnd.precedence() |
363 | } | |
364 | ||
365 | /// Suppose we have `let _ = e` and the `order` of `e`. | |
366 | /// Is the `order` such that `e` in `let _ = e` needs parenthesis when it is on the RHS? | |
367 | /// | |
368 | /// Conversely, suppose that we have `(let _ = a) OP b` and `order` is that of `OP`. | |
369 | /// Can we print this as `let _ = a OP b`? | |
370 | crate fn needs_par_as_let_scrutinee(order: i8) -> bool { | |
371 | order <= prec_let_scrutinee_needs_par() as i8 | |
372 | } | |
2c00a5a8 | 373 | |
0731742a XL |
374 | /// Expressions that syntactically contain an "exterior" struct literal i.e., not surrounded by any |
375 | /// parens or other delimiters, e.g., `X { y: 1 }`, `X { y: 1 }.method()`, `foo == X { y: 1 }` and | |
ea8adc8c XL |
376 | /// `X { y: 1 } == foo` all do, but `(X { y: 1 }) == foo` does not. |
377 | pub fn contains_exterior_struct_lit(value: &ast::Expr) -> bool { | |
e74abb32 | 378 | match value.kind { |
ea8adc8c XL |
379 | ast::ExprKind::Struct(..) => true, |
380 | ||
381 | ast::ExprKind::Assign(ref lhs, ref rhs) | | |
382 | ast::ExprKind::AssignOp(_, ref lhs, ref rhs) | | |
383 | ast::ExprKind::Binary(_, ref lhs, ref rhs) => { | |
384 | // X { y: 1 } + X { y: 2 } | |
385 | contains_exterior_struct_lit(&lhs) || contains_exterior_struct_lit(&rhs) | |
386 | } | |
416331ca | 387 | ast::ExprKind::Await(ref x) | |
ea8adc8c XL |
388 | ast::ExprKind::Unary(_, ref x) | |
389 | ast::ExprKind::Cast(ref x, _) | | |
390 | ast::ExprKind::Type(ref x, _) | | |
391 | ast::ExprKind::Field(ref x, _) | | |
ea8adc8c XL |
392 | ast::ExprKind::Index(ref x, _) => { |
393 | // &X { y: 1 }, X { y: 1 }.y | |
394 | contains_exterior_struct_lit(&x) | |
395 | } | |
396 | ||
397 | ast::ExprKind::MethodCall(.., ref exprs) => { | |
398 | // X { y: 1 }.bar(...) | |
399 | contains_exterior_struct_lit(&exprs[0]) | |
400 | } | |
401 | ||
402 | _ => false, | |
403 | } | |
404 | } |