From baec8accf514462ddc3366544cb4d0d9534610d1 Mon Sep 17 00:00:00 2001 From: ota-meshi Date: Mon, 11 Sep 2023 17:13:00 +0900 Subject: [PATCH] fix: error in expression character class with nested character class --- src/parser.ts | 12 +- ...et-expression-intersection-valid-2024.json | 396 ++++++++++++++++++ test/parser.ts | 11 +- 3 files changed, 410 insertions(+), 9 deletions(-) diff --git a/src/parser.ts b/src/parser.ts index dd273a2..3087b40 100644 --- a/src/parser.ts +++ b/src/parser.ts @@ -528,8 +528,7 @@ class RegExpParserState { if ( node.type !== "CharacterClass" || (node.parent.type !== "Alternative" && - node.parent.type !== "CharacterClass") || - (this._expressionBuffer && node.elements.length > 0) + node.parent.type !== "CharacterClass") ) { throw new Error("UnknownError") } @@ -540,10 +539,15 @@ class RegExpParserState { this._node = parent const expression = this._expressionBuffer - this._expressionBuffer = null - if (!expression) { + if ( + expression?.parent !== (node as unknown as ExpressionCharacterClass) + ) { return } + if (node.elements.length > 0) { + throw new Error("UnknownError") + } + this._expressionBuffer = null // Replace with ExpressionCharacterClass. const newNode: ExpressionCharacterClass = { diff --git a/test/fixtures/parser/literal/class-set-expression-intersection-valid-2024.json b/test/fixtures/parser/literal/class-set-expression-intersection-valid-2024.json index 6f927d6..76d474f 100644 --- a/test/fixtures/parser/literal/class-set-expression-intersection-valid-2024.json +++ b/test/fixtures/parser/literal/class-set-expression-intersection-valid-2024.json @@ -1935,6 +1935,402 @@ "unicodeSets": true } } + }, + "/[a&&b&&[c]]/v": { + "ast": { + "type": "RegExpLiteral", + "parent": null, + "start": 0, + "end": 14, + "raw": "/[a&&b&&[c]]/v", + "pattern": { + "type": "Pattern", + "parent": "♻️..", + "start": 1, + "end": 12, + "raw": "[a&&b&&[c]]", + "alternatives": [ + { + "type": "Alternative", + "parent": "♻️../..", + "start": 1, + "end": 12, + "raw": "[a&&b&&[c]]", + "elements": [ + { + "type": "ExpressionCharacterClass", + "parent": "♻️../..", + "start": 1, + "end": 12, + "raw": "[a&&b&&[c]]", + "negate": false, + "expression": { + "type": "ClassIntersection", + "parent": "♻️..", + "start": 2, + "end": 11, + "raw": "a&&b&&[c]", + "left": { + "type": "ClassIntersection", + "parent": "♻️..", + "start": 2, + "end": 6, + "raw": "a&&b", + "left": { + "type": "Character", + "parent": "♻️..", + "start": 2, + "end": 3, + "raw": "a", + "value": 97 + }, + "right": { + "type": "Character", + "parent": "♻️..", + "start": 5, + "end": 6, + "raw": "b", + "value": 98 + } + }, + "right": { + "type": "CharacterClass", + "parent": "♻️..", + "start": 8, + "end": 11, + "raw": "[c]", + "unicodeSets": true, + "negate": false, + "elements": [ + { + "type": "Character", + "parent": "♻️../..", + "start": 9, + "end": 10, + "raw": "c", + "value": 99 + } + ] + } + } + } + ] + } + ] + }, + "flags": { + "type": "Flags", + "parent": "♻️..", + "start": 13, + "end": 14, + "raw": "v", + "global": false, + "ignoreCase": false, + "multiline": false, + "unicode": false, + "sticky": false, + "dotAll": false, + "hasIndices": false, + "unicodeSets": true + } + } + }, + "/[a&&b&&[^c]]/v": { + "ast": { + "type": "RegExpLiteral", + "parent": null, + "start": 0, + "end": 15, + "raw": "/[a&&b&&[^c]]/v", + "pattern": { + "type": "Pattern", + "parent": "♻️..", + "start": 1, + "end": 13, + "raw": "[a&&b&&[^c]]", + "alternatives": [ + { + "type": "Alternative", + "parent": "♻️../..", + "start": 1, + "end": 13, + "raw": "[a&&b&&[^c]]", + "elements": [ + { + "type": "ExpressionCharacterClass", + "parent": "♻️../..", + "start": 1, + "end": 13, + "raw": "[a&&b&&[^c]]", + "negate": false, + "expression": { + "type": "ClassIntersection", + "parent": "♻️..", + "start": 2, + "end": 12, + "raw": "a&&b&&[^c]", + "left": { + "type": "ClassIntersection", + "parent": "♻️..", + "start": 2, + "end": 6, + "raw": "a&&b", + "left": { + "type": "Character", + "parent": "♻️..", + "start": 2, + "end": 3, + "raw": "a", + "value": 97 + }, + "right": { + "type": "Character", + "parent": "♻️..", + "start": 5, + "end": 6, + "raw": "b", + "value": 98 + } + }, + "right": { + "type": "CharacterClass", + "parent": "♻️..", + "start": 8, + "end": 12, + "raw": "[^c]", + "unicodeSets": true, + "negate": true, + "elements": [ + { + "type": "Character", + "parent": "♻️../..", + "start": 10, + "end": 11, + "raw": "c", + "value": 99 + } + ] + } + } + } + ] + } + ] + }, + "flags": { + "type": "Flags", + "parent": "♻️..", + "start": 14, + "end": 15, + "raw": "v", + "global": false, + "ignoreCase": false, + "multiline": false, + "unicode": false, + "sticky": false, + "dotAll": false, + "hasIndices": false, + "unicodeSets": true + } + } + }, + "/[a--b--[c]]/v": { + "ast": { + "type": "RegExpLiteral", + "parent": null, + "start": 0, + "end": 14, + "raw": "/[a--b--[c]]/v", + "pattern": { + "type": "Pattern", + "parent": "♻️..", + "start": 1, + "end": 12, + "raw": "[a--b--[c]]", + "alternatives": [ + { + "type": "Alternative", + "parent": "♻️../..", + "start": 1, + "end": 12, + "raw": "[a--b--[c]]", + "elements": [ + { + "type": "ExpressionCharacterClass", + "parent": "♻️../..", + "start": 1, + "end": 12, + "raw": "[a--b--[c]]", + "negate": false, + "expression": { + "type": "ClassSubtraction", + "parent": "♻️..", + "start": 2, + "end": 11, + "raw": "a--b--[c]", + "left": { + "type": "ClassSubtraction", + "parent": "♻️..", + "start": 2, + "end": 6, + "raw": "a--b", + "left": { + "type": "Character", + "parent": "♻️..", + "start": 2, + "end": 3, + "raw": "a", + "value": 97 + }, + "right": { + "type": "Character", + "parent": "♻️..", + "start": 5, + "end": 6, + "raw": "b", + "value": 98 + } + }, + "right": { + "type": "CharacterClass", + "parent": "♻️..", + "start": 8, + "end": 11, + "raw": "[c]", + "unicodeSets": true, + "negate": false, + "elements": [ + { + "type": "Character", + "parent": "♻️../..", + "start": 9, + "end": 10, + "raw": "c", + "value": 99 + } + ] + } + } + } + ] + } + ] + }, + "flags": { + "type": "Flags", + "parent": "♻️..", + "start": 13, + "end": 14, + "raw": "v", + "global": false, + "ignoreCase": false, + "multiline": false, + "unicode": false, + "sticky": false, + "dotAll": false, + "hasIndices": false, + "unicodeSets": true + } + } + }, + "/[a--b--[^c]]/v": { + "ast": { + "type": "RegExpLiteral", + "parent": null, + "start": 0, + "end": 15, + "raw": "/[a--b--[^c]]/v", + "pattern": { + "type": "Pattern", + "parent": "♻️..", + "start": 1, + "end": 13, + "raw": "[a--b--[^c]]", + "alternatives": [ + { + "type": "Alternative", + "parent": "♻️../..", + "start": 1, + "end": 13, + "raw": "[a--b--[^c]]", + "elements": [ + { + "type": "ExpressionCharacterClass", + "parent": "♻️../..", + "start": 1, + "end": 13, + "raw": "[a--b--[^c]]", + "negate": false, + "expression": { + "type": "ClassSubtraction", + "parent": "♻️..", + "start": 2, + "end": 12, + "raw": "a--b--[^c]", + "left": { + "type": "ClassSubtraction", + "parent": "♻️..", + "start": 2, + "end": 6, + "raw": "a--b", + "left": { + "type": "Character", + "parent": "♻️..", + "start": 2, + "end": 3, + "raw": "a", + "value": 97 + }, + "right": { + "type": "Character", + "parent": "♻️..", + "start": 5, + "end": 6, + "raw": "b", + "value": 98 + } + }, + "right": { + "type": "CharacterClass", + "parent": "♻️..", + "start": 8, + "end": 12, + "raw": "[^c]", + "unicodeSets": true, + "negate": true, + "elements": [ + { + "type": "Character", + "parent": "♻️../..", + "start": 10, + "end": 11, + "raw": "c", + "value": 99 + } + ] + } + } + } + ] + } + ] + }, + "flags": { + "type": "Flags", + "parent": "♻️..", + "start": 14, + "end": 15, + "raw": "v", + "global": false, + "ignoreCase": false, + "multiline": false, + "unicode": false, + "sticky": false, + "dotAll": false, + "hasIndices": false, + "unicodeSets": true + } + } } } } \ No newline at end of file diff --git a/test/parser.ts b/test/parser.ts index f67b088..90c99df 100644 --- a/test/parser.ts +++ b/test/parser.ts @@ -49,17 +49,18 @@ describe("parseRegExpLiteral function:", () => { } else { it(`${source} should throw syntax error.`, () => { const expected = result.error - assert.strictEqual( - expected.message.slice(0, 27), - "Invalid regular expression:", - `The error message '${expected.message}' was not syntax error.`, - ) try { parseRegExpLiteral(source, options) } catch (err) { const error = err as RegExpSyntaxError assert.strictEqual(error.message, expected.message) assert.strictEqual(error.index, expected.index) + + assert.strictEqual( + expected.message.slice(0, 27), + "Invalid regular expression:", + `The error message '${expected.message}' was not syntax error.`, + ) return } assert.fail("Should fail, but succeeded.")