0
|
1 package nabble.naml.compiler;
|
|
2
|
|
3 import fschmidt.util.java.Interner;
|
|
4 import nabble.naml.dom.Container;
|
|
5 import nabble.naml.dom.Element;
|
|
6 import nabble.naml.dom.ElementName;
|
|
7 import nabble.naml.dom.Naml;
|
|
8 import nabble.naml.dom.ParseException;
|
|
9
|
|
10 import java.util.ArrayList;
|
|
11 import java.util.Collection;
|
|
12 import java.util.Collections;
|
|
13 import java.util.HashMap;
|
|
14 import java.util.HashSet;
|
|
15 import java.util.LinkedHashSet;
|
|
16 import java.util.List;
|
|
17 import java.util.Map;
|
|
18 import java.util.Set;
|
|
19 import java.util.Arrays;
|
|
20
|
|
21
|
|
22 public final class Source implements Comparable<Source> {
|
|
23 public final String id;
|
|
24 public final String content;
|
|
25 private final Set<ElementName> ignoreTags;
|
|
26 private List<Macro> macros = null;
|
|
27 private Set<String> staticNames = null;
|
|
28
|
|
29 private Source(String id,String content,Set<ElementName> ignoreTags) {
|
|
30 this.id = id;
|
|
31 this.content = content;
|
|
32 this.ignoreTags = ignoreTags;
|
|
33 }
|
|
34
|
|
35 @Override public boolean equals(Object obj) {
|
|
36 if( !(obj instanceof Source) )
|
|
37 return false;
|
|
38 Source source = (Source)obj;
|
|
39 return source.id.equals(id) && source.content.equals(content);
|
|
40 }
|
|
41
|
|
42 @Override public int hashCode() {
|
|
43 return content.hashCode();
|
|
44 }
|
|
45
|
|
46 @Override public int compareTo(Source source) {
|
|
47 if( this == source )
|
|
48 return 0;
|
|
49 int rtn = id.compareTo(source.id);
|
|
50 if( rtn != 0 )
|
|
51 return rtn;
|
|
52 return content.compareTo(source.content);
|
|
53 }
|
|
54
|
|
55 @Override public String toString() {
|
|
56 return id;
|
|
57 }
|
|
58
|
|
59 public Collection<Macro> getMacros() throws CompileException {
|
|
60 compile();
|
|
61 return macros;
|
|
62 }
|
|
63
|
|
64 Collection<String> staticNames() throws CompileException {
|
|
65 compile();
|
|
66 return staticNames;
|
|
67 }
|
|
68
|
|
69 private static final Set<ElementName> macroNames = new LinkedHashSet<ElementName>(Arrays.asList(
|
|
70 new ElementName( Macro.Type.MACRO.tagName ),
|
|
71 new ElementName( Macro.OVERRIDE_PREFIX + Macro.Type.MACRO.tagName ),
|
|
72 new ElementName( Macro.Type.SUBROUTINE.tagName ),
|
|
73 new ElementName( Macro.OVERRIDE_PREFIX + Macro.Type.SUBROUTINE.tagName ),
|
|
74 new ElementName( Macro.Type.TRANSLATION.tagName ),
|
|
75 new ElementName( Macro.OVERRIDE_PREFIX + Macro.Type.TRANSLATION.tagName ),
|
|
76 new ElementName( Macro.Type.NAMESPACE.tagName )
|
|
77 ) );
|
|
78 private static final ElementName staticName = new ElementName("static");
|
|
79
|
|
80 public Naml parse() throws CompileException {
|
|
81 StackTrace stackTrace = new StackTrace();
|
|
82 try {
|
|
83 return Naml.parser().parse(content);
|
|
84 } catch(ParseException e) {
|
|
85 stackTrace.push( new StackTraceElement(this,e.lineNumber) );
|
|
86 try {
|
|
87 throw new CompileException(stackTrace,"parse exception in "+id+" - "+e.getMessage(),e);
|
|
88 } finally {
|
|
89 stackTrace.pop();
|
|
90 }
|
|
91 }
|
|
92 }
|
|
93
|
|
94 private synchronized void compile() throws CompileException {
|
|
95 if( macros != null )
|
|
96 return;
|
|
97 List<Macro> macros = new ArrayList<Macro>();
|
|
98 staticNames = new HashSet<String>();
|
|
99 StackTrace stackTrace = new StackTrace();
|
|
100 Map<String,Integer> macroCount = new HashMap<String,Integer>();
|
|
101 for( Object obj : parse() ) {
|
|
102 if( obj instanceof Element ) {
|
|
103 Element element = (Element)obj;
|
|
104 stackTrace.push( new StackTraceElement(this,element) );
|
|
105 try {
|
|
106 ElementName tagName = element.name();
|
|
107 if( tagName.equals(staticName) ) {
|
|
108 if( !(element instanceof Container) )
|
|
109 throw new CompileException(stackTrace,"empty '"+tagName+"' not allowed");
|
|
110 Container container = (Container)element;
|
|
111 Naml staticNaml = container.contents();
|
|
112 if( staticNaml.isEmpty() )
|
|
113 throw new CompileException(stackTrace,"static tag may not be empty");
|
|
114 if( staticNaml.size() > 1 )
|
|
115 throw new CompileException(stackTrace,"static tag may only contain a list of names");
|
|
116 Object objS = staticNaml.get(0);
|
|
117 if( !(objS instanceof String) )
|
|
118 throw new CompileException(stackTrace,"static tag may only contain a list of names");
|
|
119 String names = (String)objS;
|
|
120 for( String name : names.split("\\s+") ) {
|
|
121 name = name.toLowerCase();
|
|
122 staticNames.add(name);
|
|
123 }
|
|
124 continue;
|
|
125 }
|
|
126 if( macroNames.contains(tagName) ) {
|
|
127 Macro macro = new Macro(this,element,stackTrace,macroCount);
|
|
128 macros.add(macro);
|
|
129 } else if( !ignoreTags.contains(tagName) ) {
|
|
130 Set<ElementName> allowed = new LinkedHashSet<ElementName>();
|
|
131 allowed.addAll(macroNames);
|
|
132 allowed.add(staticName);
|
|
133 allowed.addAll(ignoreTags);
|
|
134 throw new CompileException(stackTrace,"'"+tagName+"' not valid, only "+allowed+" tags allowed at root");
|
|
135 }
|
|
136 } finally {
|
|
137 stackTrace.pop();
|
|
138 }
|
|
139 }
|
|
140 }
|
|
141 this.macros = macros;
|
|
142 }
|
|
143
|
|
144
|
|
145 private static final Interner<Source> interner = new Interner<Source>();
|
|
146
|
|
147 public static Source getInstance(String id,String content,Set<ElementName> ignoreTags) {
|
|
148 return interner.intern(new Source(id,content,ignoreTags));
|
|
149 }
|
|
150
|
|
151 public static Source getInstance(String id,String content) {
|
|
152 return getInstance(id,content,Collections.<ElementName>emptySet());
|
|
153 }
|
|
154 }
|