package org.scribble.visit.wf; import java.util.HashSet; import java.util.Set; import org.scribble.ast.Choice; import org.scribble.ast.InteractionSeq; import org.scribble.ast.ProtocolDecl; import org.scribble.ast.ScribNode; import org.scribble.ast.global.GProtocolDecl; import org.scribble.main.Job; import org.scribble.main.ScribbleException; import org.scribble.sesstype.kind.ProtocolKind; import org.scribble.visit.UnfoldingVisitor; import org.scribble.visit.wf.env.WFChoiceEnv; // Note: now only used for choice subject and enabled checking // FIXME: refactor as distinct enabling messages checker (cf. GChoiceDel, WFChoicePathChecker) public class WFChoiceChecker extends UnfoldingVisitor<WFChoiceEnv> { // N.B. using pointer equality for checking if choice previously visited // So UnfoldingVisitor cannot visit a clone // equals method identity not suitable unless Ast nodes record additional info like syntactic position private Set<Choice<?>> visited = new HashSet<>(); public WFChoiceChecker(Job job) { super(job); } @Override protected WFChoiceEnv makeRootProtocolDeclEnv(ProtocolDecl<? extends ProtocolKind> pd) { return new WFChoiceEnv(new HashSet<>(pd.header.roledecls.getRoles()), !(pd.isGlobal() && ((GProtocolDecl) pd).modifiers.contains(GProtocolDecl.Modifiers.EXPLICIT))); // FIXME: consider locals; also, explicit modifier should be carried over to local projections } @Override public ScribNode visit(ScribNode parent, ScribNode child) throws ScribbleException { if (child instanceof GProtocolDecl) { GProtocolDecl gpd = (GProtocolDecl) child; if (gpd.isAuxModifier()) { return child; // bypass aux protocols // FIXME: integrate bypass functionality into made enter/visit/leave pattern } } if (this.job.useOldWf) { if (child instanceof Choice<?>) // Only needed for old WF (for distinct enabling message checking) // FIXME: maybe move connectedness checking to a separate pass, i.e. vanilla UnfoldingVisitor (if retained as syntactic check) { return visitOverrideForChoice((InteractionSeq<?>) parent, (Choice<?>) child); } } return super.visit(parent, child); } private ScribNode visitOverrideForChoice(InteractionSeq<?> parent, Choice<?> child) throws ScribbleException { if (child instanceof Choice<?>) { Choice<?> cho = (Choice<?>) child; if (!this.visited.contains(cho)) // ** Old WF breaks connectedness checking, e.g. rec X { choice at A { connect A to B; continue X; } } because of choice visit pruning { this.visited.add(cho); //ScribNode n = cho.visitChildren(this); ScribNode n = super.visit(parent, child); this.visited.remove(cho); return n; } else { return cho; } } else { return super.visit(parent, child); } } @Override protected void unfoldingEnter(ScribNode parent, ScribNode child) throws ScribbleException { super.unfoldingEnter(parent, child); child.del().enterInlinedWFChoiceCheck(parent, child, this); } @Override protected ScribNode unfoldingLeave(ScribNode parent, ScribNode child, ScribNode visited) throws ScribbleException { visited = visited.del().leaveInlinedWFChoiceCheck(parent, child, this, visited); return super.unfoldingLeave(parent, child, visited); } }