comparison 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
comparison
equal deleted inserted replaced
-1:000000000000 0:7ecd1a4ef557
1 package nabble.naml.compiler;
2
3 import fschmidt.util.java.Computable;
4 import fschmidt.util.java.ComputationException;
5 import fschmidt.util.java.FutureValue;
6 import fschmidt.util.java.Identity;
7 import fschmidt.util.java.Interner;
8 import fschmidt.util.java.Memoizer;
9 import fschmidt.util.java.ObjectUtils;
10 import nabble.naml.dom.ElementName;
11 import org.slf4j.Logger;
12 import org.slf4j.LoggerFactory;
13
14 import java.util.ArrayList;
15 import java.util.Arrays;
16 import java.util.Collection;
17 import java.util.Collections;
18 import java.util.HashMap;
19 import java.util.HashSet;
20 import java.util.List;
21 import java.util.Map;
22 import java.util.Set;
23 import java.util.concurrent.ConcurrentHashMap;
24 import java.util.concurrent.ConcurrentMap;
25 import java.util.concurrent.CopyOnWriteArraySet;
26
27
28 public final class Program {
29 private static final Logger logger = LoggerFactory.getLogger(Program.class);
30
31 private static final class Key {
32 final String name;
33 final GenericNamespace[] base;
34
35 Key(String name,GenericNamespace[] base) {
36 if( name==null )
37 throw new NullPointerException("name is null");
38 this.name = name;
39 this.base = base;
40 }
41
42 public boolean equals(Object obj) {
43 if( !(obj instanceof Key) )
44 return false;
45 Key key = (Key)obj;
46 return key.name.equals(name) && Arrays.equals(key.base,base);
47 }
48
49 public int hashCode() {
50 return name.hashCode();
51 }
52 }
53
54 private static final class PartUsage {
55 private final Identity<ElementName.Part> part;
56 private final Usage usage;
57
58 PartUsage(ElementName.Part part,Usage usage) {
59 this.part = part.identity();
60 this.usage = usage;
61 }
62
63 public boolean equals(Object obj) {
64 if( !(obj instanceof PartUsage) )
65 return false;
66 PartUsage p = (PartUsage)obj;
67 return p.part==part && ObjectUtils.equals(p.usage,usage);
68 }
69
70 public int hashCode() {
71 int hash = part.hashCode();
72 if( usage != null )
73 hash += usage.hashCode();
74 return hash;
75 }
76 }
77
78 private final List<Source> sources;
79 private final Collection<Module> modules;
80 private final ConcurrentMap<PartUsage,Meaning> partMeaning = new ConcurrentHashMap<PartUsage,Meaning>();
81 private final ConcurrentMap<String,Meaning> meaningMap = new ConcurrentHashMap<String,Meaning>();
82 private final ConcurrentMap<Meaning,Set<Usage>> usageMap = new ConcurrentHashMap<Meaning,Set<Usage>>();
83
84 private final Memoizer<Key,Template> macroTemplates = new Memoizer<Key,Template>(new Computable<Key,Template>() {
85 public Template get(Key key) throws ComputationException {
86 try {
87 long start = System.currentTimeMillis();
88 Template t = Compiler.compile(Program.this,key.name,key.base);
89 logger.info("Compiling '"+key.name+"' took " + (System.currentTimeMillis()-start) + " ms");
90 return t;
91 } catch(CompileException e) {
92 throw new ComputationException(e);
93 }
94 }
95 });
96
97 private final FutureValue<Set<String>> moduleNames = new FutureValue<Set<String>>() {
98 protected Set<String> compute() {
99 Set<String> moduleNames = new HashSet<String>();
100 for( Module m : modules ) {
101 if( !moduleNames.add(m.getName()) )
102 throw new RuntimeException("duplicate module: "+m.getName());
103 }
104 return moduleNames;
105 }
106 };
107
108 private final FutureValue<Map<Class,List<JavaNamespace>>> extensionMap = new FutureValue<Map<Class,List<JavaNamespace>>>() {
109 protected Map<Class,List<JavaNamespace>> compute() {
110 Map<Class,List<JavaNamespace>> extensionMap = new HashMap<Class,List<JavaNamespace>>();
111 for( Module m : modules ) {
112 for( Class extensionClass : m.getExtensions() ) {
113 JavaNamespace ns = JavaNamespace.getNamespaceExt(extensionClass);
114 List<JavaNamespace> extensions = extensionMap.get(ns.extensionTarget);
115 if( extensions == null ) {
116 extensions = new ArrayList<JavaNamespace>();
117 extensionMap.put(ns.extensionTarget,extensions);
118 }
119 extensions.add(ns);
120 }
121 }
122 return extensionMap;
123 }
124 };
125
126 private final FutureValue<Set<String>> staticNames = new FutureValue<Set<String>>() {
127 protected Set<String> compute() throws CompileException {
128 Set<String> staticNames = new HashSet<String>();
129 for( Source source : getSources() ) {
130 staticNames.addAll(source.staticNames());
131 }
132 return staticNames;
133 }
134 };
135
136 private static class Macros {
137 final Map<String,List<Macro>> nameMap = new HashMap<String,List<Macro>>();
138 final Map<Macro,Macro> overrideMap = new HashMap<Macro,Macro>();
139 final Map<Macro,Macro> overriddenMap = new HashMap<Macro,Macro>();
140 }
141
142 private static final class MacroKey {
143 private final Macro macro;
144
145 MacroKey(Macro macro) {
146 this.macro = macro;
147 }
148
149 @Override public boolean equals(Object obj) {
150 if( !(obj instanceof MacroKey) )
151 return false;
152 MacroKey key = (MacroKey)obj;
153 return key.macro.name.equals(macro.name) && key.macro.requiredNamespaces.equals(macro.requiredNamespaces);
154 }
155
156 @Override public int hashCode() {
157 return macro.name.hashCode() + 31 * macro.requiredNamespaces.hashCode();
158 }
159 }
160
161 private final FutureValue<Macros> macros = new FutureValue<Macros>() {
162 protected Macros compute() throws CompileException {
163 Macros macros = new Macros();
164 Map<MacroKey,Macro> map = new HashMap<MacroKey,Macro>();
165 StackTrace stackTrace = new StackTrace();
166 for( Source source : getSources() ) {
167 for( Macro macro : source.getMacros() ) {
168 stackTrace.push( new StackTraceElement(macro) );
169 try {
170 Macro dup = map.put(new MacroKey(macro),macro);
171 if( !macro.isOverride ) {
172 if( dup != null )
173 throw new CompileException(stackTrace,"macro '"+macro+"' conflicts with '"+dup+"'");
174 } else {
175 if( dup == null )
176 throw new CompileException(stackTrace,"no macro found to override");
177 macros.overrideMap.put(macro,dup);
178 macros.overriddenMap.put(dup,macro);
179 }
180 addMeaning(macro);
181 } finally {
182 stackTrace.pop();
183 }
184 }
185 }
186 for( Macro macro : map.values() ) {
187 List<Macro> list = macros.nameMap.get(macro.name);
188 if( list == null ) {
189 list = Collections.singletonList(macro);
190 macros.nameMap.put(macro.name,list);
191 } else if( list.size() == 1 ) {
192 list = new ArrayList<Macro>(list);
193 list.add(macro);
194 macros.nameMap.put(macro.name,list);
195 } else {
196 list.add(macro);
197 }
198 }
199 return macros;
200 }
201 };
202
203 private Program(List<Module> modules) {
204 this.modules = modules;
205 this.sources = new ArrayList<Source>();
206 Set<String> names = new HashSet<String>();
207 for( Module module : modules ) {
208 for( String dependency : module.getDependencies() ) {
209 if( !names.contains(dependency) )
210 throw new IllegalArgumentException("module '"+module.getName()+"' dependency '"+dependency+"' not found");
211 }
212 if( !names.add(module.getName()) )
213 throw new IllegalArgumentException("duplicate module name: "+module.getName());
214 this.sources.addAll( module.getSources() );
215 }
216
217 // check sources
218 Set<String> ids = new HashSet<String>();
219 for( Source s : this.sources ) {
220 if( !ids.add(s.id) )
221 throw new IllegalArgumentException("duplicate source: "+s);
222 }
223 }
224
225 Set<String> moduleNames() {
226 return moduleNames.get();
227 }
228
229 Map<Class,List<JavaNamespace>> extensionMap() {
230 return extensionMap.get();
231 }
232
233 Set<String> staticNames() {
234 return staticNames.get();
235 }
236
237 Map<String,List<Macro>> macros() {
238 return macros.get().nameMap;
239 }
240
241 Map<Macro,Macro> overrides() {
242 return macros.get().overrideMap;
243 }
244
245 Map<Macro,Macro> overridden() {
246 return macros.get().overriddenMap;
247 }
248
249 public List<Source> getSources() {
250 return sources;
251 }
252
253 public Template getTemplate(String templateName)
254 throws CompileException
255 {
256 return getTemplate(templateName,new GenericNamespace[0]);
257 }
258
259 public Template getTemplate(String templateName,String... base)
260 throws CompileException
261 {
262 return getTemplate(templateName,getNamespaces(base));
263 }
264
265 public Template getTemplate(String templateName,Class... base)
266 throws CompileException
267 {
268 GenericNamespace[] nsBase = new GenericNamespace[base.length];
269 for( int i=0; i<base.length; i++ ) {
270 nsBase[i] = JavaNamespace.getNamespace(base[i]);
271 }
272 return getTemplate(templateName,nsBase);
273 }
274
275 Template getTemplate(String templateName,GenericNamespace... base)
276 throws CompileException
277 {
278 try {
279 Key key = new Key(templateName,base);
280 return macroTemplates.get(key);
281 } catch(ComputationException e) {
282 Throwable cause = e.getCause();
283 if( cause instanceof CompileException )
284 throw (CompileException)cause;
285 throw e;
286 }
287 }
288
289 public boolean equals(Object obj) {
290 if( !(obj instanceof Program) )
291 return false;
292 Program t = (Program)obj;
293 return t.sources.equals(sources) && t.modules.equals(modules);
294 }
295
296 public int hashCode() {
297 return sources.hashCode() + 31*modules.hashCode();
298 }
299
300 void addMeaning(Meaning meaning) {
301 Meaning old = meaningMap.putIfAbsent( meaning.getId(), meaning );
302 if( old != null && old != meaning ) {
303 throw new RuntimeException("meaning="+meaning+" old="+old);
304 }
305 }
306
307 void addMeaning(ElementName.Part part,Meaning meaning,MacroScope macroScope,GenericNamespace[] base) {
308 Set<Usage> usages = usageMap.get(meaning);
309 if( usages == null ) {
310 usages = Collections.newSetFromMap( new ConcurrentHashMap<Usage,Boolean>() );
311 Set<Usage> old = usageMap.putIfAbsent(meaning,usages);
312 if( old != null )
313 usages = old;
314 }
315 if( part != null ) {
316 List<Macro> macroPath = new ArrayList<Macro>();
317 addMacros(macroScope,macroPath);
318 Usage usage = new Usage(base,macroPath).intern();
319 usages.add(usage);
320 PartUsage p = new PartUsage(part,null);
321 Meaning m = partMeaning.putIfAbsent(p,meaning);
322 if( m!=null && !m.equals(meaning) ) {
323 p = new PartUsage(part,usage);
324 m = partMeaning.putIfAbsent(p,meaning);
325 if( m!=null && !m.equals(meaning) )
326 throw new RuntimeException();
327 }
328 }
329 }
330
331 private void addMacros(MacroScope macroScope,List<Macro> macroPath) {
332 if( macroScope.parentScope != null )
333 addMacros(macroScope.parentScope,macroPath);
334 macroPath.add(macroScope.macro);
335 }
336
337 public final Meaning getMeaning(ElementName.Part namePart,Usage usage) {
338 Meaning m = partMeaning.get(new PartUsage(namePart,usage));
339 if( m == null )
340 m = partMeaning.get(new PartUsage(namePart,null));
341 return m;
342 }
343
344 public final Meaning getMeaning(String id) {
345 return meaningMap.get(id);
346 }
347
348 public Macro getMacroOverriddenBy(Macro macro) {
349 return overrides().get(macro);
350 }
351
352 public Macro getMacroWhichOverrides(Macro macro) {
353 return overridden().get(macro);
354 }
355
356 public boolean isCompiled(Meaning meaning) {
357 return usageMap.containsKey(meaning);
358 }
359
360 public Set<Usage> getUsages(Meaning meaning) {
361 return usageMap.get(meaning);
362 }
363
364 private static final Interner<Program> interner = new Interner<Program>();
365
366 public static Program getInstance(List<Module> modules) {
367 return interner.intern(new Program(modules));
368 }
369
370 public Usage getUsage(String[] baseIds,List<Macro> macroPath) {
371 return new Usage(getNamespaces(baseIds),macroPath);
372 }
373
374 private GenericNamespace[] getNamespaces(String[] ids) {
375 GenericNamespace[] a = new GenericNamespace[ids.length];
376 for( int i=0; i<ids.length; i++ ) {
377 a[i] = getNamespace(ids[i]);
378 }
379 return a;
380 }
381
382 private GenericNamespace getNamespace(String id) {
383 if( id.indexOf('!') == -1 ) {
384 try {
385 return JavaNamespace.getNamespace(Class.forName(id));
386 } catch(ClassNotFoundException e) {
387 throw new RuntimeException(e);
388 }
389 } else {
390 Macro macro = (Macro)getMeaning(id);
391 try {
392 return new MacroNamespace(macro,new StackTrace(),macros());
393 } catch(CompileException e) {
394 throw new RuntimeException(e);
395 }
396 }
397 }
398
399 public Collection<Macro> getMacrosByName(String name) {
400 return macros().get(name);
401 }
402
403 }