Skip to content

recognize zero-arg name() and name!() in every operand position#239

Merged
danieljohnmorris merged 3 commits into
mainfrom
fix/zero-arg-fn-call
May 13, 2026
Merged

recognize zero-arg name() and name!() in every operand position#239
danieljohnmorris merged 3 commits into
mainfrom
fix/zero-arg-fn-call

Conversation

@danieljohnmorris
Copy link
Copy Markdown
Collaborator

Summary

Closes the routing-tsp persona friction at ilo_assessment_feedback.md:1564. SPEC.md:16 says zero-arg call is make-id() and SPEC.md:843 documents fetch!(), but the implementation only recognised the parens at statement head / RHS of =. Every operand-position use (call arg, HOF collection slot, loop subject, range bound) routed through parse_operandparse_atom, where the Ident arm returned Expr::Ref immediately and then choked on the dangling (.

Two-token lookahead inside parse_atom's Ident arm, mirroring the existing block at parse_call_or_atom:1803-1814. Bare name still parses as Expr::Ref, so HOFs like fld max xs 0 are unchanged — only the explicit () triggers invocation. What you read is what runs.

Repro

Before this PR, on 4ef85d4:

xs>L n;[1 2 3]
f>n;len xs()
ILO-P009: expected expression, got RParen

Same failure for hd xs(), at xs() 0, map dbl xs(), @v xs(){...}. After this PR all five positions parse and produce the same values across --run-tree, --run-vm, and --run-cranelift.

What's in the diff

  • c49c700 recognize name() and name!() zero-arg calls in operand position — the parser fix. ~30 lines in parse_atom, mirrors the existing statement-head lookahead. Both the bare name() form and the auto-unwrap name!() form are handled.
  • f672ece test zero-arg call in operand position across positions and engines — five new parser unit tests (one per broken position plus a HOF-regression guard) and examples/zero-arg-call.ilo exercising the same scenarios across all three engines via the existing tests/examples_engines.rs harness.

Test plan

  • cargo test --release --features cranelift green (parser + verifier + tree + VM + cranelift JIT/AOT + examples harness)
  • All five zero_arg_* parser unit tests pass
  • examples/zero-arg-call.ilo runs identically across --run-tree, --run-vm, --run-cranelift
  • HOF fn-ref regression test confirms map dbl xs (no parens) still parses as Ref so HOFs are unaffected

Follow-ups

  • @v map dbl xs() (HOF call in loop-subject position) is still rejected — parse_operand doesn't call-parse there. Separate pre-existing limitation. Workaround in the example: bind to a name first (ys=map dbl xs();@v ys{...}).
  • Auto-call (invoke a fn-ref the moment it lands in a value-shaped context) was explicitly rejected during gate review: it breaks HOF semantics and violates "what you read is what runs."

SPEC.md:16 documents `make-id()` and SPEC.md:843 documents `fetch!()`,
but `parse_atom`'s Ident arm returned `Expr::Ref` immediately without
inspecting the trailing parens. The lookahead block already existed in
`parse_call_or_atom` (line 1803-1814), so `cx=xs()` worked, but every
operand-position use went through `parse_operand` -> `parse_atom` and
failed with `ILO-P009` / `ILO-P003`:

- `len xs()`, `hd xs()`, `at xs() 0`  (zero-arg call as builtin arg)
- `map dbl xs()`                       (HOF collection slot)
- `@v xs(){...}`                       (loop subject)

Mirror the same two-token lookahead inside `parse_atom`, for both the
bare `name()` form and the auto-unwrap `name!()` form. Bare `name`
(without parens) still parses as `Expr::Ref` so HOF fn-refs like
`fld max xs 0` are unaffected.
Five parser unit tests cover the four formerly-broken positions and one
guard against regressing the HOF fn-ref path:

- `zero_arg_call_as_builtin_arg`         (`len xs()`)
- `zero_arg_call_as_hof_collection_arg`  (`map dbl xs()`)
- `zero_arg_call_as_loop_subject`        (`@v xs(){...}`)
- `zero_arg_unwrap_call_in_operand_position`  (`len fetch!()`)
- `bare_ident_still_parses_as_ref_for_hof_arg`

`examples/zero-arg-call.ilo` exercises the same five scenarios across
the tree, VM, and Cranelift engines via the `tests/examples_engines.rs`
harness, giving cross-engine regression coverage without touching any
backend code.
@codecov
Copy link
Copy Markdown

codecov Bot commented May 13, 2026

Codecov Report

❌ Patch coverage is 90.00000% with 12 lines in your changes missing coverage. Please review.
✅ All tests successful. No failed tests found.

Files with missing lines Patch % Lines
src/parser/mod.rs 90.00% 12 Missing ⚠️

📢 Thoughts on this report? Let us know!

@danieljohnmorris danieljohnmorris merged commit 20938c3 into main May 13, 2026
5 checks passed
@danieljohnmorris danieljohnmorris deleted the fix/zero-arg-fn-call branch May 13, 2026 16:54
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