package org.scribble.del.local; import java.util.Arrays; import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.Set; import org.scribble.ast.AstFactoryImpl; import org.scribble.ast.ScribNode; import org.scribble.ast.local.LContinue; import org.scribble.ast.name.simple.RecVarNode; import org.scribble.del.ContinueDel; import org.scribble.main.ScribbleException; import org.scribble.model.endpoint.EState; import org.scribble.model.endpoint.actions.EAction; import org.scribble.sesstype.name.RecVar; import org.scribble.visit.ProtocolDefInliner; import org.scribble.visit.context.EGraphBuilder; import org.scribble.visit.env.InlineProtocolEnv; import org.scribble.visit.wf.ReachabilityChecker; import org.scribble.visit.wf.env.ReachabilityEnv; public class LContinueDel extends ContinueDel implements LSimpleInteractionNodeDel { @Override public LContinue leaveProtocolInlining(ScribNode parent, ScribNode child, ProtocolDefInliner inl, ScribNode visited) throws ScribbleException { LContinue lc = (LContinue) visited; RecVarNode recvar = (RecVarNode) ((InlineProtocolEnv) lc.recvar.del().env()).getTranslation(); LContinue inlined = AstFactoryImpl.FACTORY.LContinue(lc.getSource(), recvar); inl.pushEnv(inl.popEnv().setTranslation(inlined)); return (LContinue) super.leaveProtocolInlining(parent, child, inl, lc); } @Override public LContinue leaveReachabilityCheck(ScribNode parent, ScribNode child, ReachabilityChecker checker, ScribNode visited) throws ScribbleException { // "Entering" the continue here in leave, where we can merge the new state into the parent Env // Generally: if side effecting Env state to be merged into the parent (not just popped and discarded), leave must be overridden to do so LContinue lc = (LContinue) visited; ReachabilityEnv env = checker.popEnv().addContinueLabel(lc.recvar.toName()); setEnv(env); // Env recording probably not needed for all LocalInteractionNodes, just the compound ones, like for WF-choice checking checker.pushEnv(checker.popEnv().mergeContext(env)); return lc; } @Override public LContinue leaveEGraphBuilding(ScribNode parent, ScribNode child, EGraphBuilder graph, ScribNode visited) throws ScribbleException { LContinue lr = (LContinue) visited; RecVar rv = lr.recvar.toName(); if (graph.util.isUnguardedInChoice()) { graph.util.addContinueEdge(graph.util.getEntry(), rv); } else { // ** "Overwrites" previous edge built by send/receive(s) leading to this continue Iterator<EState> preds = graph.util.getPredecessors().iterator(); Iterator<EAction> prevs = graph.util.getPreviousActions().iterator(); EState entry = graph.util.getEntry(); Set<List<Object>> removed = new HashSet<>(); // HACK: for identical edges, i.e. same pred/prev/succ (e.g. rec X { choice at A { A->B:1 } or { A->B:1 } continue X; }) // FIXME: do here, or refactor into GraphBuilder? // Because duplicate edges preemptively pruned by ModelState.addEdge, but corresponding predecessors not pruned // FIXME: make uniform while (preds.hasNext()) { EState pred = preds.next(); EAction prev = prevs.next(); List<Object> tmp = Arrays.asList(pred, prev, entry); if (!removed.contains(tmp)) { removed.add(tmp); graph.util.removeEdgeFromPredecessor(pred, prev); // Assumes pred is a predecessor, and removes pred from current predecessors.. } graph.util.addRecursionEdge(pred, prev, graph.util.getRecursionEntry(rv)); // May be repeated for non-det, but OK // Combine with removeEdgeFromPredecessor? } } return (LContinue) super.leaveEGraphBuilding(parent, child, graph, lr); } }