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