Mercurial Hosting > nabble
view src/nabble/naml/compiler/Program.java @ 0:7ecd1a4ef557
add content
author | Franklin Schmidt <fschmidt@gmail.com> |
---|---|
date | Thu, 21 Mar 2019 19:15:52 -0600 |
parents | |
children |
line wrap: on
line source
package nabble.naml.compiler; import fschmidt.util.java.Computable; import fschmidt.util.java.ComputationException; import fschmidt.util.java.FutureValue; import fschmidt.util.java.Identity; import fschmidt.util.java.Interner; import fschmidt.util.java.Memoizer; import fschmidt.util.java.ObjectUtils; import nabble.naml.dom.ElementName; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; import java.util.concurrent.CopyOnWriteArraySet; public final class Program { private static final Logger logger = LoggerFactory.getLogger(Program.class); private static final class Key { final String name; final GenericNamespace[] base; Key(String name,GenericNamespace[] base) { if( name==null ) throw new NullPointerException("name is null"); this.name = name; this.base = base; } public boolean equals(Object obj) { if( !(obj instanceof Key) ) return false; Key key = (Key)obj; return key.name.equals(name) && Arrays.equals(key.base,base); } public int hashCode() { return name.hashCode(); } } private static final class PartUsage { private final Identity<ElementName.Part> part; private final Usage usage; PartUsage(ElementName.Part part,Usage usage) { this.part = part.identity(); this.usage = usage; } public boolean equals(Object obj) { if( !(obj instanceof PartUsage) ) return false; PartUsage p = (PartUsage)obj; return p.part==part && ObjectUtils.equals(p.usage,usage); } public int hashCode() { int hash = part.hashCode(); if( usage != null ) hash += usage.hashCode(); return hash; } } private final List<Source> sources; private final Collection<Module> modules; private final ConcurrentMap<PartUsage,Meaning> partMeaning = new ConcurrentHashMap<PartUsage,Meaning>(); private final ConcurrentMap<String,Meaning> meaningMap = new ConcurrentHashMap<String,Meaning>(); private final ConcurrentMap<Meaning,Set<Usage>> usageMap = new ConcurrentHashMap<Meaning,Set<Usage>>(); private final Memoizer<Key,Template> macroTemplates = new Memoizer<Key,Template>(new Computable<Key,Template>() { public Template get(Key key) throws ComputationException { try { long start = System.currentTimeMillis(); Template t = Compiler.compile(Program.this,key.name,key.base); logger.info("Compiling '"+key.name+"' took " + (System.currentTimeMillis()-start) + " ms"); return t; } catch(CompileException e) { throw new ComputationException(e); } } }); private final FutureValue<Set<String>> moduleNames = new FutureValue<Set<String>>() { protected Set<String> compute() { Set<String> moduleNames = new HashSet<String>(); for( Module m : modules ) { if( !moduleNames.add(m.getName()) ) throw new RuntimeException("duplicate module: "+m.getName()); } return moduleNames; } }; private final FutureValue<Map<Class,List<JavaNamespace>>> extensionMap = new FutureValue<Map<Class,List<JavaNamespace>>>() { protected Map<Class,List<JavaNamespace>> compute() { Map<Class,List<JavaNamespace>> extensionMap = new HashMap<Class,List<JavaNamespace>>(); for( Module m : modules ) { for( Class extensionClass : m.getExtensions() ) { JavaNamespace ns = JavaNamespace.getNamespaceExt(extensionClass); List<JavaNamespace> extensions = extensionMap.get(ns.extensionTarget); if( extensions == null ) { extensions = new ArrayList<JavaNamespace>(); extensionMap.put(ns.extensionTarget,extensions); } extensions.add(ns); } } return extensionMap; } }; private final FutureValue<Set<String>> staticNames = new FutureValue<Set<String>>() { protected Set<String> compute() throws CompileException { Set<String> staticNames = new HashSet<String>(); for( Source source : getSources() ) { staticNames.addAll(source.staticNames()); } return staticNames; } }; private static class Macros { final Map<String,List<Macro>> nameMap = new HashMap<String,List<Macro>>(); final Map<Macro,Macro> overrideMap = new HashMap<Macro,Macro>(); final Map<Macro,Macro> overriddenMap = new HashMap<Macro,Macro>(); } private static final class MacroKey { private final Macro macro; MacroKey(Macro macro) { this.macro = macro; } @Override public boolean equals(Object obj) { if( !(obj instanceof MacroKey) ) return false; MacroKey key = (MacroKey)obj; return key.macro.name.equals(macro.name) && key.macro.requiredNamespaces.equals(macro.requiredNamespaces); } @Override public int hashCode() { return macro.name.hashCode() + 31 * macro.requiredNamespaces.hashCode(); } } private final FutureValue<Macros> macros = new FutureValue<Macros>() { protected Macros compute() throws CompileException { Macros macros = new Macros(); Map<MacroKey,Macro> map = new HashMap<MacroKey,Macro>(); StackTrace stackTrace = new StackTrace(); for( Source source : getSources() ) { for( Macro macro : source.getMacros() ) { stackTrace.push( new StackTraceElement(macro) ); try { Macro dup = map.put(new MacroKey(macro),macro); if( !macro.isOverride ) { if( dup != null ) throw new CompileException(stackTrace,"macro '"+macro+"' conflicts with '"+dup+"'"); } else { if( dup == null ) throw new CompileException(stackTrace,"no macro found to override"); macros.overrideMap.put(macro,dup); macros.overriddenMap.put(dup,macro); } addMeaning(macro); } finally { stackTrace.pop(); } } } for( Macro macro : map.values() ) { List<Macro> list = macros.nameMap.get(macro.name); if( list == null ) { list = Collections.singletonList(macro); macros.nameMap.put(macro.name,list); } else if( list.size() == 1 ) { list = new ArrayList<Macro>(list); list.add(macro); macros.nameMap.put(macro.name,list); } else { list.add(macro); } } return macros; } }; private Program(List<Module> modules) { this.modules = modules; this.sources = new ArrayList<Source>(); Set<String> names = new HashSet<String>(); for( Module module : modules ) { for( String dependency : module.getDependencies() ) { if( !names.contains(dependency) ) throw new IllegalArgumentException("module '"+module.getName()+"' dependency '"+dependency+"' not found"); } if( !names.add(module.getName()) ) throw new IllegalArgumentException("duplicate module name: "+module.getName()); this.sources.addAll( module.getSources() ); } // check sources Set<String> ids = new HashSet<String>(); for( Source s : this.sources ) { if( !ids.add(s.id) ) throw new IllegalArgumentException("duplicate source: "+s); } } Set<String> moduleNames() { return moduleNames.get(); } Map<Class,List<JavaNamespace>> extensionMap() { return extensionMap.get(); } Set<String> staticNames() { return staticNames.get(); } Map<String,List<Macro>> macros() { return macros.get().nameMap; } Map<Macro,Macro> overrides() { return macros.get().overrideMap; } Map<Macro,Macro> overridden() { return macros.get().overriddenMap; } public List<Source> getSources() { return sources; } public Template getTemplate(String templateName) throws CompileException { return getTemplate(templateName,new GenericNamespace[0]); } public Template getTemplate(String templateName,String... base) throws CompileException { return getTemplate(templateName,getNamespaces(base)); } public Template getTemplate(String templateName,Class... base) throws CompileException { GenericNamespace[] nsBase = new GenericNamespace[base.length]; for( int i=0; i<base.length; i++ ) { nsBase[i] = JavaNamespace.getNamespace(base[i]); } return getTemplate(templateName,nsBase); } Template getTemplate(String templateName,GenericNamespace... base) throws CompileException { try { Key key = new Key(templateName,base); return macroTemplates.get(key); } catch(ComputationException e) { Throwable cause = e.getCause(); if( cause instanceof CompileException ) throw (CompileException)cause; throw e; } } public boolean equals(Object obj) { if( !(obj instanceof Program) ) return false; Program t = (Program)obj; return t.sources.equals(sources) && t.modules.equals(modules); } public int hashCode() { return sources.hashCode() + 31*modules.hashCode(); } void addMeaning(Meaning meaning) { Meaning old = meaningMap.putIfAbsent( meaning.getId(), meaning ); if( old != null && old != meaning ) { throw new RuntimeException("meaning="+meaning+" old="+old); } } void addMeaning(ElementName.Part part,Meaning meaning,MacroScope macroScope,GenericNamespace[] base) { Set<Usage> usages = usageMap.get(meaning); if( usages == null ) { usages = Collections.newSetFromMap( new ConcurrentHashMap<Usage,Boolean>() ); Set<Usage> old = usageMap.putIfAbsent(meaning,usages); if( old != null ) usages = old; } if( part != null ) { List<Macro> macroPath = new ArrayList<Macro>(); addMacros(macroScope,macroPath); Usage usage = new Usage(base,macroPath).intern(); usages.add(usage); PartUsage p = new PartUsage(part,null); Meaning m = partMeaning.putIfAbsent(p,meaning); if( m!=null && !m.equals(meaning) ) { p = new PartUsage(part,usage); m = partMeaning.putIfAbsent(p,meaning); if( m!=null && !m.equals(meaning) ) throw new RuntimeException(); } } } private void addMacros(MacroScope macroScope,List<Macro> macroPath) { if( macroScope.parentScope != null ) addMacros(macroScope.parentScope,macroPath); macroPath.add(macroScope.macro); } public final Meaning getMeaning(ElementName.Part namePart,Usage usage) { Meaning m = partMeaning.get(new PartUsage(namePart,usage)); if( m == null ) m = partMeaning.get(new PartUsage(namePart,null)); return m; } public final Meaning getMeaning(String id) { return meaningMap.get(id); } public Macro getMacroOverriddenBy(Macro macro) { return overrides().get(macro); } public Macro getMacroWhichOverrides(Macro macro) { return overridden().get(macro); } public boolean isCompiled(Meaning meaning) { return usageMap.containsKey(meaning); } public Set<Usage> getUsages(Meaning meaning) { return usageMap.get(meaning); } private static final Interner<Program> interner = new Interner<Program>(); public static Program getInstance(List<Module> modules) { return interner.intern(new Program(modules)); } public Usage getUsage(String[] baseIds,List<Macro> macroPath) { return new Usage(getNamespaces(baseIds),macroPath); } private GenericNamespace[] getNamespaces(String[] ids) { GenericNamespace[] a = new GenericNamespace[ids.length]; for( int i=0; i<ids.length; i++ ) { a[i] = getNamespace(ids[i]); } return a; } private GenericNamespace getNamespace(String id) { if( id.indexOf('!') == -1 ) { try { return JavaNamespace.getNamespace(Class.forName(id)); } catch(ClassNotFoundException e) { throw new RuntimeException(e); } } else { Macro macro = (Macro)getMeaning(id); try { return new MacroNamespace(macro,new StackTrace(),macros()); } catch(CompileException e) { throw new RuntimeException(e); } } } public Collection<Macro> getMacrosByName(String name) { return macros().get(name); } }