Repository navigation
Support property access after array index / method call / parenthesized expression (unified postfix chain) #706
Description
Background
5.4.4 (#693 / #704) let variables support dotted property chains + array indexing without the # prefix, e.g. foo.bars[0].name and response.model_details[0].score now evaluate correctly.
But the property chain breaks as soon as it follows an array access / method call / parenthesized expression. Verified on 5.4.4:
| Expression | Result |
|---|---|
o.oprninfos[0].name (pure dotted-variable prefix) |
✅ OK |
xs[0].name (single variable + index + property) |
❌ unexpect token ']' |
(xs[0]).name |
❌ unexpect token ')' |
seq.get(o.oprninfos, 0).name (property on a function return value) |
❌ unexpect token ')' |
take_while(coll, fn)[0].name |
❌ unexpect token ']' |
See #606 for the user-facing case (taking an element's property from a take_while result — currently no pure-expression form).
Root cause
Property access . is not a first-class parser / codegen operation today; it is folded into the "dotted variable name":
ExpressionLexer.scanVariablescansfoo.bars[0].name(with a mid-chain integer-literal index) into a singleVariabletoken, lexeme ="foo.bars[0].name".- At evaluation time
AviatorJavaTypedetects the.in the lexeme and delegates toReflectorfor level-by-level resolution (full-key first, then property chain). - Array access
[...](CodeGenerator.onArray/onArrayIndexStart/onArrayIndexEnd) and method call (onMethodName/onMethodInvoke) are independent runtime operations in the parser. CodeGeneratorhas no property-access primitive (nothing likeonGetProperty).
So: as long as the chain prefix can be merged by the lexer into one dotted variable, it works; once the chain contains an array access / method call / parenthesized expression, the top of stack becomes an intermediate result, and the following .prop has no "get property of the top-of-stack object" operation to lower to. parseFactor0 does not handle . after ExpressionParser.arrayAccess(), so it reports a syntax error.
#704 already established that the lexer can only merge a mid-chain integer literal index (Reflector.fastGetProperty's bracket handling only supports Integer.valueOf); dynamic indices / function return values cannot be merged at the lexer stage. So "make the lexer absorb more" is not a viable path.
Proposed approach
Promote property access to a first-class postfix operation in the parser, unifying the primary suffix into:
primary ( '[' index ']' | '(' args ')' | '.' identifier )*
Changes:
- Add a property-access primitive to
CodeGenerator, e.g.onGetProperty(Token<?> propertyName)— semantics: "pop the top-of-stack object, get its property namedpropertyName, push the result". - Implement in both backends:
InterpretCodeGenerator: add aGetPropertyIR, resolved against the top-of-stack value inInterpretContextusing the existing property-resolution logic.ASMCodeGenerator: emit bytecode that calls a runtime helper (reusingReflector's property resolution).
ExpressionParser: in the unified suffix loop after variable / method / array access inparseFactor0, handle.identifier and emitonGetProperty.- Compatibility & semantics:
- Keep the lexer's merge fast-path for pure dotted-variable prefixes to preserve the current full-key precedence semantics and performance; only fall back to the parser postfix when the chain contains an array access / method call / parenthesized expression.
- Reuse
Reflector's existing property resolution andNoSuchPropertysemantics for consistency. - Mind the interaction with future nil-safe navigation ([建议]支持null-safety引用变量 #608) and options like
USE_USER_ENV_AS_TOP_ENV_DIRECTLY.
Impact / risk
- Touches the
CodeGeneratorinterface + ASM / interpreter backends + the parser suffix chain — a medium-to-large change. - The ASM backend needs a stable runtime helper and correct bytecode.
- Must cover: dynamic index
a[i].b, multi-dimensionala[0][1].b, function return valuef(x).b, parenthesized(x).b, and combinations with the existing dotted-variable chain. - Payoff: supports any
primary(.prop | [idx] | (args))*combination in one shot, removing the current "only pure dotted-variable prefix works" inconsistency.
Related to #606.
Activity
KaustAbhinand commentedon Sep 12, 2026
Hi @killme2008, have you fixed this? If not, I would like to work on it!