]> git.lizzy.rs Git - rust.git/blob - src/libsyntax/util/parser.rs
Using `...` in expressions is now an error
[rust.git] / src / libsyntax / util / parser.rs
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.
10 use parse::token::{Token, BinOpToken};
11 use symbol::keywords;
12 use ast::{self, BinOpKind, ExprKind};
13
14 /// Associative operator with precedence.
15 ///
16 /// This is the enum which specifies operator precedence and fixity to the parser.
17 #[derive(Debug, PartialEq, Eq)]
18 pub enum AssocOp {
19     /// `+`
20     Add,
21     /// `-`
22     Subtract,
23     /// `*`
24     Multiply,
25     /// `/`
26     Divide,
27     /// `%`
28     Modulus,
29     /// `&&`
30     LAnd,
31     /// `||`
32     LOr,
33     /// `^`
34     BitXor,
35     /// `&`
36     BitAnd,
37     /// `|`
38     BitOr,
39     /// `<<`
40     ShiftLeft,
41     /// `>>`
42     ShiftRight,
43     /// `==`
44     Equal,
45     /// `<`
46     Less,
47     /// `<=`
48     LessEqual,
49     /// `!=`
50     NotEqual,
51     /// `>`
52     Greater,
53     /// `>=`
54     GreaterEqual,
55     /// `=`
56     Assign,
57     /// `<-`
58     Inplace,
59     /// `?=` where ? is one of the BinOpToken
60     AssignOp(BinOpToken),
61     /// `as`
62     As,
63     /// `..` range
64     DotDot,
65     /// `..=` range
66     DotDotEq,
67     /// `:`
68     Colon,
69 }
70
71 #[derive(Debug, PartialEq, Eq)]
72 pub enum Fixity {
73     /// The operator is left-associative
74     Left,
75     /// The operator is right-associative
76     Right,
77     /// The operator is not associative
78     None
79 }
80
81 impl AssocOp {
82     /// Create a new AssocOP from a token
83     pub fn from_token(t: &Token) -> Option<AssocOp> {
84         use self::AssocOp::*;
85         match *t {
86             Token::BinOpEq(k) => Some(AssignOp(k)),
87             Token::LArrow => Some(Inplace),
88             Token::Eq => Some(Assign),
89             Token::BinOp(BinOpToken::Star) => Some(Multiply),
90             Token::BinOp(BinOpToken::Slash) => Some(Divide),
91             Token::BinOp(BinOpToken::Percent) => Some(Modulus),
92             Token::BinOp(BinOpToken::Plus) => Some(Add),
93             Token::BinOp(BinOpToken::Minus) => Some(Subtract),
94             Token::BinOp(BinOpToken::Shl) => Some(ShiftLeft),
95             Token::BinOp(BinOpToken::Shr) => Some(ShiftRight),
96             Token::BinOp(BinOpToken::And) => Some(BitAnd),
97             Token::BinOp(BinOpToken::Caret) => Some(BitXor),
98             Token::BinOp(BinOpToken::Or) => Some(BitOr),
99             Token::Lt => Some(Less),
100             Token::Le => Some(LessEqual),
101             Token::Ge => Some(GreaterEqual),
102             Token::Gt => Some(Greater),
103             Token::EqEq => Some(Equal),
104             Token::Ne => Some(NotEqual),
105             Token::AndAnd => Some(LAnd),
106             Token::OrOr => Some(LOr),
107             Token::DotDot => Some(DotDot),
108             Token::DotDotEq => Some(DotDotEq),
109             // DotDotDot is no longer supported, but we need some way to display the error
110             Token::DotDotDot => Some(DotDotEq),
111             Token::Colon => Some(Colon),
112             _ if t.is_keyword(keywords::As) => Some(As),
113             _ => None
114         }
115     }
116
117     /// Create a new AssocOp from ast::BinOpKind.
118     pub fn from_ast_binop(op: BinOpKind) -> Self {
119         use self::AssocOp::*;
120         match op {
121             BinOpKind::Lt => Less,
122             BinOpKind::Gt => Greater,
123             BinOpKind::Le => LessEqual,
124             BinOpKind::Ge => GreaterEqual,
125             BinOpKind::Eq => Equal,
126             BinOpKind::Ne => NotEqual,
127             BinOpKind::Mul => Multiply,
128             BinOpKind::Div => Divide,
129             BinOpKind::Rem => Modulus,
130             BinOpKind::Add => Add,
131             BinOpKind::Sub => Subtract,
132             BinOpKind::Shl => ShiftLeft,
133             BinOpKind::Shr => ShiftRight,
134             BinOpKind::BitAnd => BitAnd,
135             BinOpKind::BitXor => BitXor,
136             BinOpKind::BitOr => BitOr,
137             BinOpKind::And => LAnd,
138             BinOpKind::Or => LOr
139         }
140     }
141
142     /// Gets the precedence of this operator
143     pub fn precedence(&self) -> usize {
144         use self::AssocOp::*;
145         match *self {
146             As | Colon => 14,
147             Multiply | Divide | Modulus => 13,
148             Add | Subtract => 12,
149             ShiftLeft | ShiftRight => 11,
150             BitAnd => 10,
151             BitXor => 9,
152             BitOr => 8,
153             Less | Greater | LessEqual | GreaterEqual | Equal | NotEqual => 7,
154             LAnd => 6,
155             LOr => 5,
156             DotDot | DotDotEq => 4,
157             Inplace => 3,
158             Assign | AssignOp(_) => 2,
159         }
160     }
161
162     /// Gets the fixity of this operator
163     pub fn fixity(&self) -> Fixity {
164         use self::AssocOp::*;
165         // NOTE: it is a bug to have an operators that has same precedence but different fixities!
166         match *self {
167             Inplace | Assign | AssignOp(_) => Fixity::Right,
168             As | Multiply | Divide | Modulus | Add | Subtract | ShiftLeft | ShiftRight | BitAnd |
169             BitXor | BitOr | Less | Greater | LessEqual | GreaterEqual | Equal | NotEqual |
170             LAnd | LOr | Colon => Fixity::Left,
171             DotDot | DotDotEq => Fixity::None
172         }
173     }
174
175     pub fn is_comparison(&self) -> bool {
176         use self::AssocOp::*;
177         match *self {
178             Less | Greater | LessEqual | GreaterEqual | Equal | NotEqual => true,
179             Inplace | Assign | AssignOp(_) | As | Multiply | Divide | Modulus | Add | Subtract |
180             ShiftLeft | ShiftRight | BitAnd | BitXor | BitOr | LAnd | LOr |
181             DotDot | DotDotEq | Colon => false
182         }
183     }
184
185     pub fn is_assign_like(&self) -> bool {
186         use self::AssocOp::*;
187         match *self {
188             Assign | AssignOp(_) | Inplace => true,
189             Less | Greater | LessEqual | GreaterEqual | Equal | NotEqual | As | Multiply | Divide |
190             Modulus | Add | Subtract | ShiftLeft | ShiftRight | BitAnd | BitXor | BitOr | LAnd |
191             LOr | DotDot | DotDotEq | Colon => false
192         }
193     }
194
195     pub fn to_ast_binop(&self) -> Option<BinOpKind> {
196         use self::AssocOp::*;
197         match *self {
198             Less => Some(BinOpKind::Lt),
199             Greater => Some(BinOpKind::Gt),
200             LessEqual => Some(BinOpKind::Le),
201             GreaterEqual => Some(BinOpKind::Ge),
202             Equal => Some(BinOpKind::Eq),
203             NotEqual => Some(BinOpKind::Ne),
204             Multiply => Some(BinOpKind::Mul),
205             Divide => Some(BinOpKind::Div),
206             Modulus => Some(BinOpKind::Rem),
207             Add => Some(BinOpKind::Add),
208             Subtract => Some(BinOpKind::Sub),
209             ShiftLeft => Some(BinOpKind::Shl),
210             ShiftRight => Some(BinOpKind::Shr),
211             BitAnd => Some(BinOpKind::BitAnd),
212             BitXor => Some(BinOpKind::BitXor),
213             BitOr => Some(BinOpKind::BitOr),
214             LAnd => Some(BinOpKind::And),
215             LOr => Some(BinOpKind::Or),
216             Inplace | Assign | AssignOp(_) | As | DotDot | DotDotEq | Colon => None
217         }
218     }
219 }
220
221 pub const PREC_RESET: i8 = -100;
222 pub const PREC_CLOSURE: i8 = -40;
223 pub const PREC_JUMP: i8 = -30;
224 pub const PREC_RANGE: i8 = -10;
225 // The range 2 ... 14 is reserved for AssocOp binary operator precedences.
226 pub const PREC_PREFIX: i8 = 50;
227 pub const PREC_POSTFIX: i8 = 60;
228 pub const PREC_PAREN: i8 = 99;
229 pub const PREC_FORCE_PAREN: i8 = 100;
230
231 pub fn expr_precedence(expr: &ast::Expr) -> i8 {
232     match expr.node {
233         ExprKind::Closure(..) => PREC_CLOSURE,
234
235         ExprKind::Break(..) |
236         ExprKind::Continue(..) |
237         ExprKind::Ret(..) |
238         ExprKind::Yield(..) => PREC_JUMP,
239
240         // `Range` claims to have higher precedence than `Assign`, but `x .. x = x` fails to parse,
241         // instead of parsing as `(x .. x) = x`.  Giving `Range` a lower precedence ensures that
242         // `pprust` will add parentheses in the right places to get the desired parse.
243         ExprKind::Range(..) => PREC_RANGE,
244
245         // Binop-like expr kinds, handled by `AssocOp`.
246         ExprKind::Binary(op, _, _) =>
247             AssocOp::from_ast_binop(op.node).precedence() as i8,
248
249         ExprKind::InPlace(..) => AssocOp::Inplace.precedence() as i8,
250         ExprKind::Cast(..) => AssocOp::As.precedence() as i8,
251         ExprKind::Type(..) => AssocOp::Colon.precedence() as i8,
252
253         ExprKind::Assign(..) |
254         ExprKind::AssignOp(..) => AssocOp::Assign.precedence() as i8,
255
256         // Unary, prefix
257         ExprKind::Box(..) |
258         ExprKind::AddrOf(..) |
259         ExprKind::Unary(..) => PREC_PREFIX,
260
261         // Unary, postfix
262         ExprKind::Call(..) |
263         ExprKind::MethodCall(..) |
264         ExprKind::Field(..) |
265         ExprKind::TupField(..) |
266         ExprKind::Index(..) |
267         ExprKind::Try(..) |
268         ExprKind::InlineAsm(..) |
269         ExprKind::Mac(..) => PREC_POSTFIX,
270
271         // Never need parens
272         ExprKind::Array(..) |
273         ExprKind::Repeat(..) |
274         ExprKind::Tup(..) |
275         ExprKind::Lit(..) |
276         ExprKind::Path(..) |
277         ExprKind::Paren(..) |
278         ExprKind::If(..) |
279         ExprKind::IfLet(..) |
280         ExprKind::While(..) |
281         ExprKind::WhileLet(..) |
282         ExprKind::ForLoop(..) |
283         ExprKind::Loop(..) |
284         ExprKind::Match(..) |
285         ExprKind::Block(..) |
286         ExprKind::Catch(..) |
287         ExprKind::Struct(..) => PREC_PAREN,
288     }
289 }
290
291 /// Expressions that syntactically contain an "exterior" struct literal i.e. not surrounded by any
292 /// parens or other delimiters, e.g. `X { y: 1 }`, `X { y: 1 }.method()`, `foo == X { y: 1 }` and
293 /// `X { y: 1 } == foo` all do, but `(X { y: 1 }) == foo` does not.
294 pub fn contains_exterior_struct_lit(value: &ast::Expr) -> bool {
295     match value.node {
296         ast::ExprKind::Struct(..) => true,
297
298         ast::ExprKind::Assign(ref lhs, ref rhs) |
299         ast::ExprKind::AssignOp(_, ref lhs, ref rhs) |
300         ast::ExprKind::Binary(_, ref lhs, ref rhs) => {
301             // X { y: 1 } + X { y: 2 }
302             contains_exterior_struct_lit(&lhs) || contains_exterior_struct_lit(&rhs)
303         }
304         ast::ExprKind::Unary(_, ref x) |
305         ast::ExprKind::Cast(ref x, _) |
306         ast::ExprKind::Type(ref x, _) |
307         ast::ExprKind::Field(ref x, _) |
308         ast::ExprKind::TupField(ref x, _) |
309         ast::ExprKind::Index(ref x, _) => {
310             // &X { y: 1 }, X { y: 1 }.y
311             contains_exterior_struct_lit(&x)
312         }
313
314         ast::ExprKind::MethodCall(.., ref exprs) => {
315             // X { y: 1 }.bar(...)
316             contains_exterior_struct_lit(&exprs[0])
317         }
318
319         _ => false,
320     }
321 }