Skip to content

Commit

Permalink
Generalise associative operator parsing
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
nagisa committed Oct 27, 2015
1 parent 540fd3a commit 471f5a1
Show file tree
Hide file tree
Showing 9 changed files with 334 additions and 194 deletions.
20 changes: 0 additions & 20 deletions src/libsyntax/ast_util.rs
Expand Up @@ -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 {
Generics {
lifetimes: Vec::new(),
Expand Down
1 change: 1 addition & 0 deletions src/libsyntax/lib.rs
Expand Up @@ -66,6 +66,7 @@ pub mod util {
#[cfg(test)]
pub mod parser_testing;
pub mod small_vector;
pub mod parser;
}

pub mod diagnostics {
Expand Down
263 changes: 132 additions & 131 deletions src/libsyntax/parse/parser.rs

Large diffs are not rendered by default.

14 changes: 8 additions & 6 deletions src/libsyntax/print/pprust.rs
Expand Up @@ -14,6 +14,7 @@ use abi;
use ast;
use ast::{RegionTyParamBound, TraitTyParamBound, TraitBoundModifier};
use ast_util;
use util::parser::AssocOp;
use attr;
use owned_slice::OwnedSlice;
use attr::{AttrMetaMethods, AttributeMethods};
Expand Down Expand Up @@ -445,7 +446,8 @@ fn needs_parentheses(expr: &ast::Expr) -> bool {
match expr.node {
ast::ExprAssign(..) | ast::ExprBinary(..) |
ast::ExprClosure(..) |
ast::ExprAssignOp(..) | ast::ExprCast(..) => true,
ast::ExprAssignOp(..) | ast::ExprCast(..) |
ast::ExprInPlace(..) => true,
_ => false,
}
}
Expand Down Expand Up @@ -1776,8 +1778,8 @@ impl<'a> State<'a> {
binop: ast::BinOp) -> bool {
match sub_expr.node {
ast::ExprBinary(ref sub_op, _, _) => {
if ast_util::operator_prec(sub_op.node) <
ast_util::operator_prec(binop.node) {
if AssocOp::from_ast_binop(sub_op.node).precedence() <
AssocOp::from_ast_binop(binop.node).precedence() {
true
} else {
false
Expand All @@ -1802,10 +1804,10 @@ impl<'a> State<'a> {
fn print_expr_in_place(&mut self,
place: &ast::Expr,
expr: &ast::Expr) -> io::Result<()> {
try!(self.word_space("in"));
try!(self.print_expr(place));
try!(self.print_expr_maybe_paren(place));
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<()> {
Expand Down
191 changes: 191 additions & 0 deletions src/libsyntax/util/parser.rs
@@ -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
}
}

}
2 changes: 1 addition & 1 deletion src/test/compile-fail/feature-gate-placement-expr.rs
Expand Up @@ -19,6 +19,6 @@
fn main() {
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);
}
2 changes: 1 addition & 1 deletion src/test/compile-fail/issue-14084.rs
Expand Up @@ -12,6 +12,6 @@
#![feature(placement_in_syntax)]

fn main() {
in () { 0 };
() <- 0;
//~^ ERROR: the trait `core::ops::Placer<_>` is not implemented
}
17 changes: 0 additions & 17 deletions src/test/parse-fail/removed-syntax-larrow-init.rs

This file was deleted.

18 changes: 0 additions & 18 deletions src/test/parse-fail/removed-syntax-larrow-move.rs

This file was deleted.

0 comments on commit 471f5a1

Please sign in to comment.