Package org.rascalmpl.interpreter.matching

Source Code of org.rascalmpl.interpreter.matching.ListPattern

/*******************************************************************************
* Copyright (c) 2009-2013 CWI
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:

*   * Jurgen J. Vinju - Jurgen.Vinju@cwi.nl - CWI
*   * Tijs van der Storm - Tijs.van.der.Storm@cwi.nl
*   * Emilie Balland - (CWI)
*   * Paul Klint - Paul.Klint@cwi.nl - CWI
*   * Mark Hills - Mark.Hills@cwi.nl (CWI)
*   * Arnold Lankamp - Arnold.Lankamp@cwi.nl
*******************************************************************************/
package org.rascalmpl.interpreter.matching;

import java.util.HashMap;
import java.util.HashSet;
import java.util.List;

import org.eclipse.imp.pdb.facts.IConstructor;
import org.eclipse.imp.pdb.facts.IList;
import org.eclipse.imp.pdb.facts.IValue;
import org.eclipse.imp.pdb.facts.type.Type;
import org.rascalmpl.ast.AbstractAST;
import org.rascalmpl.interpreter.IEvaluatorContext;
import org.rascalmpl.interpreter.asserts.ImplementationError;
import org.rascalmpl.interpreter.env.Environment;
import org.rascalmpl.interpreter.result.Result;
import org.rascalmpl.interpreter.result.ResultFactory;
import org.rascalmpl.interpreter.staticErrors.RedeclaredVariable;
import org.rascalmpl.interpreter.types.NonTerminalType;
import org.rascalmpl.values.uptr.SymbolAdapter;

public class ListPattern extends AbstractMatchingResult  {
  private List<IMatchingResult> patternChildren;  // The elements of this list pattern
  private int patternSize;            // The number of elements in this list pattern
  private int delta = 1;              // increment to next list elements:
  // delta=1 abstract lists
  // delta=2 skip layout between elements
  // delta=4 skip layout, separator, layout between elements
  private int reducedPatternSize;     //  (patternSize + delta - 1)/delta                                   
  private IList listSubject;          // The subject as list
  private Type listSubjectType;       // The type of the subject
  private int subjectSize;            // Length of the subject
  private int reducedSubjectSize;     // (subjectSize + delta - 1) / delta
  private boolean [] isListVar;       // Determine which elements are list or variables
  private boolean [] isBindingVar;    // Determine which elements are binding occurrences of variables
  private boolean[] plusList;
  private String [] varName;          // Name of ith variable
  private HashSet<String> allVars;    // Names of list variables declared in this pattern
  private int [] listVarStart;        // Cursor start position list variable; indexed by pattern position
  private int [] listVarLength;       // Current length matched by list variable
  private int [] listVarMinLength;    // Minimal length to be matched by list variable
  private int [] listVarMaxLength;    // Maximal length that can be matched by list variable
  private int [] listVarOccurrences;  // Number of occurrences of list variable in the pattern

  private int subjectCursor;          // Cursor in the subject
  private int patternCursor;          // Cursor in the pattern

  private boolean firstMatch;         // First match after initialization?
  private boolean forward;            // Moving to the right?

  private boolean debug = false;
  private Type staticListSubjectElementType;
  private Type staticListSubjectType;


  public ListPattern(IEvaluatorContext ctx, AbstractAST x, List<IMatchingResult> list){
    this(ctx, x, list, 1)// Default delta=1; Set to 2 to run DeltaListPatternTests
  }

  ListPattern(IEvaluatorContext ctx, AbstractAST x, List<IMatchingResult> list, int delta){
    super(ctx, x);

    if(delta < 1)
      throw new ImplementationError("Wrong delta");
    this.delta = delta;
    this.patternChildren = list;         
    this.patternSize = list.size();
    this.reducedPatternSize = (patternSize + delta - 1) / delta;

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

  @Override
  public List<IVarPattern> getVariables(){
    java.util.LinkedList<IVarPattern> res = new java.util.LinkedList<IVarPattern> ();
    for (int i = 0; i < patternChildren.size(); i += delta) {
      res.addAll(patternChildren.get(i).getVariables());
    }
    return res;
  }

  public static boolean isAnyListType(Type type){
    return type.isList()|| isConcreteListType(type);
  }

  public static boolean isConcreteListType(Type type){
    if (type instanceof NonTerminalType) {
      IConstructor sym = ((NonTerminalType)type).getSymbol();
      return SymbolAdapter.isAnyList(sym);
    }
    return false;
  }

  @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];
    plusList = 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;
      plusList[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);
        }
        plusList[i] = listVar.isPlusList();
        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
        listVarLength[i] = plusList[i] ? 1 : 0;
        listVarMaxLength[i] = delta * Math.max(reducedSubjectSize - (reducedPatternSize - nListVar), 0);
        listVarMinLength[i] = delta * ((nListVar == 1) ? Math.max(reducedSubjectSize - reducedPatternSize - 1, listVarLength[i]) : listVarLength[i]);

        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);
        }
  }

  @Override
  public Type getType(Environment env, HashMap<String,IVarPattern> patternVars) {                     
    if(patternSize == 0){
      return tf.listType(tf.voidType());
    }

    Type elemType = tf.voidType();
    for(int i = 0; i < patternSize; i += delta){
      IMatchingResult child = patternChildren.get(i);
      Type childType = child.getType(env, patternVars);
      patternVars = merge(patternVars, patternChildren.get(i).getVariables());
      boolean isMultiVar = child instanceof MultiVariablePattern || child instanceof TypedMultiVariablePattern;
     
      if(childType.isList() && isMultiVar){
        elemType = elemType.lub(childType.getElementType());
      } else {
        elemType = elemType.lub(childType);
      }
    }
    if(debug) {
      System.err.println("ListPattern.getType: " + tf.listType(elemType));
    }
    return tf.listType(elemType);
  }

  @Override
  public boolean hasNext(){
    if(debug) {
      System.err.println("List: hasNext=" (initialized && hasNext));
    }
    return initialized && hasNext;
  }



  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);
  }

  /*
   * 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);
    }
  }

  /*
   * 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);
  }

  @Override
  public String toString(){
    StringBuffer s = new StringBuffer();
    s.append("[");
    if(initialized){
      String sep = "";
      for(int i = 0; i < Math.min(patternCursor,patternSize); i++){
        s.append(sep).append(patternChildren.get(i).toString());
        sep = ", ";
      }
      if(patternCursor < patternSize){
        s.append("...");
      }
      s.append("]").append("==").append(subject.toString());
    } else {
      s.append("**uninitialized**]");
    }
    return s.toString();
  }
}
TOP

Related Classes of org.rascalmpl.interpreter.matching.ListPattern

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.