From 6d190096fdf081061c2114acb39ab46d65eee888 Mon Sep 17 00:00:00 2001 From: Stephen Chung Date: Mon, 1 Jun 2020 13:03:01 +0800 Subject: [PATCH 1/3] Fix bug with bracket postfix when no_index. --- src/parser.rs | 19 ++++++++++++++----- tests/string.rs | 17 ++++++++++++++++- 2 files changed, 30 insertions(+), 6 deletions(-) diff --git a/src/parser.rs b/src/parser.rs index 8005acbc..c8f71d7c 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -622,28 +622,33 @@ impl Expr { | Self::Or(_) | Self::True(_) | Self::False(_) - | Self::Unit(_) => false, + | Self::Unit(_) + | Self::Assignment(_) => false, Self::StringConstant(_) | Self::Stmt(_) | Self::FnCall(_) - | Self::Assignment(_) | Self::Dot(_) | Self::Index(_) | Self::Array(_) | Self::Map(_) => match token { + #[cfg(not(feature = "no_index"))] Token::LeftBracket => true, _ => false, }, Self::Variable(_) => match token { - Token::LeftBracket | Token::LeftParen => true, + #[cfg(not(feature = "no_index"))] + Token::LeftBracket => true, + Token::LeftParen => true, Token::DoubleColon => true, _ => false, }, Self::Property(_) => match token { - Token::LeftBracket | Token::LeftParen => true, + #[cfg(not(feature = "no_index"))] + Token::LeftBracket => true, + Token::LeftParen => true, _ => false, }, } @@ -1267,7 +1272,11 @@ fn parse_primary<'a>( parse_index_chain(input, state, expr, token_pos, level + 1, allow_stmt_expr)? } // Unknown postfix operator - (expr, token) => panic!("unknown postfix operator {:?} for {:?}", token, expr), + (expr, token) => panic!( + "unknown postfix operator '{}' for {:?}", + token.syntax(), + expr + ), } } diff --git a/tests/string.rs b/tests/string.rs index b868f050..995bd76e 100644 --- a/tests/string.rs +++ b/tests/string.rs @@ -23,9 +23,24 @@ fn test_string() -> Result<(), Box> { assert_eq!(engine.eval::(r#""foo" + 123"#)?, "foo123"); #[cfg(not(feature = "no_object"))] - assert_eq!(engine.eval::("(42).to_string()")?, "42"); + assert_eq!(engine.eval::("to_string(42)")?, "42"); + + #[cfg(not(feature = "no_index"))] + assert_eq!(engine.eval::(r#"let y = "hello"; y[1]"#)?, 'e'); #[cfg(not(feature = "no_object"))] + assert_eq!(engine.eval::(r#"let y = "hello"; y.len"#)?, 5); + + #[cfg(not(feature = "no_object"))] + assert_eq!( + engine.eval::(r#"let y = "hello"; y.clear(); y.len"#)?, + 0 + ); + + assert_eq!(engine.eval::(r#"let y = "hello"; len(y)"#)?, 5); + + #[cfg(not(feature = "no_object"))] + #[cfg(not(feature = "no_index"))] assert_eq!(engine.eval::(r#"let y = "hello"; y[y.len-1]"#)?, 'o'); #[cfg(not(feature = "no_float"))] From 3f9d0895defb7e739c3f96bdb10d185a517a27d1 Mon Sep 17 00:00:00 2001 From: Stephen Chung Date: Mon, 1 Jun 2020 13:26:02 +0800 Subject: [PATCH 2/3] Bump version. --- Cargo.toml | 2 +- README.md | 8 ++++---- RELEASES.md | 6 +++++- 3 files changed, 10 insertions(+), 6 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 23f4aa90..210b7488 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "rhai" -version = "0.15.0" +version = "0.16.0" edition = "2018" authors = ["Jonathan Turner", "Lukáš Hozda", "Stephen Chung"] description = "Embedded scripting for Rust" diff --git a/README.md b/README.md index bd0cc029..aaf394fc 100644 --- a/README.md +++ b/README.md @@ -37,7 +37,7 @@ Features to do checked arithmetic operations); for [`no-std`](#optional-features) builds, a number of additional dependencies are pulled in to provide for functionalities that used to be in `std`. -**Note:** Currently, the version is `0.15.0`, so the language and API's may change before they stabilize. +**Note:** Currently, the version is `0.16.0`, so the language and API's may change before they stabilize. What Rhai doesn't do -------------------- @@ -71,7 +71,7 @@ Install the Rhai crate on [`crates.io`](https::/crates.io/crates/rhai/) by addin ```toml [dependencies] -rhai = "0.15.0" +rhai = "0.16.0" ``` Use the latest released crate version on [`crates.io`](https::/crates.io/crates/rhai/): @@ -427,7 +427,7 @@ are supported. | `+`, | `+=` | `INT`, `FLOAT` (if not [`no_float`]), `ImmutableString` | | `-`, `*`, `/`, `%`, `~`, | `-=`, `*=`, `/=`, `%=`, `~=` | `INT`, `FLOAT` (if not [`no_float`]) | | `<<`, `>>`, `^`, | `<<=`, `>>=`, `^=` | `INT` | -| `&`, `\|`, | `&=`, `|=` | `INT`, `bool` | +| `&`, `\|`, | `&=`, `\|=` | `INT`, `bool` | | `&&`, `\|\|` | | `bool` | | `==`, `!=` | | `INT`, `FLOAT` (if not [`no_float`]), `bool`, `char`, `()`, `ImmutableString` | | `>`, `>=`, `<`, `<=` | | `INT`, `FLOAT` (if not [`no_float`]), `char`, `()`, `ImmutableString` | @@ -496,7 +496,7 @@ In these cases, use the `compile_expression` and `eval_expression` methods or th let result = engine.eval_expression::("2 + (10 + 10) * 2")?; ``` -When evaluation _expressions_, no full-blown statement (e.g. `if`, `while`, `for`) - not even variable assignments - +When evaluating _expressions_, no full-blown statement (e.g. `if`, `while`, `for`) - not even variable assignments - is supported and will be considered parse errors when encountered. ```rust diff --git a/RELEASES.md b/RELEASES.md index 1208c720..2bb7b783 100644 --- a/RELEASES.md +++ b/RELEASES.md @@ -1,7 +1,11 @@ Rhai Release Notes ================== -Version 0.14.2 +Version 0.16.0 +============== + + +Version 0.15.0 ============== Regression fix From b8b12055b9aaa1f693fc1d74dff55bed3afeb581 Mon Sep 17 00:00:00 2001 From: Stephen Chung Date: Mon, 1 Jun 2020 13:26:20 +0800 Subject: [PATCH 3/3] Disable if-expression when parsing expressions. --- src/parser.rs | 186 ++++++++++++++++++++++++++----------------- tests/expressions.rs | 7 +- 2 files changed, 117 insertions(+), 76 deletions(-) diff --git a/src/parser.rs b/src/parser.rs index c8f71d7c..5ff5e692 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -700,7 +700,8 @@ fn parse_paren_expr<'a>( state: &mut ParseState, pos: Position, level: usize, - allow_stmt_expr: bool, + if_expr: bool, + stmt_expr: bool, ) -> Result { if level > state.max_expr_depth { return Err(PERR::ExprTooDeep.into_err(pos)); @@ -710,7 +711,7 @@ fn parse_paren_expr<'a>( return Ok(Expr::Unit(pos)); } - let expr = parse_expr(input, state, level + 1, allow_stmt_expr)?; + let expr = parse_expr(input, state, level + 1, if_expr, stmt_expr)?; match input.next().unwrap() { // ( xxx ) @@ -734,7 +735,8 @@ fn parse_call_expr<'a>( mut modules: Option>, begin: Position, level: usize, - allow_stmt_expr: bool, + if_expr: bool, + stmt_expr: bool, ) -> Result { let (token, pos) = input.peek().unwrap(); @@ -788,7 +790,7 @@ fn parse_call_expr<'a>( } loop { - args.push(parse_expr(input, state, level + 1, allow_stmt_expr)?); + args.push(parse_expr(input, state, level + 1, if_expr, stmt_expr)?); match input.peek().unwrap() { // id(...args) @@ -855,13 +857,14 @@ fn parse_index_chain<'a>( lhs: Expr, pos: Position, level: usize, - allow_stmt_expr: bool, + if_expr: bool, + stmt_expr: bool, ) -> Result { if level > state.max_expr_depth { return Err(PERR::ExprTooDeep.into_err(pos)); } - let idx_expr = parse_expr(input, state, level + 1, allow_stmt_expr)?; + let idx_expr = parse_expr(input, state, level + 1, if_expr, stmt_expr)?; // Check type of indexing - must be integer or string match &idx_expr { @@ -1008,7 +1011,8 @@ fn parse_index_chain<'a>( idx_expr, idx_pos, level + 1, - allow_stmt_expr, + if_expr, + stmt_expr, )?; // Indexing binds to right Ok(Expr::Index(Box::new((lhs, idx_expr, pos)))) @@ -1042,7 +1046,8 @@ fn parse_array_literal<'a>( state: &mut ParseState, pos: Position, level: usize, - allow_stmt_expr: bool, + if_expr: bool, + stmt_expr: bool, ) -> Result { if level > state.max_expr_depth { return Err(PERR::ExprTooDeep.into_err(pos)); @@ -1052,7 +1057,8 @@ fn parse_array_literal<'a>( if !match_token(input, Token::RightBracket)? { while !input.peek().unwrap().0.is_eof() { - arr.push(parse_expr(input, state, level + 1, allow_stmt_expr)?); + let expr = parse_expr(input, state, level + 1, if_expr, stmt_expr)?; + arr.push(expr); match input.peek().unwrap() { (Token::Comma, _) => eat_token(input, Token::Comma), @@ -1090,7 +1096,8 @@ fn parse_map_literal<'a>( state: &mut ParseState, pos: Position, level: usize, - allow_stmt_expr: bool, + if_expr: bool, + stmt_expr: bool, ) -> Result { if level > state.max_expr_depth { return Err(PERR::ExprTooDeep.into_err(pos)); @@ -1140,7 +1147,7 @@ fn parse_map_literal<'a>( } }; - let expr = parse_expr(input, state, level + 1, allow_stmt_expr)?; + let expr = parse_expr(input, state, level + 1, if_expr, stmt_expr)?; map.push(((name, pos), expr)); @@ -1191,7 +1198,8 @@ fn parse_primary<'a>( input: &mut Peekable>, state: &mut ParseState, level: usize, - allow_stmt_expr: bool, + if_expr: bool, + stmt_expr: bool, ) -> Result { let (token, pos) = input.peek().unwrap(); let pos = *pos; @@ -1202,8 +1210,8 @@ fn parse_primary<'a>( let (token, _) = match token { // { - block statement as expression - Token::LeftBrace if allow_stmt_expr => { - return parse_block(input, state, false, level + 1, allow_stmt_expr) + Token::LeftBrace if stmt_expr => { + return parse_block(input, state, false, level + 1, if_expr, stmt_expr) .map(|block| Expr::Stmt(Box::new((block, pos)))); } Token::EOF => return Err(PERR::UnexpectedEOF.into_err(pos)), @@ -1220,11 +1228,13 @@ fn parse_primary<'a>( let index = state.find(&s); Expr::Variable(Box::new(((s, pos), None, 0, index))) } - Token::LeftParen => parse_paren_expr(input, state, pos, level + 1, allow_stmt_expr)?, + Token::LeftParen => parse_paren_expr(input, state, pos, level + 1, if_expr, stmt_expr)?, #[cfg(not(feature = "no_index"))] - Token::LeftBracket => parse_array_literal(input, state, pos, level + 1, allow_stmt_expr)?, + Token::LeftBracket => { + parse_array_literal(input, state, pos, level + 1, if_expr, stmt_expr)? + } #[cfg(not(feature = "no_object"))] - Token::MapStart => parse_map_literal(input, state, pos, level + 1, allow_stmt_expr)?, + Token::MapStart => parse_map_literal(input, state, pos, level + 1, if_expr, stmt_expr)?, Token::True => Expr::True(pos), Token::False => Expr::False(pos), Token::LexError(err) => return Err(PERR::BadInput(err.to_string()).into_err(pos)), @@ -1247,7 +1257,16 @@ fn parse_primary<'a>( // Function call (Expr::Variable(x), Token::LeftParen) => { let ((name, pos), modules, _, _) = *x; - parse_call_expr(input, state, name, modules, pos, level + 1, allow_stmt_expr)? + parse_call_expr( + input, + state, + name, + modules, + pos, + level + 1, + if_expr, + stmt_expr, + )? } (Expr::Property(_), _) => unreachable!(), // module access @@ -1269,7 +1288,7 @@ fn parse_primary<'a>( // Indexing #[cfg(not(feature = "no_index"))] (expr, Token::LeftBracket) => { - parse_index_chain(input, state, expr, token_pos, level + 1, allow_stmt_expr)? + parse_index_chain(input, state, expr, token_pos, level + 1, if_expr, stmt_expr)? } // Unknown postfix operator (expr, token) => panic!( @@ -1301,7 +1320,8 @@ fn parse_unary<'a>( input: &mut Peekable>, state: &mut ParseState, level: usize, - allow_stmt_expr: bool, + if_expr: bool, + stmt_expr: bool, ) -> Result { let (token, pos) = input.peek().unwrap(); let pos = *pos; @@ -1312,15 +1332,15 @@ fn parse_unary<'a>( match token { // If statement is allowed to act as expressions - Token::If => Ok(Expr::Stmt(Box::new(( - parse_if(input, state, false, level + 1, allow_stmt_expr)?, + Token::If if if_expr => Ok(Expr::Stmt(Box::new(( + parse_if(input, state, false, level + 1, if_expr, stmt_expr)?, pos, )))), // -expr Token::UnaryMinus => { let pos = eat_token(input, Token::UnaryMinus); - match parse_unary(input, state, level + 1, allow_stmt_expr)? { + match parse_unary(input, state, level + 1, if_expr, stmt_expr)? { // Negative integer Expr::IntegerConstant(x) => { let (num, pos) = *x; @@ -1369,13 +1389,14 @@ fn parse_unary<'a>( // +expr Token::UnaryPlus => { eat_token(input, Token::UnaryPlus); - parse_unary(input, state, level + 1, allow_stmt_expr) + parse_unary(input, state, level + 1, if_expr, stmt_expr) } // !expr Token::Bang => { let pos = eat_token(input, Token::Bang); let mut args = StaticVec::new(); - args.push(parse_primary(input, state, level + 1, allow_stmt_expr)?); + let expr = parse_primary(input, state, level + 1, if_expr, stmt_expr)?; + args.push(expr); let op = "!"; let hash = calc_fn_hash(empty(), op, 2, empty()); @@ -1391,7 +1412,7 @@ fn parse_unary<'a>( // Token::EOF => Err(PERR::UnexpectedEOF.into_err(pos)), // All other tokens - _ => parse_primary(input, state, level + 1, allow_stmt_expr), + _ => parse_primary(input, state, level + 1, if_expr, stmt_expr), } } @@ -1451,7 +1472,8 @@ fn parse_op_assignment_stmt<'a>( state: &mut ParseState, lhs: Expr, level: usize, - allow_stmt_expr: bool, + if_expr: bool, + stmt_expr: bool, ) -> Result { let (token, pos) = input.peek().unwrap(); let pos = *pos; @@ -1479,7 +1501,7 @@ fn parse_op_assignment_stmt<'a>( }; let (_, pos) = input.next().unwrap(); - let rhs = parse_expr(input, state, level + 1, allow_stmt_expr)?; + let rhs = parse_expr(input, state, level + 1, if_expr, stmt_expr)?; make_assignment_stmt(op, state, lhs, rhs, pos) } @@ -1694,7 +1716,8 @@ fn parse_binary_op<'a>( parent_precedence: u8, lhs: Expr, mut level: usize, - allow_stmt_expr: bool, + if_expr: bool, + stmt_expr: bool, ) -> Result { if level > state.max_expr_depth { return Err(PERR::ExprTooDeep.into_err(lhs.position())); @@ -1715,14 +1738,14 @@ fn parse_binary_op<'a>( let (op_token, pos) = input.next().unwrap(); - let rhs = parse_unary(input, state, level, allow_stmt_expr)?; + let rhs = parse_unary(input, state, level, if_expr, stmt_expr)?; let next_precedence = input.peek().unwrap().0.precedence(); // Bind to right if the next operator has higher precedence // If same precedence, then check if the operator binds right let rhs = if (precedence == next_precedence && bind_right) || precedence < next_precedence { - parse_binary_op(input, state, precedence, rhs, level, allow_stmt_expr)? + parse_binary_op(input, state, precedence, rhs, level, if_expr, stmt_expr)? } else { // Otherwise bind to left (even if next operator has the same precedence) rhs @@ -1810,7 +1833,8 @@ fn parse_expr<'a>( input: &mut Peekable>, state: &mut ParseState, level: usize, - allow_stmt_expr: bool, + if_expr: bool, + stmt_expr: bool, ) -> Result { let (_, pos) = input.peek().unwrap(); @@ -1818,8 +1842,8 @@ fn parse_expr<'a>( return Err(PERR::ExprTooDeep.into_err(*pos)); } - let lhs = parse_unary(input, state, level + 1, allow_stmt_expr)?; - parse_binary_op(input, state, 1, lhs, level + 1, allow_stmt_expr) + let lhs = parse_unary(input, state, level + 1, if_expr, stmt_expr)?; + parse_binary_op(input, state, 1, lhs, level + 1, if_expr, stmt_expr) } /// Make sure that the expression is not a statement expression (i.e. wrapped in `{}`). @@ -1870,7 +1894,8 @@ fn parse_if<'a>( state: &mut ParseState, breakable: bool, level: usize, - allow_stmt_expr: bool, + if_expr: bool, + stmt_expr: bool, ) -> Result { // if ... let pos = eat_token(input, Token::If); @@ -1881,18 +1906,18 @@ fn parse_if<'a>( // if guard { if_body } ensure_not_statement_expr(input, "a boolean")?; - let guard = parse_expr(input, state, level + 1, allow_stmt_expr)?; + let guard = parse_expr(input, state, level + 1, if_expr, stmt_expr)?; ensure_not_assignment(input)?; - let if_body = parse_block(input, state, breakable, level + 1, allow_stmt_expr)?; + let if_body = parse_block(input, state, breakable, level + 1, if_expr, stmt_expr)?; // if guard { if_body } else ... let else_body = if match_token(input, Token::Else).unwrap_or(false) { Some(if let (Token::If, _) = input.peek().unwrap() { // if guard { if_body } else if ... - parse_if(input, state, breakable, level + 1, allow_stmt_expr)? + parse_if(input, state, breakable, level + 1, if_expr, stmt_expr)? } else { // if guard { if_body } else { else-body } - parse_block(input, state, breakable, level + 1, allow_stmt_expr)? + parse_block(input, state, breakable, level + 1, if_expr, stmt_expr)? }) } else { None @@ -1906,7 +1931,8 @@ fn parse_while<'a>( input: &mut Peekable>, state: &mut ParseState, level: usize, - allow_stmt_expr: bool, + if_expr: bool, + stmt_expr: bool, ) -> Result { // while ... let pos = eat_token(input, Token::While); @@ -1917,9 +1943,9 @@ fn parse_while<'a>( // while guard { body } ensure_not_statement_expr(input, "a boolean")?; - let guard = parse_expr(input, state, level + 1, allow_stmt_expr)?; + let guard = parse_expr(input, state, level + 1, if_expr, stmt_expr)?; ensure_not_assignment(input)?; - let body = parse_block(input, state, true, level + 1, allow_stmt_expr)?; + let body = parse_block(input, state, true, level + 1, if_expr, stmt_expr)?; Ok(Stmt::While(Box::new((guard, body)))) } @@ -1929,7 +1955,8 @@ fn parse_loop<'a>( input: &mut Peekable>, state: &mut ParseState, level: usize, - allow_stmt_expr: bool, + if_expr: bool, + stmt_expr: bool, ) -> Result { // loop ... let pos = eat_token(input, Token::Loop); @@ -1939,7 +1966,7 @@ fn parse_loop<'a>( } // loop { body } - let body = parse_block(input, state, true, level + 1, allow_stmt_expr)?; + let body = parse_block(input, state, true, level + 1, if_expr, stmt_expr)?; Ok(Stmt::Loop(Box::new(body))) } @@ -1949,7 +1976,8 @@ fn parse_for<'a>( input: &mut Peekable>, state: &mut ParseState, level: usize, - allow_stmt_expr: bool, + if_expr: bool, + stmt_expr: bool, ) -> Result { // for ... let pos = eat_token(input, Token::For); @@ -1984,12 +2012,12 @@ fn parse_for<'a>( // for name in expr { body } ensure_not_statement_expr(input, "a boolean")?; - let expr = parse_expr(input, state, level + 1, allow_stmt_expr)?; + let expr = parse_expr(input, state, level + 1, if_expr, stmt_expr)?; let prev_len = state.len(); state.push((name.clone(), ScopeEntryType::Normal)); - let body = parse_block(input, state, true, level + 1, allow_stmt_expr)?; + let body = parse_block(input, state, true, level + 1, if_expr, stmt_expr)?; state.truncate(prev_len); @@ -2002,7 +2030,8 @@ fn parse_let<'a>( state: &mut ParseState, var_type: ScopeEntryType, level: usize, - allow_stmt_expr: bool, + if_expr: bool, + stmt_expr: bool, ) -> Result { // let/const... (specified in `var_type`) let (_, pos) = input.next().unwrap(); @@ -2021,7 +2050,7 @@ fn parse_let<'a>( // let name = ... if match_token(input, Token::Equals)? { // let name = expr - let init_value = parse_expr(input, state, level + 1, allow_stmt_expr)?; + let init_value = parse_expr(input, state, level + 1, if_expr, stmt_expr)?; match var_type { // let name = expr @@ -2063,7 +2092,8 @@ fn parse_import<'a>( input: &mut Peekable>, state: &mut ParseState, level: usize, - allow_stmt_expr: bool, + if_expr: bool, + stmt_expr: bool, ) -> Result { // import ... let pos = eat_token(input, Token::Import); @@ -2073,7 +2103,7 @@ fn parse_import<'a>( } // import expr ... - let expr = parse_expr(input, state, level + 1, allow_stmt_expr)?; + let expr = parse_expr(input, state, level + 1, if_expr, stmt_expr)?; // import expr as ... match input.next().unwrap() { @@ -2169,7 +2199,8 @@ fn parse_block<'a>( state: &mut ParseState, breakable: bool, level: usize, - allow_stmt_expr: bool, + if_expr: bool, + stmt_expr: bool, ) -> Result { // Must start with { let pos = match input.next().unwrap() { @@ -2193,7 +2224,15 @@ fn parse_block<'a>( while !match_token(input, Token::RightBrace)? { // Parse statements inside the block - let stmt = parse_stmt(input, state, breakable, false, level + 1, allow_stmt_expr)?; + let stmt = parse_stmt( + input, + state, + breakable, + false, + level + 1, + if_expr, + stmt_expr, + )?; // See if it needs a terminating semicolon let need_semicolon = !stmt.is_self_terminated(); @@ -2240,7 +2279,8 @@ fn parse_expr_stmt<'a>( input: &mut Peekable>, state: &mut ParseState, level: usize, - allow_stmt_expr: bool, + if_expr: bool, + stmt_expr: bool, ) -> Result { let (_, pos) = input.peek().unwrap(); @@ -2248,8 +2288,8 @@ fn parse_expr_stmt<'a>( return Err(PERR::ExprTooDeep.into_err(*pos)); } - let expr = parse_expr(input, state, level + 1, allow_stmt_expr)?; - let expr = parse_op_assignment_stmt(input, state, expr, level + 1, allow_stmt_expr)?; + let expr = parse_expr(input, state, level + 1, if_expr, stmt_expr)?; + let expr = parse_op_assignment_stmt(input, state, expr, level + 1, if_expr, stmt_expr)?; Ok(Stmt::Expr(Box::new(expr))) } @@ -2260,7 +2300,8 @@ fn parse_stmt<'a>( breakable: bool, is_global: bool, level: usize, - allow_stmt_expr: bool, + if_expr: bool, + stmt_expr: bool, ) -> Result { use ScopeEntryType::{Constant, Normal}; @@ -2277,7 +2318,7 @@ fn parse_stmt<'a>( // Semicolon - empty statement Token::SemiColon => Ok(Stmt::Noop(*pos)), - Token::LeftBrace => parse_block(input, state, breakable, level + 1, allow_stmt_expr), + Token::LeftBrace => parse_block(input, state, breakable, level + 1, if_expr, stmt_expr), // fn ... #[cfg(not(feature = "no_function"))] @@ -2285,10 +2326,10 @@ fn parse_stmt<'a>( #[cfg(not(feature = "no_function"))] Token::Fn => unreachable!(), - Token::If => parse_if(input, state, breakable, level + 1, allow_stmt_expr), - Token::While => parse_while(input, state, level + 1, allow_stmt_expr), - Token::Loop => parse_loop(input, state, level + 1, allow_stmt_expr), - Token::For => parse_for(input, state, level + 1, allow_stmt_expr), + Token::If => parse_if(input, state, breakable, level + 1, if_expr, stmt_expr), + Token::While => parse_while(input, state, level + 1, if_expr, stmt_expr), + Token::Loop => parse_loop(input, state, level + 1, if_expr, stmt_expr), + Token::For => parse_for(input, state, level + 1, if_expr, stmt_expr), Token::Continue if breakable => { let pos = eat_token(input, Token::Continue); @@ -2318,7 +2359,7 @@ fn parse_stmt<'a>( } // `return` or `throw` with expression (_, _) => { - let expr = parse_expr(input, state, level + 1, allow_stmt_expr)?; + let expr = parse_expr(input, state, level + 1, if_expr, stmt_expr)?; let pos = expr.position(); Ok(Stmt::ReturnWithVal(Box::new(( @@ -2329,9 +2370,9 @@ fn parse_stmt<'a>( } } - Token::Let => parse_let(input, state, Normal, level + 1, allow_stmt_expr), - Token::Const => parse_let(input, state, Constant, level + 1, allow_stmt_expr), - Token::Import => parse_import(input, state, level + 1, allow_stmt_expr), + Token::Let => parse_let(input, state, Normal, level + 1, if_expr, stmt_expr), + Token::Const => parse_let(input, state, Constant, level + 1, if_expr, stmt_expr), + Token::Import => parse_import(input, state, level + 1, if_expr, stmt_expr), #[cfg(not(feature = "no_module"))] Token::Export if !is_global => Err(PERR::WrongExport.into_err(*pos)), @@ -2339,7 +2380,7 @@ fn parse_stmt<'a>( #[cfg(not(feature = "no_module"))] Token::Export => parse_export(input, state, level + 1), - _ => parse_expr_stmt(input, state, level + 1, allow_stmt_expr), + _ => parse_expr_stmt(input, state, level + 1, if_expr, stmt_expr), } } @@ -2350,7 +2391,8 @@ fn parse_fn<'a>( state: &mut ParseState, access: FnAccess, level: usize, - allow_stmt_expr: bool, + if_expr: bool, + stmt_expr: bool, ) -> Result { let pos = eat_token(input, Token::Fn); @@ -2421,7 +2463,7 @@ fn parse_fn<'a>( // Parse function body let body = match input.peek().unwrap() { - (Token::LeftBrace, _) => parse_block(input, state, false, level + 1, allow_stmt_expr)?, + (Token::LeftBrace, _) => parse_block(input, state, false, level + 1, if_expr, stmt_expr)?, (_, pos) => return Err(PERR::FnMissingBody(name).into_err(*pos)), }; @@ -2444,7 +2486,7 @@ pub fn parse_global_expr<'a>( max_expr_depth: usize, ) -> Result { let mut state = ParseState::new(max_expr_depth); - let expr = parse_expr(input, &mut state, 0, false)?; + let expr = parse_expr(input, &mut state, 0, false, false)?; match input.peek().unwrap() { (Token::EOF, _) => (), @@ -2489,7 +2531,7 @@ fn parse_global_level<'a>( #[cfg(not(feature = "no_function"))] (Token::Fn, _) => { let mut state = ParseState::new(max_expr_depth.1); - let func = parse_fn(input, &mut state, access, 0, true)?; + let func = parse_fn(input, &mut state, access, 0, true, true)?; // Qualifiers (none) + function name + number of arguments. let hash = calc_fn_hash(empty(), &func.name, func.params.len(), empty()); @@ -2508,7 +2550,7 @@ fn parse_global_level<'a>( } } // Actual statement - let stmt = parse_stmt(input, &mut state, false, true, 0, true)?; + let stmt = parse_stmt(input, &mut state, false, true, 0, true, true)?; let need_semicolon = !stmt.is_self_terminated(); diff --git a/tests/expressions.rs b/tests/expressions.rs index 35a6b90c..75790f00 100644 --- a/tests/expressions.rs +++ b/tests/expressions.rs @@ -12,10 +12,9 @@ fn test_expressions() -> Result<(), Box> { engine.eval_expression_with_scope::(&mut scope, "2 + (x + 10) * 2")?, 42 ); - assert_eq!( - engine.eval_expression_with_scope::(&mut scope, "if x > 0 { 42 } else { 123 }")?, - 42 - ); + assert!(engine + .eval_expression_with_scope::(&mut scope, "if x > 0 { 42 } else { 123 }") + .is_err()); assert!(engine.eval_expression::<()>("40 + 2;").is_err()); assert!(engine.eval_expression::<()>("40 + { 2 }").is_err());