package org.scribble.del; import java.util.Arrays; import java.util.Collection; import java.util.HashSet; import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.Set; import java.util.stream.Collectors; import org.scribble.ast.AstFactoryImpl; import org.scribble.ast.ImportDecl; import org.scribble.ast.Module; import org.scribble.ast.ModuleDecl; import org.scribble.ast.NonProtocolDecl; import org.scribble.ast.ProtocolDecl; import org.scribble.ast.ScribNode; import org.scribble.ast.context.ModuleContext; import org.scribble.ast.global.GProtocolDecl; import org.scribble.ast.local.LProtocolDecl; import org.scribble.ast.name.qualified.ModuleNameNode; import org.scribble.main.ScribbleException; import org.scribble.sesstype.name.GProtocolName; import org.scribble.sesstype.name.LProtocolName; import org.scribble.sesstype.name.Role; import org.scribble.visit.context.ModuleContextBuilder; import org.scribble.visit.context.Projector; import org.scribble.visit.wf.NameDisambiguator; public class ModuleDel extends ScribDelBase { private ModuleContext mcontext; public ModuleDel() { } private ModuleDel copy() { return new ModuleDel(); } @Override public void enterModuleContextBuilding(ScribNode parent, ScribNode child, ModuleContextBuilder builder) throws ScribbleException { builder.setModuleContext(new ModuleContext(builder.job.getContext(), (Module) child)); } // Maybe better to create on enter, so can be used during the context build pass (Context would need to be "cached" in the visitor to be accessed) @Override public Module leaveModuleContextBuilding(ScribNode parent, ScribNode child, ModuleContextBuilder builder, ScribNode visited) throws ScribbleException { ModuleDel del = setModuleContext(builder.getModuleContext()); return (Module) visited.del(del); } @Override public Module leaveDisambiguation(ScribNode parent, ScribNode child, NameDisambiguator disamb, ScribNode visited) throws ScribbleException { Module mod = (Module) visited; // Imports checked in ModuleContext -- that is built before disamb is run List<NonProtocolDecl<?>> npds = mod.getNonProtocolDecls(); List<String> npdnames = npds.stream().map((npd) -> npd.getDeclName().toString()).collect(Collectors.toList()); // Have to use Strings, as can be different kinds (datatype, sig) final Set<String> dups1 = getDuplicates(npdnames); //if (npds.size() != npdnames.stream().distinct().count()) if (!dups1.isEmpty()) { NonProtocolDecl<?> first = npds.stream().filter((npd) -> dups1.contains(npd.getDeclName().toString())).collect(Collectors.toList()).get(0); throw new ScribbleException(first.getSource(), "Duplicate non-protocol decls: " + first.getDeclName()); } List<ProtocolDecl<?>> pds = mod.getProtocolDecls(); List<String> pdnames = pds.stream().map((pd) -> pd.header.getDeclName().toString()).collect(Collectors.toList()); // Have to use Strings, as can be different kinds (global, local) final Set<String> dups2 = getDuplicates(pdnames); if (pds.size() != pdnames.stream().distinct().count()) if (!dups2.isEmpty()) { ProtocolDecl<?> first = pds.stream().filter((pd) -> dups2.contains(pd.header.getDeclName().toString())).collect(Collectors.toList()).get(0); throw new ScribbleException(first.getSource(), "Duplicate protocol decls: " + first.header.getDeclName()); // Global and locals also required to be distinct } return mod; } private static Set<String> getDuplicates(Collection<String> ss) { Set<String> uniques = new HashSet<>(); Set <String> dups = new HashSet<>(); for (String npd : ss) { if (!uniques.add(npd)) { dups.add(npd); } } return dups; } @Override public Module leaveProjection(ScribNode parent, ScribNode child, Projector proj, ScribNode visited) { proj.job.getContext().addProjections(proj.getProjections()); return (Module) visited; } // lpd is the projected local protocol public Module createModuleForProjection(Projector proj, Module root, GProtocolDecl gpd, LProtocolDecl lpd, Map<GProtocolName, Set<Role>> deps) { ModuleNameNode modname = Projector.makeProjectedModuleNameNode(root.moddecl.name.getSource(), // Or ignore blame source for purely generated? root.moddecl.getFullModuleName(), lpd.getHeader().getDeclName()); ModuleDecl moddecl = AstFactoryImpl.FACTORY.ModuleDecl(root.moddecl.getSource(), modname); List<ImportDecl<?>> imports = new LinkedList<>(); for (GProtocolName gpn : deps.keySet()) { for (Role role : deps.get(gpn)) { LProtocolName targetsimpname = Projector.projectSimpleProtocolName(gpn.getSimpleName(), role); ModuleNameNode targetmodname = Projector.makeProjectedModuleNameNode(null, // FIXME? projected import sources? gpn.getPrefix(), targetsimpname); if (!targetmodname.toName().equals(modname.toName())) // Self dependency -- each projected local is in its own module now, so can compare module names { imports.add(AstFactoryImpl.FACTORY.ImportModule(null, targetmodname, null)); // FIXME? projected import sources? } } } List<NonProtocolDecl<?>> data = new LinkedList<>(root.getNonProtocolDecls()); // FIXME: copy? // FIXME: only project the dependencies List<ProtocolDecl<?>> protos = Arrays.asList(lpd); return AstFactoryImpl.FACTORY.Module(gpd.header.getSource(), moddecl, imports, data, protos); } @Override public String toString() { return (this.mcontext == null) ? null : this.mcontext.toString(); // null before and during context building } public ModuleContext getModuleContext() { return this.mcontext; } protected ModuleDel setModuleContext(ModuleContext mcontext) { ModuleDel copy = copy(); // FIXME: should be a deep clone in principle copy.mcontext = mcontext; return copy; } }