/** * <copyright> * </copyright> * * $Id$ */ package tefkat.engine.runtime.impl; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.Map; import org.eclipse.emf.common.notify.Notification; import org.eclipse.emf.common.notify.NotificationChain; import org.eclipse.emf.common.util.EList; import org.eclipse.emf.ecore.EClass; import org.eclipse.emf.ecore.InternalEObject; import org.eclipse.emf.ecore.impl.ENotificationImpl; import org.eclipse.emf.ecore.util.EObjectContainmentEList; import org.eclipse.emf.ecore.util.InternalEList; import tefkat.engine.runtime.Binding; import tefkat.engine.runtime.BindingPair; import tefkat.engine.runtime.Context; import tefkat.engine.runtime.Function; import tefkat.engine.runtime.NotGroundException; import tefkat.engine.runtime.ResolutionException; import tefkat.engine.runtime.Term; import tefkat.engine.runtime.Tree; import tefkat.engine.runtime.TreeListener; import tefkat.engine.runtime.WrappedVar; import tefkat.engine.runtime.Expression; import tefkat.engine.runtime.PatternDefn; import tefkat.engine.runtime.PatternUse; import tefkat.engine.runtime.RuntimePackage; import tefkat.engine.runtime.Var; import tefkat.engine.runtime.VarUse; /** * <!-- begin-user-doc --> * An implementation of the model object '<em><b>Pattern Use</b></em>'. * <!-- end-user-doc --> * <p> * The following features are implemented: * <ul> * <li>{@link tefkat.engine.runtime.impl.PatternUseImpl#getDefn <em>Defn</em>}</li> * <li>{@link tefkat.engine.runtime.impl.PatternUseImpl#getArg <em>Arg</em>}</li> * </ul> * </p> * * @generated */ public class PatternUseImpl extends SimpleTermImpl implements PatternUse { /** * <!-- begin-user-doc --> * <!-- end-user-doc --> * @generated */ public static final String copyright = "Copyright michael lawley 2004"; /** * The cached value of the '{@link #getDefn() <em>Defn</em>}' reference. * <!-- begin-user-doc --> * <!-- end-user-doc --> * @see #getDefn() * @generated * @ordered */ protected PatternDefn defn = null; /** * The cached value of the '{@link #getArg() <em>Arg</em>}' containment reference list. * <!-- begin-user-doc --> * <!-- end-user-doc --> * @see #getArg() * @generated * @ordered */ protected EList arg = null; /** * <!-- begin-user-doc --> * <!-- end-user-doc --> * @generated */ protected PatternUseImpl() { super(); } /** * <!-- begin-user-doc --> * <!-- end-user-doc --> * @generated */ protected EClass eStaticClass() { return RuntimePackage.Literals.PATTERN_USE; } /** * <!-- begin-user-doc --> * <!-- end-user-doc --> * @generated */ public PatternDefn getDefn() { if (defn != null && defn.eIsProxy()) { InternalEObject oldDefn = (InternalEObject)defn; defn = (PatternDefn)eResolveProxy(oldDefn); if (defn != oldDefn) { if (eNotificationRequired()) eNotify(new ENotificationImpl(this, Notification.RESOLVE, RuntimePackage.PATTERN_USE__DEFN, oldDefn, defn)); } } return defn; } /** * <!-- begin-user-doc --> * <!-- end-user-doc --> * @generated */ public PatternDefn basicGetDefn() { return defn; } /** * <!-- begin-user-doc --> * <!-- end-user-doc --> * @generated */ public void setDefn(PatternDefn newDefn) { PatternDefn oldDefn = defn; defn = newDefn; if (eNotificationRequired()) eNotify(new ENotificationImpl(this, Notification.SET, RuntimePackage.PATTERN_USE__DEFN, oldDefn, defn)); } /** * <!-- begin-user-doc --> * <!-- end-user-doc --> * @generated */ public EList getArg() { if (arg == null) { arg = new EObjectContainmentEList(Expression.class, this, RuntimePackage.PATTERN_USE__ARG); } return arg; } /** * <!-- begin-user-doc --> * <!-- end-user-doc --> * @generated */ public NotificationChain eInverseRemove(InternalEObject otherEnd, int featureID, NotificationChain msgs) { switch (featureID) { case RuntimePackage.PATTERN_USE__ARG: return ((InternalEList)getArg()).basicRemove(otherEnd, msgs); } return super.eInverseRemove(otherEnd, featureID, msgs); } /** * <!-- begin-user-doc --> * <!-- end-user-doc --> * @generated */ public Object eGet(int featureID, boolean resolve, boolean coreType) { switch (featureID) { case RuntimePackage.PATTERN_USE__DEFN: if (resolve) return getDefn(); return basicGetDefn(); case RuntimePackage.PATTERN_USE__ARG: return getArg(); } return super.eGet(featureID, resolve, coreType); } /** * <!-- begin-user-doc --> * <!-- end-user-doc --> * @generated */ public void eSet(int featureID, Object newValue) { switch (featureID) { case RuntimePackage.PATTERN_USE__DEFN: setDefn((PatternDefn)newValue); return; case RuntimePackage.PATTERN_USE__ARG: getArg().clear(); getArg().addAll((Collection)newValue); return; } super.eSet(featureID, newValue); } /** * <!-- begin-user-doc --> * <!-- end-user-doc --> * @generated */ public void eUnset(int featureID) { switch (featureID) { case RuntimePackage.PATTERN_USE__DEFN: setDefn((PatternDefn)null); return; case RuntimePackage.PATTERN_USE__ARG: getArg().clear(); return; } super.eUnset(featureID); } /** * <!-- begin-user-doc --> * <!-- end-user-doc --> * @generated */ public boolean eIsSet(int featureID) { switch (featureID) { case RuntimePackage.PATTERN_USE__DEFN: return defn != null; case RuntimePackage.PATTERN_USE__ARG: return arg != null && !arg.isEmpty(); } return super.eIsSet(featureID); } public void match(final Context context) throws ResolutionException, NotGroundException { final PatternDefn pDefn = getDefn(); final List args = getArg(); if (null == pDefn) { // TODO fix this println hack String mesg = ""; for (int i = 0; i < args.size(); i++) { String str; try { List values = ((Expression) args.get(i)).eval(context); str = "["; for (Iterator itr = values.iterator(); itr.hasNext(); ) { Object o = itr.next(); str += (o instanceof BindingPair ? ((BindingPair) o).getValue() : o); if (itr.hasNext()) { str += ", "; } } str += "]"; } catch (NotGroundException e) { str = "(" + args.get(i) + " not sufficiently ground)"; } mesg += (i > 0 ? " " : "") + str; } context.info(mesg); context.createBranch(); return; } final List pDefVars = pDefn.getParameterVar(); // Check that the sizes of formals and actuals matches, then add the bound variables, pairwise, // to the initial binding to be passed to the tree resolution of the pattern. if (args.size() != pDefVars.size()) { context.error("Invalid arity for pattern " + pDefn.getName() + ": " + args.size()); } // handle arity-zero (no args) pattern if (pDefVars.size() == 0) { incrementalResolvePatternDefn(context, pDefVars, args, null, null); } else { // final Binding context = tree.getContext(); Function resolver; resolver = new PatternCall(pDefVars) { void invoke(Binding callContext, Binding parameterContext) throws ResolutionException { incrementalResolvePatternDefn(context, pDefVars, args, callContext, parameterContext); } }; new ExprExpander(context, resolver, context.getBindings(), args, false); } } public void ensure(Context context) throws ResolutionException, NotGroundException { } private void incrementalResolvePatternDefn(final Context context, final List pDefVars, final List args, final Binding callContext, final Binding parameterContext) throws ResolutionException { Tree resultTree = context.getResultTree(getDefn().getTerm(), parameterContext); if (!resultTree.isCompleted()) { // Register listener for any new solutions resultTree.addTreeListener(new TreeListener() { public void solution(Binding answer) throws ResolutionException { Binding unifier = createOutputBinding(context, callContext, pDefVars, args, answer); context.createBranch(unifier); } public void completed(Tree theTree) { if (!theTree.isSuccess()) { context.fail(); } } public void floundered(Tree theTree) { } }); } //System.err.println("\t" + resultTree.isSuccess()); // TODO delete if (resultTree.isSuccess()) { // Process any existing solutions for (Iterator itr = resultTree.getAnswers().iterator(); itr.hasNext();) { Binding answer = (Binding) itr.next(); Binding unifier = createOutputBinding(context, callContext, pDefVars, args, answer); context.createBranch(unifier); } } } static private Binding createOutputBinding(final Context context, final Binding callContext, final List formals, final List actuals, Binding solution) throws ResolutionException { Binding unifier = new Binding(callContext); Map linkMap = new HashMap(); // System.err.println(" s " + currentSolution); // TODO delete for (int j = 0; j < actuals.size(); j++) { Expression argExpr = (Expression) actuals.get(j); if (argExpr instanceof VarUse) { final Var var = ((VarUse) argExpr).getVar(); final Object val = context.lookup(var); Object outVal = solution.lookup((Var) formals.get(j)); // Handle case where unbound input params are unified // but not grounded within the pattern. // For example: p(A, B) call to // PATTERN p(X, Y) WHERE X = Y; // should bind A to B, but not to X or Y. // if (outVal instanceof WrappedVar) { if (linkMap.containsKey(outVal)) { outVal = linkMap.get(outVal); } else { WrappedVar wVar = new WrappedVar(var); wVar.setExtent(((WrappedVar) outVal).getExtent()); wVar.setType(((WrappedVar) outVal).getType(), ((WrappedVar) outVal).isExact()); linkMap.put(outVal, wVar); outVal = wVar; } } if (null == val) { // We only add a new binding if argExpr is not already // bound, otherwise we end up with multiple identical bindings // for the same Var. unifier.add(var, outVal); } else if (val instanceof WrappedVar) { Binding.bindWrappedVar(unifier, (WrappedVar) val, outVal); } else if (!val.equals(outVal)) { // System.err.println("Arg:\t" + argExpr); // System.err.println("actual:\t" + val); // System.err.println("formal:\t" + formals.get(j)); // System.err.println("result:\t" + outVal); context.error("conflicting pattern arg and result: " + val + "\t" + outVal); } } } return unifier; } public String toString() { final String name = null == getDefn() ? "println" : getDefn().getName(); StringBuffer result = new StringBuffer(name); result.append('('); for (Iterator itr = getArg().iterator(); itr.hasNext(); ) { Expression expr = (Expression) itr.next(); result.append(expr); if (itr.hasNext()) { result.append(", "); } } result.append(')'); return result.toString(); } } //PatternUseImpl abstract class PatternCall implements Function { private final List vars; PatternCall(List vars) { this.vars = vars; } /** * @param unifier a set of variable bindings that needs to be propagated to the answer Binding * @param actuals actual values or WrappedVaes for the pDefVars for a call */ public Object call(Context ctxt, Binding unifier, Object[] actuals) throws ResolutionException { Binding parameterUnifier = new Binding(); Binding callUnifier = new Binding(unifier); // System.err.println(pDefn.getName()); // System.err.println(" C: " + context); // System.err.println(" P: " + Arrays.asList(actuals)); // Example calls to p(X,Y,Z) that we must handle: // 1. Foo A AND p(A, A, A) -> {}, [A/Foo, A/Foo, A/Foo] // 2. Foo A AND p(A, A.a, A.b) -> {A -> 1:Foo}, [1:Foo, 2, 3] // // 1. varContext = {A -> W(X/Foo)} // parameterContext = {X -> W(X/Foo), Y -> W(X/Foo), Z -> W(X/Foo)} // // 2. varContext = {A -> 1:Foo} // parameterContext = {X -> 1:Foo, Y -> 2, Z -> 3} // for (int i = 0; i < actuals.length; i++) { Var pVar = (Var) vars.get(i); Object varValue; if (actuals[i] instanceof WrappedVar) { WrappedVar wrappedActual = (WrappedVar) actuals[i]; Var var = wrappedActual.getVar(); varValue = callUnifier.lookup(var); if (null == varValue) { WrappedVar wvar = new WrappedVar(pVar); wvar.setExtent(wrappedActual.getExtent()); wvar.setType(wrappedActual.getType(), wrappedActual.isExact()); callUnifier.add(var, wvar); varValue = wvar; } } else { varValue = actuals[i]; } if (null != varValue) { parameterUnifier.add(pVar, varValue); } // System.err.println(" " + i + " " + pVar + " = " + varValue); } // System.err.println(" V: " + varContext); invoke(callUnifier, parameterUnifier); return Collections.EMPTY_LIST; } abstract void invoke(Binding callUnifier, Binding parameterUnifier) throws ResolutionException; }