From fbfea609039d5541157d188dc7b0e2463734be6c Mon Sep 17 00:00:00 2001 From: Stephen Chung Date: Wed, 22 Apr 2020 17:37:06 +0800 Subject: [PATCH] Disallow assignments in expressions. --- src/parser.rs | 182 +++++++++++++++++++++++++++++--------------------- 1 file changed, 106 insertions(+), 76 deletions(-) diff --git a/src/parser.rs b/src/parser.rs index 9d9ae6b7..183a43bb 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -1072,14 +1072,42 @@ fn parse_assignment(lhs: Expr, rhs: Expr, pos: Position) -> Result>>( - op: S, +fn parse_assignment_stmt<'a>( + input: &mut Peekable>, lhs: Expr, - rhs: Expr, - pos: Position, + allow_stmt_expr: bool, ) -> Result> { + let pos = eat_token(input, Token::Equals); + let rhs = parse_expr(input, allow_stmt_expr)?; + parse_assignment(lhs, rhs, pos) +} + +/// Parse an operator-assignment expression. +fn parse_op_assignment_stmt<'a>( + input: &mut Peekable>, + lhs: Expr, + allow_stmt_expr: bool, +) -> Result> { + let (op, pos) = match *input.peek().unwrap() { + (Token::Equals, _) => return parse_assignment_stmt(input, lhs, allow_stmt_expr), + (Token::PlusAssign, pos) => ("+", pos), + (Token::MinusAssign, pos) => ("-", pos), + (Token::MultiplyAssign, pos) => ("*", pos), + (Token::DivideAssign, pos) => ("/", pos), + (Token::LeftShiftAssign, pos) => ("<<", pos), + (Token::RightShiftAssign, pos) => (">>", pos), + (Token::ModuloAssign, pos) => ("%", pos), + (Token::PowerOfAssign, pos) => ("~", pos), + (Token::AndAssign, pos) => ("&", pos), + (Token::OrAssign, pos) => ("|", pos), + (Token::XOrAssign, pos) => ("^", pos), + (_, _) => return Ok(lhs), + }; + + input.next(); + let lhs_copy = lhs.clone(); + let rhs = parse_expr(input, allow_stmt_expr)?; // lhs op= rhs -> lhs = op(lhs, rhs) parse_assignment( @@ -1269,9 +1297,44 @@ fn parse_binary_op<'a>( } Token::Divide => Expr::FunctionCall("/".into(), vec![current_lhs, rhs], None, pos), - Token::Equals => parse_assignment(current_lhs, rhs, pos)?, - Token::PlusAssign => parse_op_assignment("+", current_lhs, rhs, pos)?, - Token::MinusAssign => parse_op_assignment("-", current_lhs, rhs, pos)?, + Token::LeftShift => { + Expr::FunctionCall("<<".into(), vec![current_lhs, rhs], None, pos) + } + Token::RightShift => { + Expr::FunctionCall(">>".into(), vec![current_lhs, rhs], None, pos) + } + Token::Modulo => Expr::FunctionCall("%".into(), vec![current_lhs, rhs], None, pos), + Token::PowerOf => Expr::FunctionCall("~".into(), vec![current_lhs, rhs], None, pos), + + // Comparison operators default to false when passed invalid operands + Token::EqualsTo => { + Expr::FunctionCall("==".into(), vec![current_lhs, rhs], Some(false.into()), pos) + } + Token::NotEqualsTo => { + Expr::FunctionCall("!=".into(), vec![current_lhs, rhs], Some(false.into()), pos) + } + Token::LessThan => { + Expr::FunctionCall("<".into(), vec![current_lhs, rhs], Some(false.into()), pos) + } + Token::LessThanEqualsTo => { + Expr::FunctionCall("<=".into(), vec![current_lhs, rhs], Some(false.into()), pos) + } + Token::GreaterThan => { + Expr::FunctionCall(">".into(), vec![current_lhs, rhs], Some(false.into()), pos) + } + Token::GreaterThanEqualsTo => { + Expr::FunctionCall(">=".into(), vec![current_lhs, rhs], Some(false.into()), pos) + } + + Token::Or => Expr::Or(Box::new(current_lhs), Box::new(rhs), pos), + Token::And => Expr::And(Box::new(current_lhs), Box::new(rhs), pos), + Token::Ampersand => { + Expr::FunctionCall("&".into(), vec![current_lhs, rhs], None, pos) + } + Token::Pipe => Expr::FunctionCall("|".into(), vec![current_lhs, rhs], None, pos), + Token::XOr => Expr::FunctionCall("^".into(), vec![current_lhs, rhs], None, pos), + + Token::In => parse_in_expr(current_lhs, rhs, pos)?, #[cfg(not(feature = "no_object"))] Token::Period => { @@ -1300,71 +1363,6 @@ fn parse_binary_op<'a>( Expr::Dot(Box::new(current_lhs), Box::new(check_property(rhs)?), pos) } - // Comparison operators default to false when passed invalid operands - Token::EqualsTo => Expr::FunctionCall( - "==".into(), - vec![current_lhs, rhs], - Some(false.into()), - pos, - ), - Token::NotEqualsTo => Expr::FunctionCall( - "!=".into(), - vec![current_lhs, rhs], - Some(false.into()), - pos, - ), - Token::LessThan => Expr::FunctionCall( - "<".into(), - vec![current_lhs, rhs], - Some(false.into()), - pos, - ), - Token::LessThanEqualsTo => Expr::FunctionCall( - "<=".into(), - vec![current_lhs, rhs], - Some(false.into()), - pos, - ), - Token::GreaterThan => Expr::FunctionCall( - ">".into(), - vec![current_lhs, rhs], - Some(false.into()), - pos, - ), - Token::GreaterThanEqualsTo => Expr::FunctionCall( - ">=".into(), - vec![current_lhs, rhs], - Some(false.into()), - pos, - ), - - Token::Or => Expr::Or(Box::new(current_lhs), Box::new(rhs), pos), - Token::And => Expr::And(Box::new(current_lhs), Box::new(rhs), pos), - - Token::In => parse_in_expr(current_lhs, rhs, pos)?, - - Token::XOr => Expr::FunctionCall("^".into(), vec![current_lhs, rhs], None, pos), - Token::OrAssign => parse_op_assignment("|", current_lhs, rhs, pos)?, - Token::AndAssign => parse_op_assignment("&", current_lhs, rhs, pos)?, - Token::XOrAssign => parse_op_assignment("^", current_lhs, rhs, pos)?, - Token::MultiplyAssign => parse_op_assignment("*", current_lhs, rhs, pos)?, - Token::DivideAssign => parse_op_assignment("/", current_lhs, rhs, pos)?, - Token::Pipe => Expr::FunctionCall("|".into(), vec![current_lhs, rhs], None, pos), - Token::LeftShift => { - Expr::FunctionCall("<<".into(), vec![current_lhs, rhs], None, pos) - } - Token::RightShift => { - Expr::FunctionCall(">>".into(), vec![current_lhs, rhs], None, pos) - } - Token::LeftShiftAssign => parse_op_assignment("<<", current_lhs, rhs, pos)?, - Token::RightShiftAssign => parse_op_assignment(">>", current_lhs, rhs, pos)?, - Token::Ampersand => { - Expr::FunctionCall("&".into(), vec![current_lhs, rhs], None, pos) - } - Token::Modulo => Expr::FunctionCall("%".into(), vec![current_lhs, rhs], None, pos), - Token::ModuloAssign => parse_op_assignment("%", current_lhs, rhs, pos)?, - Token::PowerOf => Expr::FunctionCall("~".into(), vec![current_lhs, rhs], None, pos), - Token::PowerOfAssign => parse_op_assignment("~", current_lhs, rhs, pos)?, token => return Err(PERR::UnknownOperator(token.syntax().into()).into_err(pos)), }; } @@ -1376,12 +1374,11 @@ fn parse_expr<'a>( input: &mut Peekable>, allow_stmt_expr: bool, ) -> Result> { - // Parse a real expression let lhs = parse_unary(input, allow_stmt_expr)?; parse_binary_op(input, 1, lhs, allow_stmt_expr) } -/// Make sure that the expression is not a statement expression (i.e. wrapped in {}) +/// Make sure that the expression is not a statement expression (i.e. wrapped in `{}`). fn ensure_not_statement_expr<'a>( input: &mut Peekable>, type_name: &str, @@ -1396,6 +1393,35 @@ fn ensure_not_statement_expr<'a>( } } +/// Make sure that the expression is not a mis-typed assignment (i.e. `a = b` instead of `a == b`). +fn ensure_not_assignment<'a>( + input: &mut Peekable>, +) -> Result<(), Box> { + match input.peek().unwrap() { + (Token::Equals, pos) => { + return Err(PERR::BadInput("Possibly a typo of '=='?".to_string()).into_err(*pos)) + } + (Token::PlusAssign, pos) + | (Token::MinusAssign, pos) + | (Token::MultiplyAssign, pos) + | (Token::DivideAssign, pos) + | (Token::LeftShiftAssign, pos) + | (Token::RightShiftAssign, pos) + | (Token::ModuloAssign, pos) + | (Token::PowerOfAssign, pos) + | (Token::AndAssign, pos) + | (Token::OrAssign, pos) + | (Token::XOrAssign, pos) => { + return Err(PERR::BadInput( + "Expecting a boolean expression, not an assignment".to_string(), + ) + .into_err(*pos)) + } + + _ => Ok(()), + } +} + /// Parse an if statement. fn parse_if<'a>( input: &mut Peekable>, @@ -1408,6 +1434,7 @@ fn parse_if<'a>( // if guard { if_body } ensure_not_statement_expr(input, "a boolean")?; let guard = parse_expr(input, allow_stmt_expr)?; + ensure_not_assignment(input)?; let if_body = parse_block(input, breakable, allow_stmt_expr)?; // if guard { if_body } else ... @@ -1441,6 +1468,7 @@ fn parse_while<'a>( // while guard { body } ensure_not_statement_expr(input, "a boolean")?; let guard = parse_expr(input, allow_stmt_expr)?; + ensure_not_assignment(input)?; let body = parse_block(input, true, allow_stmt_expr)?; Ok(Stmt::While(Box::new(guard), Box::new(body))) @@ -1604,7 +1632,9 @@ fn parse_expr_stmt<'a>( input: &mut Peekable>, allow_stmt_expr: bool, ) -> Result> { - Ok(Stmt::Expr(Box::new(parse_expr(input, allow_stmt_expr)?))) + let expr = parse_expr(input, allow_stmt_expr)?; + let expr = parse_op_assignment_stmt(input, expr, allow_stmt_expr)?; + Ok(Stmt::Expr(Box::new(expr))) } /// Parse a single statement.