]> git.buserror.net Git - polintos/scott/priv.git/blob - idlcomp/namespace.cc
Add copyright to idlcomp files that were previously just "Written by:".
[polintos/scott/priv.git] / idlcomp / namespace.cc
1 // Code to maintain and search namespaces
2 //
3 // This software is copyright (c) 2006 Scott Wood <scott@buserror.net>.
4 // 
5 // Permission is hereby granted, free of charge, to any person obtaining a copy of
6 // this software and associated documentation files (the "Software"), to deal with
7 // the Software without restriction, including without limitation the rights to
8 // use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
9 // of the Software, and to permit persons to whom the Software is furnished to do
10 // so, subject to the following condition:
11 // 
12 // The above copyright notice and this permission notice shall be
13 // included in all copies or substantial portions of the Software.
14 // 
15 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
17 // FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE
18 // CONTRIBUTORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS WITH THE
21 // SOFTWARE.
22
23 #include <idlc.h>
24 #include <parser.h>
25
26 void NameSpace::lookup_imports()
27 {
28         for (list<StrListRef>::iterator i = import_strs.begin();
29              i != import_strs.end(); ++i)
30         {
31                 StrList *strl = *i;
32                 Symbol *sym = lookup_sym(toplevel, strl, this);
33                 NameSpace *ns = dynamic_cast<NameSpace *>(sym);
34                 
35                 if (!ns) {
36                         const String *str = strl->back();
37                         yyerrorfl(str->file, str->line, "\"%s\" is not a namespace.",
38                                   sym->get_fq_name()->flatten()->c_str());
39                         throw UserError();
40                 }
41                 
42                 imports.push_back(ns);
43         }
44
45         for (const_iterator i = begin(); i != end(); ++i) {
46                 Symbol *sym = (*i).second;
47                 sym->lookup_imports();
48         }
49 }
50
51
52 // Find the namespace in which "name" is declared.
53 // The rules for namespace precedence are in doc/idl/namespace-precedence.
54
55 NameSpace *NameSpace::search(const String *name, Symbol *exclude)
56 {       
57         // Rule #1: Check current namespace first.
58         Symbol *sym = lookup_noex(name, true);
59         if (sym && sym != exclude)
60                 return this;
61
62         // Rule #2: Check imported symbols
63         Symbol *found = NULL;
64         
65         for (list<NameSpaceRef>::iterator i = imports.begin();
66              i != imports.end(); ++i)
67         {
68                 NameSpace *ns = *i;
69                 Symbol *newfound = ns->lookup_noex(name, true);
70                 
71                 if (newfound) {
72                         if (newfound == exclude)
73                                 continue;
74
75                         if (found && found != newfound) {
76                                 yyerrorfl(name->file, name->line, 
77                                           "\"%s\" is ambiguous.  Two possibilities "
78                                           "(there may be more) are:",
79                                           name->c_str());
80
81                                 yyerrorfl(found->name->file, found->name->line, "   \"%s\"",
82                                           found->get_fq_name()->flatten()->c_str());
83                                 yyerrorfl(newfound->name->file, newfound->name->line, "   \"%s\"",
84                                           newfound->get_fq_name()->flatten()->c_str());
85
86                                 throw UserError();
87                         }
88                         
89                         found = newfound;
90                 }
91         }
92         
93         if (found)
94                 return found->get_ns();
95         
96         return NULL;
97 }
98
99 static NameSpace *search_for_namespace(const String *name, NameSpace *ctx,
100                                        Symbol *exclude)
101 {
102         while (ctx) {
103                 NameSpace *ret = ctx->search(name, exclude);
104
105                 if (ret)
106                         return ret;
107
108                 ctx = ctx->get_ns();
109         }
110         
111         yyerrorfl(name->file, name->line,
112                   "Unknown symbol \"%s\" in namespace search path.",
113                name->c_str());
114
115         throw UserError();
116 }
117
118 Symbol *lookup_sym(NameSpace *ns, StrList *name, NameSpace *ctx,
119                    Symbol *exclude)
120 {
121         Symbol *sym = NULL;
122         NameSpace *top = ns;
123
124         assert(current_pass != 1);
125         assert(name->size() != 0);
126         assert(ctx);
127         
128         StrList::iterator i = name->begin();
129         
130         if (name->front()->length() != 0) {
131                 ns = search_for_namespace(name->front(), ctx, exclude);
132                 
133                 if (!ns)
134                         ns = top;
135         } else {
136                 ++i;
137         }
138
139         assert(i != name->end());
140         bool first = true;
141         
142         while (i != name->end()) {
143                 try {
144                         sym = ns->lookup(*i, first)->get_concrete_sym();
145                 }
146                 
147                 catch (SymbolNotFound) {
148                         yyerrorfl((*i)->file, (*i)->line, 
149                                   "Unknown symbol \"%s\" in \"%s\".",
150                                   (*i)->c_str(), ns->get_fq_name()->flatten()->c_str());
151
152                         throw UserError();
153                 }
154                 
155                 ++i;
156                 first = false;
157
158                 ns = dynamic_cast<NameSpace *>(sym);
159
160                 if (!ns && i != name->end()) {
161                         yyerrorfl((*i)->file, (*i)->line, 
162                                   "\"%s\" is not a namespace.",
163                                   sym->get_fq_name()->flatten()->c_str());
164
165                         throw UserError();
166                 }
167         }
168
169         assert(sym);
170         return sym;
171 }
172
173 // Returns the namespace (new or otherwise) on success, or NULL on failure.
174 NameSpace *add_nspace(StrList *name, bool push)
175 {
176         StrList::iterator i = name->begin();
177         NameSpace *ns = cur_nspace;
178         Symbol *sym;
179         
180         if ((*i)->length() == 0) {
181                 yyerrorfl((*i)->file, (*i)->line, 
182                           "Namespaces cannot be declared with an absolute path.");
183                 return NULL;
184         }
185
186         for (; i != name->end(); i++) {
187                 if ((*i)->token == '*') {
188                         yyerrorfl((*i)->file, (*i)->line, 
189                                   "'*' is only allowed with \"using\".");
190                         return NULL;
191                 }
192         
193                 sym = ns->lookup_noex(*i, true);
194                 if (!sym) {
195                         UserNameSpace *new_ns;
196                         
197                         try {
198                                 // Cannot throw DuplicateSymbol, but it can throw
199                                 // InvalidArgument due to user error (such as trying
200                                 // to implicitly declare a namespace inside of a
201                                 // struct/class/whatever.
202                         
203                                 new_ns = new UserNameSpace(*i);
204                                 ns->add_user(new_ns);
205                         }
206                         
207                         catch (InvalidArgument) {
208                                 yyerrorfl((*i)->file, (*i)->line,
209                                           "Cannot create namespace \"%s\" inside of a "
210                                           "non-namespace type or import namespace.",
211                                           (*i)->c_str());
212                                 return NULL;
213                         }
214                         
215                         ns = new_ns;
216                 } else {
217                         // Don't let the user declare things in non-user namespaces. 
218                         // Besides the headache of verifying that it's something
219                         // that belongs, and of determining order for situations
220                         // where it matters, it's just icky.
221
222
223                         ns = dynamic_cast<UserNameSpace *>(sym);
224                         if (!ns) {
225                                 yyerrorfl((*i)->file, (*i)->line,
226                                           "\"%s\" is not a namespace.",
227                                           sym->get_fq_name()->flatten()->c_str());
228         
229                                 return NULL;
230                         }
231                 }
232         }
233         
234         if (push)
235                 nspace_stack.push_front(cur_nspace);
236
237         cur_nspace = ns;
238         return ns;
239 }
240
241 void pop_nspace()
242 {
243         if (nspace_stack.empty())
244                 BUG();
245
246         cur_nspace = nspace_stack.front();
247         nspace_stack.pop_front();
248 }
249
250 void NameSpace::add_user(Symbol *sym)
251 {
252         try {
253                 add(sym, false);
254         }
255
256         catch (DuplicateSymbol) {
257                 yyerrorfl(sym->name->file, sym->name->line, 
258                           "\"%s\" already exists in this %s.",
259                           sym->name->c_str(), description());
260                 throw UserError();
261         }
262 }
263
264 void NameSpace::add_import(Symbol *sym, const char *filename)
265 {
266         try {
267                 add(sym, true);
268         }
269
270         catch (DuplicateSymbol) {
271                 yyerrorf("\"%s\" already exists in %s.",
272                          sym->name->c_str(), get_fq_name()->flatten()->c_str());
273                 BUG();
274         }
275         
276         catch (InvalidArgument) {
277                 yyerrorf("\"%s\" caused an InvalidArgument upon add.", filename);
278                 throw UserError();
279         }
280 }
281
282 Symbol::~Symbol()
283 {
284         if (ns && !ns->dying) {
285                 try {
286                         ns->del(this);
287                 }
288         
289                 catch (SymbolNotFound) {
290                         fprintf(stderr, "SymbolNotFound in Symbol::~Symbol(), cannot propagate\n");
291                 }
292         }
293 }
294
295 StrList *Symbol::get_fq_name(const char *append, bool not_last) const
296 {
297         StrList *ret;
298         const String *s = NULL;
299
300         if (name) {
301                 if (append && not_last &&
302                     !dynamic_cast<const UserNameSpace *>(this))
303                 {
304                         String *mut_s = new String(name);
305                         mut_s->append(append);
306                         s = mut_s;
307                 } else {
308                         s = name;
309                 }
310         } else if (!ns && !dynamic_cast<const UserNameSpace *>(this)) {
311                 s = new String("<temporary>");
312         } else if (!ns) {
313                 s = new String("<toplevel>");
314         } else {
315                 s = new String("<anonymous>");
316         }
317         
318         if (!ns || !ns->name) {
319                 ret = new StrList();
320         } else {
321                 ret = ns->get_fq_name(append, true);
322         }
323
324         ret->push_back(s);
325         return ret;
326 }
327
328 Symbol *Symbol::find_toplevel_type()
329 {
330         Symbol *cur = this, *prev;
331
332         do {
333                 prev = cur;
334                 cur = cur->get_ns();
335         } while (!dynamic_cast<UserNameSpace *>(cur));
336         
337         return prev;
338 }
339
340 // FIXME: Use double-dot for initial delimit, but only if
341 // generating IDLC-style names.  More generally, it should
342 // allow an arbitrary alternate global scope prefix.
343
344 String *StrList::flatten(const char *delimit)
345 {
346         String *ret = new String();
347         
348         for (const_iterator i = begin(); i != end();) {
349                 const String *str = *i;
350                 ret->append(*str);
351                 
352                 if (++i != end())
353                         ret->append(delimit);
354         }
355         
356         const String *str = back();
357         ret->file = str->file;
358         ret->line = str->line;
359         
360         return ret;
361 }