diff --git a/build-src/codemirror-entry.mjs b/build-src/codemirror-entry.mjs index e48995b..06f9fcb 100644 --- a/build-src/codemirror-entry.mjs +++ b/build-src/codemirror-entry.mjs @@ -783,15 +783,26 @@ function formatRegionInner(inner) { } function formatWithRegions(text) { - const OPEN = /\$[A-Za-z]\w*\s*\{/g; + const OPEN = /\$\w+\s*\{/g; // \w+ to match the compiler/binding rule (digit-leading names too) let result = ""; let last = 0; let m; while ((m = OPEN.exec(text)) !== null) { const openBrace = m.index + m[0].length - 1; - const close = text.indexOf("}", openBrace + 1); - if (close === -1) continue; // unterminated — leave it for the linter to flag - const name = m[0].match(/^\$[A-Za-z]\w*/)[0]; + // Find the region's close by brace DEPTH, not the first `}` — otherwise a + // wildcard brace ({a|b}) inside the body ends the region early, spilling the + // rest into the wildcard formatter and splitting a phrase across lines (which + // the newline→comma rule then turns into an unwanted comma). + let depth = 1, j = openBrace + 1; + while (j < text.length && depth > 0) { + const ch = text[j]; + if (ch === "{") depth++; + else if (ch === "}") depth--; + j++; + } + if (depth !== 0) continue; // unterminated — leave it for the linter to flag + const close = j - 1; + const name = m[0].match(/^\$\w+/)[0]; const before = text.slice(last, m.index); if (before.trim()) result += formatPromptText(before).trim() + "\n\n"; const inner = text.slice(openBrace + 1, close); diff --git a/core/compiler.py b/core/compiler.py index d3476bf..3a43dbf 100644 --- a/core/compiler.py +++ b/core/compiler.py @@ -74,8 +74,48 @@ def strip_comments(text: str) -> str: # ============================================================================= # A `$name { body }` group pins `body` to the mannequin identified by `name` # (e.g. $mannequin1). Anything OUTSIDE every group is the shared/global prompt. -# Non-greedy body so adjacent groups don't merge; DOTALL so a group spans lines. -MANNEQUIN_RE = re.compile(r"\$(\w+)\s*\{(.*?)\}", re.DOTALL) +# The close brace is matched by DEPTH (not the first `}`) so a wildcard or +#