Disallow assignments in expressions.
This commit is contained in:
parent
7df36033c4
commit
fbfea60903
182
src/parser.rs
182
src/parser.rs
@ -1072,14 +1072,42 @@ fn parse_assignment(lhs: Expr, rhs: Expr, pos: Position) -> Result<Expr, Box<Par
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Parse an operator-assignment expression.
|
fn parse_assignment_stmt<'a>(
|
||||||
fn parse_op_assignment<S: Into<Cow<'static, str>>>(
|
input: &mut Peekable<TokenIterator<'a>>,
|
||||||
op: S,
|
|
||||||
lhs: Expr,
|
lhs: Expr,
|
||||||
rhs: Expr,
|
allow_stmt_expr: bool,
|
||||||
pos: Position,
|
|
||||||
) -> Result<Expr, Box<ParseError>> {
|
) -> Result<Expr, Box<ParseError>> {
|
||||||
|
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<TokenIterator<'a>>,
|
||||||
|
lhs: Expr,
|
||||||
|
allow_stmt_expr: bool,
|
||||||
|
) -> Result<Expr, Box<ParseError>> {
|
||||||
|
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 lhs_copy = lhs.clone();
|
||||||
|
let rhs = parse_expr(input, allow_stmt_expr)?;
|
||||||
|
|
||||||
// lhs op= rhs -> lhs = op(lhs, rhs)
|
// lhs op= rhs -> lhs = op(lhs, rhs)
|
||||||
parse_assignment(
|
parse_assignment(
|
||||||
@ -1269,9 +1297,44 @@ fn parse_binary_op<'a>(
|
|||||||
}
|
}
|
||||||
Token::Divide => Expr::FunctionCall("/".into(), vec![current_lhs, rhs], None, pos),
|
Token::Divide => Expr::FunctionCall("/".into(), vec![current_lhs, rhs], None, pos),
|
||||||
|
|
||||||
Token::Equals => parse_assignment(current_lhs, rhs, pos)?,
|
Token::LeftShift => {
|
||||||
Token::PlusAssign => parse_op_assignment("+", current_lhs, rhs, pos)?,
|
Expr::FunctionCall("<<".into(), vec![current_lhs, rhs], None, pos)
|
||||||
Token::MinusAssign => parse_op_assignment("-", current_lhs, rhs, 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"))]
|
#[cfg(not(feature = "no_object"))]
|
||||||
Token::Period => {
|
Token::Period => {
|
||||||
@ -1300,71 +1363,6 @@ fn parse_binary_op<'a>(
|
|||||||
Expr::Dot(Box::new(current_lhs), Box::new(check_property(rhs)?), pos)
|
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)),
|
token => return Err(PERR::UnknownOperator(token.syntax().into()).into_err(pos)),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@ -1376,12 +1374,11 @@ fn parse_expr<'a>(
|
|||||||
input: &mut Peekable<TokenIterator<'a>>,
|
input: &mut Peekable<TokenIterator<'a>>,
|
||||||
allow_stmt_expr: bool,
|
allow_stmt_expr: bool,
|
||||||
) -> Result<Expr, Box<ParseError>> {
|
) -> Result<Expr, Box<ParseError>> {
|
||||||
// Parse a real expression
|
|
||||||
let lhs = parse_unary(input, allow_stmt_expr)?;
|
let lhs = parse_unary(input, allow_stmt_expr)?;
|
||||||
parse_binary_op(input, 1, lhs, 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>(
|
fn ensure_not_statement_expr<'a>(
|
||||||
input: &mut Peekable<TokenIterator<'a>>,
|
input: &mut Peekable<TokenIterator<'a>>,
|
||||||
type_name: &str,
|
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<TokenIterator<'a>>,
|
||||||
|
) -> Result<(), Box<ParseError>> {
|
||||||
|
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.
|
/// Parse an if statement.
|
||||||
fn parse_if<'a>(
|
fn parse_if<'a>(
|
||||||
input: &mut Peekable<TokenIterator<'a>>,
|
input: &mut Peekable<TokenIterator<'a>>,
|
||||||
@ -1408,6 +1434,7 @@ fn parse_if<'a>(
|
|||||||
// if guard { if_body }
|
// if guard { if_body }
|
||||||
ensure_not_statement_expr(input, "a boolean")?;
|
ensure_not_statement_expr(input, "a boolean")?;
|
||||||
let guard = parse_expr(input, allow_stmt_expr)?;
|
let guard = parse_expr(input, allow_stmt_expr)?;
|
||||||
|
ensure_not_assignment(input)?;
|
||||||
let if_body = parse_block(input, breakable, allow_stmt_expr)?;
|
let if_body = parse_block(input, breakable, allow_stmt_expr)?;
|
||||||
|
|
||||||
// if guard { if_body } else ...
|
// if guard { if_body } else ...
|
||||||
@ -1441,6 +1468,7 @@ fn parse_while<'a>(
|
|||||||
// while guard { body }
|
// while guard { body }
|
||||||
ensure_not_statement_expr(input, "a boolean")?;
|
ensure_not_statement_expr(input, "a boolean")?;
|
||||||
let guard = parse_expr(input, allow_stmt_expr)?;
|
let guard = parse_expr(input, allow_stmt_expr)?;
|
||||||
|
ensure_not_assignment(input)?;
|
||||||
let body = parse_block(input, true, allow_stmt_expr)?;
|
let body = parse_block(input, true, allow_stmt_expr)?;
|
||||||
|
|
||||||
Ok(Stmt::While(Box::new(guard), Box::new(body)))
|
Ok(Stmt::While(Box::new(guard), Box::new(body)))
|
||||||
@ -1604,7 +1632,9 @@ fn parse_expr_stmt<'a>(
|
|||||||
input: &mut Peekable<TokenIterator<'a>>,
|
input: &mut Peekable<TokenIterator<'a>>,
|
||||||
allow_stmt_expr: bool,
|
allow_stmt_expr: bool,
|
||||||
) -> Result<Stmt, Box<ParseError>> {
|
) -> Result<Stmt, Box<ParseError>> {
|
||||||
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.
|
/// Parse a single statement.
|
||||||
|
Loading…
Reference in New Issue
Block a user