const MinCodePoint = 0x00; const MaxCodePoint = 0x10ffff; function isLatinLetter(code) { return ((code >= LatinCapitalLetterA && code <= LatinCapitalLetterZ) || (code >= LatinSmallLetterA && code <= LatinSmallLetterZ)); } function isDecimalDigit(code) { return code >= DigitZero && code <= DigitNine; } function isOctalDigit(code) { return code >= DigitZero && code <= DigitSeven; } function isHexDigit(code) { return ((code >= DigitZero && code <= DigitNine) || (code >= LatinCapitalLetterA && code <= LatinCapitalLetterF) || (code >= LatinSmallLetterA && code <= LatinSmallLetterF)); } function isLineTerminator(code) { return (code === LineFeed || code === CarriageReturn || code === LineSeparator || code === ParagraphSeparator); } function isValidUnicode(code) { return code >= MinCodePoint && code <= MaxCodePoint; } function digitToInt(code) { if (code >= LatinSmallLetterA && code <= LatinSmallLetterF) { return code - LatinSmallLetterA + 10; } if (code >= LatinCapitalLetterA && code <= LatinCapitalLetterF) { return code - LatinCapitalLetterA + 10; } return code - DigitZero; } function isSyntaxCharacter(cp) { return (cp === CircumflexAccent || cp === DollarSign || cp === ReverseSolidus || cp === FullStop || cp === Asterisk || cp === PlusSign || cp === QuestionMark || cp === LeftParenthesis || cp === RightParenthesis || cp === LeftSquareBracket || cp === RightSquareBracket || cp === LeftCurlyBracket || cp === RightCurlyBracket || cp === VerticalLine); } function isRegExpIdentifierStart(cp) { return isIdStart(cp) || cp === DollarSign || cp === LowLine; } function isRegExpIdentifierPart(cp) { return (isIdContinue(cp) || cp === DollarSign || cp === LowLine || cp === ZeroWidthNonJoiner || cp === ZeroWidthJoiner); } function isUnicodePropertyNameCharacter(cp) { return isLatinLetter(cp) || cp === LowLine; } function isUnicodePropertyValueCharacter(cp) { return isUnicodePropertyNameCharacter(cp) || isDecimalDigit(cp); } function isValidUnicodeProperty(name, value) { return PropertyData.hasOwnProperty(name) && PropertyData[name].has(value); } function isValidUnicodePropertyName(name) { return PropertyData.$LONE.has(name); } class RegExpValidator { constructor(options) { this._reader = new Reader(); this._uFlag = false; this._nFlag = false; this._lastIntValue = 0; this._lastMinValue = 0; this._lastMaxValue = 0; this._lastStrValue = ""; this._lastKeyValue = ""; this._lastValValue = ""; this._lastAssertionIsQuantifiable = false; this._numCapturingParens = 0; this._groupNames = new Set(); this._backreferenceNames = new Set(); this._options = options || {}; } validateLiteral(source, start = 0, end = source.length) { this._uFlag = this._nFlag = false; this.reset(source, start, end); this.onLiteralEnter(start); if (this.eat(Solidus) && this.eatRegExpBody() && this.eat(Solidus)) { const flagStart = this.index; const uFlag = source.indexOf("u", flagStart) !== -1; this.validateFlags(source, flagStart, end); this.validatePattern(source, start + 1, flagStart - 1, uFlag); } else if (start >= end) { this.raise("Empty"); } else { const c = String.fromCodePoint(this.currentCodePoint); this.raise(`Unexpected character '${c}'`); } this.onLiteralLeave(start, end); } validateFlags(source, start = 0, end = source.length) { const existingFlags = new Set(); let global = false; let ignoreCase = false; let multiline = false; let sticky = false; let unicode = false; let dotAll = false; for (let i = start; i < end; ++i) { const flag = source.charCodeAt(i); if (existingFlags.has(flag)) { this.raise(`Duplicated flag '${source[i]}'`); } existingFlags.add(flag); if (flag === LatinSmallLetterG) { global = true; } else if (flag === LatinSmallLetterI) { ignoreCase = true; } else if (flag === LatinSmallLetterM) { multiline = true; } else if (flag === LatinSmallLetterU && this.ecmaVersion >= 2015) { unicode = true; } else if (flag === LatinSmallLetterY && this.ecmaVersion >= 2015) { sticky = true; } else if (flag === LatinSmallLetterS && this.ecmaVersion >= 2018) { dotAll = true; } else { this.raise(`Invalid flag '${source[i]}'`); } } this.onFlags(start, end, global, ignoreCase, multiline, unicode, sticky, dotAll); } validatePattern(source, start = 0, end = source.length, uFlag = false) { this._uFlag = uFlag && this.ecmaVersion >= 2015; this._nFlag = uFlag && this.ecmaVersion >= 2018; this.reset(source, start, end); this.pattern(); if (!this._nFlag && this.ecmaVersion >= 2018 && this._groupNames.size > 0) { this._nFlag = true; this.rewind(start); this.pattern(); } } get strict() { return Boolean(this._options.strict || this._uFlag); } get ecmaVersion() { return this._options.ecmaVersion || 2018; } onLiteralEnter(start) { if (this._options.onLiteralEnter) { this._options.onLiteralEnter(start); } } onLiteralLeave(start, end) { if (this._options.onLiteralLeave) { this._options.onLiteralLeave(start, end); } } onFlags(start, end, global, ignoreCase, multiline, unicode, sticky, dotAll) { if (this._options.onFlags) { this._options.onFlags(start, end, global, ignoreCase, multiline, unicode, sticky, dotAll); } } onPatternEnter(start) { if (this._options.onPatternEnter) { this._options.onPatternEnter(start); } } onPatternLeave(start, end) { if (this._options.onPatternLeave) { this._options.onPatternLeave(start, end); } } onDisjunctionEnter(start) { if (this._options.onDisjunctionEnter) { this._options.onDisjunctionEnter(start); } } onDisjunctionLeave(start, end) { if (this._options.onDisjunctionLeave) { this._options.onDisjunctionLeave(start, end); } } onAlternativeEnter(start, index) { if (this._options.onAlternativeEnter) { this._options.onAlternativeEnter(start, index); } } onAlternativeLeave(start, end, index) { if (this._options.onAlternativeLeave) { this._options.onAlternativeLeave(start, end, index); } } onGroupEnter(start) { if (this._options.onGroupEnter) { this._options.onGroupEnter(start); } } onGroupLeave(start, end) { if (this._options.onGroupLeave) { this._options.onGroupLeave(start, end); } } onCapturingGroupEnter(start, name) { if (this._options.onCapturingGroupEnter) { this._options.onCapturingGroupEnter(start, name); } } onCapturingGroupLeave(start, end, name) { if (this._options.onCapturingGroupLeave) { this._options.onCapturingGroupLeave(start, end, name); } } onQuantifier(start, end, min, max, greedy) { if (this._options.onQuantifier) { this._options.onQuantifier(start, end, min, max, greedy); } } onLookaroundAssertionEnter(start, kind, negate) { if (this._options.onLookaroundAssertionEnter) { this._options.onLookaroundAssertionEnter(start, kind, negate); } } onLookaroundAssertionLeave(start, end, kind, negate) { if (this._options.onLookaroundAssertionLeave) { this._options.onLookaroundAssertionLeave(start, end, kind, negate); } } onEdgeAssertion(start, end, kind) { if (this._options.onEdgeAssertion) { this._options.onEdgeAssertion(start, end, kind); } } onWordBoundaryAssertion(start, end, kind, negate) { if (this._options.onWordBoundaryAssertion) { this._options.onWordBoundaryAssertion(start, end, kind, negate); } } onAnyCharacterSet(start, end, kind) { if (this._options.onAnyCharacterSet) { this._options.onAnyCharacterSet(start, end, kind); } } onEscapeCharacterSet(start, end, kind, negate) { if (this._options.onEscapeCharacterSet) { this._options.onEscapeCharacterSet(start, end, kind, negate); } } onUnicodePropertyCharacterSet(start, end, kind, key, value, negate) { if (this._options.onUnicodePropertyCharacterSet) { this._options.onUnicodePropertyCharacterSet(start, end, kind, key, value, negate); } } onCharacter(start, end, value) { if (this._options.onCharacter) { this._options.onCharacter(start, end, value); } } onBackreference(start, end, ref) { if (this._options.onBackreference) { this._options.onBackreference(start, end, ref); } } onCharacterClassEnter(start, negate) { if (this._options.onCharacterClassEnter) { this._options.onCharacterClassEnter(start, negate); } } onCharacterClassLeave(start, end, negate) { if (this._options.onCharacterClassLeave) { this._options.onCharacterClassLeave(start, end, negate); } } onCharacterClassRange(start, end, min, max) { if (this._options.onCharacterClassRange) { this._options.onCharacterClassRange(start, end, min, max); } } get source() { return this._reader.source; } get index() { return this._reader.index; } get currentCodePoint() { return this._reader.currentCodePoint; } get nextCodePoint() { return this._reader.nextCodePoint; } get nextCodePoint2() { return this._reader.nextCodePoint2; } get nextCodePoint3() { return this._reader.nextCodePoint3; } reset(source, start, end) { this._reader.reset(source, start, end, this._uFlag); } rewind(index) { this._reader.rewind(index); } advance() { this._reader.advance(); } eat(cp) { return this._reader.eat(cp); } eat2(cp1, cp2) { return this._reader.eat2(cp1, cp2); } eat3(cp1, cp2, cp3) { return this._reader.eat3(cp1, cp2, cp3); } raise(message) { throw new RegExpSyntaxError(this.source, this._uFlag, this.index, message); } eatRegExpBody() { const start = this.index; let inClass = false; let escaped = false; for (;;) { const cp = this.currentCodePoint; if (cp === -1 || isLineTerminator(cp)) { const kind = inClass ? "character class" : "regular expression"; this.raise(`Unterminated ${kind}`); } if (escaped) { escaped = false; } else if (cp === ReverseSolidus) { escaped = true; } else if (cp === LeftSquareBracket) { inClass = true; } else if (cp === RightSquareBracket) { inClass = false; } else if ((cp === Solidus && !inClass) || (cp === Asterisk && this.index === start)) { break; } this.advance(); } return this.index !== start; } pattern() { const start = this.index; this._numCapturingParens = this.countCapturingParens(); this._groupNames.clear(); this._backreferenceNames.clear(); this.onPatternEnter(start); this.disjunction(); const cp = this.currentCodePoint; if (this.currentCodePoint !== -1) { if (cp === RightParenthesis) { this.raise("Unmatched ')'"); } if (cp === RightSquareBracket || cp === RightCurlyBracket) { this.raise("Lone quantifier brackets"); } const c = String.fromCodePoint(cp); this.raise(`Unexpected character '${c}'`); } this._backreferenceNames.forEach(name => { if (!this._groupNames.has(name)) { this.raise("Invalid named capture referenced"); } }); this.onPatternLeave(start, this.index); } countCapturingParens() { const start = this.index; let inClass = false; let escaped = false; let count = 0; let cp = 0; while ((cp = this.currentCodePoint) !== -1) { if (escaped) { escaped = false; } else if (cp === ReverseSolidus) { escaped = true; } else if (cp === LeftSquareBracket) { inClass = true; } else if (cp === RightSquareBracket) { inClass = false; } else if (cp === LeftParenthesis && !inClass && (this.nextCodePoint !== QuestionMark || (this.nextCodePoint2 === LessThanSign && this.nextCodePoint3 !== EqualsSign && this.nextCodePoint3 !== ExclamationMark))) { count += 1; } this.advance(); } this.rewind(start); return count; } disjunction() { const start = this.index; let i = 0; this.onDisjunctionEnter(start); this.alternative(i++); while (this.eat(VerticalLine)) { this.alternative(i++); } if (this.eatQuantifier(true)) { this.raise("Nothing to repeat"); } if (this.eat(LeftCurlyBracket)) { this.raise("Lone quantifier brackets"); } this.onDisjunctionLeave(start, this.index); } alternative(i) { const start = this.index; this.onAlternativeEnter(start, i); while (this.currentCodePoint !== -1 && this.eatTerm()) ; this.onAlternativeLeave(start, this.index, i); } eatTerm() { if (this.eatAssertion()) { if (this._lastAssertionIsQuantifiable) { this.eatQuantifier(); } return true; } if (this.strict ? this.eatAtom() : this.eatExtendedAtom()) { this.eatQuantifier(); return true; } return false; } eatAssertion() { const start = this.index; this._lastAssertionIsQuantifiable = false; if (this.eat(CircumflexAccent)) { this.onEdgeAssertion(start, this.index, "start"); return true; } if (this.eat(DollarSign)) { this.onEdgeAssertion(start, this.index, "end"); return true; } if (this.eat2(ReverseSolidus, LatinCapitalLetterB)) { this.onWordBoundaryAssertion(start, this.index, "word", true); return true; } if (this.eat2(ReverseSolidus, LatinSmallLetterB)) { this.onWordBoundaryAssertion(start, this.index, "word", false); return true; } if (this.eat2(LeftParenthesis, QuestionMark)) { const lookbehind = this.ecmaVersion >= 2018 && this.eat(LessThanSign); let negate = false; if (this.eat(EqualsSign) || (negate = this.eat(ExclamationMark))) { const kind = lookbehind ? "lookbehind" : "lookahead"; this.onLookaroundAssertionEnter(start, kind, negate); this.disjunction(); if (!this.eat(RightParenthesis)) { this.raise("Unterminated group"); } this._lastAssertionIsQuantifiable = !lookbehind && !this.strict; this.onLookaroundAssertionLeave(start, this.index, kind, negate); return true; } this.rewind(start); } return false; } eatQuantifier(noError = false) { const start = this.index; let min = 0; let max = 0; let greedy = false; if (this.eat(Asterisk)) { min = 0; max = Number.POSITIVE_INFINITY; } else if (this.eat(PlusSign)) { min = 1; max = Number.POSITIVE_INFINITY; } else if (this.eat(QuestionMark)) { min = 0; max = 1; } else if (this.eatBracedQuantifier(noError)) { min = this._lastMinValue; max = this._lastMaxValue; } else { return false; } greedy = !this.eat(QuestionMark); if (!noError) { this.onQuantifier(start, this.index, min, max, greedy); } return true; } eatBracedQuantifier(noError) { const start = this.index; if (this.eat(LeftCurlyBracket)) { this._lastMinValue = 0; this._lastMaxValue = Number.POSITIVE_INFINITY; if (this.eatDecimalDigits()) { this._lastMinValue = this._lastMaxValue = this._lastIntValue; if (this.eat(Comma)) { this._lastMaxValue = this.eatDecimalDigits() ? this._lastIntValue : Number.POSITIVE_INFINITY; } if (this.eat(RightCurlyBracket)) { if (!noError && this._lastMaxValue < this._lastMinValue) { this.raise("numbers out of order in {} quantifier"); } return true; } } if (!noError && this.strict) { this.raise("Incomplete quantifier"); } this.rewind(start); } return false; } eatAtom() { return (this.eatPatternCharacter() || this.eatDot() || this.eatReverseSolidusAtomEscape() || this.eatCharacterClass() || this.eatUncapturingGroup() || this.eatCapturingGroup()); } eatDot() { if (this.eat(FullStop)) { this.onAnyCharacterSet(this.index - 1, this.index, "any"); return true; } return false; } eatReverseSolidusAtomEscape() { const start = this.index; if (this.eat(ReverseSolidus)) { if (this.eatAtomEscape()) { return true; } this.rewind(start); } return false; } eatUncapturingGroup() { const start = this.index; if (this.eat3(LeftParenthesis, QuestionMark, Colon)) { this.onGroupEnter(start); this.disjunction(); if (!this.eat(RightParenthesis)) { this.raise("Unterminated group"); } this.onGroupLeave(start, this.index); return true; } return false; } eatCapturingGroup() { const start = this.index; if (this.eat(LeftParenthesis)) { this._lastStrValue = ""; if (this.ecmaVersion >= 2018) { this.groupSpecifier(); } else if (this.currentCodePoint === QuestionMark) { this.raise("Invalid group"); } const name = this._lastStrValue || null; this.onCapturingGroupEnter(start, name); this.disjunction(); if (!this.eat(RightParenthesis)) { this.raise("Unterminated group"); } this.onCapturingGroupLeave(start, this.index, name); return true; } return false; } eatExtendedAtom() { return (this.eatDot() || this.eatReverseSolidusAtomEscape() || this.eatCharacterClass() || this.eatUncapturingGroup() || this.eatCapturingGroup() || this.eatInvalidBracedQuantifier() || this.eatExtendedPatternCharacter()); } eatInvalidBracedQuantifier() { if (this.eatBracedQuantifier(true)) { this.raise("Nothing to repeat"); } return false; } eatSyntaxCharacter() { if (isSyntaxCharacter(this.currentCodePoint)) { this._lastIntValue = this.currentCodePoint; this.advance(); return true; } return false; } eatPatternCharacter() { const start = this.index; const cp = this.currentCodePoint; if (cp !== -1 && !isSyntaxCharacter(cp)) { this.advance(); this.onCharacter(start, this.index, cp); return true; } return false; } eatExtendedPatternCharacter() { const start = this.index; const cp = this.currentCodePoint; if (cp !== -1 && cp !== CircumflexAccent && cp !== DollarSign && cp !== FullStop && cp !== Asterisk && cp !== PlusSign && cp !== QuestionMark && cp !== LeftParenthesis && cp !== RightParenthesis && cp !== LeftSquareBracket && cp !== VerticalLine) { this.advance(); this.onCharacter(start, this.index, cp); return true; } return false; } groupSpecifier() { this._lastStrValue = ""; if (this.eat(QuestionMark)) { if (this.eatGroupName()) { if (!this._groupNames.has(this._lastStrValue)) { this._groupNames.add(this._lastStrValue); return; } this.raise("Duplicate capture group name"); } this.raise("Invalid group"); } } eatGroupName() { this._lastStrValue = ""; if (this.eat(LessThanSign)) { if (this.eatRegExpIdentifierName() && this.eat(GreaterThanSign)) { return true; } this.raise("Invalid capture group name"); } return false; } eatRegExpIdentifierName() { this._lastStrValue = ""; if (this.eatRegExpIdentifierStart()) { this._lastStrValue += String.fromCodePoint(this._lastIntValue); while (this.eatRegExpIdentifierPart()) { this._lastStrValue += String.fromCodePoint(this._lastIntValue); } return true; } return false; } eatRegExpIdentifierStart() { const start = this.index; let cp = this.currentCodePoint; this.advance(); if (cp === ReverseSolidus && this.eatRegExpUnicodeEscapeSequence()) { cp = this._lastIntValue; } if (isRegExpIdentifierStart(cp)) { this._lastIntValue = cp; return true; } if (this.index !== start) { this.rewind(start); } return false; } eatRegExpIdentifierPart() { const start = this.index; let cp = this.currentCodePoint; this.advance(); if (cp === ReverseSolidus && this.eatRegExpUnicodeEscapeSequence()) { cp = this._lastIntValue; } if (isRegExpIdentifierPart(cp)) { this._lastIntValue = cp; return true; } if (this.index !== start) { this.rewind(start); } return false; } eatAtomEscape() { if (this.eatBackreference() || this.eatCharacterClassEscape() || this.eatCharacterEscape() || (this._nFlag && this.eatKGroupName())) { return true; } if (this.strict || this._uFlag) { this.raise("Invalid escape"); } return false; } eatBackreference() { const start = this.index; if (this.eatDecimalEscape()) { const n = this._lastIntValue; if (n <= this._numCapturingParens) { this.onBackreference(start - 1, this.index, n); return true; } if (this.strict) { this.raise("Invalid escape"); } this.rewind(start); } return false; } eatKGroupName() { const start = this.index; if (this.eat(LatinSmallLetterK)) { if (this.eatGroupName()) { const groupName = this._lastStrValue; this._backreferenceNames.add(groupName); this.onBackreference(start - 1, this.index, groupName); return true; } this.raise("Invalid named reference"); } return false; } eatCharacterEscape() { const start = this.index; if (this.eatControlEscape() || this.eatCControlLetter() || this.eatZero() || this.eatHexEscapeSequence() || this.eatRegExpUnicodeEscapeSequence() || (!this.strict && this.eatLegacyOctalEscapeSequence()) || this.eatIdentityEscape()) { this.onCharacter(start - 1, this.index, this._lastIntValue); return true; } return false; } eatCControlLetter() { const start = this.index; if (this.eat(LatinSmallLetterC)) { if (this.eatControlLetter()) { return true; } this.rewind(start); } return false; } eatZero() { if (this.currentCodePoint === DigitZero && !isDecimalDigit(this.nextCodePoint)) { this._lastIntValue = 0; this.advance(); return true; } return false; } eatControlEscape() { if (this.eat(LatinSmallLetterT)) { this._lastIntValue = CharacterTabulation; return true; } if (this.eat(LatinSmallLetterN)) { this._lastIntValue = LineFeed; return true; } if (this.eat(LatinSmallLetterV)) { this._lastIntValue = LineTabulation; return true; } if (this.eat(LatinSmallLetterF)) { this._lastIntValue = FormFeed; return true; } if (this.eat(LatinSmallLetterR)) { this._lastIntValue = CarriageReturn; return true; } return false; } eatControlLetter() { const cp = this.currentCodePoint; if (isLatinLetter(cp)) { this.advance(); this._lastIntValue = cp % 0x20; return true; } return false; } eatRegExpUnicodeEscapeSequence() { const start = this.index; if (this.eat(LatinSmallLetterU)) { if (this.eatFixedHexDigits(4)) { const lead = this._lastIntValue; if (this._uFlag && lead >= 0xd800 && lead <= 0xdbff) { const leadSurrogateEnd = this.index; if (this.eat(ReverseSolidus) && this.eat(LatinSmallLetterU) && this.eatFixedHexDigits(4)) { const trail = this._lastIntValue; if (trail >= 0xdc00 && trail <= 0xdfff) { this._lastIntValue = (lead - 0xd800) * 0x400 + (trail - 0xdc00) + 0x10000; return true; } } this.rewind(leadSurrogateEnd); this._lastIntValue = lead; } return true; } if (this._uFlag && this.eat(LeftCurlyBracket) && this.eatHexDigits() && this.eat(RightCurlyBracket) && isValidUnicode(this._lastIntValue)) { return true; } if (this.strict || this._uFlag) { this.raise("Invalid unicode escape"); } this.rewind(start); } return false; } eatIdentityEscape() { if (this._uFlag) { if (this.eatSyntaxCharacter()) { return true; } if (this.eat(Solidus)) { this._lastIntValue = Solidus; return true; } return false; } if (this.isValidIdentityEscape(this.currentCodePoint)) { this._lastIntValue = this.currentCodePoint; this.advance(); return true; } return false; } isValidIdentityEscape(cp) { if (cp === -1) { return false; } if (this.strict) { return !isIdContinue(cp); } return (cp !== LatinSmallLetterC && (!this._nFlag || cp !== LatinSmallLetterK)); } eatDecimalEscape() { this._lastIntValue = 0; let cp = this.currentCodePoint; if (cp >= DigitOne && cp <= DigitNine) { do { this._lastIntValue = 10 * this._lastIntValue + (cp - DigitZero); this.advance(); } while ((cp = this.currentCodePoint) >= DigitZero && cp <= DigitNine); return true; } return false; } eatCharacterClassEscape() { const start = this.index; if (this.eat(LatinSmallLetterD)) { this._lastIntValue = -1; this.onEscapeCharacterSet(start - 1, this.index, "digit", false); return true; } if (this.eat(LatinCapitalLetterD)) { this._lastIntValue = -1; this.onEscapeCharacterSet(start - 1, this.index, "digit", true); return true; } if (this.eat(LatinSmallLetterS)) { this._lastIntValue = -1; this.onEscapeCharacterSet(start - 1, this.index, "space", false); return true; } if (this.eat(LatinCapitalLetterS)) { this._lastIntValue = -1; this.onEscapeCharacterSet(start - 1, this.index, "space", true); return true; } if (this.eat(LatinSmallLetterW)) { this._lastIntValue = -1; this.onEscapeCharacterSet(start - 1, this.index, "word", false); return true; } if (this.eat(LatinCapitalLetterW)) { this._lastIntValue = -1; this.onEscapeCharacterSet(start - 1, this.index, "word", true); return true; } let negate = false; if (this._uFlag && this.ecmaVersion >= 2018 && (this.eat(LatinSmallLetterP) || (negate = this.eat(LatinCapitalLetterP)))) { this._lastIntValue = -1; if (this.eat(LeftCurlyBracket) && this.eatUnicodePropertyValueExpression() && this.eat(RightCurlyBracket)) { this.onUnicodePropertyCharacterSet(start - 1, this.index, "property", this._lastKeyValue, this._lastValValue || null, negate); return true; } this.raise("Invalid property name"); } return false; } eatUnicodePropertyValueExpression() { const start = this.index; if (this.eatUnicodePropertyName() && this.eat(EqualsSign)) { this._lastKeyValue = this._lastStrValue; if (this.eatUnicodePropertyValue()) { this._lastValValue = this._lastStrValue; if (isValidUnicodeProperty(this._lastKeyValue, this._lastValValue)) { return true; } this.raise("Invalid property name"); } } this.rewind(start); if (this.eatLoneUnicodePropertyNameOrValue()) { const nameOrValue = this._lastStrValue; if (isValidUnicodeProperty("General_Category", nameOrValue)) { this._lastKeyValue = "General_Category"; this._lastValValue = nameOrValue; return true; } if (isValidUnicodePropertyName(nameOrValue)) { this._lastKeyValue = nameOrValue; this._lastValValue = ""; return true; } this.raise("Invalid property name"); } return false; } eatUnicodePropertyName() { this._lastStrValue = ""; while (isUnicodePropertyNameCharacter(this.currentCodePoint)) { this._lastStrValue += String.fromCodePoint(this.currentCodePoint); this.advance(); } return this._lastStrValue !== ""; } eatUnicodePropertyValue() { this._lastStrValue = ""; while (isUnicodePropertyValueCharacter(this.currentCodePoint)) { this._lastStrValue += String.fromCodePoint(this.currentCodePoint); this.advance(); } return this._lastStrValue !== ""; } eatLoneUnicodePropertyNameOrValue() { return this.eatUnicodePropertyValue(); } eatCharacterClass() { const start = this.index; if (this.eat(LeftSquareBracket)) { const negate = this.eat(CircumflexAccent); this.onCharacterClassEnter(start, negate); this.classRanges(); if (!this.eat(RightSquareBracket)) { this.raise("Unterminated character class"); } this.onCharacterClassLeave(start, this.index, negate); return true; } return false; } classRanges() { let start = this.index; while (this.eatClassAtom()) { const left = this._lastIntValue; const hyphenStart = this.index; if (this.eat(HyphenMinus)) { this.onCharacter(hyphenStart, this.index, HyphenMinus); if (this.eatClassAtom()) { const right = this._lastIntValue; if (left === -1 || right === -1) { if (this.strict) { this.raise("Invalid character class"); } } else if (left > right) { this.raise("Range out of order in character class"); } else { this.onCharacterClassRange(start, this.index, left, right); } } } start = this.index; } } eatClassAtom() { const start = this.index; if (this.eat(ReverseSolidus)) { if (this.eatClassEscape()) { return true; } if (this._uFlag) { this.raise("Invalid escape"); } this.rewind(start); } const cp = this.currentCodePoint; if (cp !== -1 && cp !== RightSquareBracket) { this.advance(); this._lastIntValue = cp; this.onCharacter(start, this.index, cp); return true; } return false; } eatClassEscape() { const start = this.index; if (this.eat(LatinSmallLetterB)) { this._lastIntValue = Backspace; this.onCharacter(start - 1, this.index, Backspace); return true; } if (this._uFlag && this.eat(HyphenMinus)) { this._lastIntValue = HyphenMinus; this.onCharacter(start - 1, this.index, HyphenMinus); return true; } if (!this._uFlag && this.eat(LatinSmallLetterC)) { if (this.eatClassControlLetter()) { this.onCharacter(start - 1, this.index, this._lastIntValue); return true; } this.rewind(start); } return this.eatCharacterClassEscape() || this.eatCharacterEscape(); } eatClassControlLetter() { const cp = this.currentCodePoint; if (isDecimalDigit(cp) || cp === LowLine) { this.advance(); this._lastIntValue = cp % 0x20; return true; } return false; } eatHexEscapeSequence() { const start = this.index; if (this.eat(LatinSmallLetterX)) { if (this.eatFixedHexDigits(2)) { return true; } if (this._uFlag) { this.raise("Invalid escape"); } this.rewind(start); } return false; } eatDecimalDigits() { const start = this.index; this._lastIntValue = 0; while (isDecimalDigit(this.currentCodePoint)) { this._lastIntValue = 10 * this._lastIntValue + digitToInt(this.currentCodePoint); this.advance(); } return this.index !== start; } eatHexDigits() { const start = this.index; this._lastIntValue = 0; while (isHexDigit(this.currentCodePoint)) { this._lastIntValue = 16 * this._lastIntValue + digitToInt(this.currentCodePoint); this.advance(); } return this.index !== start; } eatLegacyOctalEscapeSequence() { if (this.eatOctalDigit()) { const n1 = this._lastIntValue; if (this.eatOctalDigit()) { const n2 = this._lastIntValue; if (n1 <= 3 && this.eatOctalDigit()) { this._lastIntValue = n1 * 64 + n2 * 8 + this._lastIntValue; } else { this._lastIntValue = n1 * 8 + n2; } } else { this._lastIntValue = n1; } return true; } return false; } eatOctalDigit() { const cp = this.currentCodePoint; if (isOctalDigit(cp)) { this.advance(); this._lastIntValue = cp - DigitZero; return true; } this._lastIntValue = 0; return false; } eatFixedHexDigits(length) { const start = this.index; this._lastIntValue = 0; for (let i = 0; i < length; ++i) { const cp = this.currentCodePoint; if (!isHexDigit(cp)) { this.rewind(start); return false; } this._lastIntValue = 16 * this._lastIntValue + digitToInt(cp); this.advance(); } return true; } } const DummyPattern = {}; const DummyFlags = {}; const DummyCapturingGroup = {}; function elementsToAlternative(elements, parent) { for (const element of elements) { assert(element.type !== "Disjunction"); element.parent = parent; } return elements; } function addAlternativeElement(parent, node) { if (parent.type === "Disjunction") { last(parent.alternatives).push(node); } else { parent.elements.push(node); } } function addCommonElement(parent, node) { if (parent.type === "Disjunction") { last(parent.alternatives).push(node); } else if (parent.type === "CharacterClass") { parent.elements.push(node); } else { parent.elements.push(node); } } class RegExpParserState { constructor(options) { this._node = DummyPattern; this._flags = DummyFlags; this._disjunctionStartStack = []; this._backreferences = []; this._capturingGroups = []; this.source = ""; this.strict = Boolean(options && options.strict); this.ecmaVersion = (options && options.ecmaVersion) || 2018; } get pattern() { if (this._node.type !== "Pattern") { throw new Error("UnknownError"); } return this._node; } get flags() { if (this._flags.type !== "Flags") { throw new Error("UnknownError"); } return this._flags; } onFlags(start, end, global, ignoreCase, multiline, unicode, sticky, dotAll) { this._flags = { type: "Flags", parent: null, start, end, raw: this.source.slice(start, end), global, ignoreCase, multiline, unicode, sticky, dotAll, }; } onPatternEnter(start) { this._node = { type: "Pattern", parent: null, start, end: start, raw: "", elements: [], }; this._backreferences.length = 0; this._capturingGroups.length = 0; } onPatternLeave(start, end) { this._node.end = end; this._node.raw = this.source.slice(start, end); for (const reference of this._backreferences) { const ref = reference.ref; const group = typeof ref === "number" ? this._capturingGroups[ref - 1] : this._capturingGroups.find(g => g.name === ref); reference.resolved = group; group.references.push(reference); } } onDisjunctionEnter(start) { this._disjunctionStartStack.push(start); } onDisjunctionLeave(start, end) { this._disjunctionStartStack.pop(); } onAlternativeEnter(start, index) { if (index === 0) { return; } const parentNode = this._node; if (parentNode.type === "Disjunction" || parentNode.type === "CharacterClass") { throw new Error("UnknownError"); } const prevNode = last(parentNode.elements); if (prevNode != null && prevNode.type === "Disjunction") { this._node = prevNode; prevNode.alternatives.push([]); } else { this._node = { type: "Disjunction", parent: parentNode, start: last(this._disjunctionStartStack), end: start, raw: "", alternatives: [], }; const elements = elementsToAlternative(parentNode.elements, this._node); this._node.alternatives.push(elements, []); parentNode.elements = [this._node]; } } onAlternativeLeave(start, end, index) { if (index === 0) { return; } this._node.end = end; this._node.raw = this.source.slice(this._node.start, end); this._node = this._node.parent; } onGroupEnter(start) { const parentNode = this._node; if (parentNode.type === "CharacterClass") { throw new Error("UnknownError"); } this._node = { type: "Group", parent: parentNode, start, end: start, raw: "", elements: [], }; addAlternativeElement(parentNode, this._node); } onGroupLeave(start, end) { this._node.end = end; this._node.raw = this.source.slice(start, end); this._node = this._node.parent; } onCapturingGroupEnter(start, name) { const parentNode = this._node; if (parentNode.type === "CharacterClass") { throw new Error("UnknownError"); } this._node = { type: "CapturingGroup", parent: parentNode, start, end: start, raw: "", name, elements: [], references: [], }; addAlternativeElement(parentNode, this._node); this._capturingGroups.push(this._node); } onCapturingGroupLeave(start, end, name) { this._node.end = end; this._node.raw = this.source.slice(start, end); this._node = this._node.parent; } onQuantifier(start, end, min, max, greedy) { const parentNode = this._node; if (parentNode.type === "CharacterClass") { throw new Error("UnknownError"); } const elements = parentNode.type === "Disjunction" ? last(parentNode.alternatives) : parentNode.elements; const prevNode = elements.pop(); const node = { type: "Quantifier", parent: parentNode, start, end, raw: this.source.slice(start, end), min, max, greedy, element: prevNode, }; elements.push(node); prevNode.parent = node; } onLookaroundAssertionEnter(start, kind, negate) { const parentNode = this._node; if (parentNode.type === "CharacterClass") { throw new Error("UnknownError"); } this._node = { type: "Assertion", parent: parentNode, start, end: start, raw: "", kind, negate, elements: [], }; addAlternativeElement(parentNode, this._node); } onLookaroundAssertionLeave(start, end, kind, negate) { this._node.end = end; this._node.raw = this.source.slice(start, end); this._node = this._node.parent; } onEdgeAssertion(start, end, kind) { const parentNode = this._node; if (parentNode.type === "CharacterClass") { throw new Error("UnknownError"); } addAlternativeElement(parentNode, { type: "Assertion", parent: parentNode, start, end, raw: this.source.slice(start, end), kind, }); } onWordBoundaryAssertion(start, end, kind, negate) { const parentNode = this._node; if (parentNode.type === "CharacterClass") { throw new Error("UnknownError"); } addAlternativeElement(parentNode, { type: "Assertion", parent: parentNode, start, end, raw: this.source.slice(start, end), kind, negate, }); } onAnyCharacterSet(start, end, kind) { const parentNode = this._node; if (parentNode.type === "CharacterClass") { throw new Error("UnknownError"); } addAlternativeElement(parentNode, { type: "CharacterSet", parent: parentNode, start, end, raw: this.source.slice(start, end), kind, }); } onEscapeCharacterSet(start, end, kind, negate) { addCommonElement(this._node, { type: "CharacterSet", parent: this._node, start, end, raw: this.source.slice(start, end), kind, negate, }); } onUnicodePropertyCharacterSet(start, end, kind, key, value, negate) { addCommonElement(this._node, { type: "CharacterSet", parent: this._node, start, end, raw: this.source.slice(start, end), kind, key, value, negate, }); } onCharacter(start, end, value) { addCommonElement(this._node, { type: "Character", parent: this._node, start, end, raw: this.source.slice(start, end), value, }); } onBackreference(start, end, ref) { const parentNode = this._node; if (parentNode.type === "CharacterClass") { throw new Error("UnknownError"); } const node = { type: "Backreference", parent: parentNode, start, end, raw: this.source.slice(start, end), ref, resolved: DummyCapturingGroup, }; addAlternativeElement(parentNode, node); this._backreferences.push(node); } onCharacterClassEnter(start, negate) { const parentNode = this._node; if (parentNode.type === "CharacterClass") { throw new Error("UnknownError"); } this._node = { type: "CharacterClass", parent: parentNode, start, end: start, raw: "", negate, elements: [], }; addAlternativeElement(parentNode, this._node); } onCharacterClassLeave(start, end, negate) { this._node.end = end; this._node.raw = this.source.slice(start, end); this._node = this._node.parent; } onCharacterClassRange(start, end, min, max) { const parentNode = this._node; if (parentNode.type !== "CharacterClass") { throw new Error("UnknownError"); } const elements = parentNode.elements; const rightNode = elements.pop(); elements.pop(); const leftNode = elements.pop(); const node = { type: "CharacterClassRange", parent: parentNode, start, end, raw: this.source.slice(start, end), min: leftNode, max: rightNode, }; assert(leftNode != null && leftNode.type === "Character"); assert(rightNode != null && rightNode.type === "Character"); leftNode.parent = node; rightNode.parent = node; elements.push(node); } } class RegExpParser { constructor(options) { this._state = new RegExpParserState(options); this._validator = new RegExpValidator(this._state); } parseLiteral(source, start = 0, end = source.length) { this._state.source = source; this._validator.validateLiteral(source, start, end); const pattern = this._state.pattern; const flags = this._state.flags; const literal = { type: "RegExpLiteral", parent: null, start, end, raw: source, pattern, flags, }; pattern.parent = literal; flags.parent = literal; return literal; } parseFlags(source, start = 0, end = source.length) { this._state.source = source; this._validator.validateFlags(source, start, end); return this._state.flags; } parsePattern(source, start = 0, end = source.length, uFlag = false) { this._state.source = source; this._validator.validatePattern(source, start, end, uFlag); return this._state.pattern; } } function parseRegExpLiteral(source, options) { return new RegExpParser(options).parseLiteral(source); } function validateRegExpLiteral(source, options) { return new RegExpValidator(options).validateLiteral(source); } export { ast as AST, RegExpParser, RegExpValidator, parseRegExpLiteral, validateRegExpLiteral }; //# sourceMappingURL=index.mjs.map kodo - Gogs: Go Git Service

説明なし

Kodo/kodo - Gogs: Go Git Service

1 Commits (41c58ec3f76fb031567a43ccc896a74d71e997a8)

Author SHA1 Message Date
  Brightcells 4547c709bf Add api get_server_time_api 8 years ago