From fb88b79178cecd37f6738bfaa454cd51447ec7b6 Mon Sep 17 00:00:00 2001 From: Stephen Chung Date: Wed, 19 Apr 2023 23:17:54 +0800 Subject: [PATCH] Fix bug in parsing index chains. --- CHANGELOG.md | 1 + src/parser.rs | 10 ++++++++-- tests/arrays.rs | 28 ++++++++++++++++++++++------ 3 files changed, 31 insertions(+), 8 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 7ee71de3..eee62da7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,6 +11,7 @@ Buf fixes * `is_shared` is a reserved keyword and is now handled properly (e.g. it cannot be the target of a function pointer). * Re-optimizing an AST via `optimize_ast` with constants now works correctly for closures. Previously the hidden `Share` nodes are not removed and causes variable-not-found errors during runtime if the constants are not available in the scope. +* Expressions such as `(v[0].func()).prop` now parse correctly. New features ------------ diff --git a/src/parser.rs b/src/parser.rs index a5ba7f1d..a34c851c 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -1728,6 +1728,9 @@ impl Engine { ) -> ParseResult { let mut settings = settings; + // Break just in case `lhs` is `Expr::Dot` or `Expr::Index` + let mut parent_options = ASTFlags::BREAK; + // Tail processing all possible postfix operators loop { let (tail_token, ..) = input.peek().expect(NEVER_ENDS); @@ -1842,13 +1845,16 @@ impl Engine { let rhs = self.parse_primary(input, state, lib, settings.level_up()?, options)?; - Self::make_dot_expr(state, expr, rhs, ASTFlags::empty(), op_flags, tail_pos)? + Self::make_dot_expr(state, expr, rhs, parent_options, op_flags, tail_pos)? } // Unknown postfix operator (expr, token) => { unreachable!("unknown postfix operator '{}' for {:?}", token, expr) } - } + }; + + // The chain is now extended + parent_options = ASTFlags::empty(); } // Cache the hash key for namespace-qualified variables diff --git a/tests/arrays.rs b/tests/arrays.rs index 23d0f98f..d8b24f4f 100644 --- a/tests/arrays.rs +++ b/tests/arrays.rs @@ -203,6 +203,22 @@ fn test_arrays() -> Result<(), Box> { Ok(()) } +#[cfg(not(feature = "no_float"))] +#[cfg(not(feature = "no_object"))] +#[test] +fn test_array_chaining() -> Result<(), Box> { + let engine = Engine::new(); + + assert!(engine.eval::( + " + let v = [ PI() ]; + ( v[0].cos() ).sin() == v[0].cos().sin() + " + )?); + + Ok(()) +} + #[test] fn test_array_index_types() -> Result<(), Box> { let engine = Engine::new(); @@ -508,9 +524,9 @@ fn test_arrays_map_reduce() -> Result<(), Box> { engine.eval::<()>( " - let x = [1, 2, 3, 2, 1]; - x.find(|v| v > 4) - ", + let x = [1, 2, 3, 2, 1]; + x.find(|v| v > 4) + ", )?; assert_eq!( @@ -525,9 +541,9 @@ fn test_arrays_map_reduce() -> Result<(), Box> { engine.eval::<()>( " - let x = [#{alice: 1}, #{bob: 2}, #{clara: 3}]; - x.find_map(|v| v.dave) - ", + let x = [#{alice: 1}, #{bob: 2}, #{clara: 3}]; + x.find_map(|v| v.dave) + ", )?; Ok(())