Esempio n. 1
0
 public static Tree processPatternsOnTree(List<Pair<TregexPattern, TsurgeonPattern>> ops, Tree t) {
   matchedOnTree = false;
   for (Pair<TregexPattern, TsurgeonPattern> op : ops) {
     try {
       if (DEBUG) {
         System.err.println("Running pattern " + op.first());
       }
       TregexMatcher m = op.first().matcher(t);
       while (m.find()) {
         matchedOnTree = true;
         t = op.second().evaluate(t, m);
         if (t == null) {
           return null;
         }
         m = op.first().matcher(t);
       }
     } catch (NullPointerException npe) {
       throw new RuntimeException(
           "Tsurgeon.processPatternsOnTree failed to match label for pattern: "
               + op.first()
               + ", "
               + op.second(),
           npe);
     }
   }
   return t;
 }
  /**
   * Uses regexp matching to match month, day, and year fields TODO: Find a way to mark what;s
   * already been handled in the string
   */
  public boolean extractFields(String inputDate) {

    if (tokens.size() < 2) {
      tokenizeDate(inputDate);
    }
    if (DEBUG) {
      System.err.println("Extracting date: " + inputDate);
    }
    // first we see if it's a hyphen and two parseable dates - if not, we treat it as one date
    Pair<String, String> dateEndpoints = getRangeDates(inputDate);
    if (dateEndpoints != null) {
      ISODateInstance date1 = new ISODateInstance(dateEndpoints.first());
      if (dateEndpoints.first().contains(" ") && !dateEndpoints.second().contains(" ")) {
        // consider whether it's a leading modifier; e.g., "June 8-10" will be split into June 8,
        // and 10 when really we'd like June 8 and June 10
        String date =
            dateEndpoints.first().substring(0, dateEndpoints.first().indexOf(' '))
                + ' '
                + dateEndpoints.second();
        ISODateInstance date2 = new ISODateInstance(date);
        if (!date1.isUnparseable() && !date2.isUnparseable()) {
          isoDate = (new ISODateInstance(date1, date2)).getDateString();
          return true;
        }
      }

      ISODateInstance date2 = new ISODateInstance(dateEndpoints.second());
      if (!date1.isUnparseable() && !date2.isUnparseable()) {
        isoDate = (new ISODateInstance(date1, date2)).getDateString();
        return true;
      }
    }

    if (extractYYYYMMDD(inputDate)) {
      return true;
    }
    if (extractMMDDYY(inputDate)) {
      return true;
    }
    boolean passed = false;
    passed = extractYear(inputDate) || passed;
    passed = extractMonth(inputDate) || passed;
    passed = extractDay(inputDate) || passed;

    // slightly hacky, but check for some common modifiers that get grouped into the date
    passed = addExtraRanges(inputDate) || passed;

    if (!passed) { // couldn't parse
      // try one more trick
      unparseable = true;
      boolean weekday = extractWeekday(inputDate);
      if (!weekday) {
        isoDate = inputDate;
      }
    }
    return passed;
  }
Esempio n. 3
0
  private String findNextParagraphSpeaker(
      List<CoreMap> paragraph, int paragraphOffset, Dictionaries dict) {
    CoreMap lastSent = paragraph.get(paragraph.size() - 1);
    String speaker = "";
    for (CoreLabel w : lastSent.get(CoreAnnotations.TokensAnnotation.class)) {
      if (w.get(CoreAnnotations.LemmaAnnotation.class).equals("report")
          || w.get(CoreAnnotations.LemmaAnnotation.class).equals("say")) {
        String word = w.get(CoreAnnotations.TextAnnotation.class);
        SemanticGraph dependency =
            lastSent.get(SemanticGraphCoreAnnotations.CollapsedDependenciesAnnotation.class);
        IndexedWord t = dependency.getNodeByWordPattern(word);

        for (Pair<GrammaticalRelation, IndexedWord> child : dependency.childPairs(t)) {
          if (child.first().getShortName().equals("nsubj")) {
            int subjectIndex = child.second().index(); // start from 1
            IntTuple headPosition = new IntTuple(2);
            headPosition.set(0, paragraph.size() - 1 + paragraphOffset);
            headPosition.set(1, subjectIndex - 1);
            if (mentionheadPositions.containsKey(headPosition)
                && mentionheadPositions.get(headPosition).nerString.startsWith("PER")) {
              speaker = Integer.toString(mentionheadPositions.get(headPosition).mentionID);
            }
          }
        }
      }
    }
    return speaker;
  }
Esempio n. 4
0
  /** Check one mention is the speaker of the other mention */
  public static boolean isSpeaker(Mention m, Mention ant, Dictionaries dict) {

    if (!dict.firstPersonPronouns.contains(ant.spanToString().toLowerCase())
        || ant.number == Number.PLURAL
        || ant.sentNum != m.sentNum) return false;

    int countQuotationMark = 0;
    for (int i = Math.min(m.headIndex, ant.headIndex) + 1;
        i < Math.max(m.headIndex, ant.headIndex);
        i++) {
      String word = m.sentenceWords.get(i).get(CoreAnnotations.TextAnnotation.class);
      if (word.equals("``") || word.equals("''")) countQuotationMark++;
    }
    if (countQuotationMark != 1) return false;

    IndexedWord w =
        m.dependency.getNodeByWordPattern(
            m.sentenceWords.get(m.headIndex).get(CoreAnnotations.TextAnnotation.class));
    if (w == null) return false;

    for (Pair<GrammaticalRelation, IndexedWord> parent : m.dependency.parentPairs(w)) {
      if (parent.first().getShortName().equals("nsubj")
          && dict.reportVerb.contains(parent.second().get(CoreAnnotations.LemmaAnnotation.class))) {
        return true;
      }
    }
    return false;
  }
Esempio n. 5
0
 private void commitVariableGroups(Matcher m) {
   committedVariables = true; // commit all my variable groups.
   for (Pair<Integer, String> varGroup : myNode.variableGroups) {
     String thisVarString = m.group(varGroup.first());
     variableStrings.setVar(varGroup.second(), thisVarString);
   }
 }
Esempio n. 6
0
  private void findQuotationSpeaker(
      int utterNum,
      List<CoreMap> sentences,
      Pair<Integer, Integer> beginQuotation,
      Pair<Integer, Integer> endQuotation,
      Dictionaries dict) {

    if (findSpeaker(utterNum, beginQuotation.first(), sentences, 0, beginQuotation.second(), dict))
      return;

    if (findSpeaker(
        utterNum,
        endQuotation.first(),
        sentences,
        endQuotation.second(),
        sentences.get(endQuotation.first()).get(CoreAnnotations.TokensAnnotation.class).size(),
        dict)) return;

    if (beginQuotation.second() <= 1 && beginQuotation.first() > 0) {
      if (findSpeaker(
          utterNum,
          beginQuotation.first() - 1,
          sentences,
          0,
          sentences
              .get(beginQuotation.first() - 1)
              .get(CoreAnnotations.TokensAnnotation.class)
              .size(),
          dict)) return;
    }

    if (endQuotation.second() == sentences.get(endQuotation.first()).size() - 1
        && sentences.size() > endQuotation.first() + 1) {
      if (findSpeaker(
          utterNum,
          endQuotation.first() + 1,
          sentences,
          0,
          sentences
              .get(endQuotation.first() + 1)
              .get(CoreAnnotations.TokensAnnotation.class)
              .size(),
          dict)) return;
    }
  }
 public String expandStringRegex(String regex) {
   // Replace all variables in regex
   String expanded = regex;
   for (String v : stringRegexVariables.keySet()) {
     Pair<Pattern, String> p = stringRegexVariables.get(v);
     expanded = p.first().matcher(expanded).replaceAll(p.second());
   }
   return expanded;
 }
Esempio n. 8
0
 /**
  * Compares this <code>Pair</code> to another object. If the object is a <code>Pair</code>, this
  * function will work providing the elements of the <code>Pair</code> are themselves comparable.
  * It will then return a value based on the pair of objects, where <code>
  * p &gt; q iff p.first() &gt; q.first() ||
  * (p.first().equals(q.first()) && p.second() &gt; q.second())</code>. If the other object is not
  * a <code>Pair</code>, it throws a <code>ClassCastException</code>.
  *
  * @param o the <code>Object</code> to be compared.
  * @return the value <code>0</code> if the argument is a <code>Pair</code> equal to this <code>
  *     Pair</code>; a value less than <code>0</code> if the argument is a <code>Pair</code>
  *     greater than this <code>Pair</code>; and a value greater than <code>0</code> if the
  *     argument is a <code>Pair</code> less than this <code>Pair</code>.
  * @throws ClassCastException if the argument is not a <code>Pair</code>.
  * @see java.lang.Comparable
  */
 public int compareTo(Object o) {
   Pair another = (Pair) o;
   int comp = ((Comparable) first()).compareTo(another.first());
   if (comp != 0) {
     return comp;
   } else {
     return ((Comparable) second()).compareTo(another.second());
   }
 }
Esempio n. 9
0
 /**
  * Read a string representation of a Pair from a DataStream. This might not work correctly unless
  * the pair of objects are of type <code>String</code>.
  */
 public static Pair<String, String> readStringPair(DataInputStream in) {
   Pair<String, String> p = new Pair<String, String>();
   try {
     p.first = in.readUTF();
     p.second = in.readUTF();
   } catch (Exception e) {
     e.printStackTrace();
   }
   return p;
 }
 protected void updateKeepBids(Set<Integer> bids) {
   // TODO: Is there a point when we don't need to keep these bids anymore?
   for (int i = 0; i < reachableChildBids.length; i++) {
     Set<Pair<Integer, Integer>> v = reachableChildBids[i];
     if (v != null) {
       for (Pair<Integer, Integer> p : v) {
         bids.add(p.first());
       }
     }
   }
 }
Esempio n. 11
0
 /**
  * Construct a new ISODate based on its relation to a referenceDate. relativeDate should be
  * something like "today" or "tomorrow" or "last year" and the resulting ISODate will be the same
  * as the referenceDate, a day later, or a year earlier, respectively.
  */
 public ISODateInstance(ISODateInstance referenceDate, String relativeDate) {
   Pair<DateField, Integer> relation = relativeDateMap.get(relativeDate.toLowerCase());
   if (relation != null) {
     switch (relation.first()) {
       case DAY:
         incrementDay(referenceDate, relation);
         break;
       case MONTH:
         incrementMonth(referenceDate, relation);
         break;
       case YEAR:
         incrementYear(referenceDate, relation);
         break;
     }
   }
 }
Esempio n. 12
0
 private static void extractSubtrees(List<String> codeStrings, String treeFile) {
   List<Pair<Integer, Integer>> codes = new ArrayList<Pair<Integer, Integer>>();
   for (String s : codeStrings) {
     Matcher m = codePattern.matcher(s);
     if (m.matches())
       codes.add(
           new Pair<Integer, Integer>(Integer.parseInt(m.group(1)), Integer.parseInt(m.group(2))));
     else throw new RuntimeException("Error: illegal node code " + s);
   }
   TreeReaderFactory trf = new TRegexTreeReaderFactory();
   MemoryTreebank treebank = new MemoryTreebank(trf);
   treebank.loadPath(treeFile, null, true);
   for (Pair<Integer, Integer> code : codes) {
     Tree t = treebank.get(code.first() - 1);
     t.getNodeNumber(code.second()).pennPrint();
   }
 }
Esempio n. 13
0
  private boolean findSpeaker(
      int utterNum,
      int sentNum,
      List<CoreMap> sentences,
      int startIndex,
      int endIndex,
      Dictionaries dict) {
    List<CoreLabel> sent = sentences.get(sentNum).get(CoreAnnotations.TokensAnnotation.class);
    for (int i = startIndex; i < endIndex; i++) {
      if (sent.get(i).get(CoreAnnotations.UtteranceAnnotation.class) != 0) continue;
      String lemma = sent.get(i).get(CoreAnnotations.LemmaAnnotation.class);
      String word = sent.get(i).get(CoreAnnotations.TextAnnotation.class);
      if (dict.reportVerb.contains(lemma)) {
        // find subject
        SemanticGraph dependency =
            sentences
                .get(sentNum)
                .get(SemanticGraphCoreAnnotations.CollapsedDependenciesAnnotation.class);
        IndexedWord w = dependency.getNodeByWordPattern(word);

        if (w != null) {
          for (Pair<GrammaticalRelation, IndexedWord> child : dependency.childPairs(w)) {
            if (child.first().getShortName().equals("nsubj")) {
              String subjectString = child.second().word();
              int subjectIndex = child.second().index(); // start from 1
              IntTuple headPosition = new IntTuple(2);
              headPosition.set(0, sentNum);
              headPosition.set(1, subjectIndex - 1);
              String speaker;
              if (mentionheadPositions.containsKey(headPosition)) {
                speaker = Integer.toString(mentionheadPositions.get(headPosition).mentionID);
              } else {
                speaker = subjectString;
              }
              speakers.put(utterNum, speaker);
              return true;
            }
          }
        } else {
          SieveCoreferenceSystem.logger.warning("Cannot find node in dependency for word " + word);
        }
      }
    }
    return false;
  }
 /**
  * Returns array of child branch ids that causes all child expressions to be satisfied with
  * respect to the specified child expression (assuming satisfiction with the specified branch
  * and node index) For other child expressions to have a compatible satisfiable branch, that
  * branch must also terminate with the same node index as this one.
  *
  * @param index - Index of the child expression
  * @param bid - Branch id that causes the indexed child to be satisfied
  * @param pos - Node index that causes the indexed child to be satisfied
  * @return array of child branch ids if there is a valid combination null otherwise
  */
 private int[] getAllChildMatchedBids(int index, int bid, int pos) {
   int[] matchedBids = new int[reachableChildBids.length];
   for (int i = 0; i < reachableChildBids.length; i++) {
     Set<Pair<Integer, Integer>> v = reachableChildBids[i];
     if (v == null || v.isEmpty()) return null;
     if (i != index) {
       boolean ok = false;
       for (Pair<Integer, Integer> p : v) {
         if (p.second() == pos) {
           ok = true;
           matchedBids[i] = p.first();
           break;
         }
       }
       if (!ok) {
         return null;
       }
     } else {
       matchedBids[i] = bid;
     }
   }
   return matchedBids;
 }
 protected <T> boolean match(
     int bid, SequenceMatcher.MatchedStates<T> matchedStates, boolean consume) {
   // Try to match previous node/nodes exactly
   if (consume) {
     // First element is group that is matched, second is number of nodes matched so far
     Pair<SequenceMatcher.MatchedGroup, Integer> backRefState =
         (Pair<SequenceMatcher.MatchedGroup, Integer>)
             matchedStates.getBranchStates().getMatchStateInfo(bid, this);
     if (backRefState == null) {
       // Haven't tried to match this node before, try now
       // Get element and return if it matched or not
       SequenceMatcher.MatchedGroup matchedGroup =
           matchedStates.getBranchStates().getMatchedGroup(bid, captureGroupId);
       if (matchedGroup != null) {
         // See if the first node matches
         if (matchedGroup.matchEnd > matchedGroup.matchBegin) {
           boolean matched = match(bid, matchedStates, matchedGroup, 0);
           return matched;
         } else {
           // TODO: Check handling of previous nodes that are zero elements?
           return super.match(bid, matchedStates, consume);
         }
       }
       return false;
     } else {
       SequenceMatcher.MatchedGroup matchedGroup = backRefState.first();
       int matchedNodes = backRefState.second();
       boolean matched = match(bid, matchedStates, matchedGroup, matchedNodes);
       return matched;
     }
   } else {
     // Not consuming, just add this state back to list of states to be processed
     matchedStates.addState(bid, this);
     return false;
   }
 }
  /** @param args */
  public static void main(String[] args) {
    if (args.length != 3) {
      System.err.printf(
          "Usage: java %s language filename features%n",
          TreebankFactoredLexiconStats.class.getName());
      System.exit(-1);
    }

    Language language = Language.valueOf(args[0]);
    TreebankLangParserParams tlpp = language.params;
    if (language.equals(Language.Arabic)) {
      String[] options = {"-arabicFactored"};
      tlpp.setOptionFlag(options, 0);
    } else {
      String[] options = {"-frenchFactored"};
      tlpp.setOptionFlag(options, 0);
    }
    Treebank tb = tlpp.diskTreebank();
    tb.loadPath(args[1]);

    MorphoFeatureSpecification morphoSpec =
        language.equals(Language.Arabic)
            ? new ArabicMorphoFeatureSpecification()
            : new FrenchMorphoFeatureSpecification();

    String[] features = args[2].trim().split(",");
    for (String feature : features) {
      morphoSpec.activate(MorphoFeatureType.valueOf(feature));
    }

    // Counters
    Counter<String> wordTagCounter = new ClassicCounter<>(30000);
    Counter<String> morphTagCounter = new ClassicCounter<>(500);
    //    Counter<String> signatureTagCounter = new ClassicCounter<String>();
    Counter<String> morphCounter = new ClassicCounter<>(500);
    Counter<String> wordCounter = new ClassicCounter<>(30000);
    Counter<String> tagCounter = new ClassicCounter<>(300);

    Counter<String> lemmaCounter = new ClassicCounter<>(25000);
    Counter<String> lemmaTagCounter = new ClassicCounter<>(25000);

    Counter<String> richTagCounter = new ClassicCounter<>(1000);

    Counter<String> reducedTagCounter = new ClassicCounter<>(500);

    Counter<String> reducedTagLemmaCounter = new ClassicCounter<>(500);

    Map<String, Set<String>> wordLemmaMap = Generics.newHashMap();

    TwoDimensionalIntCounter<String, String> lemmaReducedTagCounter =
        new TwoDimensionalIntCounter<>(30000);
    TwoDimensionalIntCounter<String, String> reducedTagTagCounter =
        new TwoDimensionalIntCounter<>(500);
    TwoDimensionalIntCounter<String, String> tagReducedTagCounter =
        new TwoDimensionalIntCounter<>(300);

    int numTrees = 0;
    for (Tree tree : tb) {
      for (Tree subTree : tree) {
        if (!subTree.isLeaf()) {
          tlpp.transformTree(subTree, tree);
        }
      }
      List<Label> pretermList = tree.preTerminalYield();
      List<Label> yield = tree.yield();
      assert yield.size() == pretermList.size();

      int yieldLen = yield.size();
      for (int i = 0; i < yieldLen; ++i) {
        String tag = pretermList.get(i).value();

        String word = yield.get(i).value();
        String morph = ((CoreLabel) yield.get(i)).originalText();

        // Note: if there is no lemma, then we use the surface form.
        Pair<String, String> lemmaTag = MorphoFeatureSpecification.splitMorphString(word, morph);
        String lemma = lemmaTag.first();
        String richTag = lemmaTag.second();

        // WSGDEBUG
        if (tag.contains("MW")) lemma += "-MWE";

        lemmaCounter.incrementCount(lemma);
        lemmaTagCounter.incrementCount(lemma + tag);

        richTagCounter.incrementCount(richTag);

        String reducedTag = morphoSpec.strToFeatures(richTag).toString();
        reducedTagCounter.incrementCount(reducedTag);

        reducedTagLemmaCounter.incrementCount(reducedTag + lemma);

        wordTagCounter.incrementCount(word + tag);
        morphTagCounter.incrementCount(morph + tag);
        morphCounter.incrementCount(morph);
        wordCounter.incrementCount(word);
        tagCounter.incrementCount(tag);

        reducedTag = reducedTag.equals("") ? "NONE" : reducedTag;
        if (wordLemmaMap.containsKey(word)) {
          wordLemmaMap.get(word).add(lemma);
        } else {
          Set<String> lemmas = Generics.newHashSet(1);
          wordLemmaMap.put(word, lemmas);
        }
        lemmaReducedTagCounter.incrementCount(lemma, reducedTag);
        reducedTagTagCounter.incrementCount(lemma + reducedTag, tag);
        tagReducedTagCounter.incrementCount(tag, reducedTag);
      }
      ++numTrees;
    }

    // Barf...
    System.out.println("Language: " + language.toString());
    System.out.printf("#trees:\t%d%n", numTrees);
    System.out.printf("#tokens:\t%d%n", (int) wordCounter.totalCount());
    System.out.printf("#words:\t%d%n", wordCounter.keySet().size());
    System.out.printf("#tags:\t%d%n", tagCounter.keySet().size());
    System.out.printf("#wordTagPairs:\t%d%n", wordTagCounter.keySet().size());
    System.out.printf("#lemmas:\t%d%n", lemmaCounter.keySet().size());
    System.out.printf("#lemmaTagPairs:\t%d%n", lemmaTagCounter.keySet().size());
    System.out.printf("#feattags:\t%d%n", reducedTagCounter.keySet().size());
    System.out.printf("#feattag+lemmas:\t%d%n", reducedTagLemmaCounter.keySet().size());
    System.out.printf("#richtags:\t%d%n", richTagCounter.keySet().size());
    System.out.printf("#richtag+lemma:\t%d%n", morphCounter.keySet().size());
    System.out.printf("#richtag+lemmaTagPairs:\t%d%n", morphTagCounter.keySet().size());

    // Extra
    System.out.println("==================");
    StringBuilder sbNoLemma = new StringBuilder();
    StringBuilder sbMultLemmas = new StringBuilder();
    for (Map.Entry<String, Set<String>> wordLemmas : wordLemmaMap.entrySet()) {
      String word = wordLemmas.getKey();
      Set<String> lemmas = wordLemmas.getValue();
      if (lemmas.size() == 0) {
        sbNoLemma.append("NO LEMMAS FOR WORD: " + word + "\n");
        continue;
      }
      if (lemmas.size() > 1) {
        sbMultLemmas.append("MULTIPLE LEMMAS: " + word + " " + setToString(lemmas) + "\n");
        continue;
      }
      String lemma = lemmas.iterator().next();
      Set<String> reducedTags = lemmaReducedTagCounter.getCounter(lemma).keySet();
      if (reducedTags.size() > 1) {
        System.out.printf("%s --> %s%n", word, lemma);
        for (String reducedTag : reducedTags) {
          int count = lemmaReducedTagCounter.getCount(lemma, reducedTag);
          String posTags =
              setToString(reducedTagTagCounter.getCounter(lemma + reducedTag).keySet());
          System.out.printf("\t%s\t%d\t%s%n", reducedTag, count, posTags);
        }
        System.out.println();
      }
    }
    System.out.println("==================");
    System.out.println(sbNoLemma.toString());
    System.out.println(sbMultLemmas.toString());
    System.out.println("==================");
    List<String> tags = new ArrayList<>(tagReducedTagCounter.firstKeySet());
    Collections.sort(tags);
    for (String tag : tags) {
      System.out.println(tag);
      Set<String> reducedTags = tagReducedTagCounter.getCounter(tag).keySet();
      for (String reducedTag : reducedTags) {
        int count = tagReducedTagCounter.getCount(tag, reducedTag);
        //        reducedTag = reducedTag.equals("") ? "NONE" : reducedTag;
        System.out.printf("\t%s\t%d%n", reducedTag, count);
      }
      System.out.println();
    }
    System.out.println("==================");
  }
Esempio n. 17
0
    // when finished = false; break; is called, it means I successfully matched.
    @SuppressWarnings("null")
    private void goToNextNodeMatch() {
      decommitVariableGroups(); // make sure variable groups are free.
      decommitNamedNodes();
      decommitNamedRelations();
      finished = true;
      Matcher m = null;
      while (nodeMatchCandidateIterator.hasNext()) {
        if (myNode.reln.getName() != null) {
          String foundReln = namesToRelations.get(myNode.reln.getName());
          nextMatchReln = ((GraphRelation.SearchNodeIterator) nodeMatchCandidateIterator).getReln();
          if ((foundReln != null) && (!nextMatchReln.equals(foundReln))) {
            nextMatch = nodeMatchCandidateIterator.next();
            continue;
          }
        }

        nextMatch = nodeMatchCandidateIterator.next();
        // System.err.println("going to next match: " + nextMatch.word() + " " +
        // myNode.descString + " " + myNode.isLink);
        if (myNode.descString.equals("{}") && myNode.isLink) {
          IndexedWord otherNode = namesToNodes.get(myNode.name);
          if (otherNode != null) {
            if (otherNode.equals(nextMatch)) {
              if (!myNode.negDesc) {
                finished = false;
                break;
              }
            } else {
              if (myNode.negDesc) {
                finished = false;
                break;
              }
            }
          } else {
            boolean found = myNode.nodeAttrMatch(nextMatch, hyp ? sg : sg_aligned, ignoreCase);
            if (found) {
              for (Pair<Integer, String> varGroup : myNode.variableGroups) {
                // if variables have been captured from a regex, they
                // must match any previous matchings
                String thisVariable = varGroup.second();
                String thisVarString = variableStrings.getString(thisVariable);
                if (thisVarString != null && !thisVarString.equals(m.group(varGroup.first()))) {
                  // failed to match a variable
                  found = false;
                  break;
                }
              }

              // nodeAttrMatch already checks negDesc, so no need to
              // check for that here
              finished = false;
              break;
            }
          }
        } else { // try to match the description pattern.
          boolean found = myNode.nodeAttrMatch(nextMatch, hyp ? sg : sg_aligned, ignoreCase);
          if (found) {
            for (Pair<Integer, String> varGroup : myNode.variableGroups) {
              // if variables have been captured from a regex, they
              // must match any previous matchings
              String thisVariable = varGroup.second();
              String thisVarString = variableStrings.getString(thisVariable);
              if (thisVarString != null && !thisVarString.equals(m.group(varGroup.first()))) {
                // failed to match a variable
                found = false;
                break;
              }
            }

            // nodeAttrMatch already checks negDesc, so no need to
            // check for that here
            finished = false;
            break;
          }
        }
      } // end while

      if (!finished) { // I successfully matched.
        resetChild();
        if (myNode.name != null) {
          // note: have to fill in the map as we go for backreferencing
          if (!namesToNodes.containsKey(myNode.name)) {
            // System.err.println("making namedFirst");
            namedFirst = true;
          }
          // System.err.println("adding named node: " + myNode.name + "=" +
          // nextMatch.word());
          namesToNodes.put(myNode.name, nextMatch);
        }
        if (myNode.reln.getName() != null) {
          if (!namesToRelations.containsKey(myNode.reln.getName())) relnNamedFirst = true;
          namesToRelations.put(myNode.reln.getName(), nextMatchReln);
        }
        commitVariableGroups(m); // commit my variable groups.
      }
      // finished is false exiting this if and only if nextChild exists
      // and has a label or backreference that matches
      // (also it will just have been reset)
    }