package edu.ucsd.arcum.interpreter.ast.expressions;
import static edu.ucsd.arcum.ArcumPlugin.DEBUG;
import java.util.List;
import java.util.Set;
import com.google.common.collect.Lists;
import com.google.common.collect.Sets;
import edu.ucsd.arcum.exceptions.ArcumError;
import edu.ucsd.arcum.exceptions.SourceLocation;
import edu.ucsd.arcum.interpreter.ast.TraitSignature;
import edu.ucsd.arcum.interpreter.query.Entity;
import edu.ucsd.arcum.interpreter.query.EntityType;
import edu.ucsd.arcum.interpreter.query.IEntityLookup;
import edu.ucsd.arcum.interpreter.satisfier.BindingMap;
import edu.ucsd.arcum.interpreter.satisfier.BindingsSet;
import edu.ucsd.arcum.interpreter.satisfier.Satisfier;
import edu.ucsd.arcum.util.ListUtil;
import edu.ucsd.arcum.util.StringUtil;
import edu.ucsd.arcum.util.SystemUtil;
// An expression written in a functional style: for example, predicate
// expressions or accessor expressions.
// E.g.:
// hasField(fromType, edge)
// and:
// toType == TypeOf(edge)
// In the last case, the TypeOf(edge) is an accessor, and not a predicate, and
// will have a bound variable that is non-null.
public class FunctionalExpression extends ConstraintExpression
{
private IFunction function;
private List<ConstraintExpression> args;
// valid after type checking
private List<EntityType> parameterTypes;
public FunctionalExpression(SourceLocation location, IFunction function,
List<ConstraintExpression> args)
{
super(location);
this.function = function;
this.args = args;
}
@Override public String toString() {
StringBuilder buff = new StringBuilder();
buff.append(function.toString());
buff.append("(");
StringUtil.separate(buff, args, ", ");
buff.append(")");
return buff.toString();
}
@Override public Set<String> getArcumVariableReferences() {
Set<String> result = Sets.newHashSet();
for (ConstraintExpression arg : args) {
result.addAll(arg.getArcumVariableReferences());
}
return result;
}
public BindingsSet evaluate(BindingMap in, IEntityLookup lookup, Satisfier satisfier,
boolean matchingMode)
{
if (DEBUG) {
SystemUtil.getErrStream().printf("function=%s%nargs=%s%nparameterTypes=%s%n",
function, args, parameterTypes);
}
if (args.size() != parameterTypes.size()) {
ArcumError.fatalError("Internal error: need to check arguments first");
}
List<List<Object>> allArgCombinations = Lists.newArrayList();
for (int i = 0; i < args.size(); ++i) {
ConstraintExpression arg = args.get(i);
EntityType type = parameterTypes.get(i);
BindingsSet set = satisfier.immediateMatchingSat(arg, type, in);
List<Object> ithArgPossibilities = Lists.newArrayList();
for (BindingMap theta : set) {
Object entity = theta.getResult();
entity = Entity.canonicalizeRepresentation(entity);
ithArgPossibilities.add(entity);
}
allArgCombinations.add(ithArgPossibilities);
}
BindingsSet result = BindingsSet.newEmptySet();
List<List<Object>> realAgsList = ListUtil.crossProduct(allArgCombinations);
for (List<Object> realArgs : realAgsList) {
BindingsSet set = function.evaluate(realArgs, lookup, in, matchingMode, getPosition());
// "false" results are the empty set, so they will not add to the
// result here
for (BindingMap theta : set) {
result.addEntry(theta);
}
}
return result;
}
@Override protected void doCheckUserDefinedPredicates(List<TraitSignature> tupleSets,
Set<String> varsInScope)
{
this.parameterTypes = function.checkArgs(getPosition(), tupleSets, args.size());
// EXAMPLE: This was a bug in the software! The for-loop below was forgotten about!
for (ConstraintExpression arg : args) {
arg.doCheckUserDefinedPredicates(tupleSets, varsInScope);
}
}
@Override public Set<String> findAllTraitDependencies() {
Set<String> result = flattenFindAllTraitDependencies(args);
result.add(function.getName());
return result;
}
@Override public Set<String> findNonMonotonicDependencies() {
return Sets.newHashSet();
}
}