Skip to content

Invoke intermediate Callables in dotted-name resolution#312

Open
spullara wants to merge 8 commits into
mainfrom
dotted-section-scopes
Open

Invoke intermediate Callables in dotted-name resolution#312
spullara wants to merge 8 commits into
mainfrom
dotted-section-scopes

Conversation

@spullara

@spullara spullara commented Jul 23, 2026

Copy link
Copy Markdown
Owner

Summary

Dotted-name resolution now invokes intermediate Callable values so a chain can continue into their results.

Intermediate parts participate in resolution only. They are not pushed onto the context stack; per the Mustache spec, a section pushes only the final resolved element.

Existing behavior for broken chains, literal "foo.bar" keys, context precedence, and inverted sections is unchanged.

Rendering example

Template:

{{#foo.bar}}quux{{/foo.bar}}

View:

{ foo: (Callable) () -> Map(bar -> (Function<String,String>) text -> "baz") }

Before: renders "" because the chain stops at the Callable.

After: renders "baz".

Implementation

ReflectionObjectHandler inserts a self-guarding CALLABLE_WRAPPER into dotted-name wrapper chains. Initial resolution and cached GuardedBinding replay can therefore invoke an intermediate Callable before resolving the next segment.

IterableCode keeps the normal section path and pushes only the final resolved value.

Tests

  • DotNotationTest covers Callable roots and mid-chain Callables, broken chains, literal dotted keys (including a "foo.bar"/"foo" collision), inverted sections, and final-only scope behavior.
  • The spec regression {{#a.b}}{{x}}{{/a.b}} renders ROOT rather than the intermediate a.x value.
  • mvn -q -pl compiler -am test passes: 285 tests, 0 failures or errors, 8 skipped, including SpecTest and FullSpecTest.

Also adds AGENTS.md repository guidelines.

spullara added 5 commits July 23, 2026 13:21
Agent-Id: agent-afae2211-1cf8-460a-8aea-733b035005fa
Linked-Note-Id: 804110b6-0ff6-4285-ad35-33931c601c67
Agent-Id: agent-afae2211-1cf8-460a-8aea-733b035005fa
Linked-Note-Id: 804110b6-0ff6-4285-ad35-33931c601c67
Agent-Id: agent-afae2211-1cf8-460a-8aea-733b035005fa
Agent-Id: agent-af8bcf7c-4595-4b36-a217-2b132bb9a1eb
Linked-Note-Id: 507df405-bb15-40b9-bef8-4ad8222e7ae4
@augmentcode

augmentcode Bot commented Jul 23, 2026

Copy link
Copy Markdown
🤖 Augment PR Summary

Summary: This PR updates dotted-name section rendering so {{#foo.bar}} behaves like nested sections ({{#foo}}{{#bar}}) by pushing intermediate objects onto the context stack.

Changes:

  • Compute and store intermediate bindings for dotted section names in IterableCode.
  • When executing dotted sections, push non-null intermediate scopes (and unwrap intermediate Callable values) so inner lookups can see those objects.
  • Extend dotted-name resolution in ReflectionObjectHandler to traverse through intermediate Callable results via an inserted wrapper in the wrapper chain.
  • Add comprehensive tests in DotNotationTest covering intermediate-scope visibility, precedence, broken chains, literal dotted keys, inverted sections, and callable intermediates.
  • Add AGENTS.md repository guidelines.

Technical Notes: Intermediate scopes are only pushed when the resolved wrapper indicates an actual dotted traversal (to preserve literal-key semantics) and wrapper caching/guards are intended to handle shape changes.

🤖 Was this summary useful? React with 👍 or 👎

@augmentcode augmentcode Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Review completed. 1 suggestion posted.

Fix All in Augment

Comment augment review to trigger a new review at any time.

}

private void addIntermediateScopes(List<Object> scopes) {
Wrapper resolvedWrapper = oh.find(name, scopes);

@augmentcode augmentcode Bot Jul 23, 2026

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

addIntermediateScopes calls oh.find(name, scopes) (and then resolves intermediateBindings) which re-runs dotted-name resolution and can invoke intermediate getters/Callables extra times, so {{#foo.bar}} may not fully match {{#foo}}{{#bar}} when intermediates have side effects. Consider adding a regression test with a counting intermediate callable/getter to ensure dotted vs nested sections have equivalent invocation behavior.

Severity: medium

Fix This in Augment

🤖 Was this useful? React with 👍 or 👎, or 🚀 if it prevented an incident/outage.

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Addressed in cc69244 and 8f4f4df. IterableCode now resolves reflection-backed dotted sections from one fresh find result, and ReflectionWrapper.callDotted walks that wrapper chain once to collect the intermediate scopes and final value together. This removes the per-prefix binding evaluations and preserves the literal-key gate because a literal dotted key has no intermediate wrapper chain. The counting regression records the fixed overhead precisely: the dotted form invokes the intermediate 2 times (lookup plus resolution), while the nested form invokes it 1 time. mvn -q -pl compiler -am test passes all 288 tests with 0 failures/errors.

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

After reviewing the Mustache spec, I removed intermediate scope pushing entirely. addIntermediateScopes and the scope replay path are gone, so this invocation-count concern no longer applies. Intermediate Callable values are now invoked only as part of dotted-name resolution.

spullara added 3 commits July 23, 2026 15:27
Agent-Id: agent-7d67dd5b-4ec4-4525-aaa5-f1f08c624495
Agent-Id: agent-7d67dd5b-4ec4-4525-aaa5-f1f08c624495
Agent-Id: agent-8def19d3-9b6b-4877-bbce-d3164fc53f9e
@spullara spullara changed the title Make dotted-name sections behave like nested sections Invoke intermediate Callables in dotted-name resolution Jul 23, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant