fix(common): check for invalid markers

- pattern parser update

Fixes: 12467
This commit is contained in:
Steven R. Loomis 2024-11-01 16:50:25 -05:00
parent c057112c9f
commit fb4f22da66
2 changed files with 35 additions and 2 deletions

View file

@ -81,7 +81,12 @@ export class MarkerParser {
/**
* Pattern for matching a marker reference, OR the special marker \m{.}
*/
public static readonly REFERENCE = /\\m{([0-9A-Za-z_]{1,32}|\.)}/g;
public static readonly REFERENCE = /(?<!\\)(?:\\\\)*\\m{([0-9A-Za-z_]{1,32}|\.)}/g;
/**
* Pattern for matching a broken marker reference (assuming REFERENCE was not matched)
*/
public static readonly BROKEN_REFERENCE = /(?<!\\)(?:\\\\)*\\m{([^}\\{}]*)/g;
/**
* parse a string into marker references
@ -95,6 +100,21 @@ export class MarkerParser {
return matchArray(str, MarkerParser.REFERENCE);
}
/**
* parse a string for broken marker references
* @param str input string such as "\m{a} … \m{.}"
* @returns `[]` or an array of all broken markers referenced
*/
public static allBrokenReferences(str: string): string[] {
if (!str) {
return [];
}
// exclude valid markers
str = str.replaceAll(this.REFERENCE, '');
return matchArray(str, MarkerParser.BROKEN_REFERENCE);
}
private static markerCodeToString(n: number, forMatch?: boolean): string {
if (!forMatch) {
return String.fromCharCode(n);

View file

@ -33,7 +33,7 @@ describe('Test of Pattern Parsers', () => {
// indirectly tests REFERENCE
it('should match reference strings', () => {
const cases: string[][] = [
['\\m{acute}', 'acute'],
['\\m{acute} but not \\\\m{chronic}', 'acute'], // second marker is escaped
['\\m{acute}≈\\m{acute}', 'acute acute'], // not deduped
['\\m{grave}≠\\m{acute}', 'grave acute'],
[MarkerParser.ANY_MARKER, MarkerParser.ANY_MARKER_ID],
@ -55,6 +55,19 @@ describe('Test of Pattern Parsers', () => {
assert.deepEqual(MarkerParser.allReferences(str), [], `expected no markers: ${str}`);
}
});
it('should match broken reference strings', () => {
const cases: string[][] = [
// hyphenated marker id - illegal
['\\m{chronic} \\m{a-cute} \\\\m{a-choo}', 'a-cute'], // \\m{a-choo} is literal
// marker through end of line
['\\m{chronic} \\m{oopsIforGot to terminate it', 'oopsIforGot to terminate it'],
// marker terminated by other valid marker
['\\m{chronic} \\m{what \\m{does} \\m{this button do?', 'what ', 'this button do?'],
];
for (const [str, ...reflist] of cases) {
assert.sameDeepMembers(MarkerParser.allBrokenReferences(str), reflist, `for ${str}`);
}
});
it('should be able to emit sentinel values', () => {
assert.equal(MarkerParser.markerOutput(295), '\uFFFF\u0008\u0127', 'Wrong sentinel value emitted');
assert.equal(MarkerParser.markerOutput(MarkerParser.ANY_MARKER_INDEX), '\uFFFF\u0008\uD7FF', 'Wrong sentinel value emitted for ANY_MARKER_INDEX');