Refine postfix operators handling.

This commit is contained in:
Stephen Chung 2020-04-17 19:00:52 +08:00
parent 3a93ab8240
commit 5d9a99cefc
3 changed files with 83 additions and 88 deletions

View File

@ -45,7 +45,7 @@ type IteratorFn = dyn Fn(&Dynamic) -> Box<dyn Iterator<Item = Dynamic>> + Send +
type IteratorFn = dyn Fn(&Dynamic) -> Box<dyn Iterator<Item = Dynamic>>; type IteratorFn = dyn Fn(&Dynamic) -> Box<dyn Iterator<Item = Dynamic>>;
#[cfg(debug_assertions)] #[cfg(debug_assertions)]
pub const MAX_CALL_STACK_DEPTH: usize = 32; pub const MAX_CALL_STACK_DEPTH: usize = 28;
#[cfg(not(debug_assertions))] #[cfg(not(debug_assertions))]
pub const MAX_CALL_STACK_DEPTH: usize = 256; pub const MAX_CALL_STACK_DEPTH: usize = 256;

View File

@ -470,10 +470,42 @@ impl Expr {
_ => false, _ => false,
} }
} }
/// Is a particular token allowed as a postfix operator to this expression?
pub fn is_valid_postfix(&self, token: &Token) -> bool {
match self {
Expr::IntegerConstant(_, _)
| Expr::FloatConstant(_, _)
| Expr::CharConstant(_, _)
| Expr::In(_, _, _)
| Expr::And(_, _, _)
| Expr::Or(_, _, _)
| Expr::True(_)
| Expr::False(_)
| Expr::Unit(_) => false,
Expr::StringConstant(_, _)
| Expr::Stmt(_, _)
| Expr::FunctionCall(_, _, _, _)
| Expr::Assignment(_, _, _)
| Expr::Dot(_, _, _)
| Expr::Index(_, _, _)
| Expr::Array(_, _)
| Expr::Map(_, _) => match token {
Token::LeftBracket => true,
_ => false,
},
Expr::Variable(_, _) | Expr::Property(_, _) => match token {
Token::LeftBracket | Token::LeftParen => true,
_ => false,
},
}
}
} }
/// Consume a particular token, checking that it is the expected one. /// Consume a particular token, checking that it is the expected one.
fn eat_token(input: &mut Peekable<TokenIterator>, token: Token) { fn eat_token(input: &mut Peekable<TokenIterator>, token: Token) -> Position {
if let Some((t, pos)) = input.next() { if let Some((t, pos)) = input.next() {
if t != token { if t != token {
panic!( panic!(
@ -483,6 +515,7 @@ fn eat_token(input: &mut Peekable<TokenIterator>, token: Token) {
pos pos
); );
} }
pos
} else { } else {
panic!("expecting {} but already EOF", token.syntax()); panic!("expecting {} but already EOF", token.syntax());
} }
@ -568,7 +601,9 @@ fn parse_call_expr<'a, S: Into<Cow<'static, str>> + Display>(
eat_token(input, Token::RightParen); eat_token(input, Token::RightParen);
return Ok(Expr::FunctionCall(id.into(), args_expr_list, None, begin)); return Ok(Expr::FunctionCall(id.into(), args_expr_list, None, begin));
} }
Some((Token::Comma, _)) => eat_token(input, Token::Comma), Some((Token::Comma, _)) => {
eat_token(input, Token::Comma);
}
Some((_, pos)) => { Some((_, pos)) => {
return Err(PERR::MissingToken( return Err(PERR::MissingToken(
",".into(), ",".into(),
@ -706,38 +741,6 @@ fn parse_index_expr<'a>(
} }
} }
/// Parse an expression that begins with an identifier.
fn parse_ident_expr<'a, S: Into<Cow<'static, str>> + Display>(
id: S,
input: &mut Peekable<TokenIterator<'a>>,
begin: Position,
allow_stmt_expr: bool,
) -> Result<Expr, ParseError> {
match input.peek() {
// id(...) - function call
Some((Token::LeftParen, _)) => {
eat_token(input, Token::LeftParen);
parse_call_expr(id, input, begin, allow_stmt_expr)
}
// id[...] - indexing
#[cfg(not(feature = "no_index"))]
Some((Token::LeftBracket, pos)) => {
let pos = *pos;
eat_token(input, Token::LeftBracket);
parse_index_expr(
Box::new(Expr::Variable(id.into(), begin)),
input,
pos,
allow_stmt_expr,
)
}
// id - variable
Some(_) => Ok(Expr::Variable(id.into(), begin)),
// {EOF}
None => Ok(Expr::Variable(id.into(), begin)),
}
}
/// Parse an array literal. /// Parse an array literal.
fn parse_array_literal<'a>( fn parse_array_literal<'a>(
input: &mut Peekable<TokenIterator<'a>>, input: &mut Peekable<TokenIterator<'a>>,
@ -829,7 +832,9 @@ fn parse_map_literal<'a>(
map.push((name, expr, pos)); map.push((name, expr, pos));
match input.peek() { match input.peek() {
Some((Token::Comma, _)) => eat_token(input, Token::Comma), Some((Token::Comma, _)) => {
eat_token(input, Token::Comma);
}
Some((Token::RightBrace, _)) => { Some((Token::RightBrace, _)) => {
eat_token(input, Token::RightBrace); eat_token(input, Token::RightBrace);
break; break;
@ -881,51 +886,45 @@ fn parse_primary<'a>(
None => return Err(PERR::UnexpectedEOF.into_err_eof()), None => return Err(PERR::UnexpectedEOF.into_err_eof()),
}; };
let mut can_be_indexed = false;
let mut root_expr = match token { let mut root_expr = match token {
(Token::IntegerConstant(x), pos) => Ok(Expr::IntegerConstant(x, pos)), (Token::IntegerConstant(x), pos) => Expr::IntegerConstant(x, pos),
(Token::FloatConstant(x), pos) => Ok(Expr::FloatConstant(x, pos)), (Token::FloatConstant(x), pos) => Expr::FloatConstant(x, pos),
(Token::CharConstant(c), pos) => Ok(Expr::CharConstant(c, pos)), (Token::CharConstant(c), pos) => Expr::CharConstant(c, pos),
(Token::StringConst(s), pos) => { (Token::StringConst(s), pos) => Expr::StringConstant(s.into(), pos),
can_be_indexed = true; (Token::Identifier(s), pos) => Expr::Variable(s.into(), pos),
Ok(Expr::StringConstant(s.into(), pos)) (Token::LeftParen, pos) => parse_paren_expr(input, pos, allow_stmt_expr)?,
}
(Token::Identifier(s), pos) => {
can_be_indexed = true;
parse_ident_expr(s, input, pos, allow_stmt_expr)
}
(Token::LeftParen, pos) => {
can_be_indexed = true;
parse_paren_expr(input, pos, allow_stmt_expr)
}
#[cfg(not(feature = "no_index"))] #[cfg(not(feature = "no_index"))]
(Token::LeftBracket, pos) => { (Token::LeftBracket, pos) => parse_array_literal(input, pos, allow_stmt_expr)?,
can_be_indexed = true;
parse_array_literal(input, pos, allow_stmt_expr)
}
#[cfg(not(feature = "no_object"))] #[cfg(not(feature = "no_object"))]
(Token::MapStart, pos) => { (Token::MapStart, pos) => parse_map_literal(input, pos, allow_stmt_expr)?,
can_be_indexed = true; (Token::True, pos) => Expr::True(pos),
parse_map_literal(input, pos, allow_stmt_expr) (Token::False, pos) => Expr::False(pos),
} (Token::LexError(err), pos) => return Err(PERR::BadInput(err.to_string()).into_err(pos)),
(Token::True, pos) => Ok(Expr::True(pos)),
(Token::False, pos) => Ok(Expr::False(pos)),
(Token::LexError(err), pos) => Err(PERR::BadInput(err.to_string()).into_err(pos)),
(token, pos) => { (token, pos) => {
Err(PERR::BadInput(format!("Unexpected '{}'", token.syntax())).into_err(pos)) return Err(PERR::BadInput(format!("Unexpected '{}'", token.syntax())).into_err(pos))
} }
}?; };
#[cfg(feature = "no_index")] // Tail processing all possible postfix operators
let can_be_indexed = false; while let Some((token, _)) = input.peek() {
if !root_expr.is_valid_postfix(token) {
break;
}
if can_be_indexed { let (token, pos) = input.next().unwrap();
// Tail processing all possible indexing
while let Some((Token::LeftBracket, pos)) = input.peek() { root_expr = match (root_expr, token) {
let pos = *pos; // Function call
eat_token(input, Token::LeftBracket); (Expr::Variable(id, pos), Token::LeftParen)
root_expr = parse_index_expr(Box::new(root_expr), input, pos, allow_stmt_expr)?; | (Expr::Property(id, pos), Token::LeftParen) => {
parse_call_expr(id, input, pos, allow_stmt_expr)?
}
// Indexing
(expr, Token::LeftBracket) => {
parse_index_expr(Box::new(expr), input, pos, allow_stmt_expr)?
}
// Unknown postfix operator
(expr, token) => panic!("unknown postfix operator {:?} for {:?}", token, expr),
} }
} }
@ -947,9 +946,8 @@ fn parse_unary<'a>(
)) ))
} }
// -expr // -expr
Some((Token::UnaryMinus, pos)) => { Some((Token::UnaryMinus, _)) => {
let pos = *pos; let pos = eat_token(input, Token::UnaryMinus);
eat_token(input, Token::UnaryMinus);
match parse_unary(input, allow_stmt_expr)? { match parse_unary(input, allow_stmt_expr)? {
// Negative integer // Negative integer
@ -985,9 +983,8 @@ fn parse_unary<'a>(
parse_unary(input, allow_stmt_expr) parse_unary(input, allow_stmt_expr)
} }
// !expr // !expr
Some((Token::Bang, pos)) => { Some((Token::Bang, _)) => {
let pos = *pos; let pos = eat_token(input, Token::Bang);
eat_token(input, Token::Bang);
Ok(Expr::FunctionCall( Ok(Expr::FunctionCall(
"!".into(), "!".into(),
vec![parse_primary(input, allow_stmt_expr)?], vec![parse_primary(input, allow_stmt_expr)?],
@ -1637,14 +1634,12 @@ fn parse_stmt<'a>(
(Token::Loop, _) => parse_loop(input, allow_stmt_expr), (Token::Loop, _) => parse_loop(input, allow_stmt_expr),
(Token::For, _) => parse_for(input, allow_stmt_expr), (Token::For, _) => parse_for(input, allow_stmt_expr),
(Token::Continue, pos) if breakable => { (Token::Continue, _) if breakable => {
let pos = *pos; let pos = eat_token(input, Token::Continue);
eat_token(input, Token::Continue);
Ok(Stmt::Continue(pos)) Ok(Stmt::Continue(pos))
} }
(Token::Break, pos) if breakable => { (Token::Break, _) if breakable => {
let pos = *pos; let pos = eat_token(input, Token::Break);
eat_token(input, Token::Break);
Ok(Stmt::Break(pos)) Ok(Stmt::Break(pos))
} }
(Token::Continue, pos) | (Token::Break, pos) => Err(PERR::LoopBreak.into_err(*pos)), (Token::Continue, pos) | (Token::Break, pos) => Err(PERR::LoopBreak.into_err(*pos)),

View File

@ -9,10 +9,10 @@ fn test_stack_overflow() -> Result<(), EvalAltResult> {
engine.eval::<i64>( engine.eval::<i64>(
r" r"
fn foo(n) { if n == 0 { 0 } else { n + foo(n-1) } } fn foo(n) { if n == 0 { 0 } else { n + foo(n-1) } }
foo(30) foo(25)
", ",
)?, )?,
465 325
); );
match engine.eval::<()>( match engine.eval::<()>(