class
compiler::ResolveImports
sys::Obj
compiler::CompilerSupport
compiler::CompilerStep
compiler::ResolveImports
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.verbose("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 if (!ns.depends.containsKey(podName) && !compiler.input.isScript)
79 err("Using '$podName' which is not a declared dependency for '$compiler.pod.name'", u.location)
80
81 // check for duplicate imports
82 key := podName
83 if (u.typeName != null) key += "::$u.typeName"
84 if (dups.containsKey(key))
85 {
86 err("Duplicate using '$key'", u.location)
87 return
88 }
89 dups[key] = u
90
91 // if already resolved, then just use it
92 u.resolvedPod = resolved[podName]
93
94 // resolve the import and cache in resolved map
95 if (u.resolvedPod == null)
96 {
97 pod := ns.resolvePod(podName, false)
98 if (pod == null)
99 {
100 err("Pod not found '$podName'", u.location)
101 return
102 }
103 resolved[podName] = u.resolvedPod = pod
104 }
105
106 // if type specified, then resolve type
107 if (u.typeName != null)
108 {
109 u.resolvedType = u.resolvedPod.resolveType(u.typeName, false)
110 if (u.resolvedType== null)
111 {
112 err("Type not found in pod '$podName::$u.typeName'", u.location)
113 return
114 }
115 }
116 }
117 }
118
119 **
120 ** Create a unified map of type names to CType[] for all the
121 ** imports in the specified unit (this includes types within
122 ** the pod being compilied itself). For example if foo::Thing
123 ** and bar::Thing are imported, then importedTypes would contain
124 ** "Thing" : [foo::Thing, bar::Thing]
125 **
126 private Void resolveImportedTypes(CompilationUnit unit)
127 {
128 // name -> CType[]
129 types := Str:CType[][:]
130
131 // add types for my own pod
132 addAll(types, compiler.pod.types)
133
134 // add pod level imports first
135 unit.usings.each |Using u|
136 {
137 if (u.typeName == null)
138 addAll(types, u.resolvedPod.types)
139 }
140
141 // add type specific imports last (these
142 // override any pod level imports)
143 unit.usings.each |Using u|
144 {
145 if (u.typeName != null)
146 {
147 if (u.asName == null)
148 {
149 types[u.typeName] = [u.resolvedType]
150 }
151 else
152 {
153 remove(types, u.resolvedType)
154 types[u.asName] = [u.resolvedType]
155 }
156 }
157 }
158
159 /*
160
161
162
163
164
165
166 // save away on unit
167 unit.importedTypes = types
168 }
169
170 private Void addAll(Str:CType[] types, CType[] toAdd)
171 {
172 toAdd.each |CType t|
173 {
174 list := types[t.name]
175 if (list == null) types[t.name] = list = CType[,]
176 list.add(t)
177 }
178 }
179
180 private Void remove(Str:CType[] types, CType t)
181 {
182 list := types[t.name]
183 if (list != null)
184 {
185 for (i:=0; i<list.size; ++i)
186 if (list[i].qname == t.qname) { list.removeAt(i); break }
187 }
188 }
189
190 //////////////////////////////////////////////////////////////////////////
191 // Utils
192 //////////////////////////////////////////////////////////////////////////
193
194 **
195 ** Resolve a fully qualified type name into its CType representation.
196 ** This may be a TypeDef within the compilation units or could be
197 ** an imported type. If the type name cannot be resolved then we
198 ** log an error and return null.
199 **
200 static CType resolveQualified(CompilerSupport cs, Str podName, Str typeName, Location location)
201 {
202 // first check pod being compiled
203 if (podName == cs.compiler.pod.name)
204 {
205 t := cs.compiler.pod.resolveType(typeName, false)
206 if (t == null)
207 {
208 cs.err("Type '$typeName' not found within pod being compiled", location)
209 return null
210 }
211 return t
212 }
213
214 // otherwise we need to try to resolve pod
215 pod := cs.ns.resolvePod(podName, false)
216 if (pod == null)
217 {
218 cs.err("Pod not found '$podName'", location);
219 return null
220 }
221
222 // check that we have a dependency on the pod
223 if (!cs.ns.depends.containsKey(podName) && !cs.compiler.input.isScript)
224 cs.err("Using '$podName' which is not a declared dependency for '$cs.compiler.pod.name'", location)
225
226 // now try to lookup type
227 t := pod.resolveType(typeName, false)
228 if (t == null)
229 {
230 cs.err("Type '$typeName' not found in pod '$podName'", location);
231 return null
232 }
233
234 return t
235 }
236
237 //////////////////////////////////////////////////////////////////////////
238 // Fields
239 //////////////////////////////////////////////////////////////////////////
240
241 Str:CPod resolved := Str:CPod[:] // reuse CPods across units
242
243 }
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.verbose("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 if (!ns.depends.containsKey(podName) && !compiler.input.isScript)
79 err("Using '$podName' which is not a declared dependency for '$compiler.pod.name'", u.location)
80
81 // check for duplicate imports
82 key := podName
83 if (u.typeName != null) key += "::$u.typeName"
84 if (dups.containsKey(key))
85 {
86 err("Duplicate using '$key'", u.location)
87 return
88 }
89 dups[key] = u
90
91 // if already resolved, then just use it
92 u.resolvedPod = resolved[podName]
93
94 // resolve the import and cache in resolved map
95 if (u.resolvedPod == null)
96 {
97 pod := ns.resolvePod(podName, false)
98 if (pod == null)
99 {
100 err("Pod not found '$podName'", u.location)
101 return
102 }
103 resolved[podName] = u.resolvedPod = pod
104 }
105
106 // if type specified, then resolve type
107 if (u.typeName != null)
108 {
109 u.resolvedType = u.resolvedPod.resolveType(u.typeName, false)
110 if (u.resolvedType== null)
111 {
112 err("Type not found in pod '$podName::$u.typeName'", u.location)
113 return
114 }
115 }
116 }
117 }
118
119 **
120 ** Create a unified map of type names to CType[] for all the
121 ** imports in the specified unit (this includes types within
122 ** the pod being compilied itself). For example if foo::Thing
123 ** and bar::Thing are imported, then importedTypes would contain
124 ** "Thing" : [foo::Thing, bar::Thing]
125 **
126 private Void resolveImportedTypes(CompilationUnit unit)
127 {
128 // name -> CType[]
129 types := Str:CType[][:]
130
131 // add types for my own pod
132 addAll(types, compiler.pod.types)
133
134 // add pod level imports first
135 unit.usings.each |Using u|
136 {
137 if (u.typeName == null)
138 addAll(types, u.resolvedPod.types)
139 }
140
141 // add type specific imports last (these
142 // override any pod level imports)
143 unit.usings.each |Using u|
144 {
145 if (u.typeName != null)
146 {
147 if (u.asName == null)
148 {
149 types[u.typeName] = [u.resolvedType]
150 }
151 else
152 {
153 remove(types, u.resolvedType)
154 types[u.asName] = [u.resolvedType]
155 }
156 }
157 }
158
159 /*
160
161
162
163
164
165
166 // save away on unit
167 unit.importedTypes = types
168 }
169
170 private Void addAll(Str:CType[] types, CType[] toAdd)
171 {
172 toAdd.each |CType t|
173 {
174 list := types[t.name]
175 if (list == null) types[t.name] = list = CType[,]
176 list.add(t)
177 }
178 }
179
180 private Void remove(Str:CType[] types, CType t)
181 {
182 list := types[t.name]
183 if (list != null)
184 {
185 for (i:=0; i<list.size; ++i)
186 if (list[i].qname == t.qname) { list.removeAt(i); break }
187 }
188 }
189
190 //////////////////////////////////////////////////////////////////////////
191 // Utils
192 //////////////////////////////////////////////////////////////////////////
193
194 **
195 ** Resolve a fully qualified type name into its CType representation.
196 ** This may be a TypeDef within the compilation units or could be
197 ** an imported type. If the type name cannot be resolved then we
198 ** log an error and return null.
199 **
200 static CType resolveQualified(CompilerSupport cs, Str podName, Str typeName, Location location)
201 {
202 // first check pod being compiled
203 if (podName == cs.compiler.pod.name)
204 {
205 t := cs.compiler.pod.resolveType(typeName, false)
206 if (t == null)
207 {
208 cs.err("Type '$typeName' not found within pod being compiled", location)
209 return null
210 }
211 return t
212 }
213
214 // otherwise we need to try to resolve pod
215 pod := cs.ns.resolvePod(podName, false)
216 if (pod == null)
217 {
218 cs.err("Pod not found '$podName'", location);
219 return null
220 }
221
222 // check that we have a dependency on the pod
223 if (!cs.ns.depends.containsKey(podName) && !cs.compiler.input.isScript)
224 cs.err("Using '$podName' which is not a declared dependency for '$cs.compiler.pod.name'", location)
225
226 // now try to lookup type
227 t := pod.resolveType(typeName, false)
228 if (t == null)
229 {
230 cs.err("Type '$typeName' not found in pod '$podName'", location);
231 return null
232 }
233
234 return t
235 }
236
237 //////////////////////////////////////////////////////////////////////////
238 // Fields
239 //////////////////////////////////////////////////////////////////////////
240
241 Str:CPod resolved := Str:CPod[:] // reuse CPods across units
242
243 }