UNPKG

clarity-pattern-parser

Version:

Parsing Library for Typescript and Javascript.

1,675 lines (1,651 loc) 169 kB
'use strict'; Object.defineProperty(exports, '__esModule', { value: true }); function defaultVisitor(node) { return node; } let idIndex$b = 0; class Node { get id() { return this._id; } get type() { return this._type; } get name() { return this._name; } get value() { return this.toString(); } get firstIndex() { return this._firstIndex; } get lastIndex() { return this._lastIndex; } get startIndex() { return this._firstIndex; } get endIndex() { return this._lastIndex + 1; } get parent() { return this._parent; } get children() { return this._children; } get hasChildren() { return this._children.length > 0; } get isLeaf() { return !this.hasChildren; } constructor(type, name, firstIndex, lastIndex, children = [], value = "") { this._id = String(idIndex$b++); this._type = type; this._name = name; this._firstIndex = firstIndex; this._lastIndex = lastIndex; this._parent = null; this._children = children; this._value = value; this._children.forEach(c => c._parent = this); } removeChild(node) { const index = this._children.indexOf(node); if (index > -1) { this._children.splice(index, 1); node._parent = null; } } findChildIndex(node) { return this._children.indexOf(node); } spliceChildren(index, deleteCount, ...items) { const removedItems = this._children.splice(index, deleteCount, ...items); removedItems.forEach(i => i._parent = null); items.forEach(i => i._parent = this); return removedItems; } removeAllChildren() { this.spliceChildren(0, this._children.length); } replaceChild(newNode, referenceNode) { const index = this.findChildIndex(referenceNode); if (index > -1) { this.spliceChildren(index, 1, newNode); newNode._parent = this; referenceNode._parent = null; } } replaceWith(newNode) { if (this._parent != null) { this._parent.replaceChild(newNode, this); } } insertBefore(newNode, referenceNode) { newNode._parent = this; if (referenceNode == null) { this._children.push(newNode); return; } const index = this.findChildIndex(referenceNode); if (index > -1) { this._children.splice(index, 0, newNode); } } appendChild(newNode) { this.append(newNode); } append(...nodes) { nodes.forEach((newNode) => { newNode._parent = this; this._children.push(newNode); }); } nextSibling() { if (this._parent == null) { return null; } const children = this._parent._children; const index = children.indexOf(this); if (index > -1 && index < children.length - 1) { return children[index + 1]; } return null; } previousSibling() { if (this._parent == null) { return null; } const children = this._parent._children; const index = children.indexOf(this); if (index > -1 && index > 0) { return children[index - 1]; } return null; } find(predicate, breadthFirst = false) { let match = null; if (breadthFirst) { this.walkBreadthFirst(n => { if (predicate(n)) { match = n; return false; } }); } else { this.walkUp(n => { if (predicate(n)) { match = n; return false; } }); } return match; } findAll(predicate, breadthFirst = false) { const matches = []; if (breadthFirst) { this.walkBreadthFirst(n => { if (predicate(n)) { matches.push(n); } }); } else { this.walkUp(n => { if (predicate(n)) { matches.push(n); } }); } return matches; } findRoot() { let pattern = this; while (true) { if (pattern.parent == null) { return pattern; } pattern = pattern.parent; } } findAncestor(predicate) { let parent = this._parent; while (parent != null) { if (predicate(parent)) { return parent; } parent = parent._parent; } return null; } walkUp(callback) { var _a; const childrenCopy = this._children.slice(); const result = childrenCopy.every(c => c.walkUp(callback)); return ((_a = callback(this)) !== null && _a !== void 0 ? _a : true) && result; } walkDown(callback) { var _a; const childrenCopy = this._children.slice(); return ((_a = callback(this)) !== null && _a !== void 0 ? _a : true) && childrenCopy.every(c => c.walkDown(callback)); } walkBreadthFirst(callback) { const queue = [this]; while (queue.length > 0) { const current = queue.shift(); if (callback(current) === false) { return false; } queue.push(...current.children); } return true; } transform(visitors) { const childrenCopy = this._children.slice(); const visitor = visitors[this.name] == null ? defaultVisitor : visitors[this.name]; const children = childrenCopy.map(c => c.transform(visitors)); this.removeAllChildren(); this.append(...children); return visitor(this); } flatten() { const nodes = []; this.walkDown((node) => { if (!node.hasChildren) { nodes.push(node); } }); return nodes; } compact() { const value = this.toString(); this.removeAllChildren(); this._value = value; } remove() { if (this._parent != null) { this._parent.removeChild(this); } } clone() { const node = new Node(this._type, this._name, this._firstIndex, this._lastIndex, this._children.map((c) => c.clone()), this._value); return node; } normalize(startIndex = this._firstIndex) { let length = 0; let runningOffset = startIndex; if (this.children.length === 0) { length = this._value.length; } else { for (let x = 0; x < this.children.length; x++) { const child = this.children[x]; const childLength = child.normalize(runningOffset); runningOffset += childLength; length += childLength; } } this._firstIndex = startIndex; this._lastIndex = Math.max(startIndex + length - 1, 0); return length; } toString() { if (this._children.length === 0) { return this._value; } return this._children.map(c => c.toString()).join(""); } toCycleFreeObject() { return { id: this._id, type: this._type, name: this._name, value: this.toString(), startIndex: this.startIndex, endIndex: this.endIndex, children: this._children.map(c => c.toCycleFreeObject()), }; } toJson(space) { return JSON.stringify(this.toCycleFreeObject(), null, space); } isEqual(node) { return node._type === this._type && node._name === this._name && node._firstIndex === this._firstIndex && node._lastIndex === this._lastIndex && node._value === this._value && this._children.every((child, index) => child.isEqual(node._children[index])); } static createValueNode(type, name, value = "") { return new Node(type, name, 0, 0, [], value); } static createNode(type, name, children = []) { const value = children.map(c => c.toString()).join(""); return new Node(type, name, 0, 0, children, value); } } function compact(node, nodeMap) { node.walkBreadthFirst(n => { if (nodeMap[n.name]) { n.compact(); } }); return node; } function remove(node, nodeMap) { node.walkBreadthFirst(n => { if (nodeMap[n.name]) { n.remove(); } }); return node; } /****************************************************************************** Copyright (c) Microsoft Corporation. Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted. THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. ***************************************************************************** */ function __awaiter(thisArg, _arguments, P, generator) { function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } return new (P || (P = Promise))(function (resolve, reject) { function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } step((generator = generator.apply(thisArg, _arguments || [])).next()); }); } typeof SuppressedError === "function" ? SuppressedError : function (error, suppressed, message) { var e = new Error(message); return e.name = "SuppressedError", e.error = error, e.suppressed = suppressed, e; }; class ParseError { constructor(startIndex, lastIndex, pattern) { this.firstIndex = startIndex; this.startIndex = startIndex; this.lastIndex = lastIndex; this.endIndex = lastIndex + 1; this.pattern = pattern; } } class CursorHistory { constructor() { this._isRecording = false; this._leafMatches = [{ pattern: null, node: null }]; this._furthestError = null; this._currentError = null; this._rootMatch = { pattern: null, node: null }; this._patterns = []; this._nodes = []; this._errors = []; this._records = []; } get isRecording() { return this._isRecording; } get rootMatch() { return this._rootMatch; } get leafMatch() { return this._leafMatches[this._leafMatches.length - 1]; } get leafMatches() { return this._leafMatches; } get furthestError() { return this._furthestError; } get errors() { return this._errors; } get error() { return this._currentError; } get records() { return this._records; } get nodes() { return this._nodes; } get patterns() { return this._patterns; } recordMatch(pattern, node) { const record = { pattern, ast: node, error: null }; if (this._isRecording) { this._patterns.push(pattern); this._nodes.push(node); this._records.push(record); } this._rootMatch.pattern = pattern; this._rootMatch.node = node; const leafMatch = this._leafMatches[this._leafMatches.length - 1]; const isFurthestMatch = leafMatch.node === null || node.lastIndex > leafMatch.node.lastIndex; const isSameIndexMatch = leafMatch.node === null || node.lastIndex === leafMatch.node.lastIndex; if (isFurthestMatch) { // This is to save on GC churn. const match = this._leafMatches.pop(); match.pattern = pattern; match.node = node; this._leafMatches.length = 0; this._leafMatches.push(match); } else if (isSameIndexMatch) { const isAncestor = this._leafMatches.some((m) => { var _a; let parent = (_a = m.pattern) === null || _a === void 0 ? void 0 : _a.parent; while (parent != null) { if (parent === pattern.parent) { return true; } parent = parent.parent; } return false; }); if (!isAncestor) { this._leafMatches.unshift({ pattern, node }); } } } recordErrorAt(startIndex, lastIndex, pattern) { const error = new ParseError(startIndex, lastIndex, pattern); const record = { pattern, ast: null, error }; this._currentError = error; if (this._furthestError === null || lastIndex > this._furthestError.lastIndex) { this._furthestError = error; } if (this._isRecording) { this._errors.push(error); this.records.push(record); } } resolveError() { this._currentError = null; } startRecording() { this._isRecording = true; } stopRecording() { this._isRecording = false; } } const segmenter = new Intl.Segmenter("und", { granularity: "grapheme" }); class Cursor { get text() { return this._text; } get isOnFirst() { return this._index === 0; } get isOnLast() { return this._index === this.getLastIndex(); } get isRecording() { return this._history.isRecording; } get rootMatch() { return this._history.rootMatch; } get allMatchedNodes() { return this._history.nodes; } get allMatchedPatterns() { return this._history.patterns; } get leafMatch() { return this._history.leafMatch; } get leafMatches() { return this._history.leafMatches; } get furthestError() { return this._history.furthestError; } get error() { return this._history.error; } get errors() { return this._history.errors; } get records() { return this._history.records; } get index() { return this._index; } get length() { return this._length; } get hasError() { return this._history.error != null; } get currentChar() { const index = this.getCharStartIndex(this._index); return this.text.slice(index, index + this._charSize[index]); } constructor(text) { this._text = text; this._length = text.length; this._charSize = []; this._charMap = []; this._index = 0; this._history = new CursorHistory(); let index = 0; for (const segment of segmenter.segment(text)) { const size = segment.segment.length; for (let i = 0; i < size; i++) { this._charMap.push(index); this._charSize.push(size); } index += size; } } hasNext() { const index = this._charMap[this._index]; const charSize = this._charSize[index]; return index + charSize < this._length; } next() { if (this.hasNext()) { const index = this._charMap[this._index]; const size = this._charSize[index]; this.moveTo(index + size); } } hasPrevious() { var _a; const index = this._charMap[this._index]; const previousIndex = (_a = this._charMap[index - 1]) !== null && _a !== void 0 ? _a : -1; return previousIndex >= 0; } previous() { var _a; if (this.hasPrevious()) { const index = this._charMap[this._index]; const previousIndex = (_a = this._charMap[index - 1]) !== null && _a !== void 0 ? _a : -1; this.moveTo(previousIndex); } } moveTo(position) { if (position >= 0 && position < this._length) { this._index = this._charMap[position]; } } moveToFirstChar() { this._index = 0; } moveToLastChar() { this._index = this.getLastIndex(); } getLastIndex() { return this._length - 1; } substring(first, last) { return this._text.slice(first, last + 1); } recordMatch(pattern, node) { this._history.recordMatch(pattern, node); } recordErrorAt(startIndex, lastIndex, onPattern) { this._history.recordErrorAt(startIndex, lastIndex, onPattern); } resolveError() { this._history.resolveError(); } startRecording() { this._history.startRecording(); } stopRecording() { this._history.stopRecording(); } getCharStartIndex(index) { return this._charMap[index]; } getCharEndIndex(index) { var _a; let startIndex = this.getCharStartIndex(index); return (_a = startIndex + this._charSize[startIndex]) !== null && _a !== void 0 ? _a : 1; } getCharLastIndex(index) { var _a; return (_a = this.getCharEndIndex(index) - 1) !== null && _a !== void 0 ? _a : 0; } } function execPattern(pattern, text, record = false) { const cursor = new Cursor(text); if (cursor.length === 0) { return { ast: null, cursor }; } record && cursor.startRecording(); let ast = pattern.parse(cursor); const resultLength = ast == null ? 0 : ast.value.length; if (ast != null) { const isMatch = ast.value === text; if (!isMatch && !cursor.hasError) { ast = null; cursor.recordErrorAt(resultLength, cursor.length, pattern); } } else { cursor.recordErrorAt(resultLength, cursor.length, pattern); } return { ast: ast, cursor }; } function testPattern(pattern, text, record = false) { const result = execPattern(pattern, text, record); return !result.cursor.hasError; } let idIndex$a = 0; class Literal { get id() { return this._id; } get type() { return this._type; } get name() { return this._name; } get token() { return this._token; } get parent() { return this._parent; } set parent(pattern) { this._parent = pattern; } get children() { return []; } get startedOnIndex() { return this._firstIndex; } constructor(name, value) { if (value.length === 0) { throw new Error("Value Cannot be empty."); } this._id = `literal-${idIndex$a++}`; this._type = "literal"; this._name = name; this._token = value; this._parent = null; this._firstIndex = 0; this._lastIndex = 0; } test(text, record = false) { return testPattern(this, text, record); } exec(text, record = false) { return execPattern(this, text, record); } parse(cursor) { this._firstIndex = cursor.index; this._lastIndex = cursor.index; const passed = this._tryToParse(cursor); if (passed) { cursor.resolveError(); const node = this._createNode(); cursor.recordMatch(this, node); return node; } cursor.recordErrorAt(this._firstIndex, this._lastIndex, this); return null; } _tryToParse(cursor) { const token = this._token; const compareToToken = cursor.text.slice(this._firstIndex, this._firstIndex + this._token.length); const length = Math.min(token.length, compareToToken.length); for (let i = 0; i < length; i++) { if (token[i] !== compareToToken[i]) { this._lastIndex = this._firstIndex + i; cursor.moveTo(this._lastIndex); return false; } } if (token != compareToToken) { this._lastIndex = this._firstIndex + compareToToken.length - 1; cursor.moveTo(this._lastIndex); return false; } this._lastIndex = this._firstIndex + this._token.length - 1; cursor.moveTo(this._lastIndex); return true; } _createNode() { return new Node("literal", this._name, this._firstIndex, this._lastIndex, undefined, this._token); } clone(name = this._name) { const clone = new Literal(name, this._token); clone._id = this._id; return clone; } getTokens() { return [this._token]; } getTokensAfter(_lastMatched) { return []; } getNextTokens() { if (this.parent == null) { return []; } return this.parent.getTokensAfter(this); } getPatterns() { return [this]; } getPatternsAfter() { return []; } getNextPatterns() { if (this.parent == null) { return []; } return this.parent.getPatternsAfter(this); } find(_predicate) { return null; } isEqual(pattern) { return pattern.type === this.type && pattern._token === this._token; } } let idIndex$9 = 0; class Regex { get id() { return this._id; } get type() { return this._type; } get name() { return this._name; } get regex() { return this._originalRegexString; } get parent() { return this._parent; } set parent(pattern) { this._parent = pattern; } get children() { return []; } get startedOnIndex() { return this._firstIndex; } constructor(name, regex) { this._node = null; this._cursor = null; this._firstIndex = 0; this._substring = ""; this._tokens = []; this._id = `regex-${idIndex$9++}`; this._type = "regex"; this._name = name; this._parent = null; this._originalRegexString = regex; this._regex = new RegExp(`^${regex}`, "gu"); this.assertArguments(); } assertArguments() { if (this._originalRegexString.length < 1) { throw new Error("Invalid Arguments: The regex string argument needs to be at least one character long."); } if (this._originalRegexString.charAt(0) === "^") { throw new Error("Invalid Arguments: The regex string cannot start with a '^' because it is expected to be in the middle of a string."); } if (this._originalRegexString.charAt(this._originalRegexString.length - 1) === "$") { throw new Error("Invalid Arguments: The regex string cannot end with a '$' because it is expected to be in the middle of a string."); } } test(text, record = false) { return testPattern(this, text, record); } exec(text, record = false) { return execPattern(this, text, record); } parse(cursor) { this._firstIndex = cursor.index; this.resetState(cursor); this.tryToParse(cursor); return this._node; } resetState(cursor) { this._cursor = cursor; this._regex.lastIndex = 0; this._substring = this._cursor.text.slice(this._cursor.index); this._node = null; } tryToParse(cursor) { const result = this._regex.exec(this._substring); if (result != null && result[0].length > 0 && result.index === 0) { this.processResult(cursor, result); } else { this.processError(cursor); } } processResult(cursor, result) { const currentIndex = cursor.index; const match = result[0]; const lastIndex = cursor.getCharLastIndex(currentIndex + match.length - 1); this._node = new Node("regex", this._name, currentIndex, lastIndex, undefined, result[0]); cursor.moveTo(lastIndex); cursor.recordMatch(this, this._node); } processError(cursor) { cursor.recordErrorAt(this._firstIndex, this._firstIndex, this); this._node = null; } clone(name = this._name) { const clone = new Regex(name, this._originalRegexString); clone._tokens = this._tokens.slice(); clone._id = this._id; return clone; } getTokens() { return this._tokens; } getTokensAfter(_childReference) { return []; } getNextTokens() { if (this.parent == null) { return []; } return this.parent.getTokensAfter(this); } getPatterns() { return [this]; } getPatternsAfter(_childReference) { return []; } getNextPatterns() { if (this.parent == null) { return []; } return this.parent.getPatternsAfter(this); } find(_predicate) { return null; } setTokens(tokens) { this._tokens = tokens; } isEqual(pattern) { return pattern.type === this.type && pattern._originalRegexString === this._originalRegexString; } } function findPattern(pattern, predicate) { let children = []; if (pattern.type === "reference") { children = []; } else { children = pattern.children; } for (const child of children) { const result = findPattern(child, predicate); if (result !== null) { return result; } } if (predicate(pattern)) { return pattern; } else { return null; } } let idIndex$8 = 0; class Reference { get id() { return this._id; } get type() { return this._type; } get name() { return this._name; } get parent() { return this._parent; } set parent(pattern) { this._parent = pattern; } get children() { return this._children; } get startedOnIndex() { return this._firstIndex; } constructor(name, referencePatternName) { this._id = `reference-${idIndex$8++}`; this._type = "reference"; this._name = name; this._referencePatternName = referencePatternName || name; this._parent = null; this._pattern = null; this._cachedPattern = null; this._children = []; this._firstIndex = 0; this._cachedAncestors = false; this._recursiveAncestors = []; } test(text, record = false) { return testPattern(this, text, record); } exec(text, record = false) { return execPattern(this, text, record); } parse(cursor) { this._firstIndex = cursor.index; const pattern = this.getReferencePatternSafely(); this._cacheAncestors(pattern.id); if (this._isBeyondRecursiveAllowance()) { cursor.recordErrorAt(this._firstIndex, this._firstIndex, this); return null; } return pattern.parse(cursor); } _cacheAncestors(id) { if (!this._cachedAncestors) { this._cachedAncestors = true; let pattern = this.parent; while (pattern != null) { if (pattern.id === id) { this._recursiveAncestors.push(pattern); } pattern = pattern.parent; } } } _isBeyondRecursiveAllowance() { let depth = 0; for (let pattern of this._recursiveAncestors) { if (pattern.startedOnIndex === this.startedOnIndex) { depth++; if (depth > 1) { return true; } } } return false; } getReferencePatternSafely() { if (this._pattern === null) { let pattern = null; if (this._cachedPattern == null) { pattern = this._findPattern(); } else { pattern = this._cachedPattern; } if (pattern === null) { throw new Error(`Couldn't find '${this._referencePatternName}' pattern within tree.`); } const clonedPattern = pattern.clone(this._name); clonedPattern.parent = this; this._pattern = clonedPattern; this._children = [this._pattern]; } return this._pattern; } _findPattern() { let pattern = this._parent; while (pattern != null) { if (pattern.type !== "context") { pattern = pattern.parent; continue; } const foundPattern = pattern.getPatternWithinContext(this._referencePatternName); if (foundPattern != null && this._isValidPattern(foundPattern)) { return foundPattern; } pattern = pattern.parent; } const root = this._getRoot(); return findPattern(root, (pattern) => { return pattern.name === this._referencePatternName && this._isValidPattern(pattern); }); } _isValidPattern(pattern) { if (pattern.type === "reference") { return false; } if (pattern.type === "context" && pattern.children[0].type === "reference") { return false; } return true; } _getRoot() { let node = this; while (true) { const parent = node.parent; if (parent == null) { break; } else { node = parent; } } return node; } getTokens() { return this.getReferencePatternSafely().getTokens(); } getTokensAfter(_lastMatched) { if (this._parent == null) { return []; } return this._parent.getTokensAfter(this); } getNextTokens() { if (this.parent == null) { return []; } return this.parent.getTokensAfter(this); } getPatterns() { return this.getReferencePatternSafely().getPatterns(); } getPatternsAfter(_childReference) { if (this._parent == null) { return []; } return this._parent.getPatternsAfter(this); } getNextPatterns() { if (this.parent == null) { return []; } return this.parent.getPatternsAfter(this); } find(_predicate) { return null; } clone(name = this._name) { const clone = new Reference(name, this._referencePatternName); clone._id = this._id; // Optimize future clones, by caching the pattern we already found. if (this._pattern != null) { clone._cachedPattern = this._pattern; } return clone; } isEqual(pattern) { return pattern.type === this.type && pattern.name === this.name; } } function clonePatterns(patterns) { return patterns.map(p => p.clone()); } function isRecursivePattern(pattern) { let onPattern = pattern.parent; let depth = 0; while (onPattern != null) { if (onPattern.id === pattern.id) { depth++; } onPattern = onPattern.parent; if (depth > 1) { return true; } } return false; } let idIndex$7 = 0; class Options { get id() { return this._id; } get type() { return this._type; } get name() { return this._name; } get parent() { return this._parent; } set parent(pattern) { this._parent = pattern; } get children() { return this._children; } get startedOnIndex() { return this._firstIndex; } constructor(name, options, isGreedy = false) { if (options.length === 0) { throw new Error("Need at least one pattern with an 'options' pattern."); } const children = clonePatterns(options); this._assignChildrenToParent(children); this._id = `options-${idIndex$7++}`; this._type = "options"; this._name = name; this._parent = null; this._children = children; this._firstIndex = 0; this._isGreedy = isGreedy; } _assignChildrenToParent(children) { for (const child of children) { child.parent = this; } } test(text, record = false) { return testPattern(this, text, record); } exec(text, record = false) { return execPattern(this, text, record); } parse(cursor) { this._firstIndex = cursor.index; const node = this._tryToParse(cursor); if (node != null) { cursor.moveTo(node.lastIndex); cursor.resolveError(); return node; } cursor.recordErrorAt(this._firstIndex, this._firstIndex, this); return null; } _tryToParse(cursor) { const results = []; for (const pattern of this._children) { cursor.moveTo(this._firstIndex); let result = null; result = pattern.parse(cursor); if (this._isGreedy) { results.push(result); } if (result != null && !this._isGreedy) { return result; } cursor.resolveError(); } const nonNullResults = results.filter(r => r != null); nonNullResults.sort((a, b) => b.endIndex - a.endIndex); return nonNullResults[0] || null; } getTokens() { const tokens = []; for (const pattern of this._children) { if (isRecursivePattern(pattern)) { continue; } tokens.push(...pattern.getTokens()); } return tokens; } getTokensAfter(_childReference) { if (this._parent === null) { return []; } return this._parent.getTokensAfter(this); } getNextTokens() { if (this._parent == null) { return []; } return this._parent.getTokensAfter(this); } getPatterns() { const patterns = []; for (const pattern of this._children) { if (isRecursivePattern(pattern)) { continue; } patterns.push(...pattern.getPatterns()); } return patterns; } getPatternsAfter(_childReference) { if (this._parent === null) { return []; } return this._parent.getPatternsAfter(this); } getNextPatterns() { if (this.parent == null) { return []; } return this.parent.getPatternsAfter(this); } find(predicate) { return findPattern(this, predicate); } clone(name = this._name) { const clone = new Options(name, this._children, this._isGreedy); clone._id = this._id; return clone; } isEqual(pattern) { return pattern.type === this.type && this.children.every((c, index) => c.isEqual(pattern.children[index])); } } let idIndex$6 = 0; class FiniteRepeat { get id() { return this._id; } get type() { return this._type; } get name() { return this._name; } get parent() { return this._parent; } set parent(value) { this._parent = value; } get children() { return this._children; } get min() { return this._min; } get max() { return this._max; } get startedOnIndex() { return this._firstIndex; } constructor(name, pattern, options = {}) { this._id = `finite-repeat-${idIndex$6++}`; this._type = "finite-repeat"; this._name = name; this._parent = null; this._children = []; this._hasDivider = options.divider != null; this._min = options.min != null ? Math.max(options.min, 1) : 1; this._max = Math.max(this.min, options.max || this.min); this._trimDivider = options.trimDivider == null ? false : options.trimDivider; this._firstIndex = 0; for (let i = 0; i < this._max; i++) { const child = pattern.clone(); child.parent = this; this._children.push(child); if (options.divider != null && (i < this._max - 1 || !this._trimDivider)) { const divider = options.divider.clone(); divider.parent = this; this._children.push(divider); } } } parse(cursor) { this._firstIndex = cursor.index; const nodes = []; const modulo = this._hasDivider ? 2 : 1; let matchCount = 0; for (let i = 0; i < this._children.length; i++) { const childPattern = this._children[i]; const runningIndex = cursor.index; const node = childPattern.parse(cursor); if (cursor.hasError) { break; } if (i % modulo === 0 && !cursor.hasError) { matchCount++; } if (node == null) { cursor.moveTo(runningIndex); } else { nodes.push(node); if (cursor.hasNext()) { cursor.next(); } else { break; } } } const endedOnDivider = this._hasDivider && nodes.length % modulo === 0; if (this._trimDivider && endedOnDivider) { const node = nodes.pop(); cursor.moveTo(node.firstIndex); } if (matchCount < this._min) { const lastIndex = cursor.index; cursor.moveTo(this._firstIndex); cursor.recordErrorAt(this._firstIndex, lastIndex, this); return null; } if (nodes.length === 0 && !cursor.hasError) { cursor.moveTo(this._firstIndex); return null; } const firstIndex = nodes[0].firstIndex; const lastIndex = nodes[nodes.length - 1].lastIndex; cursor.resolveError(); cursor.moveTo(lastIndex); const node = new Node(this._type, this.name, firstIndex, lastIndex, nodes); return node; } test(text, record = false) { return testPattern(this, text, record); } exec(text, record = false) { return execPattern(this, text, record); } clone(name = this._name) { let min = this._min; let max = this._max; const clone = new FiniteRepeat(name, this._children[0], { divider: this._hasDivider ? this._children[1] : undefined, min, max, trimDivider: this._trimDivider }); clone._id = this._id; return clone; } getTokens() { return this._children[0].getTokens(); } getTokensAfter(childReference) { const patterns = this.getPatternsAfter(childReference); const tokens = []; patterns.forEach(p => tokens.push(...p.getTokens())); return tokens; } getNextTokens() { if (this._parent == null) { return []; } return this._parent.getTokensAfter(this); } getPatterns() { return this._children[0].getPatterns(); } getPatternsAfter(childReference) { const childIndex = this._children.indexOf(childReference); // If Reference Pattern isn't a child. if (childIndex === -1) { return []; } // If Reference Pattern is the last pattern. Ask for the parents next patterns if (childIndex === this._children.length - 1) { if (this._parent == null) { return []; } else { return this._parent.getPatternsAfter(this); } } // Get the next childs patterns. const nextChild = this._children[childIndex + 1]; return nextChild.getPatterns(); } getNextPatterns() { if (this._parent == null) { return []; } return this._parent.getPatternsAfter(this); } find(predicate) { return findPattern(this, predicate); } isEqual(pattern) { return pattern.type === this.type && this.children.every((c, index) => c.isEqual(pattern.children[index])); } } let idIndex$5 = 0; class InfiniteRepeat { get id() { return this._id; } get type() { return this._type; } get name() { return this._name; } get parent() { return this._parent; } set parent(pattern) { this._parent = pattern; } get children() { return this._children; } get min() { return this._min; } get startedOnIndex() { return this._firstIndex; } constructor(name, pattern, options = {}) { const min = options.min != null ? Math.max(options.min, 1) : 1; const divider = options.divider; let children; if (divider != null) { children = [pattern.clone(), divider.clone()]; } else { children = [pattern.clone()]; } this._assignChildrenToParent(children); this._id = `infinite-repeat-${idIndex$5++}`; this._type = "infinite-repeat"; this._name = name; this._min = min; this._parent = null; this._children = children; this._pattern = children[0]; this._divider = children[1]; this._firstIndex = 0; this._nodes = []; this._trimDivider = options.trimDivider == null ? false : options.trimDivider; this._patterns = []; } _assignChildrenToParent(children) { for (const child of children) { child.parent = this; } } test(text, record = false) { return testPattern(this, text, record); } exec(text, record = false) { return execPattern(this, text, record); } parse(cursor) { this._firstIndex = cursor.index; this._nodes = []; this._patterns = []; const passed = this._tryToParse(cursor); if (passed) { cursor.resolveError(); const node = this._createNode(cursor); if (node != null) { cursor.moveTo(node.lastIndex); cursor.recordMatch(this, node); } return node; } if (this._min > 0) { return null; } cursor.resolveError(); return null; } _meetsMin() { if (this._divider != null) { return Math.ceil(this._nodes.length / 2) >= this._min; } return this._nodes.length >= this._min; } _tryToParse(cursor) { const firstIndex = cursor.index; let passed = false; while (true) { const runningCursorIndex = cursor.index; const repeatNode = this._pattern.parse(cursor); const hasError = cursor.hasError; const hasNoErrorAndNoResult = !cursor.hasError && repeatNode == null; const hasDivider = this._divider != null; const hasNoDivider = !hasDivider; if (hasError) { const lastValidNode = this._getLastValidNode(); if (lastValidNode != null) { passed = true; } else { cursor.moveTo(runningCursorIndex); cursor.recordErrorAt(firstIndex, runningCursorIndex, this._pattern); passed = false; } break; } else { if (hasNoErrorAndNoResult && hasNoDivider) { // If we didn't match and didn't error we need to get out. Nothing different will happen. break; } if (repeatNode != null) { this._nodes.push(repeatNode); this._patterns.push(this._pattern); if (!cursor.hasNext()) { passed = true; break; } cursor.next(); } if (this._divider != null) { const dividerStartIndex = cursor.index; const dividerNode = this._divider.parse(cursor); if (cursor.hasError) { passed = true; break; } else { if (dividerNode == null) { cursor.moveTo(dividerStartIndex); if (repeatNode == null) { // If neither the repeat pattern or divider pattern matched get out. passed = true; break; } } else { this._nodes.push(dividerNode); this._patterns.push(this._divider); if (!cursor.hasNext()) { passed = true; break; } cursor.next(); } } } } } const hasMinimum = this._meetsMin(); if (hasMinimum) { return passed; } else if (!hasMinimum && passed) { cursor.recordErrorAt(firstIndex, cursor.index, this); cursor.moveTo(this._firstIndex); return false; } return passed; } _createNode(cursor) { const hasDivider = this._divider != null; const lastPattern = this._patterns[this._patterns.length - 1]; if (hasDivider && this._trimDivider && lastPattern === this._divider) { const dividerNode = this._nodes.pop(); cursor.moveTo(dividerNode.firstIndex); } if (this._nodes.length === 0) { cursor.moveTo(this._firstIndex); return null; } const lastIndex = this._nodes[this._nodes.length - 1].lastIndex; cursor.moveTo(lastIndex); return new Node(this._type, this._name, this._firstIndex, lastIndex, this._nodes); } _getLastValidNode() { const nodes = this._nodes.filter((node) => node !== null); if (nodes.length === 0) { return null; } return nodes[nodes.length - 1]; } getTokens() { return this._pattern.getTokens(); } getTokensAfter(childReference) { const patterns = this.getPatternsAfter(childReference); const tokens = []; patterns.forEach(p => tokens.push(...p.getTokens())); return tokens; } getNextTokens() { if (this._parent == null) { return []; } return this._parent.getTokensAfter(this); } getPatterns() { return this._pattern.getPatterns(); } getPatternsAfter(childReference) { let index = -1; const patterns = []; for (let i = 0; i < this._children.length; i++) { if (this._children[i] === childReference) { index = i; } } // If the last match isn't a child of this pattern. if (index === -1) { return []; } // If the last match was the repeated patterns, then suggest the divider. if (index === 0 && this._divider) { patterns.push(this._children[1]); if (this._parent) { patterns.push(...this._parent.getPatternsAfter(this)); } } // Suggest the pattern because the divider was the last match. if (index === 1) { patterns.push(this._children[0]); } // If there is no divider then suggest the repeating pattern and the next pattern after. if (index === 0 && this._divider == null && this._parent) { patterns.push(this._children[0]); patterns.push(...this._parent.getPatternsAfter(this)); } return patterns; } getNextPatterns() { if (this._parent == null) { return []; } return this._parent.getPatternsAfter(this); } find(predicate) { return findPattern(this, predicate); } clone(name = this._name) { let min = this._min; const clone = new InfiniteRepeat(name, this._pattern, { divider: this._divider == null ? undefined : this._divider, min: min, trimDivider: this._trimDivider }); clone._id = this._id; return clone; } isEqual(pattern) { return pattern.type === this.type && this.children.every((c, index) => c.isEqual(pattern.children[index])); } } let idIndex$4 = 0; class Repeat { get id() { return this._id; } get t