import GroupBuilder from './GroupBuilder' // @ts-ignore import Regex from 'becke-ch--regex--s0-0-v1--base--pl--lib' import RegexExecArray from './RegexExecArray' export default class TreeRegexp { public regexp: RegExp private regex: any public groupBuilder: GroupBuilder constructor(regexp: RegExp | string) { this.regexp = 'string' === typeof regexp ? new RegExp(regexp) : regexp this.regex = new Regex(this.regexp.source, this.regexp.flags) const stack: GroupBuilder[] = [new GroupBuilder()] const groupStartStack: number[] = [] let last: string = null let escaping = false let nonCapturingMaybe = false let charClass = false this.regexp.source.split('').forEach((c, n) => { if (c === '[' && !escaping) { charClass = true } else if (c === ']' && !escaping) { charClass = false } else if (c === '(' && !escaping && !charClass) { stack.push(new GroupBuilder()) groupStartStack.push(n + 1) nonCapturingMaybe = false } else if (c === ')' && !escaping && !charClass) { const gb = stack.pop() const groupStart = groupStartStack.pop() if (gb.capturing) { gb.source = this.regexp.source.substring(groupStart, n) stack[stack.length - 1].add(gb) } else { gb.moveChildrenTo(stack[stack.length - 1]) } nonCapturingMaybe = false } else if (c === '?' && last === '(') { nonCapturingMaybe = true } else if ( (c === ':' || c === '!' || c === '=' || c === '<') && last === '?' && nonCapturingMaybe ) { stack[stack.length - 1].setNonCapturing() nonCapturingMaybe = false } escaping = c === '\\' && !escaping last = c }) this.groupBuilder = stack.pop() } public match(s: string) { const match: RegexExecArray = this.regex.exec(s) if (!match) { return null } let groupIndex = 0 const nextGroupIndex = () => groupIndex++ return this.groupBuilder.build(match, nextGroupIndex) } }