package org.overture.pog.contexts;
import java.util.Collection;
import java.util.LinkedList;
import java.util.List;
import org.overture.ast.analysis.AnalysisException;
import org.overture.ast.definitions.AExplicitFunctionDefinition;
import org.overture.ast.definitions.AExplicitOperationDefinition;
import org.overture.ast.definitions.AImplicitOperationDefinition;
import org.overture.ast.definitions.AInstanceVariableDefinition;
import org.overture.ast.definitions.PDefinition;
import org.overture.ast.definitions.SOperationDefinitionBase;
import org.overture.ast.expressions.AApplyExp;
import org.overture.ast.expressions.ABooleanConstExp;
import org.overture.ast.expressions.AForAllExp;
import org.overture.ast.expressions.APostOpExp;
import org.overture.ast.expressions.AVariableExp;
import org.overture.ast.expressions.PExp;
import org.overture.ast.factory.AstExpressionFactory;
import org.overture.ast.intf.lex.ILexNameToken;
import org.overture.ast.lex.LexBooleanToken;
import org.overture.ast.lex.VDMToken;
import org.overture.ast.patterns.AIdentifierPattern;
import org.overture.ast.patterns.ATypeMultipleBind;
import org.overture.ast.patterns.PMultipleBind;
import org.overture.ast.patterns.PPattern;
import org.overture.ast.statements.ACallStm;
import org.overture.ast.statements.AExternalClause;
import org.overture.pog.pub.IPOContext;
import org.overture.pog.pub.IPOContextStack;
import org.overture.pog.pub.IPogAssistantFactory;
import org.overture.pog.utility.Substitution;
import org.overture.pog.visitors.IVariableSubVisitor;
public class OpPostConditionContext extends StatefulContext implements
IPOContext
{
AForAllExp forall_exp;
PExp pred;
IVariableSubVisitor visitor;
public OpPostConditionContext(AExplicitFunctionDefinition postDef,
ACallStm stm, SOperationDefinitionBase calledOp,
IPogAssistantFactory af, IPOContextStack ctxt)
{
super(ctxt);
this.gen = ctxt.getGenerator();
this.subs = new LinkedList<Substitution>();
this.forall_exp = getChangedVarsExp(postDef, calledOp);
PExp inv = buildInvExp(calledOp, af);
this.pred = spellCondition(postDef, af, stm.getArgs(), inv);
this.visitor = af.getVarSubVisitor();
}
private PExp buildInvExp(SOperationDefinitionBase calledOp,
IPogAssistantFactory af)
{
List<PExp> invariants = new LinkedList<PExp>();
if (calledOp.getClassDefinition() != null)
{
try
{
invariants = calledOp.getClassDefinition().apply(af.getInvExpGetVisitor());
if (invariants.size() > 0)
{
PExp inv = invariants.get(0).clone();
for (int i = 1; i < invariants.size(); i++)
{
inv = AstExpressionFactory.newAAndBooleanBinaryExp(inv, invariants.get(i).clone());
}
return inv;
} else
{
return null;
}
} catch (AnalysisException e)
{
e.printStackTrace();
}
}
return null;
}
public OpPostConditionContext(AExplicitFunctionDefinition postDef,
AApplyExp exp, SOperationDefinitionBase calledOp,
IPogAssistantFactory af, IPOContextStack ctxt)
{
super(ctxt);
this.visitor = af.getVarSubVisitor();
this.gen = ctxt.getGenerator();
this.subs = new LinkedList<Substitution>();
this.forall_exp = getChangedVarsExp(postDef, calledOp);
PExp inv = buildInvExp(calledOp, af);
this.pred = spellCondition(postDef, af, exp.getArgs(), inv);
}
@Override
public String toString()
{
return forall_exp.toString() + pred.toString();
}
private AForAllExp getChangedVarsExp(AExplicitFunctionDefinition postDef,
SOperationDefinitionBase calledOp)
{
AForAllExp r = new AForAllExp();
List<PMultipleBind> binds = new LinkedList<PMultipleBind>();
if (calledOp instanceof AExplicitOperationDefinition)
{
refreshAllState(calledOp, binds);
}
if (calledOp instanceof AImplicitOperationDefinition)
{
AImplicitOperationDefinition implicitOp = (AImplicitOperationDefinition) calledOp;
if (implicitOp.getExternals().size() > 0)
{
for (AExternalClause external : implicitOp.getExternals())
{
if (external.getMode().getType().equals(VDMToken.WRITE))
{
binds.addAll(introduceFreshVars(external.getIdentifiers(), getStateVars(calledOp)));
}
}
} else
{
refreshAllState(calledOp, binds);
}
}
r.setBindList(binds);
return r;
}
private List<AInstanceVariableDefinition> getStateVars(
SOperationDefinitionBase calledOp)
{
List<PDefinition> defs;
if (calledOp.getClassDefinition() != null)
{
defs = calledOp.getClassDefinition().getDefinitions();
} else
{
if (calledOp.getState() != null)
{
defs = calledOp.getState().getStateDefs();
} else
{
return new LinkedList<AInstanceVariableDefinition>();
}
}
List<AInstanceVariableDefinition> r = new LinkedList<AInstanceVariableDefinition>();
for (PDefinition p : defs)
{
if (p instanceof AInstanceVariableDefinition)
{
r.add((AInstanceVariableDefinition) p);
}
}
return r;
}
private void refreshAllState(SOperationDefinitionBase calledOp,
List<PMultipleBind> binds)
{
List<AInstanceVariableDefinition> defs = getStateVars(calledOp);
for (AInstanceVariableDefinition p : defs)
{
binds.add(introduceFreshVar(p));
}
}
private Collection<? extends PMultipleBind> introduceFreshVars(
LinkedList<ILexNameToken> identifiers,
List<AInstanceVariableDefinition> defs)
{
List<PMultipleBind> r = new LinkedList<PMultipleBind>();
for (ILexNameToken ilt : identifiers)
{
for (AInstanceVariableDefinition d : defs)
{
if (ilt.equals(d.getName()))
{
r.add(introduceFreshVar(d));
}
}
}
return r;
}
private PMultipleBind introduceFreshVar(AInstanceVariableDefinition var)
{
ATypeMultipleBind r = new ATypeMultipleBind();
List<PPattern> pats = new LinkedList<PPattern>();
AIdentifierPattern idPat = new AIdentifierPattern();
idPat.setName(gen.getUnique(var.getName().getName()));
pats.add(idPat);
r.setPlist(pats);
r.setType(var.getType().clone());
AVariableExp newVar = new AVariableExp();
newVar.setName(idPat.getName().clone());
newVar.setOriginal(idPat.getName().getFullName());
Substitution sub = new Substitution(var.getName().clone(), newVar);
subs.add(sub);
AVariableExp old_var = last_vars.get(var.getName());
if (old_var != null)
{
Substitution sub_old = new Substitution(var.getOldname().toString(), old_var);
subs.add(sub_old);
} else
{
AVariableExp var_exp = new AVariableExp();
var_exp.setName(var.getName().clone());
var_exp.setType(var.getType().clone());
var_exp.setOriginal(var.getName().getName());
Substitution sub_old = new Substitution(var.getOldname().toString(), var_exp);
subs.add(sub_old);
}
last_vars.put(var.getName(), newVar);
return r;
}
@Override
public String getContext()
{
return null;
}
@Override
public PExp getContextNode(PExp stitch)
{
try
{
if (first)
{
first = false;
}
PExp implies_exp = AstExpressionFactory.newAImpliesBooleanBinaryExp(pred.clone(), stitch.clone());
for (Substitution sub : subs)
{
implies_exp = implies_exp.clone().apply(visitor, sub);
}
if (forall_exp.getBindList().size() > 0)
{
forall_exp.setPredicate(implies_exp);
return forall_exp.clone();
} else
{
return implies_exp.clone();
}
} catch (AnalysisException e)
{
//consider handling of exceptions inside final context construction
e.printStackTrace();
}
return null;
}
private PExp spellCondition(AExplicitFunctionDefinition def,
IPogAssistantFactory af, List<PExp> args, PExp invariant)
{
PExp post_exp;
if (def == null)
{
if (invariant == null)
{
// no postcondition: true
ABooleanConstExp r = new ABooleanConstExp();
r.setValue(new LexBooleanToken(true, null));
return r;
} else
{
return invariant;
}
} else
{
post_exp = def.getBody();
}
if (invariant != null)
{
post_exp = AstExpressionFactory.newAAndBooleanBinaryExp(post_exp, invariant);
}
List<Substitution> subs = new LinkedList<Substitution>();
for (int i = 0; i < args.size(); i++)
{
PPattern orig = def.getParamPatternList().get(0).get(i);
ILexNameToken origName = af.createPPatternAssistant().getAllVariableNames(orig).get(0).clone();
PExp new_exp = args.get(0).clone();
subs.add(new Substitution(origName, new_exp));
}
return rewritePost(post_exp, subs, af);
}
private PExp rewritePost(PExp post_exp, List<Substitution> subs,
IPogAssistantFactory af)
{
if (post_exp instanceof APostOpExp)
{
// post-expression bodies are wrapped in a PostOpExp for some reason...
post_exp = ((APostOpExp) post_exp).getPostexpression();
}
for (Substitution sub : subs)
{
try
{
post_exp = post_exp.clone().apply(af.getVarSubVisitor(), sub);
} catch (AnalysisException e)
{
e.printStackTrace();
}
}
return post_exp;
}
}