From 5daf13cae371ce4ee90450a1d3006b53395a40d7 Mon Sep 17 00:00:00 2001 From: Alex Burka Date: Wed, 13 Jan 2016 01:23:31 -0500 Subject: [PATCH] libsyntax: parse inclusive ranges --- src/libsyntax/ast.rs | 13 +++++- src/libsyntax/fold.rs | 5 +- src/libsyntax/parse/parser.rs | 87 ++++++++++++++++++++--------------- src/libsyntax/parse/token.rs | 2 +- src/libsyntax/print/pprust.rs | 8 +++- src/libsyntax/util/parser.rs | 14 ++++-- src/libsyntax/visit.rs | 2 +- 7 files changed, 81 insertions(+), 50 deletions(-) diff --git a/src/libsyntax/ast.rs b/src/libsyntax/ast.rs index 23bb6fd141a4..0dbfb2c7be65 100644 --- a/src/libsyntax/ast.rs +++ b/src/libsyntax/ast.rs @@ -886,6 +886,15 @@ impl fmt::Debug for Expr { } } +/// Limit types of a range (inclusive or exclusive) +#[derive(Copy, Clone, PartialEq, Eq, RustcEncodable, RustcDecodable, Hash, Debug)] +pub enum RangeLimits { + /// Inclusive at the beginning, exclusive at the end + HalfOpen, + /// Inclusive at the beginning and end + Closed, +} + #[derive(Clone, PartialEq, Eq, RustcEncodable, RustcDecodable, Hash, Debug)] pub enum ExprKind { /// A `box x` expression. @@ -974,8 +983,8 @@ pub enum ExprKind { TupField(P, Spanned), /// An indexing operation (`foo[2]`) Index(P, P), - /// A range (`1..2`, `1..`, or `..2`) - Range(Option>, Option>), + /// A range (`1..2`, `1..`, `..2`, `1...2`, `1...`, `...2`) + Range(Option>, Option>, RangeLimits), /// Variable reference, possibly containing `::` and/or type /// parameters, e.g. foo::bar::. diff --git a/src/libsyntax/fold.rs b/src/libsyntax/fold.rs index d75e8f796ae8..591c1295d664 100644 --- a/src/libsyntax/fold.rs +++ b/src/libsyntax/fold.rs @@ -1273,9 +1273,10 @@ pub fn noop_fold_expr(Expr {id, node, span, attrs}: Expr, folder: &mu ExprKind::Index(el, er) => { ExprKind::Index(folder.fold_expr(el), folder.fold_expr(er)) } - ExprKind::Range(e1, e2) => { + ExprKind::Range(e1, e2, lim) => { ExprKind::Range(e1.map(|x| folder.fold_expr(x)), - e2.map(|x| folder.fold_expr(x))) + e2.map(|x| folder.fold_expr(x)), + lim) } ExprKind::Path(qself, path) => { let qself = qself.map(|QSelf { ty, position }| { diff --git a/src/libsyntax/parse/parser.rs b/src/libsyntax/parse/parser.rs index b5d29a0d6dba..8b563ef00f31 100644 --- a/src/libsyntax/parse/parser.rs +++ b/src/libsyntax/parse/parser.rs @@ -20,7 +20,7 @@ use ast::{BlockCheckMode, CaptureBy}; use ast::{Constness, Crate, CrateConfig}; use ast::{Decl, DeclKind}; use ast::{EMPTY_CTXT, EnumDef, ExplicitSelf}; -use ast::{Expr, ExprKind}; +use ast::{Expr, ExprKind, RangeLimits}; use ast::{Field, FnDecl}; use ast::{ForeignItem, ForeignItemKind, FunctionRetTy}; use ast::{Ident, ImplItem, Item, ItemKind}; @@ -2054,9 +2054,10 @@ impl<'a> Parser<'a> { pub fn mk_range(&mut self, start: Option>, - end: Option>) - -> ast::ExprKind { - ExprKind::Range(start, end) + end: Option>, + limits: RangeLimits) + -> ast::Expr_ { + ExprKind::Range(start, end, limits) } pub fn mk_field(&mut self, expr: P, ident: ast::SpannedIdent) -> ast::ExprKind { @@ -2894,7 +2895,7 @@ impl<'a> Parser<'a> { LhsExpr::AttributesParsed(attrs) => Some(attrs), _ => None, }; - if self.token == token::DotDot { + if self.token == token::DotDot || self.token == token::DotDotDot { return self.parse_prefix_range_expr(attrs); } else { try!(self.parse_prefix_expr(attrs)) @@ -2940,32 +2941,32 @@ impl<'a> Parser<'a> { ExprKind::Type(lhs, rhs), None); continue } else if op == AssocOp::DotDot { - // If we didn’t have to handle `x..`, it would be pretty easy to generalise - // it to the Fixity::None code. - // - // We have 2 alternatives here: `x..y` and `x..` The other two variants are - // handled with `parse_prefix_range_expr` call above. - let rhs = if self.is_at_start_of_range_notation_rhs() { - let rhs = self.parse_assoc_expr_with(op.precedence() + 1, - LhsExpr::NotYetParsed); - match rhs { - Ok(e) => Some(e), - Err(mut e) => { - e.cancel(); - None - } + // If we didn’t have to handle `x..`, it would be pretty easy to generalise + // it to the Fixity::None code. + // + // We have 2 alternatives here: `x..y` and `x..` The other two variants are + // handled with `parse_prefix_range_expr` call above. + let rhs = if self.is_at_start_of_range_notation_rhs() { + let rhs = self.parse_assoc_expr_with(op.precedence() + 1, + LhsExpr::NotYetParsed); + match rhs { + Ok(e) => Some(e), + Err(mut e) => { + e.cancel(); + None } - } 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, None); - break + } + } 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, RangeLimits::HalfOpen); + lhs = self.mk_expr(lhs_span.lo, rhs_span.hi, r, None); + break } let rhs = try!(match op.fixity() { @@ -2981,8 +2982,8 @@ impl<'a> Parser<'a> { this.parse_assoc_expr_with(op.precedence() + 1, LhsExpr::NotYetParsed) }), - // We currently have no non-associative operators that are not handled above by - // the special cases. The code is here only for future convenience. + // the only operator handled here is `...` (the other non-associative operators are + // special-cased above) Fixity::None => self.with_res( restrictions - Restrictions::RESTRICTION_STMT_EXPR, |this| { @@ -3023,6 +3024,11 @@ impl<'a> Parser<'a> { let aopexpr = self.mk_assign_op(codemap::respan(cur_op_span, aop), lhs, rhs); self.mk_expr(lhs_span.lo, rhs_span.hi, aopexpr, None) } + AssocOp::DotDotDot => { + let (lhs_span, rhs_span) = (lhs.span, rhs.span); + let r = self.mk_range(Some(lhs), Some(rhs), RangeLimits::Closed); + self.mk_expr(lhs_span.lo, rhs_span.hi, r, None) + } AssocOp::As | AssocOp::Colon | AssocOp::DotDot => { self.bug("As, Colon or DotDot branch reached") } @@ -3054,18 +3060,19 @@ impl<'a> Parser<'a> { } } - /// Parse prefix-forms of range notation: `..expr` and `..` + /// Parse prefix-forms of range notation: `..expr`, `..`, `...expr` fn parse_prefix_range_expr(&mut self, already_parsed_attrs: Option) -> PResult<'a, P> { - debug_assert!(self.token == token::DotDot); + debug_assert!(self.token == token::DotDot || self.token == token::DotDotDot); + let tok = self.token.clone(); let attrs = try!(self.parse_or_use_outer_attributes(already_parsed_attrs)); let lo = self.span.lo; let mut hi = self.span.hi; 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; + // RHS must be parsed with more associativity than the dots. + let next_prec = AssocOp::from_token(&tok).unwrap().precedence() + 1; Some(try!(self.parse_assoc_expr_with(next_prec, LhsExpr::NotYetParsed) .map(|x|{ @@ -3075,7 +3082,13 @@ impl<'a> Parser<'a> { } else { None }; - let r = self.mk_range(None, opt_end); + let r = self.mk_range(None, + opt_end, + if tok == token::DotDot { + RangeLimits::HalfOpen + } else { + RangeLimits::Closed + }); Ok(self.mk_expr(lo, hi, r, attrs)) } diff --git a/src/libsyntax/parse/token.rs b/src/libsyntax/parse/token.rs index accbb54c629b..0c59c240bcdd 100644 --- a/src/libsyntax/parse/token.rs +++ b/src/libsyntax/parse/token.rs @@ -196,7 +196,7 @@ impl Token { BinOp(Or) => true, // in lambda syntax OrOr => true, // in lambda syntax AndAnd => true, // double borrow - DotDot => true, // range notation + DotDot | DotDotDot => true, // range notation ModSep => true, Interpolated(NtExpr(..)) => true, Interpolated(NtIdent(..)) => true, diff --git a/src/libsyntax/print/pprust.rs b/src/libsyntax/print/pprust.rs index b4e08d65a0a8..8d81787d9224 100644 --- a/src/libsyntax/print/pprust.rs +++ b/src/libsyntax/print/pprust.rs @@ -2163,11 +2163,15 @@ impl<'a> State<'a> { try!(self.print_expr(&index)); try!(word(&mut self.s, "]")); } - ast::ExprKind::Range(ref start, ref end) => { + ast::ExprKing::Range(ref start, ref end, limits) => { if let &Some(ref e) = start { try!(self.print_expr(&e)); } - try!(word(&mut self.s, "..")); + if limits == ast::RangeLimits::HalfOpen { + try!(word(&mut self.s, "..")); + } else { + try!(word(&mut self.s, "...")); + } if let &Some(ref e) = end { try!(self.print_expr(&e)); } diff --git a/src/libsyntax/util/parser.rs b/src/libsyntax/util/parser.rs index 6fb81bb6a768..df4eb1c9ed7d 100644 --- a/src/libsyntax/util/parser.rs +++ b/src/libsyntax/util/parser.rs @@ -61,6 +61,8 @@ pub enum AssocOp { As, /// `..` range DotDot, + /// `...` range + DotDotDot, /// `:` Colon, } @@ -102,6 +104,7 @@ impl AssocOp { Token::AndAnd => Some(LAnd), Token::OrOr => Some(LOr), Token::DotDot => Some(DotDot), + Token::DotDotDot => Some(DotDotDot), Token::Colon => Some(Colon), _ if t.is_keyword(keywords::As) => Some(As), _ => None @@ -147,7 +150,7 @@ impl AssocOp { Less | Greater | LessEqual | GreaterEqual | Equal | NotEqual => 7, LAnd => 6, LOr => 5, - DotDot => 4, + DotDot | DotDotDot => 4, Inplace => 3, Assign | AssignOp(_) => 2, } @@ -162,7 +165,7 @@ impl AssocOp { As | Multiply | Divide | Modulus | Add | Subtract | ShiftLeft | ShiftRight | BitAnd | BitXor | BitOr | Less | Greater | LessEqual | GreaterEqual | Equal | NotEqual | LAnd | LOr | Colon => Fixity::Left, - DotDot => Fixity::None + DotDot | DotDotDot => Fixity::None } } @@ -171,7 +174,8 @@ impl 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 | Colon => false + ShiftLeft | ShiftRight | BitAnd | BitXor | BitOr | LAnd | LOr | + DotDot | DotDotDot | Colon => false } } @@ -181,7 +185,7 @@ impl AssocOp { Assign | AssignOp(_) | Inplace => true, Less | Greater | LessEqual | GreaterEqual | Equal | NotEqual | As | Multiply | Divide | Modulus | Add | Subtract | ShiftLeft | ShiftRight | BitAnd | BitXor | BitOr | LAnd | - LOr | DotDot | Colon => false + LOr | DotDot | DotDotDot | Colon => false } } @@ -206,7 +210,7 @@ impl AssocOp { BitOr => Some(BinOpKind::BitOr), LAnd => Some(BinOpKind::And), LOr => Some(BinOpKind::Or), - Inplace | Assign | AssignOp(_) | As | DotDot | Colon => None + Inplace | Assign | AssignOp(_) | As | DotDot | DotDotDot | Colon => None } } } diff --git a/src/libsyntax/visit.rs b/src/libsyntax/visit.rs index f26610b8b8d1..73ad488e55c9 100644 --- a/src/libsyntax/visit.rs +++ b/src/libsyntax/visit.rs @@ -763,7 +763,7 @@ pub fn walk_expr<'v, V: Visitor<'v>>(visitor: &mut V, expression: &'v Expr) { visitor.visit_expr(main_expression); visitor.visit_expr(index_expression) } - ExprKind::Range(ref start, ref end) => { + ExprKind::Range(ref start, ref end, _) => { walk_list!(visitor, visit_expr, start); walk_list!(visitor, visit_expr, end); }