From 33e82031cfb6b329c2cb9431b0594b2329bb7b21 Mon Sep 17 00:00:00 2001 From: Yosuke Ota Date: Tue, 26 Sep 2023 05:34:16 +0900 Subject: [PATCH] fix: error in expression with nested expression (#143) --- src/parser.ts | 24 +- ...et-expression-intersection-valid-2024.json | 290 ++++++++++++++++++ ...set-expression-subtraction-valid-2024.json | 290 ++++++++++++++++++ 3 files changed, 593 insertions(+), 11 deletions(-) diff --git a/src/parser.ts b/src/parser.ts index 3087b40..fabbb22 100644 --- a/src/parser.ts +++ b/src/parser.ts @@ -58,8 +58,10 @@ class RegExpParserState { private _node: AppendableNode = DUMMY_PATTERN - private _expressionBuffer: ClassIntersection | ClassSubtraction | null = - null + private _expressionBufferMap = new Map< + CharacterClass | ExpressionCharacterClass, + ClassIntersection | ClassSubtraction + >() private _flags: Flags = DUMMY_FLAGS @@ -538,16 +540,14 @@ class RegExpParserState { node.raw = this.source.slice(start, end) this._node = parent - const expression = this._expressionBuffer - if ( - expression?.parent !== (node as unknown as ExpressionCharacterClass) - ) { + const expression = this._expressionBufferMap.get(node) + if (!expression) { return } if (node.elements.length > 0) { throw new Error("UnknownError") } - this._expressionBuffer = null + this._expressionBufferMap.delete(node) // Replace with ExpressionCharacterClass. const newNode: ExpressionCharacterClass = { @@ -614,7 +614,8 @@ class RegExpParserState { } // Replace the last two elements. const right = parent.elements.pop() - const left = this._expressionBuffer ?? parent.elements.pop() + const left = + this._expressionBufferMap.get(parent) ?? parent.elements.pop() if ( !left || !right || @@ -637,7 +638,7 @@ class RegExpParserState { } left.parent = node right.parent = node - this._expressionBuffer = node + this._expressionBufferMap.set(parent, node) } public onClassSubtraction(start: number, end: number): void { @@ -647,7 +648,8 @@ class RegExpParserState { } // Replace the last two elements. const right = parent.elements.pop() - const left = this._expressionBuffer ?? parent.elements.pop() + const left = + this._expressionBufferMap.get(parent) ?? parent.elements.pop() if ( !left || !right || @@ -670,7 +672,7 @@ class RegExpParserState { } left.parent = node right.parent = node - this._expressionBuffer = node + this._expressionBufferMap.set(parent, node) } public onClassStringDisjunctionEnter(start: number): void { 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 76d474f..78b98cd 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 @@ -2331,6 +2331,296 @@ "unicodeSets": true } } + }, + "/[a&&b&&[c&&d&&e]]/v": { + "ast": { + "type": "RegExpLiteral", + "parent": null, + "start": 0, + "end": 20, + "raw": "/[a&&b&&[c&&d&&e]]/v", + "pattern": { + "type": "Pattern", + "parent": "♻️..", + "start": 1, + "end": 18, + "raw": "[a&&b&&[c&&d&&e]]", + "alternatives": [ + { + "type": "Alternative", + "parent": "♻️../..", + "start": 1, + "end": 18, + "raw": "[a&&b&&[c&&d&&e]]", + "elements": [ + { + "type": "ExpressionCharacterClass", + "parent": "♻️../..", + "start": 1, + "end": 18, + "raw": "[a&&b&&[c&&d&&e]]", + "negate": false, + "expression": { + "type": "ClassIntersection", + "parent": "♻️..", + "start": 2, + "end": 17, + "raw": "a&&b&&[c&&d&&e]", + "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": "ExpressionCharacterClass", + "parent": "♻️..", + "start": 8, + "end": 17, + "raw": "[c&&d&&e]", + "negate": false, + "expression": { + "type": "ClassIntersection", + "parent": "♻️..", + "start": 9, + "end": 16, + "raw": "c&&d&&e", + "left": { + "type": "ClassIntersection", + "parent": "♻️..", + "start": 9, + "end": 13, + "raw": "c&&d", + "left": { + "type": "Character", + "parent": "♻️..", + "start": 9, + "end": 10, + "raw": "c", + "value": 99 + }, + "right": { + "type": "Character", + "parent": "♻️..", + "start": 12, + "end": 13, + "raw": "d", + "value": 100 + } + }, + "right": { + "type": "Character", + "parent": "♻️..", + "start": 15, + "end": 16, + "raw": "e", + "value": 101 + } + } + } + } + } + ] + } + ] + }, + "flags": { + "type": "Flags", + "parent": "♻️..", + "start": 19, + "end": 20, + "raw": "v", + "global": false, + "ignoreCase": false, + "multiline": false, + "unicode": false, + "sticky": false, + "dotAll": false, + "hasIndices": false, + "unicodeSets": true + } + } + }, + "/[a&&b&&[c--d--[e&&f&&g]]]/v": { + "ast": { + "type": "RegExpLiteral", + "parent": null, + "start": 0, + "end": 28, + "raw": "/[a&&b&&[c--d--[e&&f&&g]]]/v", + "pattern": { + "type": "Pattern", + "parent": "♻️..", + "start": 1, + "end": 26, + "raw": "[a&&b&&[c--d--[e&&f&&g]]]", + "alternatives": [ + { + "type": "Alternative", + "parent": "♻️../..", + "start": 1, + "end": 26, + "raw": "[a&&b&&[c--d--[e&&f&&g]]]", + "elements": [ + { + "type": "ExpressionCharacterClass", + "parent": "♻️../..", + "start": 1, + "end": 26, + "raw": "[a&&b&&[c--d--[e&&f&&g]]]", + "negate": false, + "expression": { + "type": "ClassIntersection", + "parent": "♻️..", + "start": 2, + "end": 25, + "raw": "a&&b&&[c--d--[e&&f&&g]]", + "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": "ExpressionCharacterClass", + "parent": "♻️..", + "start": 8, + "end": 25, + "raw": "[c--d--[e&&f&&g]]", + "negate": false, + "expression": { + "type": "ClassSubtraction", + "parent": "♻️..", + "start": 9, + "end": 24, + "raw": "c--d--[e&&f&&g]", + "left": { + "type": "ClassSubtraction", + "parent": "♻️..", + "start": 9, + "end": 13, + "raw": "c--d", + "left": { + "type": "Character", + "parent": "♻️..", + "start": 9, + "end": 10, + "raw": "c", + "value": 99 + }, + "right": { + "type": "Character", + "parent": "♻️..", + "start": 12, + "end": 13, + "raw": "d", + "value": 100 + } + }, + "right": { + "type": "ExpressionCharacterClass", + "parent": "♻️..", + "start": 15, + "end": 24, + "raw": "[e&&f&&g]", + "negate": false, + "expression": { + "type": "ClassIntersection", + "parent": "♻️..", + "start": 16, + "end": 23, + "raw": "e&&f&&g", + "left": { + "type": "ClassIntersection", + "parent": "♻️..", + "start": 16, + "end": 20, + "raw": "e&&f", + "left": { + "type": "Character", + "parent": "♻️..", + "start": 16, + "end": 17, + "raw": "e", + "value": 101 + }, + "right": { + "type": "Character", + "parent": "♻️..", + "start": 19, + "end": 20, + "raw": "f", + "value": 102 + } + }, + "right": { + "type": "Character", + "parent": "♻️..", + "start": 22, + "end": 23, + "raw": "g", + "value": 103 + } + } + } + } + } + } + } + ] + } + ] + }, + "flags": { + "type": "Flags", + "parent": "♻️..", + "start": 27, + "end": 28, + "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/fixtures/parser/literal/class-set-expression-subtraction-valid-2024.json b/test/fixtures/parser/literal/class-set-expression-subtraction-valid-2024.json index 5ee2f43..c3754a2 100644 --- a/test/fixtures/parser/literal/class-set-expression-subtraction-valid-2024.json +++ b/test/fixtures/parser/literal/class-set-expression-subtraction-valid-2024.json @@ -1649,6 +1649,296 @@ "unicodeSets": true } } + }, + "/[a--b--[c--d--e]]/v": { + "ast": { + "type": "RegExpLiteral", + "parent": null, + "start": 0, + "end": 20, + "raw": "/[a--b--[c--d--e]]/v", + "pattern": { + "type": "Pattern", + "parent": "♻️..", + "start": 1, + "end": 18, + "raw": "[a--b--[c--d--e]]", + "alternatives": [ + { + "type": "Alternative", + "parent": "♻️../..", + "start": 1, + "end": 18, + "raw": "[a--b--[c--d--e]]", + "elements": [ + { + "type": "ExpressionCharacterClass", + "parent": "♻️../..", + "start": 1, + "end": 18, + "raw": "[a--b--[c--d--e]]", + "negate": false, + "expression": { + "type": "ClassSubtraction", + "parent": "♻️..", + "start": 2, + "end": 17, + "raw": "a--b--[c--d--e]", + "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": "ExpressionCharacterClass", + "parent": "♻️..", + "start": 8, + "end": 17, + "raw": "[c--d--e]", + "negate": false, + "expression": { + "type": "ClassSubtraction", + "parent": "♻️..", + "start": 9, + "end": 16, + "raw": "c--d--e", + "left": { + "type": "ClassSubtraction", + "parent": "♻️..", + "start": 9, + "end": 13, + "raw": "c--d", + "left": { + "type": "Character", + "parent": "♻️..", + "start": 9, + "end": 10, + "raw": "c", + "value": 99 + }, + "right": { + "type": "Character", + "parent": "♻️..", + "start": 12, + "end": 13, + "raw": "d", + "value": 100 + } + }, + "right": { + "type": "Character", + "parent": "♻️..", + "start": 15, + "end": 16, + "raw": "e", + "value": 101 + } + } + } + } + } + ] + } + ] + }, + "flags": { + "type": "Flags", + "parent": "♻️..", + "start": 19, + "end": 20, + "raw": "v", + "global": false, + "ignoreCase": false, + "multiline": false, + "unicode": false, + "sticky": false, + "dotAll": false, + "hasIndices": false, + "unicodeSets": true + } + } + }, + "/[a--b--[c&&d&&[e--f--g]]]/v": { + "ast": { + "type": "RegExpLiteral", + "parent": null, + "start": 0, + "end": 28, + "raw": "/[a--b--[c&&d&&[e--f--g]]]/v", + "pattern": { + "type": "Pattern", + "parent": "♻️..", + "start": 1, + "end": 26, + "raw": "[a--b--[c&&d&&[e--f--g]]]", + "alternatives": [ + { + "type": "Alternative", + "parent": "♻️../..", + "start": 1, + "end": 26, + "raw": "[a--b--[c&&d&&[e--f--g]]]", + "elements": [ + { + "type": "ExpressionCharacterClass", + "parent": "♻️../..", + "start": 1, + "end": 26, + "raw": "[a--b--[c&&d&&[e--f--g]]]", + "negate": false, + "expression": { + "type": "ClassSubtraction", + "parent": "♻️..", + "start": 2, + "end": 25, + "raw": "a--b--[c&&d&&[e--f--g]]", + "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": "ExpressionCharacterClass", + "parent": "♻️..", + "start": 8, + "end": 25, + "raw": "[c&&d&&[e--f--g]]", + "negate": false, + "expression": { + "type": "ClassIntersection", + "parent": "♻️..", + "start": 9, + "end": 24, + "raw": "c&&d&&[e--f--g]", + "left": { + "type": "ClassIntersection", + "parent": "♻️..", + "start": 9, + "end": 13, + "raw": "c&&d", + "left": { + "type": "Character", + "parent": "♻️..", + "start": 9, + "end": 10, + "raw": "c", + "value": 99 + }, + "right": { + "type": "Character", + "parent": "♻️..", + "start": 12, + "end": 13, + "raw": "d", + "value": 100 + } + }, + "right": { + "type": "ExpressionCharacterClass", + "parent": "♻️..", + "start": 15, + "end": 24, + "raw": "[e--f--g]", + "negate": false, + "expression": { + "type": "ClassSubtraction", + "parent": "♻️..", + "start": 16, + "end": 23, + "raw": "e--f--g", + "left": { + "type": "ClassSubtraction", + "parent": "♻️..", + "start": 16, + "end": 20, + "raw": "e--f", + "left": { + "type": "Character", + "parent": "♻️..", + "start": 16, + "end": 17, + "raw": "e", + "value": 101 + }, + "right": { + "type": "Character", + "parent": "♻️..", + "start": 19, + "end": 20, + "raw": "f", + "value": 102 + } + }, + "right": { + "type": "Character", + "parent": "♻️..", + "start": 22, + "end": 23, + "raw": "g", + "value": 103 + } + } + } + } + } + } + } + ] + } + ] + }, + "flags": { + "type": "Flags", + "parent": "♻️..", + "start": 27, + "end": 28, + "raw": "v", + "global": false, + "ignoreCase": false, + "multiline": false, + "unicode": false, + "sticky": false, + "dotAll": false, + "hasIndices": false, + "unicodeSets": true + } + } } } } \ No newline at end of file