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.
This commit is contained in:
parent
540fd3aa71
commit
471f5a1f9a
9 changed files with 338 additions and 198 deletions
|
@ -242,26 +242,6 @@ pub fn struct_field_visibility(field: ast::StructField) -> Visibility {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Maps a binary operator to its precedence
|
|
||||||
pub fn operator_prec(op: ast::BinOp_) -> usize {
|
|
||||||
match op {
|
|
||||||
// 'as' sits here with 12
|
|
||||||
BiMul | BiDiv | BiRem => 11,
|
|
||||||
BiAdd | BiSub => 10,
|
|
||||||
BiShl | BiShr => 9,
|
|
||||||
BiBitAnd => 8,
|
|
||||||
BiBitXor => 7,
|
|
||||||
BiBitOr => 6,
|
|
||||||
BiLt | BiLe | BiGe | BiGt | BiEq | BiNe => 3,
|
|
||||||
BiAnd => 2,
|
|
||||||
BiOr => 1
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Precedence of the `as` operator, which is a binary operator
|
|
||||||
/// not appearing in the prior table.
|
|
||||||
pub const AS_PREC: usize = 12;
|
|
||||||
|
|
||||||
pub fn empty_generics() -> Generics {
|
pub fn empty_generics() -> Generics {
|
||||||
Generics {
|
Generics {
|
||||||
lifetimes: Vec::new(),
|
lifetimes: Vec::new(),
|
||||||
|
|
|
@ -66,6 +66,7 @@ pub mod util {
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
pub mod parser_testing;
|
pub mod parser_testing;
|
||||||
pub mod small_vector;
|
pub mod small_vector;
|
||||||
|
pub mod parser;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub mod diagnostics {
|
pub mod diagnostics {
|
||||||
|
|
|
@ -15,7 +15,7 @@ use ast::BareFnTy;
|
||||||
use ast::{RegionTyParamBound, TraitTyParamBound, TraitBoundModifier};
|
use ast::{RegionTyParamBound, TraitTyParamBound, TraitBoundModifier};
|
||||||
use ast::{Public, Unsafety};
|
use ast::{Public, Unsafety};
|
||||||
use ast::{Mod, BiAdd, Arg, Arm, Attribute, BindByRef, BindByValue};
|
use ast::{Mod, BiAdd, Arg, Arm, Attribute, BindByRef, BindByValue};
|
||||||
use ast::{BiBitAnd, BiBitOr, BiBitXor, BiRem, BiLt, BiGt, Block};
|
use ast::{BiBitAnd, BiBitOr, BiBitXor, BiRem, BiLt, Block};
|
||||||
use ast::{BlockCheckMode, CaptureByRef, CaptureByValue, CaptureClause};
|
use ast::{BlockCheckMode, CaptureByRef, CaptureByValue, CaptureClause};
|
||||||
use ast::{Constness, ConstImplItem, ConstTraitItem, Crate, CrateConfig};
|
use ast::{Constness, ConstImplItem, ConstTraitItem, Crate, CrateConfig};
|
||||||
use ast::{Decl, DeclItem, DeclLocal, DefaultBlock, DefaultReturn};
|
use ast::{Decl, DeclItem, DeclLocal, DefaultBlock, DefaultReturn};
|
||||||
|
@ -60,7 +60,7 @@ use ast::{UnnamedField, UnsafeBlock};
|
||||||
use ast::{ViewPath, ViewPathGlob, ViewPathList, ViewPathSimple};
|
use ast::{ViewPath, ViewPathGlob, ViewPathList, ViewPathSimple};
|
||||||
use ast::{Visibility, WhereClause};
|
use ast::{Visibility, WhereClause};
|
||||||
use ast;
|
use ast;
|
||||||
use ast_util::{self, AS_PREC, ident_to_path, operator_prec};
|
use ast_util::{self, ident_to_path};
|
||||||
use codemap::{self, Span, BytePos, Spanned, spanned, mk_sp, CodeMap};
|
use codemap::{self, Span, BytePos, Spanned, spanned, mk_sp, CodeMap};
|
||||||
use diagnostic;
|
use diagnostic;
|
||||||
use ext::tt::macro_parser;
|
use ext::tt::macro_parser;
|
||||||
|
@ -73,6 +73,7 @@ use parse::obsolete::{ParserObsoleteMethods, ObsoleteSyntax};
|
||||||
use parse::token::{self, MatchNt, SubstNt, SpecialVarNt, InternedString};
|
use parse::token::{self, MatchNt, SubstNt, SpecialVarNt, InternedString};
|
||||||
use parse::token::{keywords, special_idents, SpecialMacroVar};
|
use parse::token::{keywords, special_idents, SpecialMacroVar};
|
||||||
use parse::{new_sub_parser_from_file, ParseSess};
|
use parse::{new_sub_parser_from_file, ParseSess};
|
||||||
|
use util::parser::{AssocOp, Fixity};
|
||||||
use print::pprust;
|
use print::pprust;
|
||||||
use ptr::P;
|
use ptr::P;
|
||||||
use owned_slice::OwnedSlice;
|
use owned_slice::OwnedSlice;
|
||||||
|
@ -2597,7 +2598,7 @@ impl<'a> Parser<'a> {
|
||||||
Ok(tts)
|
Ok(tts)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Parse a prefix-operator expr
|
/// Parse a prefix-unary-operator expr
|
||||||
pub fn parse_prefix_expr(&mut self) -> PResult<P<Expr>> {
|
pub fn parse_prefix_expr(&mut self) -> PResult<P<Expr>> {
|
||||||
let lo = self.span.lo;
|
let lo = self.span.lo;
|
||||||
let hi;
|
let hi;
|
||||||
|
@ -2634,8 +2635,10 @@ impl<'a> Parser<'a> {
|
||||||
try!(self.bump());
|
try!(self.bump());
|
||||||
let place = try!(self.parse_expr_res(Restrictions::RESTRICTION_NO_STRUCT_LITERAL));
|
let place = try!(self.parse_expr_res(Restrictions::RESTRICTION_NO_STRUCT_LITERAL));
|
||||||
let blk = try!(self.parse_block());
|
let blk = try!(self.parse_block());
|
||||||
hi = blk.span.hi;
|
let span = blk.span;
|
||||||
let blk_expr = self.mk_expr(blk.span.lo, blk.span.hi, ExprBlock(blk));
|
hi = span.hi;
|
||||||
|
let blk_expr = self.mk_expr(span.lo, span.hi, ExprBlock(blk));
|
||||||
|
self.span_warn(span, "in PLACE BLOCK syntax is deprecated and will be removed soon");
|
||||||
ex = ExprInPlace(place, blk_expr);
|
ex = ExprInPlace(place, blk_expr);
|
||||||
}
|
}
|
||||||
token::Ident(..) if self.token.is_keyword(keywords::Box) => {
|
token::Ident(..) if self.token.is_keyword(keywords::Box) => {
|
||||||
|
@ -2649,65 +2652,146 @@ impl<'a> Parser<'a> {
|
||||||
return Ok(self.mk_expr(lo, hi, ex));
|
return Ok(self.mk_expr(lo, hi, ex));
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Parse an expression of binops
|
/// Parse an associative expression
|
||||||
pub fn parse_binops(&mut self) -> PResult<P<Expr>> {
|
///
|
||||||
let prefix_expr = try!(self.parse_prefix_expr());
|
/// This parses an expression accounting for associativity and precedence of the operators in
|
||||||
self.parse_more_binops(prefix_expr, 0)
|
/// the expression.
|
||||||
|
pub fn parse_assoc_expr(&mut self) -> PResult<P<Expr>> {
|
||||||
|
if self.token == token::DotDot {
|
||||||
|
// prefix-form of range notation `..expr` and `..`
|
||||||
|
// This has the precedence just higher than assignment expressions (much lower than
|
||||||
|
// other prefix expressions) to be consistent with the postfix-form `expr..`
|
||||||
|
// If it isn’t clear yet, this is a hack of the worst kind (one that also probably
|
||||||
|
// can’t be fixed anymore because stability guarantees).
|
||||||
|
let lo = self.span.lo;
|
||||||
|
let mut hi = self.span.hi;
|
||||||
|
try!(self.bump());
|
||||||
|
let opt_end = if self.is_at_start_of_range_notation_rhs() {
|
||||||
|
// RHS must be parsed with more associativity than DotDot.
|
||||||
|
let next_prec = AssocOp::from_token(&token::DotDot).unwrap().precedence() + 1;
|
||||||
|
let end = try!(self.parse_assoc_expr_with(next_prec, None));
|
||||||
|
hi = end.span.hi;
|
||||||
|
Some(end)
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
};
|
||||||
|
let r = self.mk_range(None, opt_end);
|
||||||
|
Ok(self.mk_expr(lo, hi, r))
|
||||||
|
} else {
|
||||||
|
self.parse_assoc_expr_with(0, None)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Parse an expression of binops of at least min_prec precedence
|
/// Parse an associative expression with operators of at least `min_prec` precedence
|
||||||
pub fn parse_more_binops(&mut self, lhs: P<Expr>, min_prec: usize) -> PResult<P<Expr>> {
|
pub fn parse_assoc_expr_with(&mut self, min_prec: usize, lhs: Option<P<Expr>>) -> PResult<P<Expr>> {
|
||||||
if self.expr_is_complete(&*lhs) { return Ok(lhs); }
|
let mut lhs = if lhs.is_some() {
|
||||||
|
lhs.unwrap()
|
||||||
self.expected_tokens.push(TokenType::Operator);
|
} else {
|
||||||
|
try!(self.parse_prefix_expr())
|
||||||
let cur_op_span = self.span;
|
};
|
||||||
let cur_opt = self.token.to_binop();
|
if self.expr_is_complete(&*lhs) && min_prec == 0 {
|
||||||
match cur_opt {
|
// Semi-statement forms are odd. See https://github.com/rust-lang/rust/issues/29071
|
||||||
Some(cur_op) => {
|
return Ok(lhs);
|
||||||
if ast_util::is_comparison_binop(cur_op) {
|
|
||||||
self.check_no_chained_comparison(&*lhs, cur_op)
|
|
||||||
}
|
|
||||||
let cur_prec = operator_prec(cur_op);
|
|
||||||
if cur_prec >= min_prec {
|
|
||||||
try!(self.bump());
|
|
||||||
let expr = try!(self.parse_prefix_expr());
|
|
||||||
let rhs = try!(self.parse_more_binops(expr, cur_prec + 1));
|
|
||||||
let lhs_span = lhs.span;
|
|
||||||
let rhs_span = rhs.span;
|
|
||||||
let binary = self.mk_binary(codemap::respan(cur_op_span, cur_op), lhs, rhs);
|
|
||||||
let bin = self.mk_expr(lhs_span.lo, rhs_span.hi, binary);
|
|
||||||
self.parse_more_binops(bin, min_prec)
|
|
||||||
} else {
|
|
||||||
Ok(lhs)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
None => {
|
|
||||||
if AS_PREC >= min_prec && try!(self.eat_keyword_noexpect(keywords::As) ){
|
|
||||||
let rhs = try!(self.parse_ty_nopanic());
|
|
||||||
let _as = self.mk_expr(lhs.span.lo,
|
|
||||||
rhs.span.hi,
|
|
||||||
ExprCast(lhs, rhs));
|
|
||||||
self.parse_more_binops(_as, min_prec)
|
|
||||||
} else {
|
|
||||||
Ok(lhs)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
let cur_op_span = self.span;
|
||||||
|
self.expected_tokens.push(TokenType::Operator);
|
||||||
|
while let Some(op) = AssocOp::from_token(&self.token) {
|
||||||
|
if op.precedence() < min_prec {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
try!(self.bump());
|
||||||
|
if op.is_comparison() {
|
||||||
|
self.check_no_chained_comparison(&*lhs, &op);
|
||||||
|
}
|
||||||
|
// Special cases:
|
||||||
|
if op == AssocOp::As {
|
||||||
|
let rhs = try!(self.parse_ty_nopanic());
|
||||||
|
lhs = self.mk_expr(lhs.span.lo, rhs.span.hi, ExprCast(lhs, rhs));
|
||||||
|
continue
|
||||||
|
} else if op == AssocOp::DotDot {
|
||||||
|
// If we didn’t have to handle `x..`, it would be pretty easy to generalise
|
||||||
|
// here by simply doing something along the lines of
|
||||||
|
//
|
||||||
|
// break_from_this_loop_after_setting_lhs = true;
|
||||||
|
// rhs = self.parse_assoc_expr_with(op.precedence() + 1, None);
|
||||||
|
//
|
||||||
|
// We have 2 alternatives here: `x..y` and `x..` The other two variants are
|
||||||
|
// handled in `parse_assoc_expr`
|
||||||
|
let rhs = if self.is_at_start_of_range_notation_rhs() {
|
||||||
|
self.parse_assoc_expr_with(op.precedence() + 1, None).ok()
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
};
|
||||||
|
let (lhs_span, rhs_span) = (lhs.span, if let Some(ref x) = rhs {
|
||||||
|
x.span
|
||||||
|
} else {
|
||||||
|
cur_op_span
|
||||||
|
});
|
||||||
|
let r = self.mk_range(Some(lhs), rhs);
|
||||||
|
lhs = self.mk_expr(lhs_span.lo, rhs_span.hi, r);
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
let rhs = try!(match op.fixity() {
|
||||||
|
Fixity::Right => self.parse_assoc_expr_with(op.precedence(), None),
|
||||||
|
Fixity::Left => self.parse_assoc_expr_with(op.precedence() + 1, None),
|
||||||
|
// We currently have no non-associative operators that are not handled above by
|
||||||
|
// the special cases. The code is here only for future convenience.
|
||||||
|
Fixity::None => self.parse_assoc_expr_with(op.precedence() + 1, None),
|
||||||
|
});
|
||||||
|
|
||||||
|
lhs = match op {
|
||||||
|
AssocOp::Add | AssocOp::Subtract | AssocOp::Multiply | AssocOp::Divide |
|
||||||
|
AssocOp::Modulus | AssocOp::LAnd | AssocOp::LOr | AssocOp::BitXor |
|
||||||
|
AssocOp::BitAnd | AssocOp::BitOr | AssocOp::ShiftLeft | AssocOp::ShiftRight |
|
||||||
|
AssocOp::Equal | AssocOp::Less | AssocOp::LessEqual | AssocOp::NotEqual |
|
||||||
|
AssocOp::Greater | AssocOp::GreaterEqual => {
|
||||||
|
let ast_op = op.to_ast_binop().unwrap();
|
||||||
|
let (lhs_span, rhs_span) = (lhs.span, rhs.span);
|
||||||
|
let binary = self.mk_binary(codemap::respan(cur_op_span, ast_op), lhs, rhs);
|
||||||
|
self.mk_expr(lhs_span.lo, rhs_span.hi, binary)
|
||||||
|
}
|
||||||
|
AssocOp::Assign =>
|
||||||
|
self.mk_expr(lhs.span.lo, rhs.span.hi, ExprAssign(lhs, rhs)),
|
||||||
|
AssocOp::Inplace =>
|
||||||
|
self.mk_expr(lhs.span.lo, rhs.span.hi, ExprInPlace(lhs, rhs)),
|
||||||
|
AssocOp::AssignOp(k) => {
|
||||||
|
let aop = match k {
|
||||||
|
token::Plus => BiAdd,
|
||||||
|
token::Minus => BiSub,
|
||||||
|
token::Star => BiMul,
|
||||||
|
token::Slash => BiDiv,
|
||||||
|
token::Percent => BiRem,
|
||||||
|
token::Caret => BiBitXor,
|
||||||
|
token::And => BiBitAnd,
|
||||||
|
token::Or => BiBitOr,
|
||||||
|
token::Shl => BiShl,
|
||||||
|
token::Shr => BiShr
|
||||||
|
};
|
||||||
|
let (lhs_span, rhs_span) = (lhs.span, rhs.span);
|
||||||
|
let aopexpr = self.mk_assign_op(codemap::respan(cur_op_span, aop), lhs, rhs);
|
||||||
|
self.mk_expr(lhs_span.lo, rhs_span.hi, aopexpr)
|
||||||
|
}
|
||||||
|
AssocOp::As | AssocOp::DotDot => self.bug("As or DotDot branch reached")
|
||||||
|
};
|
||||||
|
|
||||||
|
if op.fixity() == Fixity::None { break }
|
||||||
|
}
|
||||||
|
Ok(lhs)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Produce an error if comparison operators are chained (RFC #558).
|
/// Produce an error if comparison operators are chained (RFC #558).
|
||||||
/// We only need to check lhs, not rhs, because all comparison ops
|
/// We only need to check lhs, not rhs, because all comparison ops
|
||||||
/// have same precedence and are left-associative
|
/// have same precedence and are left-associative
|
||||||
fn check_no_chained_comparison(&mut self, lhs: &Expr, outer_op: ast::BinOp_) {
|
fn check_no_chained_comparison(&mut self, lhs: &Expr, outer_op: &AssocOp) {
|
||||||
debug_assert!(ast_util::is_comparison_binop(outer_op));
|
debug_assert!(outer_op.is_comparison());
|
||||||
match lhs.node {
|
match lhs.node {
|
||||||
ExprBinary(op, _, _) if ast_util::is_comparison_binop(op.node) => {
|
ExprBinary(op, _, _) if ast_util::is_comparison_binop(op.node) => {
|
||||||
// respan to include both operators
|
// respan to include both operators
|
||||||
let op_span = mk_sp(op.span.lo, self.span.hi);
|
let op_span = mk_sp(op.span.lo, self.span.hi);
|
||||||
self.span_err(op_span,
|
self.span_err(op_span,
|
||||||
"chained comparison operators require parentheses");
|
"chained comparison operators require parentheses");
|
||||||
if op.node == BiLt && outer_op == BiGt {
|
if op.node == BiLt && *outer_op == AssocOp::Greater {
|
||||||
self.fileline_help(op_span,
|
self.fileline_help(op_span,
|
||||||
"use `::<...>` instead of `<...>` if you meant to specify type arguments");
|
"use `::<...>` instead of `<...>` if you meant to specify type arguments");
|
||||||
}
|
}
|
||||||
|
@ -2716,88 +2800,6 @@ impl<'a> Parser<'a> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Parse an assignment expression....
|
|
||||||
/// actually, this seems to be the main entry point for
|
|
||||||
/// parsing an arbitrary expression.
|
|
||||||
pub fn parse_assign_expr(&mut self) -> PResult<P<Expr>> {
|
|
||||||
match self.token {
|
|
||||||
token::DotDot => {
|
|
||||||
// prefix-form of range notation '..expr'
|
|
||||||
// This has the same precedence as assignment expressions
|
|
||||||
// (much lower than other prefix expressions) to be consistent
|
|
||||||
// with the postfix-form 'expr..'
|
|
||||||
let lo = self.span.lo;
|
|
||||||
let mut hi = self.span.hi;
|
|
||||||
try!(self.bump());
|
|
||||||
let opt_end = if self.is_at_start_of_range_notation_rhs() {
|
|
||||||
let end = try!(self.parse_binops());
|
|
||||||
hi = end.span.hi;
|
|
||||||
Some(end)
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
};
|
|
||||||
let ex = self.mk_range(None, opt_end);
|
|
||||||
Ok(self.mk_expr(lo, hi, ex))
|
|
||||||
}
|
|
||||||
_ => {
|
|
||||||
let lhs = try!(self.parse_binops());
|
|
||||||
self.parse_assign_expr_with(lhs)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn parse_assign_expr_with(&mut self, lhs: P<Expr>) -> PResult<P<Expr>> {
|
|
||||||
let restrictions = self.restrictions & Restrictions::RESTRICTION_NO_STRUCT_LITERAL;
|
|
||||||
let op_span = self.span;
|
|
||||||
match self.token {
|
|
||||||
token::Eq => {
|
|
||||||
try!(self.bump());
|
|
||||||
let rhs = try!(self.parse_expr_res(restrictions));
|
|
||||||
Ok(self.mk_expr(lhs.span.lo, rhs.span.hi, ExprAssign(lhs, rhs)))
|
|
||||||
}
|
|
||||||
token::BinOpEq(op) => {
|
|
||||||
try!(self.bump());
|
|
||||||
let rhs = try!(self.parse_expr_res(restrictions));
|
|
||||||
let aop = match op {
|
|
||||||
token::Plus => BiAdd,
|
|
||||||
token::Minus => BiSub,
|
|
||||||
token::Star => BiMul,
|
|
||||||
token::Slash => BiDiv,
|
|
||||||
token::Percent => BiRem,
|
|
||||||
token::Caret => BiBitXor,
|
|
||||||
token::And => BiBitAnd,
|
|
||||||
token::Or => BiBitOr,
|
|
||||||
token::Shl => BiShl,
|
|
||||||
token::Shr => BiShr
|
|
||||||
};
|
|
||||||
let rhs_span = rhs.span;
|
|
||||||
let span = lhs.span;
|
|
||||||
let assign_op = self.mk_assign_op(codemap::respan(op_span, aop), lhs, rhs);
|
|
||||||
Ok(self.mk_expr(span.lo, rhs_span.hi, assign_op))
|
|
||||||
}
|
|
||||||
// A range expression, either `expr..expr` or `expr..`.
|
|
||||||
token::DotDot => {
|
|
||||||
let lo = lhs.span.lo;
|
|
||||||
let mut hi = self.span.hi;
|
|
||||||
try!(self.bump());
|
|
||||||
|
|
||||||
let opt_end = if self.is_at_start_of_range_notation_rhs() {
|
|
||||||
let end = try!(self.parse_binops());
|
|
||||||
hi = end.span.hi;
|
|
||||||
Some(end)
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
};
|
|
||||||
let range = self.mk_range(Some(lhs), opt_end);
|
|
||||||
return Ok(self.mk_expr(lo, hi, range));
|
|
||||||
}
|
|
||||||
|
|
||||||
_ => {
|
|
||||||
Ok(lhs)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn is_at_start_of_range_notation_rhs(&self) -> bool {
|
fn is_at_start_of_range_notation_rhs(&self) -> bool {
|
||||||
if self.token.can_begin_expr() {
|
if self.token.can_begin_expr() {
|
||||||
// parse `for i in 1.. { }` as infinite loop, not as `for i in (1..{})`.
|
// parse `for i in 1.. { }` as infinite loop, not as `for i in (1..{})`.
|
||||||
|
@ -2982,7 +2984,7 @@ impl<'a> Parser<'a> {
|
||||||
pub fn parse_expr_res(&mut self, r: Restrictions) -> PResult<P<Expr>> {
|
pub fn parse_expr_res(&mut self, r: Restrictions) -> PResult<P<Expr>> {
|
||||||
let old = self.restrictions;
|
let old = self.restrictions;
|
||||||
self.restrictions = r;
|
self.restrictions = r;
|
||||||
let e = try!(self.parse_assign_expr());
|
let e = try!(self.parse_assoc_expr());
|
||||||
self.restrictions = old;
|
self.restrictions = old;
|
||||||
return Ok(e);
|
return Ok(e);
|
||||||
}
|
}
|
||||||
|
@ -3624,8 +3626,7 @@ impl<'a> Parser<'a> {
|
||||||
let e = self.mk_mac_expr(span.lo, span.hi,
|
let e = self.mk_mac_expr(span.lo, span.hi,
|
||||||
mac.and_then(|m| m.node));
|
mac.and_then(|m| m.node));
|
||||||
let e = try!(self.parse_dot_or_call_expr_with(e));
|
let e = try!(self.parse_dot_or_call_expr_with(e));
|
||||||
let e = try!(self.parse_more_binops(e, 0));
|
let e = try!(self.parse_assoc_expr_with(0, Some(e)));
|
||||||
let e = try!(self.parse_assign_expr_with(e));
|
|
||||||
try!(self.handle_expression_like_statement(
|
try!(self.handle_expression_like_statement(
|
||||||
e,
|
e,
|
||||||
span,
|
span,
|
||||||
|
|
|
@ -14,6 +14,7 @@ use abi;
|
||||||
use ast;
|
use ast;
|
||||||
use ast::{RegionTyParamBound, TraitTyParamBound, TraitBoundModifier};
|
use ast::{RegionTyParamBound, TraitTyParamBound, TraitBoundModifier};
|
||||||
use ast_util;
|
use ast_util;
|
||||||
|
use util::parser::AssocOp;
|
||||||
use attr;
|
use attr;
|
||||||
use owned_slice::OwnedSlice;
|
use owned_slice::OwnedSlice;
|
||||||
use attr::{AttrMetaMethods, AttributeMethods};
|
use attr::{AttrMetaMethods, AttributeMethods};
|
||||||
|
@ -445,7 +446,8 @@ fn needs_parentheses(expr: &ast::Expr) -> bool {
|
||||||
match expr.node {
|
match expr.node {
|
||||||
ast::ExprAssign(..) | ast::ExprBinary(..) |
|
ast::ExprAssign(..) | ast::ExprBinary(..) |
|
||||||
ast::ExprClosure(..) |
|
ast::ExprClosure(..) |
|
||||||
ast::ExprAssignOp(..) | ast::ExprCast(..) => true,
|
ast::ExprAssignOp(..) | ast::ExprCast(..) |
|
||||||
|
ast::ExprInPlace(..) => true,
|
||||||
_ => false,
|
_ => false,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1776,8 +1778,8 @@ impl<'a> State<'a> {
|
||||||
binop: ast::BinOp) -> bool {
|
binop: ast::BinOp) -> bool {
|
||||||
match sub_expr.node {
|
match sub_expr.node {
|
||||||
ast::ExprBinary(ref sub_op, _, _) => {
|
ast::ExprBinary(ref sub_op, _, _) => {
|
||||||
if ast_util::operator_prec(sub_op.node) <
|
if AssocOp::from_ast_binop(sub_op.node).precedence() <
|
||||||
ast_util::operator_prec(binop.node) {
|
AssocOp::from_ast_binop(binop.node).precedence() {
|
||||||
true
|
true
|
||||||
} else {
|
} else {
|
||||||
false
|
false
|
||||||
|
@ -1802,10 +1804,10 @@ impl<'a> State<'a> {
|
||||||
fn print_expr_in_place(&mut self,
|
fn print_expr_in_place(&mut self,
|
||||||
place: &ast::Expr,
|
place: &ast::Expr,
|
||||||
expr: &ast::Expr) -> io::Result<()> {
|
expr: &ast::Expr) -> io::Result<()> {
|
||||||
try!(self.word_space("in"));
|
try!(self.print_expr_maybe_paren(place));
|
||||||
try!(self.print_expr(place));
|
|
||||||
try!(space(&mut self.s));
|
try!(space(&mut self.s));
|
||||||
self.print_expr(expr)
|
try!(self.word_space("<-"));
|
||||||
|
self.print_expr_maybe_paren(expr)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn print_expr_vec(&mut self, exprs: &[P<ast::Expr>]) -> io::Result<()> {
|
fn print_expr_vec(&mut self, exprs: &[P<ast::Expr>]) -> io::Result<()> {
|
||||||
|
|
191
src/libsyntax/util/parser.rs
Normal file
191
src/libsyntax/util/parser.rs
Normal file
|
@ -0,0 +1,191 @@
|
||||||
|
use parse::token::{Token, BinOpToken, keywords};
|
||||||
|
use ast;
|
||||||
|
|
||||||
|
/// Associative operator with precedence.
|
||||||
|
///
|
||||||
|
/// This is the enum which specifies operator precedence and fixity to the parser.
|
||||||
|
#[derive(Debug, PartialEq, Eq)]
|
||||||
|
pub enum AssocOp {
|
||||||
|
/// `+`
|
||||||
|
Add,
|
||||||
|
/// `-`
|
||||||
|
Subtract,
|
||||||
|
/// `*`
|
||||||
|
Multiply,
|
||||||
|
/// `/`
|
||||||
|
Divide,
|
||||||
|
/// `%`
|
||||||
|
Modulus,
|
||||||
|
/// `&&`
|
||||||
|
LAnd,
|
||||||
|
/// `||`
|
||||||
|
LOr,
|
||||||
|
/// `^`
|
||||||
|
BitXor,
|
||||||
|
/// `&`
|
||||||
|
BitAnd,
|
||||||
|
/// `|`
|
||||||
|
BitOr,
|
||||||
|
/// `<<`
|
||||||
|
ShiftLeft,
|
||||||
|
/// `>>`
|
||||||
|
ShiftRight,
|
||||||
|
/// `==`
|
||||||
|
Equal,
|
||||||
|
/// `<`
|
||||||
|
Less,
|
||||||
|
/// `<=`
|
||||||
|
LessEqual,
|
||||||
|
/// `!=`
|
||||||
|
NotEqual,
|
||||||
|
/// `>`
|
||||||
|
Greater,
|
||||||
|
/// `>=`
|
||||||
|
GreaterEqual,
|
||||||
|
/// `=`
|
||||||
|
Assign,
|
||||||
|
/// `<-`
|
||||||
|
Inplace,
|
||||||
|
/// `?=` where ? is one of the BinOpToken
|
||||||
|
AssignOp(BinOpToken),
|
||||||
|
/// `as`
|
||||||
|
As,
|
||||||
|
/// `..` range
|
||||||
|
DotDot
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, PartialEq, Eq)]
|
||||||
|
pub enum Fixity {
|
||||||
|
/// The operator is left-associative
|
||||||
|
Left,
|
||||||
|
/// The operator is right-associative
|
||||||
|
Right,
|
||||||
|
/// The operator is not associative
|
||||||
|
None
|
||||||
|
}
|
||||||
|
|
||||||
|
impl AssocOp {
|
||||||
|
/// Create a new AssocOP from a token
|
||||||
|
pub fn from_token(t: &Token) -> Option<AssocOp> {
|
||||||
|
use self::AssocOp::*;
|
||||||
|
match *t {
|
||||||
|
Token::BinOpEq(k) => Some(AssignOp(k)),
|
||||||
|
Token::LArrow => Some(Inplace),
|
||||||
|
Token::Eq => Some(Assign),
|
||||||
|
Token::BinOp(BinOpToken::Star) => Some(Multiply),
|
||||||
|
Token::BinOp(BinOpToken::Slash) => Some(Divide),
|
||||||
|
Token::BinOp(BinOpToken::Percent) => Some(Modulus),
|
||||||
|
Token::BinOp(BinOpToken::Plus) => Some(Add),
|
||||||
|
Token::BinOp(BinOpToken::Minus) => Some(Subtract),
|
||||||
|
Token::BinOp(BinOpToken::Shl) => Some(ShiftLeft),
|
||||||
|
Token::BinOp(BinOpToken::Shr) => Some(ShiftRight),
|
||||||
|
Token::BinOp(BinOpToken::And) => Some(BitAnd),
|
||||||
|
Token::BinOp(BinOpToken::Caret) => Some(BitXor),
|
||||||
|
Token::BinOp(BinOpToken::Or) => Some(BitOr),
|
||||||
|
Token::Lt => Some(Less),
|
||||||
|
Token::Le => Some(LessEqual),
|
||||||
|
Token::Ge => Some(GreaterEqual),
|
||||||
|
Token::Gt => Some(Greater),
|
||||||
|
Token::EqEq => Some(Equal),
|
||||||
|
Token::Ne => Some(NotEqual),
|
||||||
|
Token::AndAnd => Some(LAnd),
|
||||||
|
Token::OrOr => Some(LOr),
|
||||||
|
Token::DotDot => Some(DotDot),
|
||||||
|
_ if t.is_keyword(keywords::As) => Some(As),
|
||||||
|
_ => None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Create a new AssocOp from ast::BinOp_.
|
||||||
|
pub fn from_ast_binop(op: ast::BinOp_) -> Self {
|
||||||
|
use self::AssocOp::*;
|
||||||
|
match op {
|
||||||
|
ast::BiLt => Less,
|
||||||
|
ast::BiGt => Greater,
|
||||||
|
ast::BiLe => LessEqual,
|
||||||
|
ast::BiGe => GreaterEqual,
|
||||||
|
ast::BiEq => Equal,
|
||||||
|
ast::BiNe => NotEqual,
|
||||||
|
ast::BiMul => Multiply,
|
||||||
|
ast::BiDiv => Divide,
|
||||||
|
ast::BiRem => Modulus,
|
||||||
|
ast::BiAdd => Add,
|
||||||
|
ast::BiSub => Subtract,
|
||||||
|
ast::BiShl => ShiftLeft,
|
||||||
|
ast::BiShr => ShiftRight,
|
||||||
|
ast::BiBitAnd => BitAnd,
|
||||||
|
ast::BiBitXor => BitXor,
|
||||||
|
ast::BiBitOr => BitOr,
|
||||||
|
ast::BiAnd => LAnd,
|
||||||
|
ast::BiOr => LOr
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Gets the precedence of this operator
|
||||||
|
pub fn precedence(&self) -> usize {
|
||||||
|
use self::AssocOp::*;
|
||||||
|
match *self {
|
||||||
|
As => 14,
|
||||||
|
Multiply | Divide | Modulus => 13,
|
||||||
|
Add | Subtract => 12,
|
||||||
|
ShiftLeft | ShiftRight => 11,
|
||||||
|
BitAnd => 10,
|
||||||
|
BitXor => 9,
|
||||||
|
BitOr => 8,
|
||||||
|
Less | Greater | LessEqual | GreaterEqual | Equal | NotEqual => 7,
|
||||||
|
LAnd => 6,
|
||||||
|
LOr => 5,
|
||||||
|
DotDot => 4,
|
||||||
|
Inplace => 3,
|
||||||
|
Assign | AssignOp(_) => 2,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Gets the fixity of this operator
|
||||||
|
pub fn fixity(&self) -> Fixity {
|
||||||
|
use self::AssocOp::*;
|
||||||
|
// NOTE: it is a bug to have an operators that has same precedence but different fixities!
|
||||||
|
match *self {
|
||||||
|
Inplace | Assign | AssignOp(_) => Fixity::Right,
|
||||||
|
As | Multiply | Divide | Modulus | Add | Subtract | ShiftLeft | ShiftRight | BitAnd |
|
||||||
|
BitXor | BitOr | Less | Greater | LessEqual | GreaterEqual | Equal | NotEqual |
|
||||||
|
LAnd | LOr => Fixity::Left,
|
||||||
|
DotDot => Fixity::None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn is_comparison(&self) -> bool {
|
||||||
|
use self::AssocOp::*;
|
||||||
|
match *self {
|
||||||
|
Less | Greater | LessEqual | GreaterEqual | Equal | NotEqual => true,
|
||||||
|
Inplace | Assign | AssignOp(_) | As | Multiply | Divide | Modulus | Add | Subtract |
|
||||||
|
ShiftLeft | ShiftRight | BitAnd | BitXor | BitOr | LAnd | LOr | DotDot => false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn to_ast_binop(&self) -> Option<ast::BinOp_> {
|
||||||
|
use self::AssocOp::*;
|
||||||
|
match *self {
|
||||||
|
Less => Some(ast::BiLt),
|
||||||
|
Greater => Some(ast::BiGt),
|
||||||
|
LessEqual => Some(ast::BiLe),
|
||||||
|
GreaterEqual => Some(ast::BiGe),
|
||||||
|
Equal => Some(ast::BiEq),
|
||||||
|
NotEqual => Some(ast::BiNe),
|
||||||
|
Multiply => Some(ast::BiMul),
|
||||||
|
Divide => Some(ast::BiDiv),
|
||||||
|
Modulus => Some(ast::BiRem),
|
||||||
|
Add => Some(ast::BiAdd),
|
||||||
|
Subtract => Some(ast::BiSub),
|
||||||
|
ShiftLeft => Some(ast::BiShl),
|
||||||
|
ShiftRight => Some(ast::BiShr),
|
||||||
|
BitAnd => Some(ast::BiBitAnd),
|
||||||
|
BitXor => Some(ast::BiBitXor),
|
||||||
|
BitOr => Some(ast::BiBitOr),
|
||||||
|
LAnd => Some(ast::BiAnd),
|
||||||
|
LOr => Some(ast::BiOr),
|
||||||
|
Inplace | Assign | AssignOp(_) | As | DotDot => None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -19,6 +19,6 @@
|
||||||
fn main() {
|
fn main() {
|
||||||
use std::boxed::HEAP;
|
use std::boxed::HEAP;
|
||||||
|
|
||||||
let x = in HEAP { 'c' }; //~ ERROR placement-in expression syntax is experimental
|
let x = HEAP <- 'c'; //~ ERROR placement-in expression syntax is experimental
|
||||||
println!("x: {}", x);
|
println!("x: {}", x);
|
||||||
}
|
}
|
||||||
|
|
|
@ -12,6 +12,6 @@
|
||||||
#![feature(placement_in_syntax)]
|
#![feature(placement_in_syntax)]
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
in () { 0 };
|
() <- 0;
|
||||||
//~^ ERROR: the trait `core::ops::Placer<_>` is not implemented
|
//~^ ERROR: the trait `core::ops::Placer<_>` is not implemented
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,17 +0,0 @@
|
||||||
// Copyright 2013 The Rust Project Developers. See the COPYRIGHT
|
|
||||||
// file at the top-level directory of this distribution and at
|
|
||||||
// http://rust-lang.org/COPYRIGHT.
|
|
||||||
//
|
|
||||||
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
|
||||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
|
||||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
|
||||||
// option. This file may not be copied, modified, or distributed
|
|
||||||
// except according to those terms.
|
|
||||||
|
|
||||||
// compile-flags: -Z parse-only
|
|
||||||
|
|
||||||
fn removed_moves() {
|
|
||||||
let mut x = 0;
|
|
||||||
let y <- x;
|
|
||||||
//~^ ERROR expected one of `:`, `;`, `=`, or `@`, found `<-`
|
|
||||||
}
|
|
|
@ -1,18 +0,0 @@
|
||||||
// Copyright 2013 The Rust Project Developers. See the COPYRIGHT
|
|
||||||
// file at the top-level directory of this distribution and at
|
|
||||||
// http://rust-lang.org/COPYRIGHT.
|
|
||||||
//
|
|
||||||
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
|
||||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
|
||||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
|
||||||
// option. This file may not be copied, modified, or distributed
|
|
||||||
// except according to those terms.
|
|
||||||
|
|
||||||
// compile-flags: -Z parse-only
|
|
||||||
|
|
||||||
fn removed_moves() {
|
|
||||||
let mut x = 0;
|
|
||||||
let y = 0;
|
|
||||||
y <- x;
|
|
||||||
//~^ ERROR expected one of `!`, `.`, `::`, `;`, `{`, `}`, or an operator, found `<-`
|
|
||||||
}
|
|
Loading…
Add table
Add a link
Reference in a new issue