Skip to content

Grammar: accept combining marks/ZWNJ/ZWJ as prose content; restrict entity regex to semicolon-terminated names (bd-96fswwce, bd-v8qc9zyc) - #489

Merged
cscheid merged 2 commits into
mainfrom
feature/bd-96fswwce-combining-marks
Aug 10, 2026
Merged

Grammar: accept combining marks/ZWNJ/ZWJ as prose content; restrict entity regex to semicolon-terminated names (bd-96fswwce, bd-v8qc9zyc)#489
cscheid merged 2 commits into
mainfrom
feature/bd-96fswwce-combining-marks

Conversation

@cscheid

@cscheid cscheid commented Aug 10, 2026

Copy link
Copy Markdown
Member

Summary

  • Combining marks (Mn/Mc/Me) and the join controls ZWNJ/ZWJ (U+200C/U+200D) were absent from pandoc_str's content character classes, so any of them in prose was a hard parse error: Devanagari vowel signs (का — i.e. any Hindi text), NFD/decomposed accents (cafe + U+0301), enclosing marks, ZWNJ/ZWJ between letters (Persian/Indic joining), and the combining sequences that named entities like ≂̸ decode to (discovered while implementing Decode named HTML entity references in prose (bd-named-entities-w6xbfftj) #488). Pandoc folds all of these into Str verbatim.
  • Adds PANDOC_COMBINING_MARKS = \p{M}\u{200C}\u{200D} to grammar.js as a single-char alternative and in the word-continuation class — the same fix shape as bd-6kewx (which added non-ASCII Po/Pc). ZWJ inside emoji sequences is unaffected (longest match wins). parser.c/grammar.json are regenerated.
  • Restores ≂̸ to the named_entities.qmd roundtrip fixture, as promised when it was omitted in Decode named HTML entity references in prose (bd-named-entities-w6xbfftj) #488.

Discovered-from bd-named-entities-w6xbfftj (#488). Plan: claude-notes/plans/2026-08-10-combining-marks-parse.md.

Test plan

  • 6 new corpus tests (test/corpus/combining_marks.txt) and 6 new pampa coverage tests, all verified failing pre-fix
  • Full corpus suite 551/551; tree-sitter test green
  • New roundtrip fixture combining_marks.qmd (literal NFD/Devanagari/ZWNJ text)
  • pampa + tree-sitter-qmd suites 4326/4326 on the rebased base; full workspace suite 11237/11237 pre-rebase
  • hub-client build:all (WASM from regenerated parser) + test:ci 131/131
  • Manual: q2 render of literal combining-mark text — output byte-verified (NFD preserved, not normalized)

Note: single-shot cargo xtask verify was blocked locally by an environmental ephemeral-port leak (orphaned Jupyter kernels, being handled separately); all verify legs passed individually. CI here is the independent single-shot check.

🤖 Generated with Claude Code

…ar (bd-96fswwce)

Combining marks (Mn/Mc/Me) and the join controls U+200C/U+200D were
absent from pandoc_str's content character classes, so any of them in
prose was a hard parse error: Devanagari vowel signs (i.e. any Hindi
text), NFD/decomposed accents, enclosing marks, ZWNJ/ZWJ between
letters (Persian/Indic), and the combining sequences that named
entities like ≂̸ decode to. Pandoc folds all of these
into Str verbatim.

Add PANDOC_COMBINING_MARKS (\p{M}\u{200C}\u{200D}) to grammar.js as a
single-char alternative and in the word-continuation class (same fix
shape as bd-6kewx, which added non-ASCII Po/Pc). ZWJ inside emoji
sequences is unaffected (longest match wins). parser.c/grammar.json
are regenerated.

Tests: 6 new corpus tests (test/corpus/combining_marks.txt) and 6 new
pampa coverage tests, all verified failing pre-fix; roundtrip fixture
combining_marks.qmd added and ≂̸ restored to
named_entities.qmd as promised in the strand. No snapshot files
changed. Corpus suite 551/551; workspace suite 11237/11237; hub-client
build:all (WASM from regenerated parser) + test:ci 131/131 green.
Single-shot cargo xtask verify was blocked by an environmental port
leak (orphaned Jupyter kernels holding ~14k ephemeral ports) — all
legs verified individually; see the plan's Phase 2 caveat.

Plan: claude-notes/plans/2026-08-10-combining-marks-parse.md

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@posit-snyk-bot

posit-snyk-bot commented Aug 10, 2026

Copy link
Copy Markdown

Snyk checks have passed. No issues have been found so far.

Status Scan Engine Critical High Medium Low Total (0)
Open Source Security 0 0 0 0 0 issues
Licenses 0 0 0 0 0 issues

💻 Catch issues earlier using the plugins for VS Code, JetBrains IDEs, Visual Studio, and Eclipse.

…-v8qc9zyc)

The regex builder mapped every key of html_entities.json through
name.substring(1, name.length - 1), which strips '&'…';' correctly for
the 2,125 semicolon-terminated names but strips the last LETTER of the
106 legacy no-semicolon keys ('&AMP' -> alternative 'AM'), so bogus
references like '&AM;' lexed as entity_reference. Filter to keys ending
in ';' — CommonMark only recognizes semicolon-terminated entities in
markdown, so the legacy keys contribute nothing legitimate. parser.c
regenerated (net −121 lines).

No user-visible AST change ('&AM;' merged to the same Str via the
converter's verbatim fallback, which stays as defense-in-depth); the
fix is pinned at the CST level by 3 new corpus tests
(test/corpus/entity_reference_legacy.txt), with the '&AM;' case
verified failing pre-fix. Stale pre-fix comments in pampa updated.
No snapshot files changed.

Corpus suite 554/554; pampa suites 4326/4326; workspace 11285/11285;
full cargo xtask verify green (single-shot — environment recovered).
Unrelated macOS-only flake discovered during verification filed as
bd-zazptk5s.

Plan: claude-notes/plans/2026-08-10-entity-regex-legacy-names.md

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@cscheid cscheid changed the title Accept combining marks and ZWNJ/ZWJ as prose content in the qmd grammar (bd-96fswwce) Grammar: accept combining marks/ZWNJ/ZWJ as prose content; restrict entity regex to semicolon-terminated names (bd-96fswwce, bd-v8qc9zyc) Aug 10, 2026
@cscheid

cscheid commented Aug 10, 2026

Copy link
Copy Markdown
Member Author

Pushed a second, directly-related grammar commit onto this branch: 05c2454e fixes bd-v8qc9zyc — html_entity_regex() now filters html_entities.json to semicolon-terminated names, removing the 106 mangled legacy alternatives (&AMP → bogus AM). CST behavior pinned by 3 new corpus tests (&AM; case verified failing pre-fix); no user-visible AST change. Full cargo xtask verify green locally for the stacked pair.

About the macOS CI failure on the previous run: it is a pre-existing, unrelated flake, now diagnosed and filed as bd-zazptk5s. The automerge filesystem storage splays doc ids into prefix subdirectories; when two random ids differ in the 2-char prefix only by case (2x… vs 2X…), case-insensitive APFS reuses the first splay directory and ids reconstructed from paths come back with the wrong case — the failed assertion differs by exactly one letter's case at position 2. Same signature observed locally today in a different test of the same file. Linux CI (case-sensitive) can't hit it.

🤖 Generated with Claude Code

@cscheid
cscheid merged commit bcdbce6 into main Aug 10, 2026
8 checks passed
@cscheid
cscheid deleted the feature/bd-96fswwce-combining-marks branch August 10, 2026 18:13
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.

2 participants