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.
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};
12 use ast::{self, BinOpKind, ExprKind};
14 /// Associative operator with precedence.
16 /// This is the enum which specifies operator precedence and fixity to the parser.
17 #[derive(Debug, PartialEq, Eq)]
59 /// `?=` where ? is one of the BinOpToken
71 #[derive(Debug, PartialEq, Eq)]
73 /// The operator is left-associative
75 /// The operator is right-associative
77 /// The operator is not associative
82 /// Create a new AssocOP from a token
83 pub fn from_token(t: &Token) -> Option<AssocOp> {
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),
117 /// Create a new AssocOp from ast::BinOpKind.
118 pub fn from_ast_binop(op: BinOpKind) -> Self {
119 use self::AssocOp::*;
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,
142 /// Gets the precedence of this operator
143 pub fn precedence(&self) -> usize {
144 use self::AssocOp::*;
147 Multiply | Divide | Modulus => 13,
148 Add | Subtract => 12,
149 ShiftLeft | ShiftRight => 11,
153 Less | Greater | LessEqual | GreaterEqual | Equal | NotEqual => 7,
156 DotDot | DotDotEq => 4,
158 Assign | AssignOp(_) => 2,
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!
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
175 pub fn is_comparison(&self) -> bool {
176 use self::AssocOp::*;
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
185 pub fn is_assign_like(&self) -> bool {
186 use self::AssocOp::*;
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
195 pub fn to_ast_binop(&self) -> Option<BinOpKind> {
196 use self::AssocOp::*;
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
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;
231 pub fn expr_precedence(expr: &ast::Expr) -> i8 {
233 ExprKind::Closure(..) => PREC_CLOSURE,
235 ExprKind::Break(..) |
236 ExprKind::Continue(..) |
238 ExprKind::Yield(..) => PREC_JUMP,
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,
245 // Binop-like expr kinds, handled by `AssocOp`.
246 ExprKind::Binary(op, _, _) =>
247 AssocOp::from_ast_binop(op.node).precedence() as i8,
249 ExprKind::InPlace(..) => AssocOp::Inplace.precedence() as i8,
250 ExprKind::Cast(..) => AssocOp::As.precedence() as i8,
251 ExprKind::Type(..) => AssocOp::Colon.precedence() as i8,
253 ExprKind::Assign(..) |
254 ExprKind::AssignOp(..) => AssocOp::Assign.precedence() as i8,
258 ExprKind::AddrOf(..) |
259 ExprKind::Unary(..) => PREC_PREFIX,
263 ExprKind::MethodCall(..) |
264 ExprKind::Field(..) |
265 ExprKind::TupField(..) |
266 ExprKind::Index(..) |
268 ExprKind::InlineAsm(..) |
269 ExprKind::Mac(..) => PREC_POSTFIX,
272 ExprKind::Array(..) |
273 ExprKind::Repeat(..) |
277 ExprKind::Paren(..) |
279 ExprKind::IfLet(..) |
280 ExprKind::While(..) |
281 ExprKind::WhileLet(..) |
282 ExprKind::ForLoop(..) |
284 ExprKind::Match(..) |
285 ExprKind::Block(..) |
286 ExprKind::Catch(..) |
287 ExprKind::Struct(..) => PREC_PAREN,
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 {
296 ast::ExprKind::Struct(..) => true,
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)
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)
314 ast::ExprKind::MethodCall(.., ref exprs) => {
315 // X { y: 1 }.bar(...)
316 contains_exterior_struct_lit(&exprs[0])