Skip to content

fix(lexer): bare ${X:-${Y}} — extend VarRef to the balanced closing brace#174

Merged
tobert merged 2 commits into
mainfrom
fix/nested-varref-173
Jul 16, 2026
Merged

fix(lexer): bare ${X:-${Y}} — extend VarRef to the balanced closing brace#174
tobert merged 2 commits into
mainfrom
fix/nested-varref-173

Conversation

@tobert

@tobert tobert commented Jul 16, 2026

Copy link
Copy Markdown
Owner

Closes #173.

The bug

Bare ${X:-${Y}} was a parse error: the VarRef regex \$\{[^}]+\} stops at the first }, so it lexed as VarRef("${X:-${Y}") + RBrace. The quoted form always worked (the in-string interpolation parser hand-rolls brace matching), as did ${X:-$Y}. Found during the #95 census; pre-existing, unchanged by #172.

The fix

logos regexes can't count nesting, but a callback can extend a match: the pattern is now just \$\{ and lex_varref walks lex.remainder() counting brace depth, extending the token to the balanced } via lex.bump(). The parser needed zero changesfind_default_separator was already ${}-nesting-aware and default words already route through parse_interpolated_string, which is exactly why the quoted form worked all along.

Contracts preserved / clarified:

  • ${#X} / ${#u[tags]} still lex as VarLength — its full regex out-matches the two-character opener in logos rule selection.
  • Depth counting stays quote-blind, matching both the old regex and the scanner's ${...} region tracking (they use the identical algorithm on identical bytes, so they can't disagree on where a region ends).
  • ${} stays an error (old regex required one inner char); unbalanced ${X:-${Y} is now a proper unterminated variable reference instead of a generic unexpected-character.
  • ${a}b} closes at the first balanced } — pinned by a test.
$ kaish -c 'echo ${X:-${Y:-fallback}}'   # fallback
$ kaish -c 'Y=mid; echo ${X:-${Y:-fallback}}'   # mid
$ kaish -c 'X=top; echo ${X:-${Y:-fallback}}'   # top

Testing & review

  • Full gates: cargo test --all (4,610 passed), clippy --all-targets 0 warnings, insta clean, no-default-features check.
  • New tests: token-shape (single/double nesting, VarLength tie), error cases (unterminated, extra open brace, empty), early-close contract, and kernel-routed evaluation tests; nested example added to LANGUAGE.md's :- section (verified in the REPL).
  • kaibo deepseek consult on the worktree: change judged sound on all five review axes (rule selection, bump math, scanner consistency, marker interaction, coverage); its one suggested pin (${a}b}) is included. CHANGELOG bullet under Unreleased/Fixed.

🤖 Generated with Claude Code

tobert and others added 2 commits July 16, 2026 12:57
…race (GH #173)

Found during the #95 census: the VarRef regex \$\{[^}]+\} stops at the
first }, so a nested braced reference in a bare default word lexed as
VarRef("${X:-${Y}") + RBrace and died as a confusing parse error, while
the quoted form worked (the in-string interpolation parser hand-rolls
brace matching) and ${X:-$Y} worked (no inner braces).

logos regexes can't count nesting, but a callback can extend a match:
the pattern now matches only the `${` opener and lex_varref walks the
remainder to the balanced `}` via lex.bump(). The parser side needed
nothing — find_default_separator was already ${}-nesting-aware and
default words already route through parse_interpolated_string, which is
why the quoted form always worked. ${#X} still lexes as VarLength (its
full regex out-matches the two-character opener in logos rule selection).
Depth counting stays quote-blind, matching both the old regex and the
scanner's ${...} region tracking; `${}` stays an error (the old regex
required one inner character); `${X:-${Y}` unbalanced is now a proper
UnterminatedVarRef instead of a generic unexpected-character.

Tests: token-shape + error inline tests, kernel-routed evaluation tests
(single/double nesting, set-wins-over-default), and a nested example in
LANGUAGE.md's `:-` section. Full gates green (4,609 tests, clippy clean,
insta clean, no-default-features).

Co-Authored-By: Claude Fable 5 <[email protected]>
@tobert tobert merged commit e9032af into main Jul 16, 2026
3 checks passed
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.

Bare ${X:-${Y}} (nested braced reference outside strings) is a parse error — VarRef regex stops at the first }

1 participant