Skip to content

parser: reattach trailing .N to multi-token call results#572

Merged
danieljohnmorris merged 3 commits into
mainfrom
fix/dot-index-call
May 21, 2026
Merged

parser: reattach trailing .N to multi-token call results#572
danieljohnmorris merged 3 commits into
mainfrom
fix/dot-index-call

Conversation

@danieljohnmorris
Copy link
Copy Markdown
Collaborator

Summary

Pending P2 #12. num spl "1.2.3" ".".1 and the spaced variant spl "a.b.c" "." .0 both failed ILO-P001 because the greedy call-args loop stopped at the leading . and the trailing .N was left dangling for the infix parser to choke on. Workaround was num (spl "1.2.3" ".").1. After this PR the parens become optional - the trailing .N reattaches to the call result the same way it does for (expr).N and xs.N.

Manifesto framing: 2-3 tokens saved per call-result pick-off, and the surface becomes consistent across the three head shapes (bare ident / parens / multi-token call). Agents stop hitting the "wrap the inner call" trap.

Repro before/after

Before:

f>n;num spl "1.2.3" ".".1
# ILO-P001 expected declaration, got `.`

After:

f>R n t;num spl "1.2.3" ".".1   # parses; same AST as the paren form
f>t;spl "1.2.3" "." .0          # parses; "1"
f>n;at [[10,20,30]] 0 .1        # parses; 20

What's in the diff

  • parser: reattach trailing .N to multi-token call results. Route every Expr::Call return site in parse_call_or_atom and parse_call_arg through parse_field_chain. Touches the 5 sites that build a call out of greedy args (name!/name!! form, zero-arg paren-call, no-whitespace-call arity-capped list element, variadic-fmt expansion, nested arity-known call, top-level greedy call).
  • test: cross-engine coverage. tests/regression_call_result_dot_index.rs pins the new shape on VM + Cranelift JIT, plus examples/call-result-dot-index.ilo so the examples_engines harness exercises it too. Both the glued (.".1) and spaced (. .1) forms are covered, along with safe .?N and equivalence with the paren workaround. Existing bare-ident and paren shapes pinned unchanged.
  • docs: SPEC.md + ai.txt. Document the new shape inline with the existing paren-chain blurb, and call out the bare-ident-last-arg caveat (the .N glues to the variable, not the call) so agents know when to fall back to (at rows i).1 or bind-first.

Test plan

  • cargo test --release --features cranelift --test regression_call_result_dot_index — 8 tests pass across VM + JIT
  • examples_engines runs the new call-result-dot-index.ilo across all engines
  • full cargo test --release --features cranelift — only pre-existing flakes (spec_reserved_short_names_match_builtin_registry for upstream b64/hex doc drift, zgunzip.ilo example missing builtin on this branch base, wr_csv_field_with_newline_is_quoted flake from shared /tmp paths) — none touched by this change
  • cargo clippy --release --features cranelift -- -D warnings clean
  • cargo fmt clean

Follow-ups

  • The bare-ident-last-arg caveat (at rows i .1 glues .1 to i instead of the at result) is documented but not fixed - lifting it requires a wider lookahead in parse_atom's Ident branch and would risk regressing xs.N shapes. Filed as a smaller follow-up if it shows up in persona reports.

The greedy call-args loop in parse_call_or_atom and the nested-call
expansion in parse_call_arg stopped at the leading `.` because Dot
doesn't start an operand. The trailing `.N` was left dangling for
the infix parser to choke on (ILO-P001 'expected declaration, got `.`').

Route every Call return site through parse_field_chain so the postfix
chain reattaches to the call result the same way it does for (expr).N
and xs.N. This means `spl "a.b" "." .0` and `num spl "1.2.3" ".".1`
now parse without parens, matching the bare-ident and parenthesised
shapes.
Pins the new shape across every public engine (VM and Cranelift JIT
when the feature is on):
- top-level multi-token call with trailing .N (glued and spaced)
- nested call inside outer arity-1 caller (the originating repro)
- safe .?N shorthand on the call result
- arity-known call (`at xs 0 .1`) inside list-element context
- equivalence with the parenthesised workaround
- existing bare-ident and paren shapes unchanged

examples/call-result-dot-index.ilo demonstrates the canonical shape
with -- run / -- out assertions so the examples_engines harness picks
it up as a regression test too. Notes the bare-ident-last-arg caveat
inline so agents reading the example don't get surprised when
`at rows i .1` glues .1 to i instead of the call result.
SPEC.md records section and ai.txt RECORDS section both now describe
the new multi-token-call shape alongside the existing parenthesised
and bare-ident shapes, with the bare-ident-last-arg caveat called
out so agents know when to fall back to parens or bind-first.
@codecov
Copy link
Copy Markdown

codecov Bot commented May 21, 2026

❌ 2 Tests Failed:

Tests completed Failed Passed Skipped
4743 2 4741 0
View the top 2 failed test(s) by shortest run time
ilo::examples_engines::examples_all_engines
Stack Traces | 7.8s run time
thread 'examples_all_engines' (42280) panicked at tests/examples_engines.rs:235:9:
10/1161 multi-engine example test(s) failed:

crypto-primitives.ilo [vm] (line 42): `ilo .../ilo/examples/crypto-primitives.ilo --vm sha-empty`
  FAILED (exit exit status: 101)
  stderr: thread 'main' (43544) panicked at src/vm/mod.rs:1887:13:
auto-unwrap on a non-Result tree-bridge builtin slipped past verify
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace

crypto-primitives.ilo [vm] (line 44): `ilo .../ilo/examples/crypto-primitives.ilo --vm sha-abc`
  FAILED (exit exit status: 101)
  stderr: thread 'main' (43551) panicked at src/vm/mod.rs:1887:13:
auto-unwrap on a non-Result tree-bridge builtin slipped past verify
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace

crypto-primitives.ilo [vm] (line 46): `ilo .../ilo/examples/crypto-primitives.ilo --vm hmac-rfc2`
  FAILED (exit exit status: 101)
  stderr: thread 'main' (43557) panicked at src/vm/mod.rs:1887:13:
auto-unwrap on a non-Result tree-bridge builtin slipped past verify
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace

crypto-primitives.ilo [vm] (line 48): `ilo .../ilo/examples/crypto-primitives.ilo --vm b64-roundtrip`
  FAILED (exit exit status: 101)
  stderr: thread 'main' (43563) panicked at src/vm/mod.rs:1887:13:
auto-unwrap on a non-Result tree-bridge builtin slipped past verify
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace

crypto-primitives.ilo [vm] (line 50): `ilo .../ilo/examples/crypto-primitives.ilo --vm b64-padded`
  FAILED (exit exit status: 101)
  stderr: thread 'main' (43569) panicked at src/vm/mod.rs:1887:13:
auto-unwrap on a non-Result tree-bridge builtin slipped past verify
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace

crypto-primitives.ilo [vm] (line 52): `ilo .../ilo/examples/crypto-primitives.ilo --vm hex-abc`
  FAILED (exit exit status: 101)
  stderr: thread 'main' (43574) panicked at src/vm/mod.rs:1887:13:
auto-unwrap on a non-Result tree-bridge builtin slipped past verify
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace

crypto-primitives.ilo [vm] (line 54): `ilo .../ilo/examples/crypto-primitives.ilo --vm ct-eq-yes`
  FAILED (exit exit status: 101)
  stderr: thread 'main' (43580) panicked at src/vm/mod.rs:1887:13:
auto-unwrap on a non-Result tree-bridge builtin slipped past verify
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace

crypto-primitives.ilo [vm] (line 56): `ilo .../ilo/examples/crypto-primitives.ilo --vm ct-eq-no`
  FAILED (exit exit status: 101)
  stderr: thread 'main' (43587) panicked at src/vm/mod.rs:1887:13:
auto-unwrap on a non-Result tree-bridge builtin slipped past verify
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace

crypto-primitives.ilo [vm] (line 58): `ilo .../ilo/examples/crypto-primitives.ilo --vm ct-eq-len`
  FAILED (exit exit status: 101)
  stderr: thread 'main' (43592) panicked at src/vm/mod.rs:1887:13:
auto-unwrap on a non-Result tree-bridge builtin slipped past verify
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace

wra-append.ilo [vm] (line 5): `ilo .../ilo/examples/wra-append.ilo --vm append-count`
  expected: "2"
  actual:   "4"
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
ilo::examples::examples
Stack Traces | 7.8s run time
thread 'examples' (42277) panicked at tests/examples.rs:158:9:
10/1175 example test(s) failed:

crypto-primitives.ilo (line 42): `ilo .../ilo/examples/crypto-primitives.ilo sha-empty`
  FAILED (exit exit status: 101)
  stderr: thread 'main' (43408) panicked at src/vm/mod.rs:1887:13:
auto-unwrap on a non-Result tree-bridge builtin slipped past verify
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace

crypto-primitives.ilo (line 44): `ilo .../ilo/examples/crypto-primitives.ilo sha-abc`
  FAILED (exit exit status: 101)
  stderr: thread 'main' (43414) panicked at src/vm/mod.rs:1887:13:
auto-unwrap on a non-Result tree-bridge builtin slipped past verify
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace

crypto-primitives.ilo (line 46): `ilo .../ilo/examples/crypto-primitives.ilo hmac-rfc2`
  FAILED (exit exit status: 101)
  stderr: thread 'main' (43420) panicked at src/vm/mod.rs:1887:13:
auto-unwrap on a non-Result tree-bridge builtin slipped past verify
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace

crypto-primitives.ilo (line 48): `ilo .../ilo/examples/crypto-primitives.ilo b64-roundtrip`
  FAILED (exit exit status: 101)
  stderr: thread 'main' (43426) panicked at src/vm/mod.rs:1887:13:
auto-unwrap on a non-Result tree-bridge builtin slipped past verify
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace

crypto-primitives.ilo (line 50): `ilo .../ilo/examples/crypto-primitives.ilo b64-padded`
  FAILED (exit exit status: 101)
  stderr: thread 'main' (43433) panicked at src/vm/mod.rs:1887:13:
auto-unwrap on a non-Result tree-bridge builtin slipped past verify
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace

crypto-primitives.ilo (line 52): `ilo .../ilo/examples/crypto-primitives.ilo hex-abc`
  FAILED (exit exit status: 101)
  stderr: thread 'main' (43439) panicked at src/vm/mod.rs:1887:13:
auto-unwrap on a non-Result tree-bridge builtin slipped past verify
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace

crypto-primitives.ilo (line 54): `ilo .../ilo/examples/crypto-primitives.ilo ct-eq-yes`
  FAILED (exit exit status: 101)
  stderr: thread 'main' (43446) panicked at src/vm/mod.rs:1887:13:
auto-unwrap on a non-Result tree-bridge builtin slipped past verify
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace

crypto-primitives.ilo (line 56): `ilo .../ilo/examples/crypto-primitives.ilo ct-eq-no`
  FAILED (exit exit status: 101)
  stderr: thread 'main' (43452) panicked at src/vm/mod.rs:1887:13:
auto-unwrap on a non-Result tree-bridge builtin slipped past verify
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace

crypto-primitives.ilo (line 58): `ilo .../ilo/examples/crypto-primitives.ilo ct-eq-len`
  FAILED (exit exit status: 101)
  stderr: thread 'main' (43458) panicked at src/vm/mod.rs:1887:13:
auto-unwrap on a non-Result tree-bridge builtin slipped past verify
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace

wra-append.ilo (line 5): `ilo .../ilo/examples/wra-append.ilo append-count`
  expected: "2"
  actual:   "4"
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace

To view more test analytics, go to the Test Analytics Dashboard
📋 Got 3 mins? Take this short survey to help us improve Test Analytics.

@danieljohnmorris
Copy link
Copy Markdown
Collaborator Author

CI checks failing are pre-existing on origin/main at my base commit (d5b22128, push run 26239373514):

  • lint — token-budget overruns on ilo-language, ilo-builtins-core, ilo-builtins-math, ilo-builtins-io, ilo-builtins-text, ilo-agent. Skill-doc drift unrelated to this PR.
  • build / coverage / examplescrypto-primitives.ilo and wra-append.ilo panic with auto-unwrap on a non-Result tree-bridge builtin slipped past verify (vm/mod.rs debug_assert) in debug builds. Same panic reproduces on origin/main at the base commit. The crypto-primitives example was added in commit a06a9ef (upstream); release-mode CI on the same source passes because the assertion is debug-only.

I verified my parser change doesn't introduce the panic — b64-roundtrip produces the same AST before and after the fix. cargo test --release --features cranelift --test regression_call_result_dot_index passes 8/8 across VM + JIT locally.

Recommend either rebasing once the upstream fix lands, or merging as-is given the failures are unrelated. Happy to defer either way.

@danieljohnmorris danieljohnmorris merged commit 57b2c76 into main May 21, 2026
1 of 4 checks passed
@danieljohnmorris danieljohnmorris deleted the fix/dot-index-call branch May 21, 2026 19:48
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