Skip to content

parser: chain 2-arg numeric builtins without parens#523

Merged
danieljohnmorris merged 3 commits into
mainfrom
fix/chained-1arg-parse-hint
May 21, 2026
Merged

parser: chain 2-arg numeric builtins without parens#523
danieljohnmorris merged 3 commits into
mainfrom
fix/chained-1arg-parse-hint

Conversation

@danieljohnmorris
Copy link
Copy Markdown
Collaborator

Summary

Closes pending #5ao. abs rndn 0 1 (and any chain putting a 2-arg numeric builtin under a unary outer) was misparsing as abs(rndn, 0, 1) and erroring with "arity mismatch: abs expects 1 args, got 3". Workaround had been abs (rndn 0 1) — agents kept reaching for parens that the manifesto wants to avoid.

Root cause: parse_call_arg only recurses into a nested call when the inner ident has a registered arity in builtin_arity_tables(). rndn, atan2, fmod, clamp, and the unary trig forms (tan, asin, acos, atan, log10, log2) were missing from that table, so the parser fell through to parse_operand, returned a bare Ref(rndn), and let the outer greedy loop swallow the remaining operands.

The structural fix is to register the missing arities. The nested-call branch already exists and is the right code path; this just lets it fire for these builtins exactly like it already does for pow and min/max.

Repro

Before:

$ ilo 'g>n;abs rndn 0 0.1' g
{"code":"ILO-T004","message":"undefined variable 'rndn'", ...}
{"code":"ILO-T006","message":"arity mismatch: 'abs' expects 1 args, got 3", ...}

After:

$ ilo 'g>n;abs rndn 0 0.1' g
0.04951257174436721

What's in the diff

  • parser: register arity for missing numeric builtins — adds the missing entries to builtin_arity_tables(). Lowest-blast-radius fix that takes the existing nested-call branch.
  • test: cover chained 2-arg numeric calls cross-engine — 9 regression tests across --vm and --jit covering abs/sqrt/pow outer × atan2/fmod/clamp/rndn/log10 inner, the pending repro shape (y=+1 rndn 0 0.1), and a composed pow atan2 1 1 2. Adds examples/chained-num-args.ilo for agent in-context learning, exercised by examples_engines.rs.
  • docs: spell out known-arity call chaining rule — SPEC.md Call Arguments section and ai.txt CALLS line now document the chain-without-parens behaviour, with the variadic/unknown-arity carve-out called out.

Test plan

  • cargo test --release --features cranelift passes (all 3273 lib + every integration suite green)
  • cargo test --release --features cranelift --test regression_chained_1arg_hint — 9/9 pass
  • cargo test --release --features cranelift --test examples_engines — new example exercised
  • cargo fmt --check clean
  • cargo clippy --release --features cranelift --all-targets -- -D warnings clean
  • Manual: abs rndn 0 0.1, sqrt atan2 1 1, abs clamp 5 0 10, pow atan2 1 1 2 all run on both engines

Follow-ups

There are ~60 more builtins still missing from the parser arity table (incl. take, drop, enumerate, chunks, window, quantile, median, stdev, variance, matmul, dot, transpose, inv, det, solve, zip). Each has the same latent chained-call trap. This PR fixes the cluster the pending entry called out; happy to widen in a follow-up once we've watched main bake.

rndn, atan2, fmod, clamp, and the unary trig forms (tan, asin, acos,
atan, log10, log2) were absent from builtin_arity_tables, so the
parse_call_arg nested-call branch never fired for them. A chained
shape like 'abs rndn 0 1' fell through to parse_operand, returning a
bare Ref(rndn), and the outer call swallowed the remaining operands
producing 'arity mismatch: abs expects 1 args, got 3'.

With the arities registered, parse_call_arg recurses into the inner
call exactly arity operands deep, matching the existing behaviour
for pow and min/max. Workaround paren-grouping is no longer needed.

Closes pending #5ao.
9 regression tests pin the new parse behaviour across every public
engine: abs/sqrt/pow outer composed with atan2/fmod/clamp/rndn/log10
inner, plus the original pending repro shape (y=+1 rndn 0 0.1) and
a stochastic rndn run that asserts parse + execute succeed.

examples/chained-num-args.ilo gives agents an in-context template
for the now-working chain shape and runs through the examples
harness, so any future grammar regression that drops the structural
fix shows up there too.
SPEC.md Call Arguments section and ai.txt CALLS line now state that
known-arity calls can chain directly without parens, with examples
for the canonical 2-arg-numeric-under-unary shape. Variadic and
unknown-arity calls still need paren grouping, called out explicitly
so agents reach for parens only when they actually help.
@codecov
Copy link
Copy Markdown

codecov Bot commented May 21, 2026

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ All tests successful. No failed tests found.

📢 Thoughts on this report? Let us know!

@danieljohnmorris danieljohnmorris merged commit 55f390f into main May 21, 2026
5 checks passed
@danieljohnmorris danieljohnmorris deleted the fix/chained-1arg-parse-hint branch May 21, 2026 08:39
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