Skip to content

fix(parser): recognize options after rest positional items - #12

Merged
takeokunn merged 1 commit into
mainfrom
fix/fix-rest-positional-options
Sep 26, 2026
Merged

takeokunn merged 1 commit into
mainfrom
fix/fix-rest-positional-options

Conversation

@takeokunn

Copy link
Copy Markdown
Collaborator

Why

A :rest-p positional took every remaining token, so options written after its first item were collected as positionals. search foo src --output count produced paths ("src" "--output" "count"). docs/src/guide/cli-behavior.md ("Interspersed arguments") already promised that command and global options may follow positionals. A consumer (aitools) had to replace its rest positional with 16 optional positionals to work around it.

What to review

  • src/parser-cps.lisp: the mixed-argument scanner takes rest items one token at a time and keeps checking later tokens as options until a literal --. The rest spec is applied once when the tokens run out, so :min-count/:max-count, value parsing and defaults behave as before. Only internal % functions changed arity.
  • Behavior change for consumers: a CLI that relied on a rest positional swallowing option-looking tokens (for example nshell build.ns --json) now gets cli-unknown-option and needs -- or a :stop-parsing-p option. docs/src/guide/migration-guide.md notes this for nshell. Downstream CLIs were not checked.
  • Error ordering: rest items are parsed and count-checked at the end of the scan instead of at the first rest token.

Verification outside CI

t/parser-cps-rest-positional-test.lisp (15 tests) covers long options in both spellings, flags, short clusters, global options after the subcommand, -- (including a second --), negative numbers allowed and rejected, zero rest items, min/max counts across split items, unknown options, --help, bare -, and a root-level rest positional. Against the unfixed parser, 13 of them failed.

A consumer executable (aitools, asdf:program-op) builds against this commit, loading cl-cli starts no threads, and aitools replace x y a.txt --expect-count 4 now reads the option after the path.

Release

main already declares 1.4.0 (asd, demo, README pin) from unreleased feature commits (define-option, define-positional, the cl-cli/concurrent system), and v1.4.0 was never tagged. This fix ships in that release.

A :rest-p positional took every remaining token, so options written
after its first item were collected as positionals: `search foo src
--output count` produced paths ("src" "--output" "count"). This
contradicted the documented interspersed-arguments behavior. The scanner
now takes rest items one token at a time and keeps checking later tokens
as options until a literal `--`; the rest spec is applied once at the end,
so min/max counts, parsing and defaults behave as before.
@takeokunn
takeokunn merged commit 40f2072 into main Sep 26, 2026
2 checks passed
@takeokunn
takeokunn deleted the fix/fix-rest-positional-options branch September 26, 2026 06:28
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