package org.scribble.visit;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import org.scribble.ast.InteractionSeq;
import org.scribble.ast.ProtocolBlock;
import org.scribble.ast.ProtocolDecl;
import org.scribble.ast.Recursion;
import org.scribble.ast.ScribNode;
import org.scribble.ast.global.GProtocolBlock;
import org.scribble.ast.global.GRecursion;
import org.scribble.ast.local.LProtocolBlock;
import org.scribble.ast.local.LRecursion;
import org.scribble.ast.name.simple.RecVarNode;
import org.scribble.del.ProtocolDefDel;
import org.scribble.main.Job;
import org.scribble.main.ScribbleException;
import org.scribble.sesstype.kind.Global;
import org.scribble.sesstype.kind.ProtocolKind;
import org.scribble.sesstype.name.RecVar;
import org.scribble.visit.env.UnfoldingEnv;
// Statically unfolds unguarded recursions and continues "directly under" choices
// N.B. cf. UnfoldingVisitor "lazily" unfolds every rec once on demand
public class InlinedProtocolUnfolder extends InlinedProtocolVisitor<UnfoldingEnv>
{
public static final String DUMMY_REC_LABEL = "__";
// NOTE: assumes unique recvars up to this point, i.e. no recvar shadowing (treated internally by protocoldef-inlining) -- unfolding of unguardeds will, however, result in "unfolding shadowing"
private Map<RecVar, Recursion<?>> recs = new HashMap<>(); // Could parameterise recvars to be global/local
private Set<RecVar> recsToUnfold = new HashSet<>();
public InlinedProtocolUnfolder(Job job)
{
super(job);
}
public boolean shouldUnfoldForUnguardedRec(RecVar rv)
{
return this.recsToUnfold.contains(rv);
}
// Unguarded continues directly under choices need to be unfolded to make current graph building work (continue side effects GraphBuilder state to re-set entry to rec state -- which makes output dependent on choice block order, and can attach subsequent choice paths onto the rec state instead of the original choice state)
// Maybe fix this problem inside graph building rather than unfolding here (this unfolding is conservative -- not always needed for every unguarded continue) -- depends if it helps any other inlined passes?
public boolean isContinueUnguarded(RecVar rv)
{
return peekEnv().shouldUnfold();
}
@Override
protected UnfoldingEnv makeRootProtocolDeclEnv(ProtocolDecl<? extends ProtocolKind> pd)
{
return new UnfoldingEnv();
}
@Override
public ScribNode visit(ScribNode parent, ScribNode child) throws ScribbleException
{
if (child instanceof Recursion)
{
if (peekEnv().shouldUnfold())
{
enter(parent, child);
ScribNode visited = unfold((Recursion<?>) child);
return leave(parent, child, visited);
}
else
{
return super.visit(parent, child);
}
}
else
{
ScribNode visited = super.visit(parent, child);
if (visited instanceof ProtocolDecl<?>)
{
ProtocolDecl<?> pd = (ProtocolDecl<?>) visited;
this.job.debugPrintln("\n[DEBUG] Unfolded inlined protocol "
+ pd.getFullMemberName(this.job.getContext().getModule(getModuleContext().root)) + ":\n"
+ ((ProtocolDefDel) pd.def.del()).getInlinedProtocolDef());
}
return visited;
}
}
// Not doing the actual unfolding here: replace the rec with a dummy (i.e. alpha the original rec to another unused lab) and will do any actual unfolding inside the recursive child accept (upon Continue)
private <K extends ProtocolKind> ScribNode unfold(Recursion<K> rec) throws ScribbleException
{
RecVar rv = rec.recvar.toName();
ProtocolBlock<K> pb = rec.block;
// Clone unnecessary: can visit the original block, apart from any continues to substitute (done in InteractionSeqDel)
this.recsToUnfold.add(rv);
/*RecVarNode dummy = (RecVarNode) AstFactoryImpl.FACTORY.SimpleNameNode(RecVarKind.KIND, DUMMY_REC_LABEL);
//RecVarNode dummy = rec.recvar.clone();
ScribNode n = rec.reconstruct(dummy, ScribUtil.checkNodeClassEquality(pb, pb.accept(this))); // Returning a rec because it needs to go into the InteractionSeq
// reconstruct makes sense here, actually reconstructing this rec with new label but same block (and keep the same del etc)*/
InteractionSeq<K> seq = pb.getInteractionSeq();
ScribNode n = seq.accept(this);
this.recsToUnfold.remove(rv);
return n;
}
@Override
protected void inlinedEnter(ScribNode parent, ScribNode child) throws ScribbleException
{
super.inlinedEnter(parent, child);
child.del().enterInlinedProtocolUnfolding(parent, child, this);
}
@Override
protected ScribNode inlinedLeave(ScribNode parent, ScribNode child, ScribNode visited) throws ScribbleException
{
visited = visited.del().leaveInlinedProtocolUnfolding(parent, child, this, visited);
return super.inlinedLeave(parent, child, visited);
}
public Recursion<?> getRecVar(RecVar recvar)
{
return this.recs.get(recvar);
}
// Maybe possible to revise this algorithm to handle shadowed recs, but currently requires unique recvar names
public void setRecVar(RecVar recvar, Recursion<?> rec) throws ScribbleException
{
ProtocolBlock<?> block = (ProtocolBlock<?>) rec.getBlock().accept(this);
RecVarNode rv = rec.recvar.clone();
Recursion<?> unfolded;
if (rec.getKind() == Global.KIND)
{
unfolded = ((GRecursion) rec).reconstruct(rv, (GProtocolBlock) block);
}
else
{
unfolded = ((LRecursion) rec).reconstruct(rv, (LProtocolBlock) block);
}
this.recs.put(recvar, unfolded);
}
public void removeRecVar(RecVar recvar)
{
this.recs.remove(recvar);
}
}