UNPKG

clarity-pattern-parser

Version:

Parsing Library for Typescript and Javascript.

193 lines (153 loc) 4.39 kB
import { Node } from "../ast/Node"; import { Pattern } from "./Pattern"; import { Cursor } from "./Cursor"; import { ParseResult } from "./ParseResult"; import { testPattern } from "./testPattern"; import { execPattern } from "./execPattern"; let idIndex = 0; export class Regex implements Pattern { private _id: string; private _type: string; private _name: string; private _parent: Pattern | null; private _originalRegexString: string; private _regex: RegExp; private _node: Node | null = null; private _cursor: Cursor | null = null; private _firstIndex = 0; private _substring = ""; private _tokens: string[] = []; get id(): string { return this._id; } get type(): string { return this._type; } get name(): string { return this._name; } get regex(): string { return this._originalRegexString; } get parent(): Pattern | null { return this._parent; } set parent(pattern: Pattern | null) { this._parent = pattern; } get children(): Pattern[] { return []; } get startedOnIndex() { return this._firstIndex; } constructor(name: string, regex: string) { this._id = `regex-${idIndex++}`; this._type = "regex"; this._name = name; this._parent = null; this._originalRegexString = regex; this._regex = new RegExp(`^${regex}`, "g"); this.assertArguments(); } private 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: string, record = false): boolean { return testPattern(this, text, record); } exec(text: string, record = false): ParseResult { return execPattern(this, text, record); } parse(cursor: Cursor) { this._firstIndex = cursor.index; this.resetState(cursor); this.tryToParse(cursor); return this._node; } private resetState(cursor: Cursor) { this._cursor = cursor; this._regex.lastIndex = 0; this._substring = this._cursor.text.substr(this._cursor.index); this._node = null; } private tryToParse(cursor: 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); } } private processResult(cursor: Cursor, result: RegExpExecArray) { const currentIndex = cursor.index; const newIndex = currentIndex + result[0].length - 1; this._node = new Node( "regex", this._name, currentIndex, newIndex, undefined, result[0] ); cursor.moveTo(newIndex); cursor.recordMatch(this, this._node); } private processError(cursor: 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: Pattern): string[] { return []; } getNextTokens(): string[] { if (this.parent == null) { return []; } return this.parent.getTokensAfter(this); } getPatterns(): Pattern[] { return [this]; } getPatternsAfter(_childReference: Pattern): Pattern[] { return []; } getNextPatterns(): Pattern[] { if (this.parent == null) { return []; } return this.parent.getPatternsAfter(this); } find(_predicate: (p: Pattern) => boolean): Pattern | null { return null; } setTokens(tokens: string[]) { this._tokens = tokens; } isEqual(pattern: Regex): boolean { return pattern.type === this.type && pattern._originalRegexString === this._originalRegexString; } }