/*
Loops over all text nodes in the given subtree and searches for text fragments
that match the given RegExp. Each match is wrapped in its own span and the
span is given the class corresponding to the match.
976208
function markText(node, expr, classes) {

		/* Names of Element nodes whose children must not be visited */
		var rtags = /object|embed|script|select|textarea/i;

		function replace(node) {
			var text = node.nodeValue,
				p = node.parentNode,
				start = 0,
				match,span;

			while ( (match = expr.exec(text)) != null ) {

				// add a new text node with the string before the match
				if ( start < match.index ) {
					p.insertBefore(document.createTextNode(text.slice(start, match.index)), node);
				}

				// add a span for the match
				span = document.createElement("span");
				span.appendChild(document.createTextNode(match[0]));
				span.className = classes[match[0].toLowerCase()];
				p.insertBefore(span, node);

				// robustness: should a non-empty search term result in an empty match, then exit the loop
				if ( start <= expr.lastindex ) {
					break;
				}

				// continue search after current match
				start = expr.lastIndex;
			}

			// if there was any match, then reduce the text of original text node
			// to the substring after the last match (might be empty)
			if ( start > 0 ) {
				node.nodeValue = text.slice(start);
			}
		}

		function visit(node) {
			if (node.nodeType == 3) { // Node.TEXT_NODE
				replace(node);
			} else if ( !rtags.test(node.nodeName) ) { // skip 'critical' nodes
				for (node = node.firstChild; node; node = node.nextSibling) {
					visit(node);
				}
			}
		}

		visit(node);
	}
260684
private static void processText(NameFinder[] finders, String[] tags, BufferedReader input) throws IOException {
    String[][] finderTags = new String[finders.length][];
    Map[] prevTokenMaps = createPrevTokenMaps(finders); 
    for (String line = input.readLine(); null != line; line = input.readLine()) {
      if (line.equals("")) {
        clearPrevTokenMaps(prevTokenMaps);
        System.out.println();
        continue;
      }
      Span[] spans = tokenizeToSpans(line);
      String[] tokens = spansToStrings(spans,line);
      for (int fi = 0, fl = finders.length; fi < fl; fi++) {
        finderTags[fi] = finders[fi].find(tokens, prevTokenMaps[fi]);
        //System.err.println("EnglighNameFinder.processText: "+tags[fi] + " " + java.util.Arrays.asList(finderTags[fi]));
      }
      updatePrevTokenMaps(prevTokenMaps,tokens,finderTags);
      for (int ti = 0, tl = tokens.length; ti < tl; ti++) {
        for (int fi = 0, fl = finders.length; fi < fl; fi++) {
          //check for end tags
          if (ti != 0) {
            if ((finderTags[fi][ti].equals(NameFinderME.START) || finderTags[fi][ti].equals(NameFinderME.OTHER)) && (finderTags[fi][ti - 1].equals(NameFinderME.START) || finderTags[fi][ti - 1].equals(NameFinderME.CONTINUE))) {
              System.out.print("</" + tags[fi] + ">");
            }
          }
        }
        if (ti > 0 && spans[ti - 1].getEnd() < spans[ti].getStart()) {
          System.out.print(line.substring(spans[ti - 1].getEnd(), spans[ti].getStart()));
        }
        //check for start tags
        for (int fi = 0, fl = finders.length; fi < fl; fi++) {
          if (finderTags[fi][ti].equals(NameFinderME.START)) {
            System.out.print("<" + tags[fi] + ">");
          }
        }
        System.out.print(tokens[ti]);
      }
      //final end tags
      if (tokens.length != 0) {
        for (int fi = 0, fl = finders.length; fi < fl; fi++) {
          if (finderTags[fi][tokens.length - 1].equals(NameFinderME.START) || finderTags[fi][tokens.length - 1].equals(NameFinderME.CONTINUE)) {
            System.out.print("</" + tags[fi] + ">");
          }
        }
      }
      if (tokens.length != 0) {
        if (spans[tokens.length - 1].getEnd() < line.length()) {
          System.out.print(line.substring(spans[tokens.length - 1].getEnd()));
        }
      }
      System.out.println();
    }
  }
744304
public static List<String> getStringsByRegex(final String text, final String regex) {

    Pattern pattern = Pattern.compile(regex);
    Matcher matcher = pattern.matcher(text);
    List<String> matchedStrings = new ArrayList<String>();
    while (matcher.find()) {
      matchedStrings.add(matcher.group(0));
    }
    return matchedStrings;
  }
1297453
function matches(elem, selector) {
  // Vendor-specific implementations of `Element.prototype.matches()`.
  var proto = window.Element.prototype;
  var nativeMatches = proto.matches ||
      proto.mozMatchesSelector ||
      proto.msMatchesSelector ||
      proto.oMatchesSelector ||
      proto.webkitMatchesSelector;

  if (!elem || elem.nodeType !== 1) {
    return false;
  }

  var parentElem = elem.parentNode;

  // use native 'matches'
  if (nativeMatches) {
    return nativeMatches.call(elem, selector);
  }

  // native support for `matches` is missing and a fallback is required
  var nodes = parentElem.querySelectorAll(selector);
  var len = nodes.length;

  for (var i = 0; i < len; i++) {
    if (nodes[i] === elem) {
      return true;
    }
  }

  return false;
}
568381
public boolean matches(String text) {
        if (text == null) {
            return false;
        }
        if (this.regex) {
            final Pattern rx;
            if (this.caseSensitive) {
                rx = Pattern.compile(this.value);
            } else {
                rx = Pattern.compile(this.value, Pattern.CASE_INSENSITIVE);
            }
            return rx.matcher(text).matches();
        } else {
            if (this.caseSensitive) {
                return value.equals(text);
            } else {
                return value.equalsIgnoreCase(text);
            }
        }
    }
222016
public T find(String text)
    {
        T match = matcher.match(text, true);
        if (match != null)
        {
            String name = match.name();
            if (name.substring(0, Math.min(name.length(), text.length())).equalsIgnoreCase(text))
            {
                return match;
            }
        }
        return null;
    }
1173341
private boolean matchRegexp(String s, Pattern pattern) {
    final Matcher m = pattern.matcher(s);
    return m.matches();
  }
627686
function(regex){
		//if it isn't global, `exec()` will not start at `lastIndex`
		if(!regex.global)
			regex.compile(regex.source, flags(regex));
		regex.lastIndex = this.position;
		var m = regex.exec(this.source);

		//all matches must start at the current position.
		if(m && m.index!=this.position){
			return null;
		}

		if(m) this.position += m[0].length;
		return m;
	}
926697
def highlightAllMatches(self):
        
        # Remove any previous highlighting.
        self.qteWidget.SCISetStylingEx(0, 0, self.styleOrig)
        SCI = self.qteWidget

        # Compile a list of spans that contain the specified string.
        self.compileMatchList()

        # Return if the substring does not exist in the text.
        if len(self.matchList) == 0:
            return

        # ------------------------------------------------------------
        # Make a copy of the style bits of the document, overwrite
        # those parts containing a substring, and then write them
        # back all at once. This is much faster than calling the
        # styling methods repeatedly.
        # ------------------------------------------------------------

        # Make a copy of the document style bits and determine the
        # cursor position in the document.
        style = bytearray(self.styleOrig)
        pos = SCI.getCursorPosition()
        cur = SCI.positionFromLineIndex(*pos)

        # Style all matches.
        self.selMatchIdx = 0
        for start, stop in self.matchList:
            if start < cur:
                self.selMatchIdx += 1
            style[start:stop] = bytes(b'\x1e') * (stop - start)

        if self.selMatchIdx >= len(self.matchList):
            self.selMatchIdx = 0

        # Style the first match after the current cursor position
        # differently to indicate that it is the currently
        # selected one.
        start, stop = self.matchList[self.selMatchIdx]
        style[start:stop] = bytes(b'\x1f') * (stop - start)

        # Place the cursor at the start of the currently selected match.
        line, col = SCI.lineIndexFromPosition(start)
        SCI.setCursorPosition(line, col)

        # Apply the modified style array to the document.
        self.qteWidget.SCISetStylingEx(0, 0, style)
952130
function(regex, text){
    var nextIndex = 0;
    var result = [];

    var execResult;
    while ((execResult = regex.exec(text))!== null) {
        result.push({
            start: execResult.index, 
            matchLength: execResult[0].length});
    }

    return {
        next: function(){
            return nextIndex < result.length ?
                result[nextIndex++] :
                null;
        }
    };
}
736436
private static void checkRegexp(String regexp) {
        String checking = regexp.replaceAll("\\\\\\(", "X").replaceAll("\\(\\?", "X");
        int checked = checking.indexOf('(');
        if (checked > -1) {
            StringBuilder msg = new StringBuilder("Only non-capturing groups allowed:\r\n" +
                    regexp + "\r\n");
            for (int i = 0; i < checked; i++) {
                msg.append(" ");
            }
            msg.append("^");
            throw new IllegalArgumentException(msg.toString());
        }
    }
251350
function instancesFromMatches(word, text, selector) {
  var match, ret = [];
  var re = new RegExp('\\b'+utils.escapeRegex(word)+'\\b', 'gi');
  var instance = 1;
  while ((match = re.exec(text)) !== null) {
    GLOBAL.debug(text.length, text.substring(0, 10), word, match.index, text.substr(match.index, word.length));
    // It's not in a tag
    if (text.indexOf('<' < 0) || (text.indexOf('>', match.index) > text.indexOf('<'.match.index))) {
      ret.push(annoLib.createInstance({exact: text.substr(match.index, word.length), instance: instance, selector: selector}));
    }
    instance++;
  }
  return ret;
}
834094
function DoubleMatcherIterator(node, match) {
  this.n = node;
  this.m = match;
  this.p = match.length;
  this.c = node._map['**'];
}
738885
function(match, startNode, visibleOnly) {
			match =
				typeof match === "string"
					? _makeNodeTitleStartMatcher(match)
					: match;
			startNode = startNode || this.getFirstChild();

			var stopNode = null,
				parentChildren = startNode.parent.children,
				matchingNode = null,
				walkVisible = function(parent, idx, fn) {
					var i,
						grandParent,
						parentChildren = parent.children,
						siblingCount = parentChildren.length,
						node = parentChildren[idx];
					// visit node itself
					if (node && fn(node) === false) {
						return false;
					}
					// visit descendants
					if (node && node.children && node.expanded) {
						if (walkVisible(node, 0, fn) === false) {
							return false;
						}
					}
					// visit subsequent siblings
					for (i = idx + 1; i < siblingCount; i++) {
						if (walkVisible(parent, i, fn) === false) {
							return false;
						}
					}
					// visit parent's subsequent siblings
					grandParent = parent.parent;
					if (grandParent) {
						return walkVisible(
							grandParent,
							grandParent.children.indexOf(parent) + 1,
							fn
						);
					} else {
						// wrap-around: restart with first node
						return walkVisible(parent, 0, fn);
					}
				};

			walkVisible(
				startNode.parent,
				parentChildren.indexOf(startNode),
				function(node) {
					// Stop iteration if we see the start node a second time
					if (node === stopNode) {
						return false;
					}
					stopNode = stopNode || node;
					// Ignore nodes hidden by a filter
					if (!$(node.span).is(":visible")) {
						node.debug("quicksearch: skipping hidden node");
						return;
					}
					// Test if we found a match, but search for a second match if this
					// was the currently active node
					if (match(node)) {
						// node.debug("quicksearch match " + node.title, startNode);
						matchingNode = node;
						if (matchingNode !== startNode) {
							return false;
						}
					}
				}
			);
			return matchingNode;
		}
1358631
function (node) {
      if (node.superClass) {
        return new RegExp('^(' + pragma + '\\.)?PureComponent$').test(sourceCode.getText(node.superClass));
      }
      return false;
    }
599977
public static RegExpTree parseRegExp(
      final String pattern, final String flags) {

    /** A recursive descent parser that closes over pattern and flags above. */
    class Parser {
      /** The number of characters in pattern consumed. */
      int pos;
      /** The number of capturing groups seen so far. */
      int numCapturingGroups;
      /** The names of capturing groups in the regex expression */
      Set<String> capturingGroupNames = new HashSet<>();
      /** The length of pattern. */
      final int limit = pattern.length();
      /** Boolean indicating whether we should look for named capture group backreferences */
      boolean lookForNamedCaptureBackreferences;

      RegExpTree parseTopLevel() {
        // First assume there are no named capture backreferences,
        // because the spec says they should only be recognized
        // if the pattern contains at least one named capture group.
        this.pos = 0;
        this.numCapturingGroups = 0;
        this.lookForNamedCaptureBackreferences = false;
        RegExpTree out = parse();

        // If there were named capture groups, we must parse the pattern string again
        // so we can check for any backreferences to them.
        if (!capturingGroupNames.isEmpty()) {
          this.pos = 0;
          this.numCapturingGroups = 0;
          this.lookForNamedCaptureBackreferences = true;
          out = parse();
        }

        if (pos < limit) {  // Unmatched closed paren maybe.
          throw new IllegalArgumentException(pattern.substring(pos));
        }
        return out;
      }

      RegExpTree parse() {
        // Collects ["foo", "bar", "baz"] for /foo|bar|baz/.
        ImmutableList.Builder<RegExpTree> alternatives = null;
        // The last item parsed within an alternation.
        RegExpTree preceder = null;

        topLoop:
        while (pos < limit) {
          char ch = pattern.charAt(pos);
          RegExpTree atom;
          switch (ch) {
            case '[':
              atom = parseCharset();
              break;
            case '(':
              atom = parseParenthetical();
              break;
            case ')':
              break topLoop;
            case '\\':
              atom = parseEscape();
              break;
            case '^':
            case '$':
              atom = new Anchor(ch);
              ++pos;
              break;
            case '.':
              // We represent . as a character set to make it easy to simplify
              // things like /.|[\r\n]/.
              atom = DOT_CHARSET;
              ++pos;
              break;
            case '|':
              // An alternative may be empty as in /foo||bar/.
              // The '|' is consumed below.
              atom = Empty.INSTANCE;
              break;
            default:
              // Find a run of concatenated characters to avoid building a
              // tree node per literal character.
              int start = pos;
              int end = pos + 1;
              charsLoop:
              while (end < limit) {
                switch (pattern.charAt(end)) {
                  case '[':
                  case '(':
                  case ')':
                  case '\\':
                  case '^':
                  case '$':
                  case '|':
                  case '.':
                  case '*':
                  case '+':
                  case '?':
                  case '{':
                    break charsLoop;
                  default:
                    // Repetition binds more tightly than concatenation.
                    // Only consume up to "foo" in /foob*/ so that the suffix
                    // operator parser below has the right precedence.
                    if (end + 1 >= limit
                        || !isRepetitionStart(pattern.charAt(end + 1))) {
                      ++end;
                    } else {
                      break charsLoop;
                    }
                }
              }
              atom = new Text(pattern.substring(start, end));
              pos = end;
              break;
          }
          if (pos < limit && isRepetitionStart(pattern.charAt(pos))) {
            atom = parseRepetition(atom);
          }
          if (preceder == null) {
            preceder = atom;
          } else {
            preceder = new Concatenation(preceder, atom);
          }
          // If this is an alternative in a alternation, then add it to the
          // list of complete alternatives, and reset the parser state for the
          // next alternative.
          if (pos < limit && pattern.charAt(pos) == '|') {
            if (alternatives == null) {
              alternatives = ImmutableList.builder();
            }
            alternatives.add(preceder);
            preceder = null;
            ++pos;
          }
        }
        // An alternative may have no parsed content blank as in /foo|/.
        if (preceder == null) { preceder = Empty.INSTANCE; }
        if (alternatives != null) {
          alternatives.add(preceder);
          return new Alternation(alternatives.build());
        } else {
          return preceder;
        }
      }

      /**
       * Handles capturing groups {@code (...)},
       * non-capturing groups {@code (?:...)}, and lookahead assertions
       * {@code (?=...)}.
       */
      private RegExpTree parseParenthetical() {
        checkState(pattern.charAt(pos) == '(');
        int start = pos;
        ++pos;
        ParentheticalType type;
        String captureName = null;
        if (pos < limit && pattern.charAt(pos) == '?') {
          if (pos + 1 < limit) {
            char ch = pattern.charAt(pos + 1);
            switch (ch) {
                // (?:...) Non-capturing groups.
              case ':':
                pos += 2;
                type = ParentheticalType.NONCAPTURING;
                break;

                // (?=...) and (?!...) Lookahead Assertions
              case '=':
                pos += 2;
                type = ParentheticalType.POSITIVE_LOOKAHEAD;
                break;
              case '!':
                pos += 2;
                type = ParentheticalType.NEGATIVE_LOOKAHEAD;
                break;

                // (?<=...) and (?<!...) Lookbehind Assertions, (?<name>) named groups
              case '<':
                if (pos + 2 < limit && pattern.charAt(pos + 2) == '=') {
                  pos += 3;
                  type = ParentheticalType.POSITIVE_LOOKBEHIND;
                  break;
                } else if (pos + 2 < limit && pattern.charAt(pos + 2) == '!') {
                  pos += 3;
                  type = ParentheticalType.NEGATIVE_LOOKBEHIND;
                  break;
                } else {
                  pos += 2;
                  captureName = scanNamedGroupName();
                  capturingGroupNames.add(captureName);
                  type = ParentheticalType.NAMED_GROUPS;
                  break;
                }
              default:
                throw new IllegalArgumentException(
                    "Malformed parenthetical: " + pattern.substring(start));
            }
          } else {
            throw new IllegalArgumentException(
                "Malformed parenthetical: " + pattern.substring(start));
          }
        } else {
          type = ParentheticalType.CAPTURING;
        }
        RegExpTree body = parse();
        if (pos < limit && pattern.charAt(pos) == ')') {
          ++pos;
        } else {
          throw new IllegalArgumentException(
              "Unclosed parenthetical group: " + pattern.substring(start));
        }
        switch (type) {
          case CAPTURING:
            ++numCapturingGroups;
            return new CapturingGroup(body);
          case NONCAPTURING:
            return body;

          case POSITIVE_LOOKAHEAD:
            return new LookaheadAssertion(body, true);
          case NEGATIVE_LOOKAHEAD:
            return new LookaheadAssertion(body, false);

          case POSITIVE_LOOKBEHIND:
            return new LookbehindAssertion(body, true);
          case NEGATIVE_LOOKBEHIND:
            return new LookbehindAssertion(body, false);

          case NAMED_GROUPS:
            if (captureName != null) {
              ++numCapturingGroups;
              return new NamedCaptureGroup(body, captureName);
            } else {
              throw new IllegalArgumentException(
                  "Malformed named capture group: " + pattern.substring(start));
            }
        }
        throw new AssertionError("Unrecognized ParentheticalType " + type);
      }

      /**
       * Helper that scans the pattern for a named group name. Assumes that {@code pos} points to
       * the character after '<'
       *
       * @return the group name
       */
      private String scanNamedGroupName() {
        int start = pos;
        int end = pos;
        if (!isIdentifierStart(pattern.charAt(start))) {
          throw new IllegalArgumentException(
              "Invalid capture group name: <" + pattern.substring(start));
        }
        ++end;
        while (end < limit) {
          if (pattern.charAt(end) == '>') {
            pos = end + 1;
            return pattern.substring(start, end);
          } else if (isIdentifierPart(pattern.charAt(end))) {
            ++end;
          } else {
            throw new IllegalArgumentException(
                "Invalid capture group name: <" + pattern.substring(start));
          }
        }
        throw new IllegalArgumentException(
            "Malformed named capture group: <" + pattern.substring(start));
      }

      /**
       * Parses a square bracketed character set.
       * Standalone character groups (@code /\d/} are handled by
       * {@link #parseEscape}.
       */
      private RegExpTree parseCharset() {
        checkState(pattern.charAt(pos) == '[');
        ++pos;

        boolean isCaseInsensitive = flags.indexOf('i') >= 0;
        boolean inverse = pos < limit && pattern.charAt(pos) == '^';
        if (inverse) { ++pos; }
        CharRanges ranges = CharRanges.EMPTY;
        CharRanges ieExplicits = CharRanges.EMPTY;
        while (pos < limit && pattern.charAt(pos) != ']') {
          char ch = pattern.charAt(pos);
          char start;
          if (ch == '\\') {
            ++pos;
            char possibleGroupName = pattern.charAt(pos);
            CharRanges group = NAMED_CHAR_GROUPS.get(possibleGroupName);
            if (group != null) {
              ++pos;
              ranges = ranges.union(group);
              continue;
            }
            start = parseEscapeChar();
          } else {
            start = ch;
            ++pos;
          }
          char end = start;
          if (pos + 1 < limit && pattern.charAt(pos) == '-'
              && pattern.charAt(pos + 1) != ']') {
            ++pos;
            ch = pattern.charAt(pos);
            if (ch == '\\') {
              ++pos;
              end = parseEscapeChar();
            } else {
              end = ch;
              ++pos;
            }
          }
          CharRanges range = CharRanges.inclusive(start, end);
          ranges = ranges.union(range);
          if (IE_SPEC_ERRORS.contains(start) && IE_SPEC_ERRORS.contains(end)) {
            ieExplicits = ieExplicits.union(range.intersection(IE_SPEC_ERRORS));
          }
          if (isCaseInsensitive) {
            // If the flags contain the 'i' flag, then it is not correct to
            // say that [^a-z] contains the letter 'A', or that [a-z] does not
            // contain the letter 'A'.
            // We expand out letter groups here so that parse returns something
            // that is valid independent of flags.
            // Calls to simplify(flags) may later reintroduce flag assumptions.
            // but without this step, later steps might conflate
            //     /[a-z]/i
            // and
            //     /[^\0-`{-\uffff]/i
            // which matches nothing because the information about whether the
            // ^ is present has been lost during optimizations and charset
            // unionizing as in /[...]|[^...]/.
            ranges = CaseCanonicalize.expandToAllMatched(ranges);
          }
        }
        ++pos;  // Consume ']'

        if (inverse) {
          ranges = CharRanges.ALL_CODE_UNITS.difference(ranges);
        }

        return new Charset(ranges, ieExplicits);
      }

      /**
       * Parses an escape to a code point.
       * Some of the characters parsed here have special meanings in various
       * contexts, so contexts must filter those instead.
       * E.g. '\b' means a different thing inside a charset than without.
       */
      private char parseEscapeChar() {
        char ch = pattern.charAt(pos++);
        switch (ch) {
          case 'b': return '\b';
          case 'f': return '\f';
          case 'n': return '\n';
          case 'r': return '\r';
          case 't': return '\t';
          case 'u': return parseHex(4);
          case 'v': return '\u000b';
          case 'x': return parseHex(2);
          default:
            if ('0' <= ch && ch <= '7') {
              char codeUnit = (char) (ch - '0');
              // Allow octal literals in the range \0-\377.
              // \41 might be a group, but \041 is not a group.
              // We read, but do not emit octal literals since they
              // are deprecated in ES5.
              int octLimit = Math.min(
                  limit, pos + (ch <= '3' ? 2 : 1) + (ch == '0' ? 1 : 0));
              while (pos < octLimit) {
                ch = pattern.charAt(pos);
                if ('0' <= ch && ch <= '7') {
                  codeUnit = (char) ((codeUnit << 3) + (ch - '0'));
                  ++pos;
                } else {
                  break;
                }
              }
              return codeUnit;
            }
            return ch;
        }
      }

      /**
       * Parses an escape that appears outside a charset.
       */
      private RegExpTree parseEscape() {
        checkState(pattern.charAt(pos) == '\\');
        int start = pos;
        ++pos;
        char ch = pattern.charAt(pos);
        if (ch == 'b' || ch == 'B') {
          ++pos;
          return new WordBoundary(ch);
        } else if ((ch == 'p' || ch == 'P') && flags.contains("u")) {
          // handle ES2018 unicode property tests, e.g.
          // /\p{ASCII_Hex_Digit=true}/ only hex digits
          // /\P{Script=Greek}/  no greek letters
          boolean negated = ch == 'P';
          ++pos;
          if (pos < limit && pattern.charAt(pos) == '{') {
            StringBuilder lhs = new StringBuilder();
            while (++pos < limit
                && ((ch = pattern.charAt(pos)) == '_'
                    || ('a' <= ch && ch <= 'z')
                    || ('A' <= ch && ch <= 'Z')
                    || ('0' <= ch && ch <= '9'))) {
              lhs.append(ch);
            }
            if (pos < limit && ch == '}') {
              // Case of shorthand like /\p{ASCII_Hex_Digit}/u
              ++pos;
              return new UnicodePropertyEscape(null, lhs.toString(), negated);
            } else if (pos < limit && ch == '=') {
              // Case of having '=' like /\p{Script=Greek}/u
              StringBuilder rhs = new StringBuilder();
              while (++pos < limit
                  && ((ch = pattern.charAt(pos)) == '_'
                      || ('a' <= ch && ch <= 'z')
                      || ('A' <= ch && ch <= 'Z')
                      || ('0' <= ch && ch <= '9'))) {
                rhs.append(ch);
              }
              if (pos < limit && ch == '}') {
                ++pos;
                return new UnicodePropertyEscape(lhs.toString(), rhs.toString(), negated);
              } else {
                throw new IllegalArgumentException(
                    "Malformed Unicode Property Escape: expected '}' after "
                        + pattern.substring(start, pos));
              }
            } else {
              throw new IllegalArgumentException(
                  "Malformed Unicode Property Escape: expected '=' or '}' after "
                      + pattern.substring(start, pos));
            }
          } else {
            throw new IllegalArgumentException(
                "Malformed Unicode Property Escape: expected '{' after "
                    + pattern.substring(start, pos));
          }
        } else if ('1' <= ch && ch <= '9') {
          ++pos;
          int possibleGroupIndex = ch - '0';
          if (numCapturingGroups >= possibleGroupIndex) {
            if (pos < limit) {
              char next = pattern.charAt(pos);
              if ('0' <= next && next <= '9') {
                int twoDigitGroupIndex = possibleGroupIndex * 10 + (next - '0');
                if (numCapturingGroups >= twoDigitGroupIndex) {
                  ++pos;
                  possibleGroupIndex = twoDigitGroupIndex;
                }
              }
            }
            return new BackReference(possibleGroupIndex);
          } else {
            // \1 - \7 are octal escapes if there is no such group.
            // \8 and \9 are the literal characters '8' and '9' if there
            // is no such group.
            return new Text(Character.toString(
              possibleGroupIndex <= 7 ? (char) possibleGroupIndex : ch));
          }
        } else if (lookForNamedCaptureBackreferences
            && ch == 'k'
            && pos + 1 < limit
            && pattern.charAt(pos + 1) == '<'
            // According to the spec
            // https://github.com/tc39/proposal-regexp-named-groups#backwards-compatibility-of-new-syntax
            // we want to treat \k as a normal string if there are no named capturing groups present
            && !capturingGroupNames.isEmpty()) {
          pos += 2;
          String potentialName = scanNamedGroupName();
          if (!capturingGroupNames.contains(potentialName)) {
            throw new IllegalArgumentException(
                "Invalid named capture referenced: " + pattern.substring(start));
          }
          return new NamedBackReference(potentialName);
        } else {
          CharRanges charGroup = NAMED_CHAR_GROUPS.get(ch);
          if (charGroup != null) {  // Handle \d, etc.
            ++pos;
            return new Charset(charGroup, CharRanges.EMPTY);
          }
          return new Text("" + parseEscapeChar());
        }
      }

      /**
       * Parses n hex digits to a code-unit.
       */
      private char parseHex(int n) {
        if (pos + n > limit) {
          throw new IllegalArgumentException(
              "Abbreviated hex escape " + pattern.substring(pos));
        }
        int result = 0;
        while (--n >= 0) {
          char ch = pattern.charAt(pos);
          int digit;
          if ('0' <= ch && ch <= '9') {
            digit = ch - '0';
          } else if ('a' <= ch && ch <= 'f') {
            digit = ch + (10 - 'a');
          } else if ('A' <= ch && ch <= 'F') {
            digit = ch + (10 - 'A');
          } else {
            throw new IllegalArgumentException(pattern.substring(pos));
          }
          ++pos;
          result = (result << 4) | digit;
        }
        return (char) result;
      }

      private boolean isRepetitionStart(char ch) {
        switch (ch) {
          case '?':
          case '*':
          case '+':
          case '{':
            return true;
          default:
            return false;
        }
      }

      /**
       * Parse a repetition.  {@code x?} is treated as a repetition --
       * an optional production can be matched 0 or 1 time.
       */
      private RegExpTree parseRepetition(RegExpTree body) {
        if (pos == limit) { return body; }
        int min, max;
        switch (pattern.charAt(pos)) {
          case '+':
            ++pos;
            min = 1;
            max = Integer.MAX_VALUE;
            break;
          case '*':
            ++pos;
            min = 0;
            max = Integer.MAX_VALUE;
            break;
          case '?':
            ++pos;
            min = 0;
            max = 1;
            break;
          case '{':
            ++pos;
            int start = pos;
            int end = pattern.indexOf('}', start);
            if (end < 0) {
              pos = start - 1;
              return body;
            }
            String counts = pattern.substring(start, end);
            pos = end + 1;
            int comma = counts.indexOf(',');
            try {
              min = Integer.parseInt(
                  comma >= 0 ? counts.substring(0, comma) : counts);
              max = comma >= 0
                  ? comma + 1 != counts.length()
                      ? Integer.parseInt(counts.substring(comma + 1))
                      : Integer.MAX_VALUE
                  : min;
            } catch (NumberFormatException ex) {
              min = max = -1;
            }
            if (min < 0 || min > max) {
              // Treat the open curly bracket literally.
              pos = start - 1;
              return body;
            }
            break;
          default:
            return body;
        }
        boolean greedy = true;
        if (pos < limit && pattern.charAt(pos) == '?') {
          greedy = false;
          ++pos;
        }
        return new Repetition(body, min, max, greedy);
      }
    }

    return new Parser().parseTopLevel();
  }
862280
function ( node, exemplar ) {
            // If the node is completely contained by the range then
            // we're going to remove all formatting so ignore it.
            if ( isNodeContainedInRange( range, node, false ) ) {
                return;
            }

            var isText = ( node.nodeType === TEXT_NODE ),
                child, next;

            // If not at least partially contained, wrap entire contents
            // in a clone of the tag we're removing and we're done.
            if ( !isNodeContainedInRange( range, node, true ) ) {
                // Ignore bookmarks and empty text nodes
                if ( node.nodeName !== 'INPUT' &&
                        ( !isText || node.data ) ) {
                    toWrap.push([ exemplar, node ]);
                }
                return;
            }

            // Split any partially selected text nodes.
            if ( isText ) {
                if ( node === endContainer && endOffset !== node.length ) {
                    toWrap.push([ exemplar, node.splitText( endOffset ) ]);
                }
                if ( node === startContainer && startOffset ) {
                    node.splitText( startOffset );
                    toWrap.push([ exemplar, node ]);
                }
            }
            // If not a text node, recurse onto all children.
            // Beware, the tree may be rewritten with each call
            // to examineNode, hence find the next sibling first.
            else {
                for ( child = node.firstChild; child; child = next ) {
                    next = child.nextSibling;
                    examineNode( child, exemplar );
                }
            }
        }