fix: preserve dynamic array index on dotted property paths#704
Merged
Conversation
The lexer/parser refactor for `a.b.c` property access made scanVariable greedily absorb any `[...]` following a dotted name into the variable lexeme, which is then resolved via Reflector.fastGetProperty. That path only supports integer-literal indices (Integer.valueOf), so it regressed expressions that used to work through the parser's array-access path: - `a.b[i]` -> NumberFormatException at runtime (variable not found) - `a.b[i + 1]` -> compile-time syntax error - `a.b[0][1]` -> broken multi-dimensional index scanVariable now only absorbs a mid-chain integer-literal index (a `[digits]` group immediately followed by another `.`, e.g. the `[0]` in `foo.bars[0].name`), which is the only case that cannot be expressed via array access. Dynamic, expression, trailing and multi-dimensional indices are left for the parser's getElement handling, restoring the old behavior while keeping the new property-chain feature. Add lexer tokenization tests and end-to-end tests covering dynamic and multi-dimensional indices on dotted paths.
There was a problem hiding this comment.
Pull request overview
Restores pre-refactor behavior for dotted variables with array access by ensuring ExpressionLexer.scanVariable only absorbs mid-chain integer-literal indices (e.g. foo.bars[0].name) into the variable lexeme, while leaving dynamic/expression/trailing/multi-dimensional indices for the parser’s array-access path.
Changes:
- Refines
ExpressionLexer.scanVariableto stop before[unless it’s a[digits]immediately followed by.. - Adds a focused lexer helper (
tryAbsorbChainedIndex) with rollback viaStringCharacterIterator.setIndex. - Adds/extends tests covering dynamic, trailing, and multi-dimensional indices on dotted paths (lexer-level + end-to-end).
Reviewed changes
Copilot reviewed 3 out of 3 changed files in this pull request and generated 1 comment.
| File | Description |
|---|---|
| src/main/java/com/googlecode/aviator/lexer/ExpressionLexer.java | Changes variable scanning to only absorb mid-chain [digits]. indices, preserving parser handling for dynamic/trailing/multi-dim indexing. |
| src/test/java/com/googlecode/aviator/lexer/ExpressionLexerUnitTest.java | Adds tokenization assertions to ensure indices on dotted vars are (or aren’t) absorbed as intended. |
| src/test/java/com/googlecode/aviator/test/function/QuoteVarTest.java | Adds end-to-end coverage for dynamic and multi-dimensional indices on dotted paths. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Problem
The recent lexer/parser refactor for
a.b.cproperty access (PR #693) madeExpressionLexer.scanVariablegreedily absorb any[...]following a dotted name into the variable lexeme. That lexeme is then resolved viaReflector.fastGetProperty, whose bracket handling only supports integer-literal indices (Integer.valueOf). As a result, expressions that previously worked through the parser's array-access path regressed:a.b[i]NumberFormatExceptionat runtime → "Could not find variable a.b[i]"a.b[i + 1]ExpressionSyntaxErrorExceptiona.b[0][1]foo.bars[0].nameFix
scanVariablenow only absorbs a mid-chain integer-literal index into the lexeme — a[digits]group immediately followed by another.(e.g. the[0]infoo.bars[0].name). That is the only case that cannot be expressed through the parser's array-access path.Every other case — dynamic (
a.b[i]), expression (a.b[i + 1]), trailing (a.b[0]) and multi-dimensional (a.b[0][1]) — stops before[and is handled by the parser'sgetElementarray access, which supports arbitrary index expressions. This restores the old behavior while keeping the new property-chain feature intact.Position rollback uses
StringCharacterIterator.setIndex.Tests
ExpressionLexerUnitTest: tokenization tests asserting dynamic / trailing / multi-dimensional indices are not absorbed, and mid-chain literal indices are.QuoteVarTest: end-to-end tests for dynamic and multi-dimensional indices on dotted paths.Full suite passes locally (
mvn test): 870 tests, 0 failures.