private void matchBoundListVar(IList previousBinding) {

    if (debug) {
      System.err.println("matchBoundListVar: " + previousBinding);
    }
    assert isListVar[patternCursor];

    int start = listVarStart[patternCursor];
    int length = listVarLength[patternCursor];

    for (int i = 0; i < previousBinding.length(); i += delta) {
      if (debug)
        System.err.println(
            "comparing: " + previousBinding.get(i) + " and " + listSubject.get(subjectCursor + i));
      if (!previousBinding.get(i).isEqual(listSubject.get(subjectCursor + i))) {
        forward = false;
        listVarLength[patternCursor] = 0;
        patternCursor -= delta;
        if (debug) System.err.println("child fails");
        return;
      }
    }
    subjectCursor = start + length;
    if (debug) System.err.println("child matches, subjectCursor=" + subjectCursor);
    patternCursor += delta;
  }
  private IList makeSubList(int start, int length) {
    assert isListVar[patternCursor];

    // TODO: wrap concrete lists in an appl(list( )) again.
    if (start > subjectSize) return listSubject.sublist(0, 0);

    return listSubject.sublist(start, length);
  }
  @Override
  public <U extends IValue, V extends IValue> Result<U> fieldUpdate(
      String name, Result<V> repl, TypeStore store) {
    IConstructor tree = getValue();

    if (TreeAdapter.isAppl(tree)) {
      int found = -1;
      IConstructor foundType = null;
      IConstructor prod = TreeAdapter.getProduction(tree);
      IList syms = ProductionAdapter.getSymbols(prod);

      // TODO: find deeper into optionals, alternatives and sequences checking the actual arguments
      // for presence/absence of optional trees.
      for (int i = 0; i < syms.length(); i++) {
        IConstructor sym = (IConstructor) syms.get(i);
        if (SymbolAdapter.isLabel(sym)) {
          if (SymbolAdapter.getLabel(sym).equals(name)) {
            found = i;
            foundType = SymbolAdapter.delabel(sym);
            break;
          }
        }
      }

      if (found != -1) {
        Type nont = RascalTypeFactory.getInstance().nonTerminalType(foundType);
        if (repl.getType().isSubtypeOf(nont)) {
          IList args = TreeAdapter.getArgs(tree).put(found, repl.getValue());
          return makeResult(getType(), tree.set("args", args), ctx);
        }
        throw new UnexpectedType(nont, repl.getType(), ctx.getCurrentAST());
      }

      if (Factory.Tree_Appl.hasField(name)) {
        Type fieldType = Factory.Tree_Appl.getFieldType(name);
        if (repl.getType().isSubtypeOf(fieldType)) {
          throw new UnsupportedOperation(
              "changing " + name + " in concrete tree", ctx.getCurrentAST());
        }
        throw new UnexpectedType(fieldType, repl.getType(), ctx.getCurrentAST());
      }

      throw RuntimeExceptionFactory.noSuchField(name, ctx.getCurrentAST(), ctx.getStackTrace());
    }
    throw new UnsupportedOperation("field update", ctx.getCurrentAST());
  }
  @Override
  public <U extends IValue> Result<U> fieldAccess(String name, TypeStore store) {
    IConstructor tree = getValue();

    if (TreeAdapter.isAppl(tree)) {
      int found = -1;
      IConstructor foundType = null;
      IConstructor prod = TreeAdapter.getProduction(tree);

      if (!ProductionAdapter.isRegular(prod)) {
        IList syms = ProductionAdapter.getSymbols(prod);

        // TODO: find deeper into optionals, checking the actual arguments for presence/absence of
        // optional trees.
        for (int i = 0; i < syms.length(); i++) {
          IConstructor sym = (IConstructor) syms.get(i);

          while (SymbolAdapter.isConditional(sym)) {
            sym = SymbolAdapter.getSymbol(sym);
          }
          if (SymbolAdapter.isLabel(sym)) {
            if (SymbolAdapter.getLabel(sym).equals(name)) {
              found = i;
              foundType = SymbolAdapter.delabel(sym);
            }
          }
        }

        if (found != -1) {
          Type nont = RascalTypeFactory.getInstance().nonTerminalType(foundType);
          IValue child = TreeAdapter.getArgs(tree).get(found);
          return makeResult(nont, child, ctx);
        }
      }
    }

    if (tree.getConstructorType().hasField(name)) {
      return makeResult(tree.getConstructorType().getFieldType(name), tree.get(name), ctx);
    }

    throw RuntimeExceptionFactory.noSuchField(name, ctx.getCurrentAST(), ctx.getStackTrace());
  }
  /*
   * We are positioned in the pattern at a list variable and match it with
   * the current subject starting at the current position.
   * On success, the cursors are advanced.
   * On failure, switch to backtracking (forward = false) mode.
   */
  private void matchBindingListVar(IMatchingResult child) {

    assert isListVar[patternCursor];

    int start = listVarStart[patternCursor];
    int length = listVarLength[patternCursor];

    //  int reducedLength = (length == 0) ? 0 : ((length < delta) ? 1 : Math.max(length - delta + 1,
    // 0));  // round to nearest unskipped element

    int reducedLength = (length <= 1) ? length : (length - (length - 1) % delta);

    if (debug) {
      System.err.println("length=" + length);
      System.err.println("reducedLength=" + reducedLength);
    }

    IList sublist = makeSubList(start, reducedLength);

    if (debug)
      System.err.println(
          "matchBindingListVar: init child #" + patternCursor + " (" + child + ") with " + sublist);
    // TODO : check if we can use a static type here!?
    child.initMatch(ResultFactory.makeResult(sublist.getType(), sublist, ctx));

    if (child.next()) {
      subjectCursor = start + length;
      if (debug) System.err.println("child matches, subjectCursor=" + subjectCursor);
      patternCursor += delta;
    } else {
      forward = false;
      listVarLength[patternCursor] = 0;
      patternCursor -= delta;
      if (debug) System.err.println("child fails, subjectCursor=" + subjectCursor);
    }
  }
  public Object loadPT(IConstructor[] fileRef, String sourcePath, String sourceName) {
    IConstructor file = fileRef[0];
    ISourceLocation loc = TreeAdapter.getLocation(file);
    int lineNumber = loc.getBeginLine();

    Object ret = null;

    Var.pushThreadBindings(
        RT.map(
            LOADER,
            RT.makeClassLoader(),
            SOURCE_PATH,
            sourcePath,
            SOURCE,
            sourceName,
            METHOD,
            null,
            LOCAL_ENV,
            null,
            LOOP_LOCALS,
            null,
            NEXT_LOCAL_NUM,
            0,
            RT.CURRENT_NS,
            RT.CURRENT_NS.deref(),
            LINE_BEFORE,
            lineNumber,
            LINE_AFTER,
            lineNumber,
            RT.UNCHECKED_MATH,
            RT.UNCHECKED_MATH.deref(),
            RT.WARN_ON_REFLECTION,
            RT.WARN_ON_REFLECTION.deref(),
            RT.DATA_READERS,
            RT.DATA_READERS.deref()));

    UPTRLispReader reader = new UPTRLispReader(vf, errors);

    try {

      if (TreeAdapter.isAmb(file)) {
        System.err.println("Amb");
      }

      // File is start[File], so
      IConstructor file2 = (IConstructor) TreeAdapter.getArgs(file).get(1);
      IList args = TreeAdapter.getArgs(file2);
      // Probably only this is the list of forms; don't forget to fix
      // below.
      IList forms = TreeAdapter.getArgs((IConstructor) args.get(0));

      IListWriter newArgs = vf.listWriter();
      for (int i = 0; i < forms.length(); i++) {
        IConstructor form = (IConstructor) forms.get(i);
        // only forms, no literals at this level.
        UPTRLispReader.Pair p = reader.read(form);
        newArgs.append(p.tree);
        LINE_AFTER.set(TreeAdapter.getLocation(form).getEndLine());
        ret = eval(p.obj, false);
        LINE_BEFORE.set(TreeAdapter.getLocation(form).getBeginLine());
        if (i < forms.length() - 2) {
          i++;
          newArgs.append(forms.get(i)); // layout
        }
      }
      // Fix tree
      file2 = file2.set("args", newArgs.done());
      file =
          file.set(
              "args",
              vf.list(TreeAdapter.getArgs(file).get(0), file2, TreeAdapter.getArgs(file).get(2)));

    } catch (UPTRLispReader.ReaderException e) {
      throw new CompilerException(sourcePath, e.line, e.getCause());
    } finally {
      Var.popThreadBindings();
    }
    fileRef[0] = file;
    return ret;
  }
  @Override
  protected Result<IBool> equalToConcreteSyntax(ConcreteSyntaxResult that) {
    IConstructor left = this.getValue();
    IConstructor right = that.getValue();

    if (TreeAdapter.isLayout(left) && TreeAdapter.isLayout(right)) {
      return bool(true, ctx);
    }

    if (TreeAdapter.isAppl(left) && TreeAdapter.isAppl(right)) {
      IConstructor p1 = TreeAdapter.getProduction(left);
      IConstructor p2 = TreeAdapter.getProduction(right);

      if (!p1.isEqual(p2)) {
        return bool(false, ctx);
      }

      IList l1 = TreeAdapter.getArgs(left);
      IList l2 = TreeAdapter.getArgs(right);

      if (l1.length() != l2.length()) {
        return bool(false, ctx);
      }
      for (int i = 0; i < l1.length(); i++) {
        IValue kid1 = l1.get(i);
        IValue kid2 = l2.get(i);
        // Recurse here on kids to reuse layout handling etc.
        Result<IBool> result =
            makeResult(kid1.getType(), kid1, ctx).equals(makeResult(kid2.getType(), kid2, ctx));
        if (!result.getValue().getValue()) {
          return bool(false, ctx);
        }
        if (TreeAdapter.isContextFree(left)) {
          i++; // skip layout
        }
      }
      return bool(true, ctx);
    }

    if (TreeAdapter.isChar(left) && TreeAdapter.isChar(right)) {
      return bool((TreeAdapter.getCharacter(left) == TreeAdapter.getCharacter(right)), ctx);
    }

    if (TreeAdapter.isAmb(left) && TreeAdapter.isAmb(right)) {
      ISet alts1 = TreeAdapter.getAlternatives(left);
      ISet alts2 = TreeAdapter.getAlternatives(right);

      if (alts1.size() != alts2.size()) {
        return bool(false, ctx);
      }

      // TODO: this is very inefficient
      again:
      for (IValue alt1 : alts1) {
        for (IValue alt2 : alts2) {
          Result<IBool> result =
              makeResult(alt1.getType(), alt1, ctx).equals(makeResult(alt2.getType(), alt2, ctx));
          if (result.getValue().getValue()) {
            // As soon an alt1 is equal to an alt2
            // continue the outer loop.
            continue again;
          }
        }
        // If an alt1 is not equal to any of the the alt2's return false;
        return bool(false, ctx);
      }
      return bool(true, ctx);
    }

    return bool(false, ctx);
  }
Beispiel #8
0
  public void write(IValue rel, ISourceLocation loc, IEvaluatorContext ctx) {

    OutputStream out = null;

    Type paramType = ctx.getCurrentEnvt().getTypeBindings().get(types.parameterType("T"));
    if (!paramType.isRelation() && !paramType.isListRelation()) {
      throw RuntimeExceptionFactory.illegalTypeArgument(
          "A relation type is required instead of " + paramType,
          ctx.getCurrentAST(),
          ctx.getStackTrace());
    }

    try {
      boolean isListRel = rel instanceof IList;
      out = ctx.getResolverRegistry().getOutputStream(loc.getURI(), false);
      ISet irel = null;
      IList lrel = null;
      if (isListRel) {
        lrel = (IList) rel;
      } else {
        irel = (ISet) rel;
      }

      int nfields = isListRel ? lrel.asRelation().arity() : irel.asRelation().arity();
      if (header) {
        for (int i = 0; i < nfields; i++) {
          if (i > 0) out.write(separator);
          String label = paramType.getFieldName(i);
          if (label == null || label.isEmpty()) label = "field" + i;
          writeString(out, label);
        }
        out.write('\n');
      }
      String separatorAsString = new String(Character.toChars(separator));
      for (IValue v : (isListRel ? lrel : irel)) {
        ITuple tup = (ITuple) v;
        int sep = 0;
        for (IValue w : tup) {
          if (sep == 0) sep = separator;
          else out.write(sep);
          if (w.getType().isString()) {
            String s = ((IString) w).getValue();

            if (s.contains(separatorAsString)
                || s.contains("\n")
                || s.contains("\r")
                || s.contains("\"")) {
              s = s.replaceAll("\"", "\"\"");
              out.write('"');
              writeString(out, s);
              out.write('"');
            } else writeString(out, s);
          } else {
            writeString(out, w.toString());
          }
        }
        out.write('\n');
      }
      out.flush();
      out.close();
    } catch (IOException e) {
      throw RuntimeExceptionFactory.io(values.string(e.getMessage()), null, null);
    } finally {
      if (out != null) {
        try {
          out.flush();
          out.close();
        } catch (IOException ioex) {
          throw RuntimeExceptionFactory.io(values.string(ioex.getMessage()), null, null);
        }
      }
    }
  }
  /*
   * Perform a list match. When forward=true we move to the right in the pattern
   * and try to match the corresponding elements of the subject. When the end of the pattern
   * and the subject are reaching, match returns true.
   *
   * When a non-matching element is encountered, we switch to moving to the left (forward==false)
   * and try to find alternative options in list variables.
   *
   * When the left-hand side of the pattern is reached while moving to the left, match return false,
   * and no more options are available: hasNext() will return false.
   *
   * @see org.meta_environment.rascal.interpreter.MatchPattern#match()
   */
  @Override
  public boolean next() {
    if (debug) System.err.println("List.next: entering");
    checkInitialized();
    if (debug) System.err.println("AbstractPatternList.match: " + subject);

    if (!hasNext) return false;

    forward = firstMatch;
    firstMatch = false;

    do {

      /*
       * Determine the various termination conditions.
       */

      if (debug)
        System.err.println(
            "List.do: patternCursor=" + patternCursor + ", subjectCursor=" + subjectCursor);

      if (forward) {
        if (patternCursor >= patternSize) {
          if (subjectCursor >= subjectSize) {
            if (debug) System.err.println(">>> match returns true");
            //            hasNext = (subjectCursor > subjectSize) && (patternCursor > patternSize);
            // // JURGEN GUESSES THIS COULD BE RIGHT
            return true;
          }
          forward = false;
          patternCursor -= delta;
        }
      } else {
        if (patternCursor >= patternSize) {
          patternCursor -= delta;
          subjectCursor -= delta; // Ok?
        }
      }

      if (patternCursor < 0 || subjectCursor < 0) {
        hasNext = false;
        if (debug)
          System.err.println(
              ">>> match returns false: patternCursor="
                  + patternCursor
                  + ", forward="
                  + forward
                  + ", subjectCursor="
                  + subjectCursor);
        return false;
      }

      /*
       * Perform actions for the current pattern element
       */

      IMatchingResult child = patternChildren.get(patternCursor);
      if (debug) {
        System.err.println(this);
        System.err.println(
            "loop: patternCursor="
                + patternCursor
                + ", forward="
                + forward
                + ", subjectCursor= "
                + subjectCursor
                + ", child="
                + child
                + ", isListVar="
                + isListVar[patternCursor]
                + ", class="
                + child.getClass());
      }

      /*
       * A binding occurrence of a list variable
       */

      if (isListVar[patternCursor] && isBindingVar[patternCursor]) {
        if (forward) {
          listVarStart[patternCursor] = subjectCursor;
          if (patternCursor == patternSize - 1) {
            if (debug) {
              System.err.println("subjectSize=" + subjectSize);
              System.err.println("subjectCursor=" + subjectCursor);
            }
            listVarLength[patternCursor] = Math.max(subjectSize - subjectCursor, 0);
          } else {
            listVarLength[patternCursor] = listVarMinLength[patternCursor];
          }
        } else {
          listVarLength[patternCursor] += delta;
          forward = true;
        }
        if (debug)
          System.err.println(
              "list var: start: "
                  + listVarStart[patternCursor]
                  + ", len="
                  + listVarLength[patternCursor]
                  + ", minlen="
                  + listVarMinLength[patternCursor]
                  + ", maxlen="
                  + listVarMaxLength[patternCursor]);
        if (listVarLength[patternCursor] > listVarMaxLength[patternCursor]
            || listVarStart[patternCursor] + listVarLength[patternCursor] >= subjectSize + delta) {

          subjectCursor = listVarStart[patternCursor];
          if (debug) System.err.println("Length failure, subjectCursor=" + subjectCursor);

          forward = false;
          listVarLength[patternCursor] = 0;
          patternCursor -= delta;
        } else {
          matchBindingListVar(child);
        }

        /*
         * Reference to a previously defined list variable
         */
      } else if (isListVar[patternCursor]
          && !isBindingVar[patternCursor]
          && ctx.getCurrentEnvt().getVariable(varName[patternCursor]).getType().isList()) {
        if (forward) {
          listVarStart[patternCursor] = subjectCursor;

          Result<IValue> varRes = ctx.getCurrentEnvt().getVariable(varName[patternCursor]);
          IValue varVal = varRes.getValue();

          if (varRes.getType().isList()) {
            assert varVal != null && varVal.getType().isList();

            int varLength = ((IList) varVal).length();
            listVarLength[patternCursor] = varLength;

            if (subjectCursor + varLength > subjectSize) {
              forward = false;
              patternCursor -= delta;
            } else {
              matchBoundListVar((IList) varVal);
            }
          }
        } else {
          subjectCursor = listVarStart[patternCursor];
          patternCursor -= delta;
        }

        /*
         * Any other element of the pattern
         */
      } else {
        if (forward && subjectCursor < subjectSize) {
          if (debug)
            System.err.println(
                "AbstractPatternList.match: init child "
                    + patternCursor
                    + " with "
                    + listSubject.get(subjectCursor));
          IValue childValue = listSubject.get(subjectCursor);
          // TODO: check if we can use a static type here?!
          child.initMatch(ResultFactory.makeResult(childValue.getType(), childValue, ctx));
          if (child.next()) {
            subjectCursor += delta;
            patternCursor += delta;
            if (debug)
              System.err.println(
                  "AbstractPatternList.match: child matches, subjectCursor=" + subjectCursor);
          } else {
            forward = false;
            //  why is here no subjectCursor -= delta;  needed?
            patternCursor -= delta;
          }
        } else {
          if (subjectCursor < subjectSize && child.next()) {
            if (debug) System.err.println("child has next:" + child);
            forward = true;
            subjectCursor += delta;
            patternCursor += delta;
          } else {
            if (debug) System.err.println("child has no next:" + child);
            forward = false;
            subjectCursor -= delta;
            patternCursor -= delta;
          }
        }
      }

    } while (true);
  }
Beispiel #10
0
  @Override
  public void initMatch(Result<IValue> subject) {
    if (debug) {
      System.err.println("List: initMatch: subject=" + subject);
    }

    //    This is an experiment to add abstract list matching for concrete subjects
    //    if (subject.getType().isSubtypeOf(Factory.Tree)) {
    //      IConstructor tree = (IConstructor) subject.getValue();
    //      if (TreeAdapter.isList(tree)) {
    //        subject = ResultFactory.makeResult(Factory.Args, TreeAdapter.getArgs(tree), ctx);
    //        IConstructor rhs = TreeAdapter.getType(tree);
    //
    //        if (SymbolAdapter.isIterPlusSeps(rhs) || SymbolAdapter.isIterStarSeps(rhs)) {
    //          this.delta = SymbolAdapter.getSeparators(rhs).length() + 1;
    //        }
    //      }
    //      else {
    //        hasNext = false;
    //        return;
    //      }
    //    }

    super.initMatch(subject);

    if (!subject.getValue().getType().isList()) {
      hasNext = false;
      return;
    }
    listSubject = (IList) subject.getValue();
    listSubjectType = listSubject.getType();
    staticListSubjectType = subject.getType();
    staticListSubjectElementType =
        staticListSubjectType.isList() ? subject.getType().getElementType() : tf.valueType();

    subjectCursor = 0;
    patternCursor = 0;
    subjectSize = ((IList) subject.getValue()).length();
    reducedSubjectSize = (subjectSize + delta - 1) / delta;

    if (debug) {
      System.err.println("reducedPatternSize=" + reducedPatternSize);
      System.err.println("reducedSubjectSize=" + reducedSubjectSize);
    }

    isListVar = new boolean[patternSize];
    isBindingVar = new boolean[patternSize];
    varName = new String[patternSize];
    allVars = new HashSet<String>();
    listVarStart = new int[patternSize];
    listVarLength = new int[patternSize];
    listVarMinLength = new int[patternSize];
    listVarMaxLength = new int[patternSize];
    listVarOccurrences = new int[patternSize];

    int nListVar = 0;
    /*
     * Pass #1: determine the list variables
     */
    for (int i = 0; i < patternSize; i += delta) {
      IMatchingResult child = patternChildren.get(i);
      isListVar[i] = false;
      isBindingVar[i] = false;
      Environment env = ctx.getCurrentEnvt();

      if (child instanceof TypedMultiVariablePattern) {
        TypedMultiVariablePattern tmvVar = (TypedMultiVariablePattern) child;
        Type tmvType = tmvVar.getType(env, null);
        String name = tmvVar.getName();

        varName[i] = name;
        isListVar[i] = true;
        listVarOccurrences[i] = 1;
        ++nListVar;

        if (!tmvVar.isAnonymous() && allVars.contains(name)) {
          throw new RedeclaredVariable(name, getAST());
        } else if (tmvType.comparable(listSubject.getType().getElementType())
            || (tmvVar.bindingInstance() && tmvType.comparable(listSubject.getType()))) {
          tmvVar.convertToListType();
          if (!tmvVar.isAnonymous()) {
            allVars.add(name);
          }
          isBindingVar[i] = true;
        } else {
          hasNext = false;
          return;
        }
      } else if (child instanceof MultiVariablePattern) {
        MultiVariablePattern multiVar = (MultiVariablePattern) child;
        String name = multiVar.getName();
        varName[i] = name;
        isListVar[i] = true;
        nListVar++;

        if (!multiVar.isAnonymous() && allVars.contains(name)) {
          isBindingVar[i] = false;
        } else if (multiVar.isAnonymous()) {
          isBindingVar[i] = true;
        } else {
          allVars.add(name);
          Result<IValue> varRes = env.getVariable(name);

          if (varRes == null || multiVar.bindingInstance()) {
            isBindingVar[i] = true;
          } else {
            isBindingVar[i] = false;
            Type varType = varRes.getType();
            if (isAnyListType(varType)) {
              if (!varType.comparable(listSubjectType)) {
                hasNext = false;
                return;
              }
            } else {
              if (!(varType instanceof NonTerminalType)
                  && !(varType.comparable(staticListSubjectElementType))) {
                hasNext = false;
                return;
              }
            }
          }
        }
      } else if (child instanceof ConcreteListVariablePattern) {
        ConcreteListVariablePattern listVar = (ConcreteListVariablePattern) child;
        String name = listVar.getName();
        varName[i] = name;
        isListVar[i] = true;
        if (!listVar.isAnonymous()) allVars.add(name);
        isBindingVar[i] = true;
        listVarOccurrences[i] = 1;
        nListVar++;
      } else if (child instanceof QualifiedNamePattern) {
        QualifiedNamePattern qualName = (QualifiedNamePattern) child;
        String name = qualName.getName();
        varName[i] = name;
        if (!qualName.isAnonymous() && allVars.contains(name)) {
          /*
           * A variable that was declared earlier in the pattern
           */
          isListVar[i] = true;
          nListVar++;
          listVarOccurrences[i]++;
        } else if (qualName.isAnonymous()) {
          /*
           * Nothing to do
           */
        } else {
          Result<IValue> varRes = env.getVariable(name);

          if (varRes == null || qualName.bindingInstance()) {
            // A completely new non-list variable, nothing to do
          } else {
            Type varType = varRes.getType();
            if (isAnyListType(varType)) {
              /*
               * A variable declared in the current scope.
               */
              if (varType.comparable(listSubjectType)) {
                isListVar[i] = true;
                isBindingVar[i] = varRes.getValue() == null;
                nListVar++;
              } else {
                hasNext = false;
                return;
              }
            } else {
              if (varType instanceof NonTerminalType) {
                // suppress comparable test for Nonterminal types
                // TODO: this should be done better
              } else if (!varType.comparable(staticListSubjectElementType)) {
                hasNext = false;
                return;
              }
            }
          }
        }
      } else if (child instanceof VariableBecomesPattern) {
        // Nothing to do
      } else {
        if (debug) {
          System.err.println("List: child " + child);
          System.err.println("List: child is a" + child.getClass());
        }
        Type childType = child.getType(env, null);

        // TODO: pattern matching should be specialized such that matching appl(prod...)'s does not
        // need to use list matching on the fixed arity children of the application of a production
        if (!(childType instanceof NonTerminalType)
            && !childType.comparable(staticListSubjectElementType)) {
          hasNext = false;
          return;
        }
        java.util.List<IVarPattern> childVars = child.getVariables();
        if (!childVars.isEmpty()) {
          for (IVarPattern vp : childVars) { // TODO: This does not profit from extra information
            allVars.add(vp.name());
          }
          isListVar[i] = false;
          nListVar++;
        }
      }
    }
    /*
     * Pass #2: assign minimum and maximum length to each list variable
     */
    for (int i = 0; i < patternSize; i += delta) {
      if (isListVar[i]) {
        // TODO: reduce max length according to number of occurrences
        listVarMaxLength[i] =
            delta * Math.max(reducedSubjectSize - (reducedPatternSize - nListVar), 0);
        listVarLength[i] = 0;
        listVarMinLength[i] =
            delta
                * ((nListVar == 1) ? Math.max(reducedSubjectSize - reducedPatternSize - 1, 0) : 0);

        if (debug) {
          System.err.println(
              "listvar " + i + " min= " + listVarMinLength[i] + " max=" + listVarMaxLength[i]);
        }
      }
    }

    firstMatch = true;

    hasNext =
        subject.getValue().getType().isList()
            && reducedSubjectSize >= reducedPatternSize - nListVar;

    if (debug) {
      System.err.println("List: hasNext=" + hasNext);
    }
  }