package org.scribble.ast.global;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Set;
import org.antlr.runtime.tree.CommonTree;
import org.scribble.ast.AstFactoryImpl;
import org.scribble.ast.ConnectionAction;
import org.scribble.ast.Do;
import org.scribble.ast.MessageTransfer;
import org.scribble.ast.ProtocolBlock;
import org.scribble.ast.Recursion;
import org.scribble.ast.local.LChoice;
import org.scribble.ast.local.LContinue;
import org.scribble.ast.local.LInteractionNode;
import org.scribble.ast.local.LProtocolBlock;
import org.scribble.ast.local.LRecursion;
import org.scribble.ast.name.simple.RecVarNode;
import org.scribble.del.ScribDel;
import org.scribble.sesstype.kind.Global;
import org.scribble.sesstype.name.RecVar;
import org.scribble.sesstype.name.Role;
public class GRecursion extends Recursion<Global> implements GCompoundInteractionNode
{
public GRecursion(CommonTree source, RecVarNode recvar, GProtocolBlock block)
{
super(source, recvar, block);
}
public LRecursion project(Role self, LProtocolBlock block)
{
RecVarNode recvar = this.recvar.clone();
LRecursion projection = null;
Set<RecVar> rvs = new HashSet<>();
rvs.add(recvar.toName());
LProtocolBlock pruned = prune(block, rvs);
if (!pruned.isEmpty())
{
projection = AstFactoryImpl.FACTORY.LRecursion(this.source, recvar, pruned);
}
return projection;
}
// Pruning must be considered here (at Recursion) due to unguarded recvars
// Set should be unnecessary (singleton OK) -- *nested* irrelevant continues should already have been pruned
// FIXME? refactor and separate into dels? -- maybe not: since pruning is a bit too much of a "centralised algorithm" -- currently relying on TODO exception for unhandled cases
private static LProtocolBlock prune(LProtocolBlock block, Set<RecVar> rvs) // FIXME: Set unnecessary
{
if (block.isEmpty())
{
return block;
}
List<? extends LInteractionNode> lis = block.getInteractionSeq().getInteractions();
if (lis.size() > 1)
{
return block;
}
else //if (lis.size() == 1)
{
// Only pruning if single statement body: if more than 1, must be some (non-empty?) statement before a continue -- cannot (shouldn't?) be a continue followed by some other statement due to reachability
LInteractionNode lin = lis.get(0);
if (lin instanceof LContinue)
{
if (rvs.contains(((LContinue) lin).recvar.toName()))
{
// FIXME: need equivalent for projection-irrelevant recursive-do in a protocoldecl
return AstFactoryImpl.FACTORY.LProtocolBlock(block.getSource(),
AstFactoryImpl.FACTORY.LInteractionSeq(block.seq.getSource(), Collections.emptyList()));
}
else
{
return block;
}
}
else if (lin instanceof MessageTransfer<?> || lin instanceof Do<?> || lin instanceof ConnectionAction<?>)
{
return block;
}
else
{
if (lin instanceof LChoice)
{
List<LProtocolBlock> pruned = new LinkedList<LProtocolBlock>();
for (LProtocolBlock b : ((LChoice) lin).getBlocks())
{
if (!prune(b, rvs).isEmpty())
{
pruned.add(b);
}
}
if (pruned.isEmpty())
{
return AstFactoryImpl.FACTORY.LProtocolBlock(block.getSource(),
AstFactoryImpl.FACTORY.LInteractionSeq(block.seq.getSource(), Collections.emptyList()));
}
else
{
return AstFactoryImpl.FACTORY.LProtocolBlock(block.getSource(),
AstFactoryImpl.FACTORY.LInteractionSeq(block.seq.getSource(), Arrays.asList(
AstFactoryImpl.FACTORY.LChoice(lin.getSource(), ((LChoice) lin).subj, pruned))));
}
}
else if (lin instanceof LRecursion)
{
rvs = new HashSet<>(rvs);
//rvs.add(((LRecursion) lin).recvar.toName()); // Set unnecessary
LProtocolBlock pruned = prune(((LRecursion) lin).getBlock(), rvs); // Need to check if the current rec has any cases to prune in the nested rec (already pruned, but for the nested recvars only)
if (pruned.isEmpty())
{
return pruned;
}
else
{
return AstFactoryImpl.FACTORY.LProtocolBlock(block.getSource(),
AstFactoryImpl.FACTORY.LInteractionSeq(block.seq.getSource(), Arrays.asList(
AstFactoryImpl.FACTORY.LRecursion(lin.getSource(), ((LRecursion) lin).recvar, pruned))));
}
/*if (((LRecursion) lin).block.isEmpty())
{
return AstFactoryImpl.FACTORY.LProtocolBlock(AstFactoryImpl.FACTORY.LInteractionSeq(Arrays.asList(AstFactoryImpl.FACTORY.LRecursion(((LRecursion) lin).recvar, ((LRecursion) lin).getBlock()))));
}
else
{
return ((LRecursion) lin).getBlock();
}*/
}
else
{
throw new RuntimeException("TODO: " + lin);
}
}
}
}
@Override
protected GRecursion copy()
{
return new GRecursion(this.source, this.recvar, getBlock());
}
@Override
public GRecursion clone()
{
RecVarNode recvar = this.recvar.clone();
GProtocolBlock block = getBlock().clone();
return AstFactoryImpl.FACTORY.GRecursion(this.source, recvar, block);
}
@Override
public GRecursion reconstruct(RecVarNode recvar, ProtocolBlock<Global> block)
{
ScribDel del = del();
GRecursion gr = new GRecursion(this.source, recvar, (GProtocolBlock) block);
gr = (GRecursion) gr.del(del);
return gr;
}
@Override
public GProtocolBlock getBlock()
{
return (GProtocolBlock) this.block;
}
// FIXME: shouldn't be needed, but here due to Eclipse bug https://bugs.eclipse.org/bugs/show_bug.cgi?id=436350
@Override
public Global getKind()
{
return GCompoundInteractionNode.super.getKind();
}
}