From d996ce383e4e42519fdf00f2aa5fdb098009ecd4 Mon Sep 17 00:00:00 2001 From: gHashTag Date: Fri, 14 Aug 2026 18:19:20 +0000 Subject: [PATCH] parser: nesting decides the separator, never the terminator (#2127) parse_struct_body collected raw lexemes until a comma, so a comma inside a nested type ended the field. Map became field `generic : Map depth += 1, + TokenKind::RParen | TokenKind::RBracket => { + if depth > 0 { + depth -= 1; + } + } + // `<` opens a generic argument list only directly after an + // identifier (`Map<`); as an operator it must not open depth. + TokenKind::Lt if prev_was_ident => depth += 1, + TokenKind::Gt => { + if depth > 0 { + depth -= 1; + } + } + TokenKind::ShiftRight => { + // `>>` closes two generic levels (`Vec>`). + depth = if depth >= 2 { depth - 2 } else { 0 }; + } + _ => {} + } + prev_was_ident = k == TokenKind::Ident; type_str.push_str(&self.current.lexeme); self.advance(); } diff --git a/bootstrap/stage0/FROZEN_HASH b/bootstrap/stage0/FROZEN_HASH index eeb26dd44..7d1a34f56 100644 --- a/bootstrap/stage0/FROZEN_HASH +++ b/bootstrap/stage0/FROZEN_HASH @@ -1 +1 @@ -c3ec9fba947b9d5845af10d1270619f6e49298698139d0e227ab669f93868bbf bootstrap/src/compiler.rs +65f033d04125aea94dba708fdd7a565ddead2ec492f434268a11985eba1cf705 bootstrap/src/compiler.rs diff --git a/docs/now/2026-08-21-struct-body-terminator.md b/docs/now/2026-08-21-struct-body-terminator.md new file mode 100644 index 000000000..a89dc05f4 --- /dev/null +++ b/docs/now/2026-08-21-struct-body-terminator.md @@ -0,0 +1,13 @@ +# NOW -- parser: nesting may decide the separator, never the terminator (2026-08-21) + +## The terminator fixtures become tests, with a hard timeout and an honest cost (Closes #2127) + +- Five tests in `bootstrap/tests/struct_body_terminator.rs`, over five fixtures. Three are liveness tests under a 10 s in-process wall-clock ceiling: truncated input leaves the bracket depth positive, so a depth-gated terminator is never accepted and the field loop runs past the end of the token stream. Asserting on the error text alone would pass even on a hang, because a hung process never produces text to compare -- it would sit until the CI job timeout and report as infrastructure flake rather than as this defect. The child is killed before the assertion fires, so a wedged parser cannot outlive the test binary +- Those three do NOT discriminate the fixed parser from the unfixed one. Checked against the pre-fix binary: identical verdict, identical message, identical timing on all three. They are regression guards, not evidence +- `semicolon_phantom.t27` is the improvement. `Map `[]constu8`) with checkpoint/restore and field-default parsing. That rewrite and this one are complementary, not rival: master's version still falls back to a naive raw-lexeme join that breaks on the first comma. The depth-aware collector from this branch now occupies that fallback, so both fixes survive -- the real type grammar leads, and the raw join behind it lets nesting decide the separator while `RBrace`/`Semicolon`/`Eof` still terminate unconditionally +- `bootstrap/stage0/FROZEN_HASH` was regenerated from the resolved `compiler.rs` with `shasum -a 256`, not transcribed. The three `scripts/tri_loop/` helpers landed on master ahead of this branch in newer rewrites, so master's versions were kept and this branch's older drafts dropped +- Entry migrated from `docs/NOW.md` to `docs/now/` (the layout #2298 introduced); the original entry was dated 2026-08-14. The branch's own commit had deleted the heading `# NOW -- BNF: the control that measures what ternary is worth (2026-08-09)` while keeping its body; `docs/NOW.md` is restored to master byte-for-byte, so that heading survives