$ — $1 one token, $a zero or more, $A one or moreRuled 2026-09-18 (gritzko): a spot snippet's variables are $-prefixed and a bare letter is always a literal. BEE-074 ported be's matcher with bare-letter variables, where a one-letter name (i, union U) could never be matched as itself and a block could not be empty, so every callable needed two def rules. Patch bee's matcher and def table; libdog changes only where needed. Main-tree work permitted, no commits.
index/match.js: isPlaceholder takes any single letter; lowercase binds one token, uppercase a balanced block of one or more.index/match.js:241:vT a block refuses to start on a closing bracket, so f(A) never matches f().index/defs.js carries rule pairs (s(A){B} and s(){B}) and an allow-list entry for an empty { } body.$: .c, .js, .sh keep $1 whole, .go, .py, .rs, .rl cut it (12 vs 15 tokens for one needle).tok/LINK.rl takes a snippet body as opaque bytes, so the link grammar and tok.link need no change (DOG-050).$1..$9 bind exactly ONE token; $a..$z a balanced block of ZERO or more; $A..$Z a block of ONE or more.$0 is a def template's name slot, swapped for the symbol before lexing; a user needle may not hold it. $$ is a literal $.$x become opaque identifiers BEFORE lexing, in every language alike; a $x inside a string token stays text.$a tries empty first, then shortest-first; an empty bound block recurs as empty.;; two adjacent blocks are a malformed needle.String.replace.index/defs.js in the new notation ($0($a){$b}, typedef $A $0;): rule pairs collapse, the allow-list goes if it can.f($a) takes f(), f($A) does not, a bare i is literal, one needle works in .c, .js, .go.x/X comment at tok/LINK.h:20:Ha (fixed); a lexer change there needs its own DOG ticket.spot/SPOT.c and be's views/spot/match.js keep the old notation; bee and links are the work here.= $a - 1 may bind empty because the author wrote $a.$ fusing: no dependence on what a tokenizer does with $."f($1)" is f() to sh, fish and PowerShell alike (BEE-073).$a empty-first, the trailing block, adjacent-block rejectionZq<nn>qZ: one identifier token in c, h, cpp, js, jsx, ts, tsx, go, py, rs, rl, mkd, md, txt, sh, java.$a$b then reads as two.$ no variable follows ($(, $ ) is a LITERAL $; only $0 in a needle is refused.spot/SPOT.c, which is untouched..go/.rs lost their two-space gaps to a $b before the body brace.tok/LINK.h:20:Ha was NOT touched: dog/ is read-only here, so the stale x/X comment is reported, not fixed.Working tree only, nothing committed. index/match.js swaps every $x for an
opaque identifier before lexing, tells variables apart by sentinel after it, and
binds a block by enumerating its extents shortest-first — empty first for $a,
maximal for a block no literal follows; a needle with adjacent blocks or a block
beside a gap is refused in words at make. index/defs.js carries the
templates in the new notation, $0 swapped by hand for the symbol. test/anchor
is 14 shell + 50 JS checks (21 of them new), test/scope 7 + 14; all 79 suites
green.