/*******************************************************************************
* 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
* * 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.Iterator;
import java.util.List;
import org.eclipse.imp.pdb.facts.ISet;
import org.eclipse.imp.pdb.facts.IValue;
import org.eclipse.imp.pdb.facts.type.Type;
import org.rascalmpl.ast.Expression;
import org.rascalmpl.interpreter.IEvaluatorContext;
import org.rascalmpl.interpreter.control_exceptions.InterruptException;
import org.rascalmpl.interpreter.env.Environment;
import org.rascalmpl.interpreter.result.Result;
import org.rascalmpl.interpreter.result.ResultFactory;
import org.rascalmpl.interpreter.staticErrors.RedeclaredVariable;
public class SetPattern extends AbstractMatchingResult {
private List<IMatchingResult> patternChildren; // The elements of the set pattern
private int patternSize; // Number of elements in the set pattern
private ISet setSubject; // Current subject
private Type setSubjectType; // Type of the subject
private Type setSubjectElementType; // Type of the elements of current subject
private ISet fixedSetElements; // The fixed, non-variable elements in the pattern
private ISet availableSetElements; // The elements in the subject that are available:
// = setSubject - fixedSetElements
/*
* The variables are indexed from 0, ..., nVar-1 in the order in which they occur in the pattern.
* There are three kinds:
* - a set variable
* - an element variable
* - a non-literal pattern that contains variables
*/
private int nVar; // Number of variables
private HashSet<String> patVars; // List of names of variables at top-level of the pattern
private HashMap<String,IVarPattern> allVars;// Map of names of all the variables in the pattern
// (including nested subpatterns)
private String[] varName; // Name of each variable
private IValue[] varVal; // Value of each variable
private IMatchingResult[] varPat; // The pattern value for non-literal patterns
private boolean[] isSetVar; // Is this a set variable?
private boolean[] isBinding; // Is this a binding variable occurrence?
private boolean[] isNested; // Is this a nested pattern?
private Iterator<?>[] varGen; // Value generator for this variable
private int currentVar; // The currently matched variable
private boolean firstMatch; // First match of this pattern?
private boolean debug = false;
private Type staticSetSubjectType;
private Type staticSubjectElementType;
public SetPattern(IEvaluatorContext ctx, Expression x, List<IMatchingResult> list){
super(ctx, x);
this.patternChildren = list;
this.patternSize = list.size();
}
@Override
public Type getType(Environment env, HashMap<String,IVarPattern> patternVars) {
if(patternSize == 0){
return tf.setType(tf.voidType());
}
Type elemType = tf.voidType();
for(int i = 0; i < patternSize; i++){
IMatchingResult child = patternChildren.get(i);
Type childType = child.getType(env, patternVars);
patternVars = merge(patternVars, patternChildren.get(i).getVariables());
if(debug)System.err.println(" i = " + i + ": " + patternChildren.get(i) + ", type = " + childType);
boolean isMultiVar = child instanceof MultiVariablePattern || child instanceof TypedMultiVariablePattern;
if(childType.isSet() && isMultiVar){
elemType = elemType.lub(childType.getElementType());
} else {
elemType = elemType.lub(childType);
}
}
if(debug)System.err.println("SetPattern.getType: " + this + " returns " + tf.setType(elemType));
return tf.setType(elemType);
}
@Override
public List<IVarPattern> getVariables(){
java.util.LinkedList<IVarPattern> res = new java.util.LinkedList<IVarPattern> ();
for (int i = 0; i < patternChildren.size(); i++) {
res.addAll(patternChildren.get(i).getVariables());
}
return res;
}
private boolean isSetVar(int i){
return isSetVar[i];
}
// Sort the variables: element variables and non-literal patterns should
// go before set variables since only set variables may be empty.
private void sortVars(){
String[] newVarName = new String[patternSize];
IValue[]newVarVal= new ISet[patternSize];
IMatchingResult[] newVarPat = new IMatchingResult[patternSize];
boolean[] newIsSetVar = new boolean[patternSize];
boolean[] newIsBinding = new boolean[patternSize];
boolean[] newIsNested = new boolean[patternSize];
int nw = 0;
for(int i = 0; i < nVar; i++){
if(!isSetVar(i)){
newVarName[nw] = varName[i];
newVarVal[nw] = varVal[i];
newVarPat[nw] = varPat[i];
newIsSetVar[nw] = isSetVar[i];
newIsBinding[nw] = isBinding[i];
newIsNested[nw] = isNested[i];
nw++;
}
}
for(int i = 0; i < nVar; i++){
if(isSetVar(i)){
newVarName[nw] = varName[i];
newVarVal[nw] = varVal[i];
newVarPat[nw] = varPat[i];
newIsSetVar[nw] = isSetVar[i];
newIsBinding[nw] = isBinding[i];
newIsNested[nw] = isNested[i];
nw++;
}
}
assert nw == nVar;
for(int i = 0; i < nVar; i++){
varName[i] = newVarName[i];
varVal[i] = newVarVal[i];
varPat[i] = newVarPat[i];
isSetVar[i] = newIsSetVar[i];
isBinding[i] = newIsBinding[i];
isNested[i] = newIsNested[i];
}
}
private void printVars(){
for(int i = 0; i < nVar; i++){
System.err.printf("%d: %s, isSetVar=%b, isBinding=%b, isNested=%b, val=%s\n", i, varName[i], isSetVar(i), isBinding[i], isNested[i], varVal[i]);
}
}
private boolean declaredAsSetVar(String name){
for(int i = 0; i < nVar; i++){
if(varName[i].equals(name))
return isSetVar(i);
}
return false;
}
@Override
public void initMatch(Result<IValue> subject) {
super.initMatch(subject);
if (!subject.getValue().getType().isSet()) {
hasNext = false;
return;
}
setSubject = (ISet) subject.getValue();
if(debug) System.err.println("\n*** begin initMatch for " + this + " := " + setSubject);
setSubjectType = setSubject.getType(); // have to use static type here
staticSetSubjectType = subject.getType();
setSubjectElementType = setSubject.getElementType();
staticSubjectElementType = staticSetSubjectType.isSet() ? staticSetSubjectType.getElementType() : tf.valueType();
if(debug)System.err.println("setSubjectType = " + setSubjectType + ", staticSetSubjectType = " + staticSetSubjectType + ", setSubjectElementType = " + setSubjectElementType + ", staticSubjectElementType =" + staticSubjectElementType);
Environment env = ctx.getCurrentEnvt();
//fixedSetElements = ctx.getValueFactory().set(getType(env).getElementType());
fixedSetElements = ctx.getValueFactory().set(setSubjectElementType);
nVar = 0;
patVars = new HashSet<String>();
allVars = new HashMap<String, IVarPattern>();
varName = new String[patternSize]; // Some overestimations
isSetVar = new boolean[patternSize];
isBinding = new boolean[patternSize];
isNested = new boolean[patternSize];
varVal = new IValue[patternSize];
varPat = new IMatchingResult[patternSize];
varGen = new Iterator<?>[patternSize];
/*
* Pass #1: determine the (ordinary and set) variables in the pattern
*/
for(int i = 0; i < patternSize; i++){
IMatchingResult child = patternChildren.get(i);
if(debug)System.err.println("child = " + child);
if (child instanceof TypedMultiVariablePattern) {
TypedMultiVariablePattern tmvVar = (TypedMultiVariablePattern) child;
Type childType = child.getType(env, null);
String name = tmvVar.getName();
if (!tmvVar.isAnonymous() && allVars.containsKey(name)) {
throw new RedeclaredVariable(name, getAST());
}
if(childType.comparable(staticSubjectElementType)
|| (tmvVar.bindingInstance() && childType.comparable(staticSetSubjectType))) {
tmvVar.covertToSetType();
if (!tmvVar.isAnonymous()) {
patVars.add(name);
allVars.put(name, (IVarPattern)child);
}
varName[nVar] = name;
varPat[nVar] = child;
isSetVar[nVar] = true;
isBinding[nVar] = true;
isNested[nVar] = false;
++nVar;
} else {
hasNext = false;
return;
}
} else if(child instanceof TypedVariablePattern){
TypedVariablePattern patVar = (TypedVariablePattern) child;
Type childType = child.getType(env, null);
String name = ((TypedVariablePattern)child).getName();
if(!patVar.isAnonymous() && allVars.containsKey(name)){
throw new RedeclaredVariable(name, getAST());
}
if(childType.comparable(staticSubjectElementType)){
/*
* An explicitly declared set or element variable.
*/
if(!patVar.isAnonymous()){
patVars.add(name);
allVars.put(name, (IVarPattern)child);
}
varName[nVar] = name;
varPat[nVar] = child;
isSetVar[nVar] = false;
isBinding[nVar] = true;
isNested[nVar] = false;
nVar++;
} else {
hasNext = false;
return;
// We would like to throw new UnexpectedType(setSubject.getType(), childType, getAST());
// but we can't do this in the context of a visit, because we might actually visit another set!
}
} else if(child instanceof MultiVariablePattern){
/*
* Explicitly declared set variable
*/
MultiVariablePattern multiVar = (MultiVariablePattern) child;
String name = multiVar.getName();
varName[nVar] = name;
varPat[nVar] = child;
isSetVar[nVar] = true;
isBinding[nVar] = true;
isNested[nVar] = false;
nVar++;
} else if(child instanceof QualifiedNamePattern){
/*
* Use of a variable
*/
QualifiedNamePattern qualName = (QualifiedNamePattern) child;
String name = qualName.getName();
if (!qualName.isAnonymous() && allVars.containsKey(name)) {
/*
* A set/element variable that was declared earlier in the pattern itself,
* or in a preceding nested pattern element.
*/
if(!patVars.contains(name)){
/*
* It occurred in an earlier nested subpattern.
*/
varName[nVar] = name;
varPat[nVar] = child;
// If is was declared as set in the current pattern then we are sure it is a set variable,
// otherwise we assume for now that it is not but we check this again later in matchVar.
isSetVar[nVar] = declaredAsSetVar(name);
isBinding[nVar] = false;
isNested[nVar] = false;
nVar++;
} else {
/*
* Ignore it (we are dealing with sets, remember).
*/
}
} else if(qualName.isAnonymous()){
varName[nVar] = name;
varPat[nVar] = child;
isSetVar[nVar] = false;
isBinding[nVar] = false;
isNested[nVar] = false;
nVar++;
} else {
/*
* A non-anonymous variable, not seen before.
*/
if(debug)System.err.println("Non-anonymous var, not seen before: " + name);
Result<IValue> varRes = env.getVariable(name);
if(varRes == null || qualName.bindingInstance()){
// Completely new variable that was not yet declared in this pattern or its subpatterns
varName[nVar] = name;
varPat[nVar] = child;
isSetVar[nVar] = false;
isBinding[nVar] = true;
isNested[nVar] = false;
nVar++;
// TODO: Why is this here? The pattern also declares the variable,
// so this just causes errors when we use variables in set patterns.
// env.declareVariable(staticSubjectElementType, name);
} else {
if(varRes.getValue() != null){
Type varType = varRes.getType();
if (varType.comparable(staticSetSubjectType)){
/*
* A set variable declared in the current scope: add its elements
*/
fixedSetElements = fixedSetElements.union((ISet)varRes.getValue());
} else if(varType.comparable(staticSubjectElementType)){
/*
* An element variable in the current scope, add its value.
*/
fixedSetElements = fixedSetElements.insert(varRes.getValue());
} else {
hasNext = false;
return;
// can't throw type error: throw new UnexpectedType(staticSetSubjectType,varType, getAST());
}
}
else {
// Support pre-declared set variables
if(varRes.getType().comparable(staticSetSubjectType) || varRes.getType().comparable(staticSubjectElementType)){
/*
* An explicitly declared set or element variable.
*/
if(!name.equals("_")){
patVars.add(name);
allVars.put(name, (IVarPattern) child);
}
varName[nVar] = name;
varPat[nVar] = child;
isSetVar[nVar] = varRes.getType().isSet();
isBinding[nVar] = false;
isNested[nVar] = false;
nVar++;
}
}
}
}
} else if(child instanceof LiteralPattern){
/*
* A literal pattern: add it to the set of fixed element
*/
IValue lit = ((LiteralPattern) child).toIValue(env);
Type childType = child.getType(env, null);
if(!childType.comparable(staticSubjectElementType)){
// throw new UnexpectedType(setSubject.getType(), childType, getAST());
hasNext = false;
return;
}
fixedSetElements = fixedSetElements.insert(lit);
if(debug)System.err.println("fixedSetElements => " + fixedSetElements);
} else {
/*
* All other cases of a nested pattern that is not a variable or a literal.
*/
Type childType = child.getType(env, null);
if(!childType.comparable(staticSubjectElementType)){
hasNext = false;
return;
// can't throw type error: throw new UnexpectedType(setSubject.getType(), childType, getAST());
}
java.util.List<IVarPattern> childVars = child.getVariables();
if(!childVars.isEmpty()){
boolean varIntro = false;
for(IVarPattern vp : childVars){
if(debug) System.err.println("var =" + vp);
IVarPattern prev = allVars.get(vp.name());
boolean vpVarIntro = vp.isVarIntroducing();
varIntro = varIntro || vpVarIntro;
if(prev == null || vpVarIntro)
allVars.put(vp.name(), vp);
}
varName[nVar] = child.toString();
varPat[nVar] = child;
isSetVar[nVar] = false;
isBinding[nVar] = varIntro;
isNested[nVar] = true;
nVar++;
} else {
// A nested pattern without variables: treat it as a literal
fixedSetElements = fixedSetElements.insert(child.toIValue());
if(debug)System.err.println("fixedSetElements => " + fixedSetElements);
}
}
}
/*
* Pass #2: set up subset generation
*/
firstMatch = true;
hasNext = fixedSetElements.isSubsetOf(setSubject);
if(debug){
System.err.println("fixedSetElements => " + fixedSetElements);
System.err.println("setSubject => " + setSubject);
}
availableSetElements = setSubject.subtract(fixedSetElements);
sortVars();
if(debug){
System.err.println("Pattern " + this);
printVars();
System.err.printf("hasNext = %b\n", hasNext);
System.err.println("FixedSetElements: " + fixedSetElements);
System.err.println("availableSetElements: " + availableSetElements);
if(debug) System.err.println("*** end initMatch for " + this + "\n");
}
}
@Override
public boolean hasNext(){
return initialized && hasNext;
}
/**
* @return the remaining subject set elements that are available to be matched
*/
private ISet available(){
ISet avail = availableSetElements;
for(int j = 0; j < currentVar; j++){
if(isSetVar(j)){
if(varVal[j] != null)
avail = avail.subtract((ISet)varVal[j]);
} else {
if(varVal[j] != null)
avail = avail.delete(varVal[j]);
}
}
return avail;
}
private boolean unexploredAlternatives(){
assert currentVar == nVar;
for(int j = 0; j < nVar; j++){
if(isSetVar(j)){
if(varGen[j].hasNext())
return true;
} else if(isNested[j] && varPat[j].hasNext())
return true;
}
return false;
}
/**
* Create a value generator for variable i.
* @param i the variable
* @param elements set elements that may be assigned to it
* @return whether creation succeeded
*/
private boolean makeGen(int i, ISet elements) {
if(debug) System.err.println("makeGen: " + i +", " + elements);
Environment env = ctx.getCurrentEnvt();
if(varPat[i] instanceof QualifiedNamePattern){
QualifiedNamePattern qualName = (QualifiedNamePattern) varPat[i];
String name = qualName.getName();
// Binding occurrence of this variable?
if(isBinding[i] || qualName.isAnonymous() || env.getVariable(name) == null || env.getVariable(name).getValue() == null){
if(isSetVar(i)){
varGen[i] = new SubSetGenerator(elements, ctx);
} else {
if(elements.size() == 0)
return false;
varGen[i] = new SingleElementIterator(elements, ctx);
}
} else {
// Variable has been set before, use its dynamic type to distinguish set variables.
IValue val = env.getVariable(name).getValue();
if (val != null) {
if (val != null && val.getType().isSet()){
isSetVar[i] = true;
if(elements.equals(val)){
varGen[i] = new SingleIValueIterator(val);
return true;
}
return false;
}
if (elements.contains(val)){
varGen[i] = new SingleIValueIterator(val);
} else {
return false;
}
}
}
return true;
}
if(isSetVar(i)){
varGen[i] = new SubSetGenerator(elements, ctx);
return true;
}
if(elements.size() == 0)
return false;
varGen[i] = new SingleElementIterator(elements, ctx);
return true;
}
@Override
public boolean next(){
checkInitialized();
if(!hasNext)
return false;
if(firstMatch){
firstMatch = hasNext = false;
if(nVar == 0){
return fixedSetElements.isEqual(setSubject);
}
if(!fixedSetElements.isSubsetOf(setSubject)){
return false;
}
if(patternSize == 1){
if(isSetVar(0) || availableSetElements.size() == 1) {
IValue elem ;
if(isSetVar(0)){
// Var #i is a set variable: should match all elements
varVal[0] = (ISet) availableSetElements;
elem = availableSetElements;
}
else {
// Var #i is not a set variable.
if(availableSetElements.getType().isSet()){
// Var #i should match single element in elements
ISet set = (ISet) availableSetElements;
assert set.size() == 1;
varVal[0] = elem = set.iterator().next();
} else {
varVal[0] = elem = availableSetElements;
//varVal[i] = ctx.getValueFactory().set(elem.getType()).insert(elem); // TODO: Should this be a set?
}
}
varPat[0].initMatch(ResultFactory.makeResult(elem.getType(), elem, ctx));
hasNext = varPat[0].hasNext();
}
}
currentVar = 0;
if(!makeGen(currentVar, availableSetElements)){
return false;
}
} else {
currentVar = nVar - 1;
}
hasNext = true;
if(debug)System.err.println("\nStart assigning Vars for " + this + ":= " + subject);
if (patternSize == 1 && (isSetVar(0) || availableSetElements.size() == 1)) {
if (varPat[0].hasNext() && varPat[0].next()) {
return true;
}
return false;
}
main:
do {
if (ctx.isInterrupted()) {
throw new InterruptException(ctx.getStackTrace(), ctx.getCurrentAST().getLocation());
}
if(debug)System.err.println("\n=== MAIN: Pattern = " + this + ":= " + subject + "\ncurrentVar[" + currentVar + "]=" + varName[currentVar]);
if(debug)printVars();
IValue v = null;
boolean b = false;
if(isNested[currentVar]){
if(varPat[currentVar].hasNext()){
b = varPat[currentVar].next();
if(b)
v = varVal[currentVar];
} else if(varGen[currentVar].hasNext()){
v = (IValue) varGen[currentVar].next();
Result<IValue> r = ResultFactory.makeResult(v.getType(), v, ctx);
varPat[currentVar].initMatch(r);
b = varPat[currentVar].next();
if(b)
varVal[currentVar] = v;
}
} else if(isSetVar[currentVar]){
if(varGen[currentVar].hasNext()){
v = (IValue) varGen[currentVar].next();
Result<IValue> r = ResultFactory.makeResult(v.getType().lub(setSubjectType), v, ctx);
varPat[currentVar].initMatch(r);
b = varPat[currentVar].next();
if(b){
varVal[currentVar] = v;
ctx.getCurrentEnvt().storeVariable(varName[currentVar], r);
if(debug)System.err.println("Store in " + varName[currentVar] + ": " + r + " / " + v + " / " + v.getType() + " / " +
ctx.getCurrentEnvt().getVariable(varName[currentVar]).getType());
}
}
} else if(isBinding[currentVar]){
if(varGen[currentVar].hasNext()){
v = (IValue) varGen[currentVar].next();
Result<IValue> r = ResultFactory.makeResult(v.getType(), v, ctx);
varPat[currentVar].initMatch(r);
b = varPat[currentVar].next();
if(b){
varVal[currentVar] = v;
ctx.getCurrentEnvt().storeVariable(varName[currentVar], r);
if(debug)System.err.println("Store in " + varName[currentVar] + ": " + r + " / " + v + " / " + v.getType() + " / " +
ctx.getCurrentEnvt().getVariable(varName[currentVar]).getType());
}
}
} else if(varPat[currentVar] instanceof QualifiedNamePattern && ((QualifiedNamePattern)varPat[currentVar] ).isAnonymous()){
if(varGen[currentVar].hasNext()){
v = (IValue) varGen[currentVar].next();
Result<IValue> r = ResultFactory.makeResult(v.getType(), v, ctx);
varPat[currentVar].initMatch(r);
b = varPat[currentVar].next();
if(b){
varVal[currentVar] = v;
}
}
} else if(!isBinding[currentVar] && (varPat[currentVar] instanceof QualifiedNamePattern || varPat[currentVar] instanceof TypedVariablePattern) && varGen[currentVar].hasNext()){
v = (IValue) varGen[currentVar].next();
Result<IValue> r = ResultFactory.makeResult(v.getType(), v, ctx);
varPat[currentVar].initMatch(r);
b = varPat[currentVar].next();
System.err.println("Try match " + varName[currentVar] + ": " + r + " / " + v + " / " + v.getType());
if(b){
varVal[currentVar] = v;
System.err.println("Matches " + varName[currentVar] + ": " + r + " / " + v + " / " + v.getType());
}
} else {
System.err.println("CANNOT HANDLE THIS 2");
}
//if(debug) System.err.println("currentVar[" + currentVar + "] = " + varName[currentVar] + "; v = " + v + "; type: " + v.getType() + "; matchPatternElement = " + b);
if(b){
currentVar++;
ISet avail = available();
if(currentVar <= nVar - 1){
if(!makeGen(currentVar, avail)){
varGen[currentVar] = null;
currentVar--;
}
continue main;
} else {
if(!avail.isEmpty()){
if(debug)System.err.println("nomatch: currentVar = " + currentVar + " avail = " + available());
currentVar--;
continue main;
}
hasNext = unexploredAlternatives();
if(debug) System.err.println("currentVar > nVar -1\n" + this + ":= " + subject + " returns true, hasNext = " + hasNext + ", available = " + available());
return true;
}
}
if(!varGen[currentVar].hasNext()){
varGen[currentVar] = null;
currentVar--;
}
if(debug)System.err.println("currentVar becomes: " + currentVar);
continue main;
} while(currentVar >= 0 && currentVar < nVar);
if(currentVar < 0){
hasNext = false;
if(debug) System.err.println("Pattern " + this + " := " + subject + " returns false");
return false;
}
hasNext = unexploredAlternatives();
if(debug) System.err.println("Pattern " + this + " := " + subject + " returns true, hasNext = " + hasNext + ", available = " + available());
return available().isEmpty();
}
@Override
public String toString(){
StringBuilder res = new StringBuilder();
res.append("{");
String sep = "";
for (IBooleanResult mp : patternChildren){
res.append(sep);
sep = ", ";
res.append(mp.toString());
}
res.append("}");
return res.toString();
}
}