1 //
2 // Copyright (c) 2006, Brian Frank and Andy Frank
3 // Licensed under the Academic Free License version 3.0
4 //
5 // History:
6 // 26 Dec 05 Brian Frank Creation
7 // 5 Jun 06 Brian Frank Ported from Java to Fan
8 //
9
10 **
11 ** ResolveImports maps every Using node in each CompilationUnit to a pod
12 ** and ensures that it exists and that no imports are duplicated. Then we
13 ** create a map for all the types which are imported into the CompilationUnit
14 ** so that the Parser can quickly distinguish between a type identifier and
15 ** other identifiers. The results of this step populate Using.resolvedXXX and
16 ** CompilationUnit.importedTypes.
17 **
18 class ResolveImports : CompilerStep
19 {
20
21 //////////////////////////////////////////////////////////////////////////
22 // Construction
23 //////////////////////////////////////////////////////////////////////////
24
25 **
26 ** Constructor takes the associated Compiler
27 **
28 new make(Compiler compiler)
29 : super(compiler)
30 {
31 resolved[compiler.pod.name] = compiler.pod
32 }
33
34 //////////////////////////////////////////////////////////////////////////
35 // Methods
36 //////////////////////////////////////////////////////////////////////////
37
38 **
39 ** Run the step
40 **
41 override Void run()
42 {
43 log.debug("ResolveImports")
44
45 // process each unit for Import.pod
46 units.each |CompilationUnit unit|
47 {
48 resolveImports(unit)
49 }
50 bombIfErr
51
52 // process each unit for CompilationUnit.importedTypes
53 units.each |CompilationUnit unit|
54 {
55 resolveImportedTypes(unit)
56 }
57 bombIfErr
58 }
59
60 **
61 ** Resolve all the imports in the specified unit
62 ** and ensure there are no duplicates.
63 **
64 private Void resolveImports(CompilationUnit unit)
65 {
66 // map to keep track of duplicate imports
67 // within this compilation unit
68 dups := Str:Using[:]
69
70 // process each import statement (remember the
71 // first one is an implicit import of sys)
72 unit.usings.each |Using u|
73 {
74 podName := u.podName
75
76 // check that this podName was in the compiler's
77 // input dependencies
78 checkUsingPod(this, podName, u.location)
79
80 // check for duplicate imports
81 key := podName
82 if (u.typeName != null) key += "::$u.typeName"
83 if (dups.containsKey(key))
84 {
85 err("Duplicate using '$key'", u.location)
86 return
87 }
88 dups[key] = u
89
90 // if already resolved, then just use it
91 u.resolvedPod = resolved[podName]
92
93 // resolve the import and cache in resolved map
94 if (u.resolvedPod == null)
95 {
96 pod := ns.resolvePod(podName, false)
97 if (pod == null)
98 {
99 err("Pod not found '$podName'", u.location)
100 return
101 }
102 resolved[podName] = u.resolvedPod = pod
103 }
104
105 // if type specified, then resolve type
106 if (u.typeName != null)
107 {
108 u.resolvedType = u.resolvedPod.resolveType(u.typeName, false)
109 if (u.resolvedType== null)
110 {
111 err("Type not found in pod '$podName::$u.typeName'", u.location)
112 return
113 }
114 }
115 }
116 }
117
118 **
119 ** Create a unified map of type names to CType[] for all the
120 ** imports in the specified unit (this includes types within
121 ** the pod being compilied itself). For example if foo::Thing
122 ** and bar::Thing are imported, then importedTypes would contain
123 ** "Thing" : [foo::Thing, bar::Thing]
124 **
125 private Void resolveImportedTypes(CompilationUnit unit)
126 {
127 // name -> CType[]
128 types := Str:CType[][:]
129
130 // add types for my own pod
131 addAll(types, compiler.pod.types)
132
133 // add pod level imports first
134 unit.usings.each |Using u|
135 {
136 if (u.typeName == null)
137 addAll(types, u.resolvedPod.types)
138 }
139
140 // add type specific imports last (these
141 // override any pod level imports)
142 unit.usings.each |Using u|
143 {
144 if (u.typeName != null)
145 {
146 if (u.asName == null)
147 {
148 types[u.typeName] = [u.resolvedType]
149 }
150 else
151 {
152 remove(types, u.resolvedType)
153 types[u.asName] = [u.resolvedType]
154 }
155 }
156 }
157
158 /*
159
160
161
162
163
164
165 // save away on unit
166 unit.importedTypes = types
167 }
168
169 private Void addAll(Str:CType[] types, CType[] toAdd)
170 {
171 toAdd.each |CType t|
172 {
173 list := types[t.name]
174 if (list == null) types[t.name] = list = CType[,]
175 list.add(t)
176 }
177 }
178
179 private Void remove(Str:CType[] types, CType t)
180 {
181 list := types[t.name]
182 if (list != null)
183 {
184 for (i:=0; i<list.size; ++i)
185 if (list[i].qname == t.qname) { list.removeAt(i); break }
186 }
187 }
188
189 //////////////////////////////////////////////////////////////////////////
190 // Utils
191 //////////////////////////////////////////////////////////////////////////
192
193 **
194 ** Resolve a fully qualified type name into its CType representation.
195 ** This may be a TypeDef within the compilation units or could be
196 ** an imported type. If the type name cannot be resolved then we
197 ** log an error and return null.
198 **
199 static CType resolveQualified(CompilerSupport cs, Str podName, Str typeName, Location location)
200 {
201 // first check pod being compiled
202 if (podName == cs.compiler.pod.name)
203 {
204 t := cs.compiler.pod.resolveType(typeName, false)
205 if (t == null)
206 {
207 cs.err("Type '$typeName' not found within pod being compiled", location)
208 return null
209 }
210 return t
211 }
212
213 // otherwise we need to try to resolve pod
214 pod := cs.ns.resolvePod(podName, false)
215 if (pod == null)
216 {
217 cs.err("Pod not found '$podName'", location);
218 return null
219 }
220
221 // check that we have a dependency on the pod
222 checkUsingPod(cs, podName, location)
223
224 // now try to lookup type
225 t := pod.resolveType(typeName, false)
226 if (t == null)
227 {
228 cs.err("Type '$typeName' not found in pod '$podName'", location);
229 return null
230 }
231
232 return t
233 }
234
235 **
236 ** Check that a pod name is in the dependency list.
237 **
238 static Void checkUsingPod(CompilerSupport cs, Str podName, Location loc)
239 {
240 if (!cs.ns.depends.containsKey(podName) &&
241 cs.compiler.pod.name != podName &&
242 !cs.compiler.input.isScript)
243 {
244 cs.err("Using '$podName' which is not a declared dependency for '$cs.compiler.pod.name'", loc)
245 }
246 }
247
248 //////////////////////////////////////////////////////////////////////////
249 // Fields
250 //////////////////////////////////////////////////////////////////////////
251
252 Str:CPod resolved := Str:CPod[:] // reuse CPods across units
253
254 }