]>
Commit | Line | Data |
---|---|---|
92a42be0 SL |
1 | // Copyright 2015 The Rust Project Developers. See the COPYRIGHT |
2 | // file at the top-level directory of this distribution and at | |
3 | // http://rust-lang.org/COPYRIGHT. | |
4 | // | |
5 | // Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or | |
6 | // http://www.apache.org/licenses/LICENSE-2.0> or the MIT license | |
7 | // <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your | |
8 | // option. This file may not be copied, modified, or distributed | |
9 | // except according to those terms. | |
476ff2be SL |
10 | use parse::token::{Token, BinOpToken}; |
11 | use symbol::keywords; | |
2c00a5a8 XL |
12 | use ast::{self, BinOpKind}; |
13 | ||
14 | use std::cmp::Ordering; | |
92a42be0 SL |
15 | |
16 | /// Associative operator with precedence. | |
17 | /// | |
18 | /// This is the enum which specifies operator precedence and fixity to the parser. | |
19 | #[derive(Debug, PartialEq, Eq)] | |
20 | pub enum AssocOp { | |
21 | /// `+` | |
22 | Add, | |
23 | /// `-` | |
24 | Subtract, | |
25 | /// `*` | |
26 | Multiply, | |
27 | /// `/` | |
28 | Divide, | |
29 | /// `%` | |
30 | Modulus, | |
31 | /// `&&` | |
32 | LAnd, | |
33 | /// `||` | |
34 | LOr, | |
35 | /// `^` | |
36 | BitXor, | |
37 | /// `&` | |
38 | BitAnd, | |
39 | /// `|` | |
40 | BitOr, | |
41 | /// `<<` | |
42 | ShiftLeft, | |
43 | /// `>>` | |
44 | ShiftRight, | |
45 | /// `==` | |
46 | Equal, | |
47 | /// `<` | |
48 | Less, | |
49 | /// `<=` | |
50 | LessEqual, | |
51 | /// `!=` | |
52 | NotEqual, | |
53 | /// `>` | |
54 | Greater, | |
55 | /// `>=` | |
56 | GreaterEqual, | |
57 | /// `=` | |
58 | Assign, | |
59 | /// `<-` | |
83c7162d | 60 | ObsoleteInPlace, |
92a42be0 SL |
61 | /// `?=` where ? is one of the BinOpToken |
62 | AssignOp(BinOpToken), | |
63 | /// `as` | |
64 | As, | |
65 | /// `..` range | |
9cc50fc6 | 66 | DotDot, |
ea8adc8c XL |
67 | /// `..=` range |
68 | DotDotEq, | |
9cc50fc6 SL |
69 | /// `:` |
70 | Colon, | |
92a42be0 SL |
71 | } |
72 | ||
73 | #[derive(Debug, PartialEq, Eq)] | |
74 | pub enum Fixity { | |
75 | /// The operator is left-associative | |
76 | Left, | |
77 | /// The operator is right-associative | |
78 | Right, | |
79 | /// The operator is not associative | |
80 | None | |
81 | } | |
82 | ||
83 | impl AssocOp { | |
84 | /// Create a new AssocOP from a token | |
85 | pub fn from_token(t: &Token) -> Option<AssocOp> { | |
86 | use self::AssocOp::*; | |
87 | match *t { | |
88 | Token::BinOpEq(k) => Some(AssignOp(k)), | |
83c7162d | 89 | Token::LArrow => Some(ObsoleteInPlace), |
92a42be0 SL |
90 | Token::Eq => Some(Assign), |
91 | Token::BinOp(BinOpToken::Star) => Some(Multiply), | |
92 | Token::BinOp(BinOpToken::Slash) => Some(Divide), | |
93 | Token::BinOp(BinOpToken::Percent) => Some(Modulus), | |
94 | Token::BinOp(BinOpToken::Plus) => Some(Add), | |
95 | Token::BinOp(BinOpToken::Minus) => Some(Subtract), | |
96 | Token::BinOp(BinOpToken::Shl) => Some(ShiftLeft), | |
97 | Token::BinOp(BinOpToken::Shr) => Some(ShiftRight), | |
98 | Token::BinOp(BinOpToken::And) => Some(BitAnd), | |
99 | Token::BinOp(BinOpToken::Caret) => Some(BitXor), | |
100 | Token::BinOp(BinOpToken::Or) => Some(BitOr), | |
101 | Token::Lt => Some(Less), | |
102 | Token::Le => Some(LessEqual), | |
103 | Token::Ge => Some(GreaterEqual), | |
104 | Token::Gt => Some(Greater), | |
105 | Token::EqEq => Some(Equal), | |
106 | Token::Ne => Some(NotEqual), | |
107 | Token::AndAnd => Some(LAnd), | |
108 | Token::OrOr => Some(LOr), | |
109 | Token::DotDot => Some(DotDot), | |
ea8adc8c | 110 | Token::DotDotEq => Some(DotDotEq), |
abe05a73 XL |
111 | // DotDotDot is no longer supported, but we need some way to display the error |
112 | Token::DotDotDot => Some(DotDotEq), | |
9cc50fc6 | 113 | Token::Colon => Some(Colon), |
92a42be0 SL |
114 | _ if t.is_keyword(keywords::As) => Some(As), |
115 | _ => None | |
116 | } | |
117 | } | |
118 | ||
7453a54e SL |
119 | /// Create a new AssocOp from ast::BinOpKind. |
120 | pub fn from_ast_binop(op: BinOpKind) -> Self { | |
92a42be0 SL |
121 | use self::AssocOp::*; |
122 | match op { | |
7453a54e SL |
123 | BinOpKind::Lt => Less, |
124 | BinOpKind::Gt => Greater, | |
125 | BinOpKind::Le => LessEqual, | |
126 | BinOpKind::Ge => GreaterEqual, | |
127 | BinOpKind::Eq => Equal, | |
128 | BinOpKind::Ne => NotEqual, | |
129 | BinOpKind::Mul => Multiply, | |
130 | BinOpKind::Div => Divide, | |
131 | BinOpKind::Rem => Modulus, | |
132 | BinOpKind::Add => Add, | |
133 | BinOpKind::Sub => Subtract, | |
134 | BinOpKind::Shl => ShiftLeft, | |
135 | BinOpKind::Shr => ShiftRight, | |
136 | BinOpKind::BitAnd => BitAnd, | |
137 | BinOpKind::BitXor => BitXor, | |
138 | BinOpKind::BitOr => BitOr, | |
139 | BinOpKind::And => LAnd, | |
140 | BinOpKind::Or => LOr | |
92a42be0 SL |
141 | } |
142 | } | |
143 | ||
144 | /// Gets the precedence of this operator | |
145 | pub fn precedence(&self) -> usize { | |
146 | use self::AssocOp::*; | |
147 | match *self { | |
9cc50fc6 | 148 | As | Colon => 14, |
92a42be0 SL |
149 | Multiply | Divide | Modulus => 13, |
150 | Add | Subtract => 12, | |
151 | ShiftLeft | ShiftRight => 11, | |
152 | BitAnd => 10, | |
153 | BitXor => 9, | |
154 | BitOr => 8, | |
155 | Less | Greater | LessEqual | GreaterEqual | Equal | NotEqual => 7, | |
156 | LAnd => 6, | |
157 | LOr => 5, | |
ea8adc8c | 158 | DotDot | DotDotEq => 4, |
83c7162d | 159 | ObsoleteInPlace => 3, |
92a42be0 SL |
160 | Assign | AssignOp(_) => 2, |
161 | } | |
162 | } | |
163 | ||
164 | /// Gets the fixity of this operator | |
165 | pub fn fixity(&self) -> Fixity { | |
166 | use self::AssocOp::*; | |
167 | // NOTE: it is a bug to have an operators that has same precedence but different fixities! | |
168 | match *self { | |
83c7162d | 169 | ObsoleteInPlace | Assign | AssignOp(_) => Fixity::Right, |
92a42be0 SL |
170 | As | Multiply | Divide | Modulus | Add | Subtract | ShiftLeft | ShiftRight | BitAnd | |
171 | BitXor | BitOr | Less | Greater | LessEqual | GreaterEqual | Equal | NotEqual | | |
9cc50fc6 | 172 | LAnd | LOr | Colon => Fixity::Left, |
ea8adc8c | 173 | DotDot | DotDotEq => Fixity::None |
92a42be0 SL |
174 | } |
175 | } | |
176 | ||
177 | pub fn is_comparison(&self) -> bool { | |
178 | use self::AssocOp::*; | |
179 | match *self { | |
180 | Less | Greater | LessEqual | GreaterEqual | Equal | NotEqual => true, | |
83c7162d XL |
181 | ObsoleteInPlace | Assign | AssignOp(_) | As | Multiply | Divide | Modulus | Add | |
182 | Subtract | ShiftLeft | ShiftRight | BitAnd | BitXor | BitOr | LAnd | LOr | | |
ea8adc8c | 183 | DotDot | DotDotEq | Colon => false |
92a42be0 SL |
184 | } |
185 | } | |
186 | ||
187 | pub fn is_assign_like(&self) -> bool { | |
188 | use self::AssocOp::*; | |
189 | match *self { | |
83c7162d | 190 | Assign | AssignOp(_) | ObsoleteInPlace => true, |
92a42be0 SL |
191 | Less | Greater | LessEqual | GreaterEqual | Equal | NotEqual | As | Multiply | Divide | |
192 | Modulus | Add | Subtract | ShiftLeft | ShiftRight | BitAnd | BitXor | BitOr | LAnd | | |
ea8adc8c | 193 | LOr | DotDot | DotDotEq | Colon => false |
92a42be0 SL |
194 | } |
195 | } | |
196 | ||
7453a54e | 197 | pub fn to_ast_binop(&self) -> Option<BinOpKind> { |
92a42be0 SL |
198 | use self::AssocOp::*; |
199 | match *self { | |
7453a54e SL |
200 | Less => Some(BinOpKind::Lt), |
201 | Greater => Some(BinOpKind::Gt), | |
202 | LessEqual => Some(BinOpKind::Le), | |
203 | GreaterEqual => Some(BinOpKind::Ge), | |
204 | Equal => Some(BinOpKind::Eq), | |
205 | NotEqual => Some(BinOpKind::Ne), | |
206 | Multiply => Some(BinOpKind::Mul), | |
207 | Divide => Some(BinOpKind::Div), | |
208 | Modulus => Some(BinOpKind::Rem), | |
209 | Add => Some(BinOpKind::Add), | |
210 | Subtract => Some(BinOpKind::Sub), | |
211 | ShiftLeft => Some(BinOpKind::Shl), | |
212 | ShiftRight => Some(BinOpKind::Shr), | |
213 | BitAnd => Some(BinOpKind::BitAnd), | |
214 | BitXor => Some(BinOpKind::BitXor), | |
215 | BitOr => Some(BinOpKind::BitOr), | |
216 | LAnd => Some(BinOpKind::And), | |
217 | LOr => Some(BinOpKind::Or), | |
83c7162d | 218 | ObsoleteInPlace | Assign | AssignOp(_) | As | DotDot | DotDotEq | Colon => None |
92a42be0 SL |
219 | } |
220 | } | |
92a42be0 | 221 | } |
ea8adc8c XL |
222 | |
223 | pub const PREC_RESET: i8 = -100; | |
224 | pub const PREC_CLOSURE: i8 = -40; | |
225 | pub const PREC_JUMP: i8 = -30; | |
226 | pub const PREC_RANGE: i8 = -10; | |
227 | // The range 2 ... 14 is reserved for AssocOp binary operator precedences. | |
228 | pub const PREC_PREFIX: i8 = 50; | |
229 | pub const PREC_POSTFIX: i8 = 60; | |
230 | pub const PREC_PAREN: i8 = 99; | |
231 | pub const PREC_FORCE_PAREN: i8 = 100; | |
232 | ||
2c00a5a8 XL |
233 | #[derive(Debug, Clone, Copy, PartialEq, Eq)] |
234 | pub enum ExprPrecedence { | |
235 | Closure, | |
236 | Break, | |
237 | Continue, | |
238 | Ret, | |
239 | Yield, | |
240 | ||
241 | Range, | |
242 | ||
243 | Binary(BinOpKind), | |
244 | ||
83c7162d | 245 | ObsoleteInPlace, |
2c00a5a8 XL |
246 | Cast, |
247 | Type, | |
248 | ||
249 | Assign, | |
250 | AssignOp, | |
251 | ||
252 | Box, | |
253 | AddrOf, | |
254 | Unary, | |
255 | ||
256 | Call, | |
257 | MethodCall, | |
258 | Field, | |
2c00a5a8 XL |
259 | Index, |
260 | Try, | |
261 | InlineAsm, | |
262 | Mac, | |
263 | ||
264 | Array, | |
265 | Repeat, | |
266 | Tup, | |
267 | Lit, | |
268 | Path, | |
269 | Paren, | |
270 | If, | |
271 | IfLet, | |
272 | While, | |
273 | WhileLet, | |
274 | ForLoop, | |
275 | Loop, | |
276 | Match, | |
277 | Block, | |
278 | Catch, | |
279 | Struct, | |
280 | } | |
ea8adc8c | 281 | |
2c00a5a8 XL |
282 | impl PartialOrd for ExprPrecedence { |
283 | fn partial_cmp(&self, other: &Self) -> Option<Ordering> { | |
284 | Some(self.order().cmp(&other.order())) | |
285 | } | |
286 | } | |
ea8adc8c | 287 | |
2c00a5a8 XL |
288 | impl Ord for ExprPrecedence { |
289 | fn cmp(&self, other: &Self) -> Ordering { | |
290 | self.order().cmp(&other.order()) | |
291 | } | |
292 | } | |
ea8adc8c | 293 | |
2c00a5a8 XL |
294 | impl ExprPrecedence { |
295 | pub fn order(self) -> i8 { | |
296 | match self { | |
297 | ExprPrecedence::Closure => PREC_CLOSURE, | |
ea8adc8c | 298 | |
2c00a5a8 XL |
299 | ExprPrecedence::Break | |
300 | ExprPrecedence::Continue | | |
301 | ExprPrecedence::Ret | | |
302 | ExprPrecedence::Yield => PREC_JUMP, | |
ea8adc8c | 303 | |
2c00a5a8 XL |
304 | // `Range` claims to have higher precedence than `Assign`, but `x .. x = x` fails to |
305 | // parse, instead of parsing as `(x .. x) = x`. Giving `Range` a lower precedence | |
306 | // ensures that `pprust` will add parentheses in the right places to get the desired | |
307 | // parse. | |
308 | ExprPrecedence::Range => PREC_RANGE, | |
ea8adc8c | 309 | |
2c00a5a8 XL |
310 | // Binop-like expr kinds, handled by `AssocOp`. |
311 | ExprPrecedence::Binary(op) => AssocOp::from_ast_binop(op).precedence() as i8, | |
83c7162d | 312 | ExprPrecedence::ObsoleteInPlace => AssocOp::ObsoleteInPlace.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 | | |
322 | ExprPrecedence::Unary => PREC_PREFIX, | |
323 | ||
324 | // Unary, postfix | |
325 | ExprPrecedence::Call | | |
326 | ExprPrecedence::MethodCall | | |
327 | ExprPrecedence::Field | | |
2c00a5a8 XL |
328 | ExprPrecedence::Index | |
329 | ExprPrecedence::Try | | |
330 | ExprPrecedence::InlineAsm | | |
331 | ExprPrecedence::Mac => PREC_POSTFIX, | |
332 | ||
333 | // Never need parens | |
334 | ExprPrecedence::Array | | |
335 | ExprPrecedence::Repeat | | |
336 | ExprPrecedence::Tup | | |
337 | ExprPrecedence::Lit | | |
338 | ExprPrecedence::Path | | |
339 | ExprPrecedence::Paren | | |
340 | ExprPrecedence::If | | |
341 | ExprPrecedence::IfLet | | |
342 | ExprPrecedence::While | | |
343 | ExprPrecedence::WhileLet | | |
344 | ExprPrecedence::ForLoop | | |
345 | ExprPrecedence::Loop | | |
346 | ExprPrecedence::Match | | |
347 | ExprPrecedence::Block | | |
348 | ExprPrecedence::Catch | | |
349 | ExprPrecedence::Struct => PREC_PAREN, | |
350 | } | |
ea8adc8c XL |
351 | } |
352 | } | |
353 | ||
2c00a5a8 | 354 | |
ea8adc8c XL |
355 | /// Expressions that syntactically contain an "exterior" struct literal i.e. not surrounded by any |
356 | /// parens or other delimiters, e.g. `X { y: 1 }`, `X { y: 1 }.method()`, `foo == X { y: 1 }` and | |
357 | /// `X { y: 1 } == foo` all do, but `(X { y: 1 }) == foo` does not. | |
358 | pub fn contains_exterior_struct_lit(value: &ast::Expr) -> bool { | |
359 | match value.node { | |
360 | ast::ExprKind::Struct(..) => true, | |
361 | ||
362 | ast::ExprKind::Assign(ref lhs, ref rhs) | | |
363 | ast::ExprKind::AssignOp(_, ref lhs, ref rhs) | | |
364 | ast::ExprKind::Binary(_, ref lhs, ref rhs) => { | |
365 | // X { y: 1 } + X { y: 2 } | |
366 | contains_exterior_struct_lit(&lhs) || contains_exterior_struct_lit(&rhs) | |
367 | } | |
368 | ast::ExprKind::Unary(_, ref x) | | |
369 | ast::ExprKind::Cast(ref x, _) | | |
370 | ast::ExprKind::Type(ref x, _) | | |
371 | ast::ExprKind::Field(ref x, _) | | |
ea8adc8c XL |
372 | ast::ExprKind::Index(ref x, _) => { |
373 | // &X { y: 1 }, X { y: 1 }.y | |
374 | contains_exterior_struct_lit(&x) | |
375 | } | |
376 | ||
377 | ast::ExprKind::MethodCall(.., ref exprs) => { | |
378 | // X { y: 1 }.bar(...) | |
379 | contains_exterior_struct_lit(&exprs[0]) | |
380 | } | |
381 | ||
382 | _ => false, | |
383 | } | |
384 | } |