Skip to content

Commit cb91489

Browse files
authored
Merge pull request #11 from theodevelop/fix/issue-4-out-of-bounds-literals
fix: count literals when checking \$n bounds (closes #4)
2 parents 4aaa465 + 1033915 commit cb91489

2 files changed

Lines changed: 59 additions & 2 deletions

File tree

server/src/parser/bisonParser.ts

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -359,9 +359,15 @@ function strLiteralPlaceholder(content: string): string {
359359
return `__s${hex}__`;
360360
}
361361

362-
/** Replace every `"..."` in `text` with its unique strLiteralPlaceholder. */
362+
/**
363+
* Replace every `"..."` and `'...'` in `text` with their unique strLiteralPlaceholder.
364+
* Single-quoted character literals like `'('` are included in the content with the
365+
* surrounding apostrophes to keep them distinct from double-quoted aliases like `"("`.
366+
*/
363367
function replaceStringLiterals(text: string): string {
364-
return text.replace(/"((?:[^"\\]|\\.)*)"/g, (_, content) => ` ${strLiteralPlaceholder(content)} `);
368+
return text
369+
.replace(/"((?:[^"\\]|\\.)*)"/g, (_, content) => ` ${strLiteralPlaceholder(content)} `)
370+
.replace(/'((?:[^'\\]|\\.)*)'/g, (_, content) => ` ${strLiteralPlaceholder(`'${content}'`)} `);
365371
}
366372

367373
/**

tests/test-parsers.ts

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1838,6 +1838,57 @@ console.log('\n\n=== TEST: Flex single-line /* */ comments not parsed as rules =
18381838
`Exactly 3 rules parsed (not the /* */ comments), got ${docComment.rules.length}`);
18391839
}
18401840

1841+
// ════════════════════════════════════════
1842+
// TEST: $n with single-quoted and double-quoted literals (issue #4)
1843+
// ════════════════════════════════════════
1844+
console.log('\n\n=== TEST: $n bounds — literals counted as symbols ===\n');
1845+
1846+
{
1847+
// rule: A '(' B ')' { $$ = $3; } → 4 symbols, $3 valid → 0 diagnostics
1848+
const bisonLit = [
1849+
'%token A B',
1850+
'%%',
1851+
'start : rule ;',
1852+
"rule : A '(' B ')' { $$ = $3; }",
1853+
'%%',
1854+
].join('\n');
1855+
const docLit = parseBisonDocument(bisonLit);
1856+
const diagsLit = computeBisonDiagnostics(docLit, bisonLit);
1857+
const oobLit = diagsLit.filter(d => d.message.includes('out of bounds'));
1858+
assert(oobLit.length === 0,
1859+
`Single-quoted literals '(' and ')' are counted as symbols — $3 in A '(' B ')' must not be flagged (got: ${oobLit.map(d => d.message).join('; ')})`);
1860+
1861+
// rule: A "and" B "or" C { $$ = $4; } → 5 symbols, $4 valid → 0 diagnostics
1862+
const bisonAlias = [
1863+
'%token A B C',
1864+
'%%',
1865+
'start : rule ;',
1866+
'rule : A "and" B "or" C { $$ = $4; }',
1867+
'%%',
1868+
].join('\n');
1869+
const docAlias = parseBisonDocument(bisonAlias);
1870+
const diagsAlias = computeBisonDiagnostics(docAlias, bisonAlias);
1871+
const oobAlias = diagsAlias.filter(d => d.message.includes('out of bounds'));
1872+
assert(oobAlias.length === 0,
1873+
`Double-quoted aliases "and" and "or" are counted as symbols — $4 in A "and" B "or" C must not be flagged (got: ${oobAlias.map(d => d.message).join('; ')})`);
1874+
1875+
// rule: A B { $$ = $3; } → 2 symbols, $3 out of bounds → 1 Error
1876+
const bisonOob = [
1877+
'%token A B',
1878+
'%%',
1879+
'start : rule ;',
1880+
'rule : A B { $$ = $3; }',
1881+
'%%',
1882+
].join('\n');
1883+
const docOob = parseBisonDocument(bisonOob);
1884+
const diagsOob = computeBisonDiagnostics(docOob, bisonOob);
1885+
const oobOob = diagsOob.filter(d => d.message.includes('$3') && d.message.includes('out of bounds'));
1886+
assert(oobOob.length >= 1,
1887+
`$3 in a 2-symbol rule A B must be flagged as out of bounds`);
1888+
assert(oobOob[0]?.severity === 1,
1889+
`$3 out-of-bounds diagnostic has Error severity`);
1890+
}
1891+
18411892
// ════════════════════════════════════════
18421893
// SUMMARY
18431894
// ════════════════════════════════════════

0 commit comments

Comments
 (0)