UNPKG

eslint-plugin-regexp

Version:

ESLint plugin for finding RegExp mistakes and RegExp style guide violations.

189 lines (188 loc) 6.75 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.PartialParser = void 0; const refa_1 = require("refa"); const util_1 = require("./util"); class Context { constructor(alternative) { this.alternative = alternative; const ancestors = new Set(); for (let n = alternative; n; n = n.parent) { ancestors.add(n); } this.ancestors = ancestors; } } class PartialParser { constructor(parser, options = {}) { this.nativeCache = new WeakMap(); this.parser = parser; this.options = options; } parse(node, alternative) { switch (node.type) { case "Pattern": return { type: "Expression", alternatives: this.parseAlternatives(node.alternatives, new Context(alternative)), }; case "Alternative": return { type: "Expression", alternatives: [ this.parseAlternative(node, new Context(alternative)), ], }; default: return { type: "Expression", alternatives: [ { type: "Concatenation", elements: [ this.parseElement(node, new Context(alternative)), ], }, ], }; } } parseAlternatives(alternatives, context) { const ancestor = alternatives.find((a) => context.ancestors.has(a)); if (ancestor) { return [this.parseAlternative(ancestor, context)]; } const result = []; for (const a of alternatives) { result.push(...this.parser.parseElement(a, this.options).expression .alternatives); } return result; } parseAlternative(alternative, context) { return { type: "Concatenation", elements: alternative.elements.map((e) => this.parseElement(e, context)), }; } parseStringAlternatives(alternatives, context) { const ancestor = alternatives.find((a) => context.ancestors.has(a)); if (ancestor) { return [this.parseStringAlternative(ancestor)]; } return alternatives.map((a) => this.parseStringAlternative(a)); } parseStringAlternative(alternative) { return { type: "Concatenation", elements: alternative.elements.map((e) => this.nativeParseElement(e)), }; } parseElement(element, context) { if (!context.ancestors.has(element)) { return this.nativeParseElement(element); } switch (element.type) { case "Assertion": case "Backreference": case "Character": case "CharacterSet": case "ExpressionCharacterClass": return this.nativeParseElement(element); case "CharacterClassRange": if (context.alternative === element.min) { return this.nativeParseElement(context.alternative); } else if (context.alternative === element.max) { return this.nativeParseElement(context.alternative); } return this.nativeParseElement(element); case "CharacterClass": return this.parseCharacterClass(element, context); case "ClassStringDisjunction": return { type: "Alternation", alternatives: this.parseStringAlternatives(element.alternatives, context), }; case "Group": case "CapturingGroup": return { type: "Alternation", alternatives: this.parseAlternatives(element.alternatives, context), }; case "Quantifier": { const alternatives = element.element.type === "Group" || element.element.type === "CapturingGroup" ? this.parseAlternatives(element.element.alternatives, context) : [ { type: "Concatenation", elements: [ this.parseElement(element.element, context), ], }, ]; return { type: "Quantifier", min: element.min, max: element.max, lazy: !element.greedy, alternatives, }; } default: throw (0, util_1.assertNever)(element); } } parseCharacterClass(cc, context) { if (cc.negate || !context.ancestors.has(cc) || context.alternative.type === "Alternative") { return this.nativeParseElement(cc); } for (const e of cc.elements) { if (context.ancestors.has(e)) { return this.parseElement(e, context); } } return this.nativeParseElement(cc); } nativeParseElement(element) { let cached = this.nativeCache.get(element); if (!cached) { cached = this.nativeParseElementUncached(element); this.nativeCache.set(element, cached); } return cached; } nativeParseElementUncached(element) { if (element.type === "CharacterClassRange") { const range = { min: element.min.value, max: element.max.value, }; return { type: "CharacterClass", characters: refa_1.JS.createCharSet([range], this.parser.flags), }; } else if (element.type === "ClassStringDisjunction") { return { type: "Alternation", alternatives: element.alternatives.map((a) => this.parseStringAlternative(a)), }; } const { expression } = this.parser.parseElement(element, this.options); if (expression.alternatives.length === 1) { const elements = expression.alternatives[0].elements; if (elements.length === 1) { return elements[0]; } } return { type: "Alternation", alternatives: expression.alternatives, }; } } exports.PartialParser = PartialParser;