Skip to content

Commit 471f5a1

Browse files
committed
Generalise associative operator parsing
This commit generalises parsing of associative operators from left-associative only (with some ugly hacks to support right-associative assignment) to properly left/right-associative operators. Parsing still is not general enough to handle non-associative, non-highest-precedence prefix or non-highest-precedence postfix operators (e.g. `..` range syntax), though. That should be fixed in the future. Lastly, this commit adds support for parsing right-associative `<-` (left arrow) operator with precedence higher than assignment as the operator for placement-in feature.
1 parent 540fd3a commit 471f5a1

File tree

9 files changed

+334
-194
lines changed

9 files changed

+334
-194
lines changed

src/libsyntax/ast_util.rs

Lines changed: 0 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -242,26 +242,6 @@ pub fn struct_field_visibility(field: ast::StructField) -> Visibility {
242242
}
243243
}
244244

245-
/// Maps a binary operator to its precedence
246-
pub fn operator_prec(op: ast::BinOp_) -> usize {
247-
match op {
248-
// 'as' sits here with 12
249-
BiMul | BiDiv | BiRem => 11,
250-
BiAdd | BiSub => 10,
251-
BiShl | BiShr => 9,
252-
BiBitAnd => 8,
253-
BiBitXor => 7,
254-
BiBitOr => 6,
255-
BiLt | BiLe | BiGe | BiGt | BiEq | BiNe => 3,
256-
BiAnd => 2,
257-
BiOr => 1
258-
}
259-
}
260-
261-
/// Precedence of the `as` operator, which is a binary operator
262-
/// not appearing in the prior table.
263-
pub const AS_PREC: usize = 12;
264-
265245
pub fn empty_generics() -> Generics {
266246
Generics {
267247
lifetimes: Vec::new(),

src/libsyntax/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,7 @@ pub mod util {
6666
#[cfg(test)]
6767
pub mod parser_testing;
6868
pub mod small_vector;
69+
pub mod parser;
6970
}
7071

7172
pub mod diagnostics {

src/libsyntax/parse/parser.rs

Lines changed: 132 additions & 131 deletions
Large diffs are not rendered by default.

src/libsyntax/print/pprust.rs

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ use abi;
1414
use ast;
1515
use ast::{RegionTyParamBound, TraitTyParamBound, TraitBoundModifier};
1616
use ast_util;
17+
use util::parser::AssocOp;
1718
use attr;
1819
use owned_slice::OwnedSlice;
1920
use attr::{AttrMetaMethods, AttributeMethods};
@@ -445,7 +446,8 @@ fn needs_parentheses(expr: &ast::Expr) -> bool {
445446
match expr.node {
446447
ast::ExprAssign(..) | ast::ExprBinary(..) |
447448
ast::ExprClosure(..) |
448-
ast::ExprAssignOp(..) | ast::ExprCast(..) => true,
449+
ast::ExprAssignOp(..) | ast::ExprCast(..) |
450+
ast::ExprInPlace(..) => true,
449451
_ => false,
450452
}
451453
}
@@ -1776,8 +1778,8 @@ impl<'a> State<'a> {
17761778
binop: ast::BinOp) -> bool {
17771779
match sub_expr.node {
17781780
ast::ExprBinary(ref sub_op, _, _) => {
1779-
if ast_util::operator_prec(sub_op.node) <
1780-
ast_util::operator_prec(binop.node) {
1781+
if AssocOp::from_ast_binop(sub_op.node).precedence() <
1782+
AssocOp::from_ast_binop(binop.node).precedence() {
17811783
true
17821784
} else {
17831785
false
@@ -1802,10 +1804,10 @@ impl<'a> State<'a> {
18021804
fn print_expr_in_place(&mut self,
18031805
place: &ast::Expr,
18041806
expr: &ast::Expr) -> io::Result<()> {
1805-
try!(self.word_space("in"));
1806-
try!(self.print_expr(place));
1807+
try!(self.print_expr_maybe_paren(place));
18071808
try!(space(&mut self.s));
1808-
self.print_expr(expr)
1809+
try!(self.word_space("<-"));
1810+
self.print_expr_maybe_paren(expr)
18091811
}
18101812

18111813
fn print_expr_vec(&mut self, exprs: &[P<ast::Expr>]) -> io::Result<()> {

src/libsyntax/util/parser.rs

Lines changed: 191 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,191 @@
1+
use parse::token::{Token, BinOpToken, keywords};
2+
use ast;
3+
4+
/// Associative operator with precedence.
5+
///
6+
/// This is the enum which specifies operator precedence and fixity to the parser.
7+
#[derive(Debug, PartialEq, Eq)]
8+
pub enum AssocOp {
9+
/// `+`
10+
Add,
11+
/// `-`
12+
Subtract,
13+
/// `*`
14+
Multiply,
15+
/// `/`
16+
Divide,
17+
/// `%`
18+
Modulus,
19+
/// `&&`
20+
LAnd,
21+
/// `||`
22+
LOr,
23+
/// `^`
24+
BitXor,
25+
/// `&`
26+
BitAnd,
27+
/// `|`
28+
BitOr,
29+
/// `<<`
30+
ShiftLeft,
31+
/// `>>`
32+
ShiftRight,
33+
/// `==`
34+
Equal,
35+
/// `<`
36+
Less,
37+
/// `<=`
38+
LessEqual,
39+
/// `!=`
40+
NotEqual,
41+
/// `>`
42+
Greater,
43+
/// `>=`
44+
GreaterEqual,
45+
/// `=`
46+
Assign,
47+
/// `<-`
48+
Inplace,
49+
/// `?=` where ? is one of the BinOpToken
50+
AssignOp(BinOpToken),
51+
/// `as`
52+
As,
53+
/// `..` range
54+
DotDot
55+
}
56+
57+
#[derive(Debug, PartialEq, Eq)]
58+
pub enum Fixity {
59+
/// The operator is left-associative
60+
Left,
61+
/// The operator is right-associative
62+
Right,
63+
/// The operator is not associative
64+
None
65+
}
66+
67+
impl AssocOp {
68+
/// Create a new AssocOP from a token
69+
pub fn from_token(t: &Token) -> Option<AssocOp> {
70+
use self::AssocOp::*;
71+
match *t {
72+
Token::BinOpEq(k) => Some(AssignOp(k)),
73+
Token::LArrow => Some(Inplace),
74+
Token::Eq => Some(Assign),
75+
Token::BinOp(BinOpToken::Star) => Some(Multiply),
76+
Token::BinOp(BinOpToken::Slash) => Some(Divide),
77+
Token::BinOp(BinOpToken::Percent) => Some(Modulus),
78+
Token::BinOp(BinOpToken::Plus) => Some(Add),
79+
Token::BinOp(BinOpToken::Minus) => Some(Subtract),
80+
Token::BinOp(BinOpToken::Shl) => Some(ShiftLeft),
81+
Token::BinOp(BinOpToken::Shr) => Some(ShiftRight),
82+
Token::BinOp(BinOpToken::And) => Some(BitAnd),
83+
Token::BinOp(BinOpToken::Caret) => Some(BitXor),
84+
Token::BinOp(BinOpToken::Or) => Some(BitOr),
85+
Token::Lt => Some(Less),
86+
Token::Le => Some(LessEqual),
87+
Token::Ge => Some(GreaterEqual),
88+
Token::Gt => Some(Greater),
89+
Token::EqEq => Some(Equal),
90+
Token::Ne => Some(NotEqual),
91+
Token::AndAnd => Some(LAnd),
92+
Token::OrOr => Some(LOr),
93+
Token::DotDot => Some(DotDot),
94+
_ if t.is_keyword(keywords::As) => Some(As),
95+
_ => None
96+
}
97+
}
98+
99+
/// Create a new AssocOp from ast::BinOp_.
100+
pub fn from_ast_binop(op: ast::BinOp_) -> Self {
101+
use self::AssocOp::*;
102+
match op {
103+
ast::BiLt => Less,
104+
ast::BiGt => Greater,
105+
ast::BiLe => LessEqual,
106+
ast::BiGe => GreaterEqual,
107+
ast::BiEq => Equal,
108+
ast::BiNe => NotEqual,
109+
ast::BiMul => Multiply,
110+
ast::BiDiv => Divide,
111+
ast::BiRem => Modulus,
112+
ast::BiAdd => Add,
113+
ast::BiSub => Subtract,
114+
ast::BiShl => ShiftLeft,
115+
ast::BiShr => ShiftRight,
116+
ast::BiBitAnd => BitAnd,
117+
ast::BiBitXor => BitXor,
118+
ast::BiBitOr => BitOr,
119+
ast::BiAnd => LAnd,
120+
ast::BiOr => LOr
121+
}
122+
}
123+
124+
/// Gets the precedence of this operator
125+
pub fn precedence(&self) -> usize {
126+
use self::AssocOp::*;
127+
match *self {
128+
As => 14,
129+
Multiply | Divide | Modulus => 13,
130+
Add | Subtract => 12,
131+
ShiftLeft | ShiftRight => 11,
132+
BitAnd => 10,
133+
BitXor => 9,
134+
BitOr => 8,
135+
Less | Greater | LessEqual | GreaterEqual | Equal | NotEqual => 7,
136+
LAnd => 6,
137+
LOr => 5,
138+
DotDot => 4,
139+
Inplace => 3,
140+
Assign | AssignOp(_) => 2,
141+
}
142+
}
143+
144+
/// Gets the fixity of this operator
145+
pub fn fixity(&self) -> Fixity {
146+
use self::AssocOp::*;
147+
// NOTE: it is a bug to have an operators that has same precedence but different fixities!
148+
match *self {
149+
Inplace | Assign | AssignOp(_) => Fixity::Right,
150+
As | Multiply | Divide | Modulus | Add | Subtract | ShiftLeft | ShiftRight | BitAnd |
151+
BitXor | BitOr | Less | Greater | LessEqual | GreaterEqual | Equal | NotEqual |
152+
LAnd | LOr => Fixity::Left,
153+
DotDot => Fixity::None
154+
}
155+
}
156+
157+
pub fn is_comparison(&self) -> bool {
158+
use self::AssocOp::*;
159+
match *self {
160+
Less | Greater | LessEqual | GreaterEqual | Equal | NotEqual => true,
161+
Inplace | Assign | AssignOp(_) | As | Multiply | Divide | Modulus | Add | Subtract |
162+
ShiftLeft | ShiftRight | BitAnd | BitXor | BitOr | LAnd | LOr | DotDot => false
163+
}
164+
}
165+
166+
pub fn to_ast_binop(&self) -> Option<ast::BinOp_> {
167+
use self::AssocOp::*;
168+
match *self {
169+
Less => Some(ast::BiLt),
170+
Greater => Some(ast::BiGt),
171+
LessEqual => Some(ast::BiLe),
172+
GreaterEqual => Some(ast::BiGe),
173+
Equal => Some(ast::BiEq),
174+
NotEqual => Some(ast::BiNe),
175+
Multiply => Some(ast::BiMul),
176+
Divide => Some(ast::BiDiv),
177+
Modulus => Some(ast::BiRem),
178+
Add => Some(ast::BiAdd),
179+
Subtract => Some(ast::BiSub),
180+
ShiftLeft => Some(ast::BiShl),
181+
ShiftRight => Some(ast::BiShr),
182+
BitAnd => Some(ast::BiBitAnd),
183+
BitXor => Some(ast::BiBitXor),
184+
BitOr => Some(ast::BiBitOr),
185+
LAnd => Some(ast::BiAnd),
186+
LOr => Some(ast::BiOr),
187+
Inplace | Assign | AssignOp(_) | As | DotDot => None
188+
}
189+
}
190+
191+
}

src/test/compile-fail/feature-gate-placement-expr.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,6 @@
1919
fn main() {
2020
use std::boxed::HEAP;
2121

22-
let x = in HEAP { 'c' }; //~ ERROR placement-in expression syntax is experimental
22+
let x = HEAP <- 'c'; //~ ERROR placement-in expression syntax is experimental
2323
println!("x: {}", x);
2424
}

src/test/compile-fail/issue-14084.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,6 @@
1212
#![feature(placement_in_syntax)]
1313

1414
fn main() {
15-
in () { 0 };
15+
() <- 0;
1616
//~^ ERROR: the trait `core::ops::Placer<_>` is not implemented
1717
}

src/test/parse-fail/removed-syntax-larrow-init.rs

Lines changed: 0 additions & 17 deletions
This file was deleted.

src/test/parse-fail/removed-syntax-larrow-move.rs

Lines changed: 0 additions & 18 deletions
This file was deleted.

0 commit comments

Comments
 (0)