protected Frag build() {
      Frag f = pattern.build();
      if (minMatch == 1 && maxMatch == 1) {
        return f;
      } else if (minMatch <= 10 && maxMatch <= 10 && greedyMatch) {
        // Make copies if number of matches is low
        // Doesn't handle nongreedy matches yet
        // For non greedy match need to move curOut before the recursive connect

        // Create NFA fragment that
        // have child pattern repeating for minMatch times
        if (minMatch > 0) {
          //  frag.start -> pattern NFA -> pattern NFA ->
          for (int i = 0; i < minMatch - 1; i++) {
            Frag f2 = pattern.build();
            f.connect(f2);
          }
        } else {
          // minMatch is 0
          // frag.start ->
          f = new Frag(new State());
        }
        if (maxMatch < 0) {
          // Unlimited (loop back to self)
          //        --------
          //       \|/     |
          // ---> pattern NFA --->
          Set<State> curOut = f.out;
          Frag f2 = pattern.build();
          f2.connect(f2);
          f.connect(f2);
          f.add(curOut);
        } else {
          // Limited number of times this pattern repeat,
          // just keep add pattern (with option of being done) until maxMatch reached
          // ----> pattern NFA ----> pattern NFA --->
          //   |                |
          //   -->              --->
          for (int i = minMatch; i < maxMatch; i++) {
            Set<State> curOut = f.out;
            Frag f2 = pattern.build();
            f.connect(f2);
            f.add(curOut);
          }
        }
        return f;
      } else {
        // More general but more expensive matching (when branching, need to keep state explicitly)
        State s = new RepeatState(f.start, minMatch, maxMatch, greedyMatch);
        f.connect(s);
        return new Frag(s);
      }
    }
 protected Frag build() {
   Frag frag = null;
   if (patterns.size() > 0) {
     PatternExpr first = patterns.get(0);
     frag = first.build();
     for (int i = 1; i < patterns.size(); i++) {
       PatternExpr pattern = patterns.get(i);
       Frag f = pattern.build();
       frag.connect(f);
     }
   }
   return frag;
 }
  protected SequencePattern(
      String patternStr,
      SequencePattern.PatternExpr nodeSequencePattern,
      SequenceMatchAction<T> action) {
    this.patternStr = patternStr;
    this.patternExpr = nodeSequencePattern;
    this.action = action;

    nodeSequencePattern = new GroupPatternExpr(nodeSequencePattern, true);
    this.totalGroups = nodeSequencePattern.assignGroupIds(0);
    Frag f = nodeSequencePattern.build();
    f.connect(MATCH_STATE);
    this.root = f.start;
    varGroupBindings = new VarGroupBindings(totalGroups + 1);
    nodeSequencePattern.updateBindings(varGroupBindings);
  }
    protected Frag build() {
      ConjStartState conjStart = new ConjStartState(patterns.size());
      Frag frag = new Frag();
      frag.start = conjStart;
      // Create NFA fragment that
      // have one starting state that branches out to NFAs created by the children expressions
      // AND START ---> pattern 1 --->  AND END (0/n)
      //            |
      //             ---> pattern 2 ---> AND END (1/n)
      //             ...
      for (int i = 0; i < patterns.size(); i++) {
        PatternExpr pattern = patterns.get(i);
        // Build child NFA
        Frag f = pattern.build();
        // Add child NFA to next states of fragment start
        frag.start.add(f.start);

        f.connect(new ConjEndState(conjStart, i));
        // Add child NFA out (unlinked) states to out (unlinked) states of this fragment
        frag.add(f.out);
      }
      return frag;
    }
 protected Frag build() {
   Frag f = pattern.build();
   Frag frag = new Frag(new GroupStartState(captureGroupId, f.start), f.out);
   frag.connect(new GroupEndState(captureGroupId));
   return frag;
 }