package org.scribble.visit.context;
import java.util.HashMap;
import java.util.Map;
import java.util.Stack;
import org.antlr.runtime.tree.CommonTree;
import org.scribble.ast.AstFactoryImpl;
import org.scribble.ast.Module;
import org.scribble.ast.ProtocolDecl;
import org.scribble.ast.ScribNode;
import org.scribble.ast.global.GProtocolDecl;
import org.scribble.ast.name.qualified.LProtocolNameNode;
import org.scribble.ast.name.qualified.ModuleNameNode;
import org.scribble.main.Job;
import org.scribble.main.ScribbleException;
import org.scribble.sesstype.kind.Local;
import org.scribble.sesstype.kind.ModuleKind;
import org.scribble.sesstype.kind.ProtocolKind;
import org.scribble.sesstype.name.GProtocolName;
import org.scribble.sesstype.name.LProtocolName;
import org.scribble.sesstype.name.ModuleName;
import org.scribble.sesstype.name.Role;
import org.scribble.visit.EnvVisitor;
import org.scribble.visit.context.env.ProjectionEnv;
// Uses visitor infrastructure to traverse AST and generate local nodes from global with original nodes unchanged (so does not use normal visitChildren pattern -- env used to pass the working projections)
public class Projector extends EnvVisitor<ProjectionEnv>
{
private Stack<Role> selfs = new Stack<>(); // Is a stack needed? roles only pushed from GlobalProtocolDecl, which should be only done once at the root?
// protocol name is full global protocol name
private Map<GProtocolName, Map<Role, Module>> projections = new HashMap<>();
public Projector(Job job)
{
super(job);
}
@Override
protected ProjectionEnv makeRootProtocolDeclEnv(ProtocolDecl<? extends ProtocolKind> pd)
{
return new ProjectionEnv();
}
@Override
public ScribNode visit(ScribNode parent, ScribNode child) throws ScribbleException
{
// "Override" SubprotocolVisitor visitChildrenInSubprotocols pattern, avoids adding visitForProjection to every ModelNode
// Or else need to build in a "visit override" mechanism into the parent visitors
if (child instanceof GProtocolDecl)
{
return visitOverrideForGProtocolDecl((Module) parent, (GProtocolDecl) child);
}
else
{
return super.visit(parent, child);
}
}
// Projector uses this to "override" the base SubprotocolVisitor visitChildrenInSubprotocols pattern
// Better to be in the visitor than in the del for visibility of visitor enter/leave -- also localises special visiting pattern inside the visitor, while keeping the del enter/leave methods uniform (e.g. GlobalProtocolDeclDelegate enter/leave relies on the same peekSelf API as for other nodes)
protected GProtocolDecl visitOverrideForGProtocolDecl(Module parent, GProtocolDecl child) throws ScribbleException
{
for (Role self : child.header.roledecls.getRoles())
{
pushSelf(self);
enter(parent, child);
GProtocolDecl visited = (GProtocolDecl) child.visitChildren(this); // enter/leave around visitChildren for this GlobalProtocolDecl done above -- cf. SubprotocolVisitor.visit
visited = (GProtocolDecl) leave(parent, child, visited); // projection will not change original global protocol (visited can be discarded)
popSelf();
}
return child;
}
@Override
protected final void envEnter(ScribNode parent, ScribNode child) throws ScribbleException
{
super.envEnter(parent, child);
child.del().enterProjection(parent, child, this);
}
@Override
protected ScribNode envLeave(ScribNode parent, ScribNode child, ScribNode visited) throws ScribbleException
{
visited = visited.del().leaveProjection(parent, child, this, visited);
return super.envLeave(parent, child, visited);
}
public void pushSelf(Role self)
{
this.selfs.push(self);
}
public Role peekSelf()
{
return this.selfs.peek();
}
public Role popSelf()
{
return this.selfs.pop();
}
// gpn is full global protocol name, mod is a module with the single local protocol projection
public void addProjection(GProtocolName gpn, Role role, Module mod)
{
Map<Role, Module> tmp = this.projections.get(gpn);
if (tmp == null)
{
tmp = new HashMap<>();
this.projections.put(gpn, tmp);
}
tmp.put(role, mod);
}
public Map<GProtocolName, Map<Role, Module>> getProjections()
{
return this.projections;
}
public static LProtocolName projectSimpleProtocolName(GProtocolName simpname, Role role)
{
return new LProtocolName(simpname.toString() + "_" + role.toString());
}
// Role is the target subprotocol parameter (not the current projector self -- actually the self just popped)
public static LProtocolName projectFullProtocolName(GProtocolName fullname, Role role)
{
LProtocolName simplename = projectSimpleProtocolName(fullname.getSimpleName(), role);
ModuleName modname = projectModuleName(fullname.getPrefix(), simplename);
return new LProtocolName(modname, simplename);
}
// fullname is the un-projected name; localname is the already projected simple name
public static ModuleName projectModuleName(ModuleName fullname, LProtocolName localname)
{
ModuleName simpname = new ModuleName(fullname.getSimpleName().toString() + "_" + localname.toString());
return new ModuleName(fullname.getPrefix(), simpname); // Supports unary fullname
}
public static LProtocolNameNode makeProjectedSimpleNameNode(CommonTree source, GProtocolName simpname, Role role)
{
return (LProtocolNameNode)
AstFactoryImpl.FACTORY.QualifiedNameNode(source, Local.KIND, projectSimpleProtocolName(simpname, role).toString());
}
public static LProtocolNameNode makeProjectedFullNameNode(CommonTree source, GProtocolName fullname, Role role)
{
return (LProtocolNameNode)
AstFactoryImpl.FACTORY.QualifiedNameNode(source, Local.KIND, projectFullProtocolName(fullname, role).getElements());
}
// fullname is the un-projected name; localname is the already projected simple name
public static ModuleNameNode makeProjectedModuleNameNode(CommonTree source, ModuleName fullname, LProtocolName localname)
{
return (ModuleNameNode)
AstFactoryImpl.FACTORY.QualifiedNameNode(source, ModuleKind.KIND, projectModuleName(fullname, localname).getElements());
}
// Returns true if should ignore for projection
/*public static boolean prune(LProtocolBlock block)
{
if (block.isEmpty())
{
return true;
}
List<? extends LInteractionNode> lis = block.getInteractionSeq().getInteractions();
if (lis.size() > 1)
{
return false;
}
else //if (lis.size() == 1)
{
LInteractionNode lin = lis.get(0);
if (lin instanceof LContinue)
{
return true;
}
else if (lin instanceof MessageTransfer<?>)
{
return false;
}
else
{
if (lin instanceof LChoice)
{
for (LProtocolBlock b : ((LChoice) lin).getBlocks())
{
if (!prune(b))
{
return false;
}
}
return true;
}
else if (lin instanceof LRecursion)
{
return prune(((LRecursion) lin).getBlock());
}
else
{
throw new RuntimeException("TODO: " + lin);
}
}
}
}*/
}