0
|
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 }
|