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