1 // Entry point for the interface compiler
3 // This software is copyright (c) 2006 Scott Wood <scott@buserror.net>.
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:
12 // The above copyright notice and this permission notice shall be
13 // included in all copies or substantial portions of the Software.
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
23 // The idlc program implements two main functions. One is to turn
24 // .idl files into binary type descriptions. The other is to turn
25 // those type descriptions into language-specific stubs (though a
26 // compiler/interpreter could also just read the binary types
27 // directly, as the ORB does).
29 // For the former function, it is necessary to do the work in multiple
30 // passes, as not all information is available the first time through due
31 // to the absence of forward declarations. The passes are as follows:
33 // 1: Parse the input text, and determine which symbols exist in
34 // which namespaces. At this point, no non-namespace-qualified
35 // symbol lookups will be done (though namespace-qualified lookups
36 // are done, such as to check for duplicate symbols).
38 // Objects will be created for each entity (Interface, Struct,
39 // Datum, Alias, etc.), but any reference to another entity will be
40 // stored as a StrList, not as an object pointer. This must be the
41 // case even if a lookup during this pass would have found
42 // something, as on the second pass it might find a "closer"
43 // matching symbol added later.
45 // The StrList will later be looked up with only the entity
46 // containing the reference as context, so namespace search rules
47 // must not depend on anything else.
49 // This is the only pass that involves actually reading and
50 // parsing the input file(s).
52 // 2: Look up the namespace names of using namespace.* statements.
53 // These are the only lookups where order of the statements in the
54 // file matters, and it alters the result of a lookup, so it must be
55 // completed before chain lookups are done.
57 // 3: Look up the symbol names recorded in pass 1 for aliases, and
58 // typedefs, and const datum initializers and types. This is done
59 // before the rest of the lookups, so there will always be a
60 // complete chain to follow in subsequent passes.
62 // This is done by calling the virtual lookup_chain() method on
63 // the root namespace, which recursively calls it on all of its
66 // 4: Look up other symbol names recorded in pass 1. At this point,
67 // unresolved alias chains may still exist and must be handled,
68 // though they should be fixed during this phase so that they do not
69 // appear in phase 4. Typedefs will remain fully chained, as the
70 // chain is semantically meaningful and must be preserved in the
73 // Const datum initializers are looked up in this pass despite being
74 // chains, since aliases aren't resolved in pass 2, and it's
75 // (slightly) easier to do the lookup in pass 3 and resolve the
76 // chain in pass 4 than to store a Symbol reference in pass 2,
77 // get the concerete sym in pass 3, and still have to either
78 // concrete-ize other Datums' initializers or wait until pass 4
79 // to resolve the chain.
81 // This is done by calling the virtual lookup_misc() method on the
82 // root namespace, which recursively calls it on all of its
83 // contents. Upon receiving this call, each entity object should
84 // call lookup_sym() or lookup_type() on the various StrLists it has
85 // stored, but should not do anything with the object pointer
86 // until pass 4 (unless the object was satisfactorily initialized
89 // 5: Perform any remaining semantic analysis, now that all references
90 // to other entities have been looked up.
92 // This is done by calling the virtual final_analysis() method on
93 // the root namespace, which recursively calls it on all of its
96 // 6: Generate output.
97 // This is done by calling the virtual output() method on
98 // the root namespace, which recursively calls it on all of its
101 // All circular dependencies must be contained within the set of files
102 // passed to the compiler in one invocation. Supporting circular
103 // dependencies over multiple invocations of the compiler would require
104 // explicitly running only certain passes, then compiling the other
105 // batch, and then finishing the original batch. It could be done with
106 // some pain (intermediate states would need to be serialized), but I
107 // only want to do it if there's a real need. Circular dependencies
108 // ought to be isolated fairly locally...
123 unsigned int enum_pos;
130 String **yylval_string;
132 UserNameSpaceRef toplevel, cdl_toplevel;
133 UserNameSpace *output_ns;
134 NameSpaceRef cur_nspace;
135 const char *cmdname, *output_dir, *cur_input_file = "<none>", *output_ns_name;
137 list<const char *> inputs;
138 list<NameSpaceRef> nspace_stack;
139 Language *first_lang, *output_lang;
140 Interface *System_Object;
141 Struct *System_VStruct;
142 AutoReleasePool autorelease_pool;
147 "Usage: %s [options] [<input(s)>]\n\n"
149 "-d (--debug) Print parser debugging information.\n"
150 "-h (--help) Print this help screen.\n"
151 "-i DIR (--include=DIR) Look in DIR for resolving external type definitions.\n"
152 " Multiple include directories can be specified, and\n"
153 " will be mounted at the --namespace specified when\n"
154 " they were created.\n"
155 "-l LANG (--lang=LANG) Generate stubs for this language.\n"
156 "-M (--makedep) Output dependency information for make.\n"
157 "-n (--no-output) Do not produce output; only check for errors.\n"
158 "-o DIR (--output=DIR) Generate output in DIR.\n"
159 "-r (--server) Generate server stubs according to the input CDL files.\n"
160 "-s NS (--namespace=NS) Input files constitute the given namespace.\n"
161 "-t TARGET (--target=T) Generate stubs for the specified target architecture.\n"
162 " By default, the current architecture is targeted.\n"
163 "--show-languages List languages for which stub generation is supported.\n"
164 "--show-targets List supported target architectures.\n"
165 "If a language is specified, language-specific stubs for the output --namespace\n"
166 "will be placed in the specified output directory. All inputs must be specified\n"
167 "as --includes (including all components of the output namespace and anything\n"
168 "referenced by them). No non-option inputs may be specified.\n\n"
169 "If a language is not specified, inputs are treated as IDL source\n"
170 "files, and are compiled into the specified output directory, as\n"
171 "the specified output --namespace.\n\n",
175 static void set_lang(const char *lang)
178 fprintf(stderr, "Only one language may be specified.\n");
182 for (Language *l = first_lang; l; l = l->next) {
183 if (!strcasecmp(l->name, lang)) {
189 fprintf(stderr, "Unsupported language \"%s\".\n", lang);
193 static void set_target(const char *targ)
195 if (target != &targets[0]) {
196 fprintf(stderr, "Only one target architecture may be specified.\n");
200 for (int i = 1; i <= max_target; i++) {
201 if (!strcasecmp(targ, targets[i].name)) {
202 target = &targets[i];
207 fprintf(stderr, "Unsupported target \"%s\".\n", targ);
211 // Returns true if the argument is consumed.
212 static bool shortopt(char c, const char *arg)
226 fprintf(stderr, "The -i option requires an argument.\n");
230 UserNameSpace::declare_import(arg);
243 fprintf(stderr, "The -l option requires an argument.\n");
252 fprintf(stderr, "Only one output directory may be specified.\n");
257 fprintf(stderr, "The -o option requires an argument.\n");
269 if (output_ns_name) {
270 fprintf(stderr, "Only one output namespace may be specified.\n");
274 output_ns_name = arg;
279 fprintf(stderr, "The -t option requires an argument.\n");
287 fprintf(stderr, "%s: unknown option %c\n", cmdname, c);
294 static void longopt(const char *s, const char *arg)
296 if (!strcmp(s, "help")) {
299 } else if (!strcmp(s, "debug")) {
302 } else if (!strcmp(s, "include")) {
304 fprintf(stderr, "The --include option requires an argument.\n");
308 UserNameSpace::declare_import(arg);
309 } else if (!strcmp(s, "makedep")) {
311 } else if (!strcmp(s, "no-output")) {
313 } else if (!strcmp(s, "output")) {
315 fprintf(stderr, "Only one output directory may be specified.\n");
320 fprintf(stderr, "The --output option requires an argument.\n");
325 } else if (!strcmp(s, "namespace")) {
326 if (output_ns_name) {
327 fprintf(stderr, "Only one output namespace may be specified.\n");
332 fprintf(stderr, "The --namespace option requires an argument.\n");
336 output_ns_name = arg;
337 } else if (!strcmp(s, "language")) {
339 fprintf(stderr, "The --language option requires an argument.\n");
344 } else if (!strcmp(s, "target")) {
346 fprintf(stderr, "The --target option requires an argument.\n");
351 } else if (!strcmp(s, "show-languages")) {
352 printf("Supported language bindings:\n");
353 for (Language *l = first_lang; l; l = l->next)
354 printf(" %s\n", l->name);
357 } else if (!strcmp(s, "show-targets")) {
358 printf("Supported target architectures:\n");
359 for (int i = 1; i <= max_target; i++)
360 printf(" %s\n", targets[i].name);
363 } else if (!strcmp(s, "server")) {
366 fprintf(stderr, "%s: unknown option \"%s\"\n", cmdname, s);
371 static int global_argc;
372 static const char **global_argv;
373 static int got_dashdash;
375 static void process_args(void)
381 for (i = 1; i < global_argc; i++) {
382 if (global_argv[i][0] == '-' && global_argv[i][1] && !got_dashdash) {
383 const char *opt = global_argv[i];
389 s = strchr(&opt[2], '=');
392 longopt(&opt[2], NULL);
395 longopt(&opt[2], s+1);
398 } else for (j = 1; j < strlen(opt); j++) {
399 if (shortopt(opt[j], global_argv[i + 1]))
403 inputs.push_back(global_argv[i]);
408 // Provide automatic closing of a file handle upon exception
424 void parse_inputs(bool cdl)
426 for (list<const char *>::iterator i = inputs.begin();
427 i != inputs.end(); ++i)
434 if (strcmp(cur_input_file, "-")) {
435 yyin = fh.f = fopen(cur_input_file, "r");
437 fprintf(stderr, "Cannot open input file \"%s\": %s\n",
438 cur_input_file, strerror(errno));
443 cur_input_file = "stdin";
447 cur_nspace = cdl_toplevel;
453 cur_nspace = output_ns;
460 if (finish_lex() || num_err)
464 cur_input_file = "<none>";
467 if (!nspace_stack.empty())
471 static void init_output_ns()
476 String *str = new String(output_ns_name);
477 strl = new StrList(str);
480 catch (InvalidArgument) {
481 fprintf(stderr, "Output namespace \"%s\" is not vaild.\n",
486 cur_nspace = toplevel;
487 NameSpace *new_ns = add_nspace(strl, false);
492 NameSpace *conflict = check_for_imports(new_ns);
494 fprintf(stderr, "Output namespace \"%s\" conflicts"
495 " with \"%s\" at \"%s\".\n",
496 output_ns_name, conflict->get_fq_name()->flatten()->c_str(),
497 conflict->get_path()->c_str());
504 output_ns = dynamic_cast<UserNameSpace *>(new_ns);
509 void lookup_system_object()
512 StrList *sl = new StrList;
514 sl->push_back(new String(""));
515 sl->push_back(new String("System"));
516 sl->push_back(new String("Object"));
518 Symbol *sym = lookup_sym(toplevel, sl, toplevel);
519 System_Object = dynamic_cast<Interface *>(sym);
523 yyerrorf("Could not find System.Object.");
524 yyerrorf("Be sure to include the file or directory containing "
525 "the System namespace.");
530 if (!System_Object) {
531 yyerrorf("System.Object is not an interface.");
536 void lookup_system_vstruct()
539 StrList *sl = new StrList();
541 sl->push_back(new String(""));
542 sl->push_back(new String("System"));
543 sl->push_back(new String("VStruct"));
545 Symbol *sym = lookup_sym(toplevel, sl, toplevel);
546 System_VStruct = dynamic_cast<Struct *>(sym);
550 yyerrorf("Could not find System.VStruct.");
551 yyerrorf("Be sure to include the file or directory containing "
552 "the System namespace.");
557 if (!System_VStruct) {
558 yyerrorf("System.VStruct is not a struct.");
562 if (!System_VStruct->is_virtual()) {
563 yyerrorf("System.VStruct is not virtual.");
570 if (inputs.empty()) {
571 fprintf(stderr, "No inputs given.\n");
577 fprintf(stderr, "Dependency generation is only supported "
578 "when generating language output.\n");
582 if (!no_output && !output_dir) {
583 fprintf(stderr, "An output directory must be specified "
584 "when not using -n.\n");
588 if (!output_ns_name) {
589 fprintf(stderr, "An output namespace must be specified.\n");
593 if (!no_output && output_dir)
594 makepath(output_dir);
599 autorelease_pool.clean();
604 autorelease_pool.clean();
606 lookup_system_object();
607 lookup_system_vstruct();
608 output_ns->lookup_imports();
611 autorelease_pool.clean();
613 output_ns->lookup_chain();
618 autorelease_pool.clean();
620 output_ns->lookup_misc();
625 autorelease_pool.clean();
627 output_ns->final_analysis();
632 autorelease_pool.clean();
635 output_ns->output(output_dir);
637 autorelease_pool.clean();
640 void language_output()
643 if (inputs.empty()) {
644 fprintf(stderr, "No inputs given.\n");
649 if (!inputs.empty()) {
650 fprintf(stderr, "Inputs may not be specified when generating "
651 "language-specific client output.\n"
652 "Use --include to specify input namespaces.\n");
656 if (!output_ns_name) {
657 fprintf(stderr, "An output namespace must be specified.\n");
662 fprintf(stderr, "Dependency information is currently only supported "
663 "for server stubs.\n");
669 fprintf(stderr, "-n may not be used when generating "
670 "language-specific output.\n");
675 fprintf(stderr, "An output directory must be specified when "
676 "generating language-specific output.\n");
680 if (!makedep && target == &targets[0]) {
681 fprintf(stderr, "Target detection not yet implemented.\n"
682 "Please specify a target architecture.\n");
687 makepath(output_dir);
690 current_pass = INT_MAX;
691 lookup_system_object();
692 lookup_system_vstruct();
694 autorelease_pool.clean();
695 output_lang->output_server(cdl_toplevel, output_dir);
697 StrList *output_ns_strl;
699 String *output_ns_str = new String(output_ns_name);
700 output_ns_strl = new StrList(output_ns_str);
703 catch (InvalidArgument) {
704 fprintf(stderr, "Output namespace \"%s\" is not vaild.\n",
709 cur_nspace = toplevel;
710 current_pass = INT_MAX;
711 lookup_system_object();
712 lookup_system_vstruct();
714 Symbol *sym = lookup_sym(toplevel, output_ns_strl, toplevel);
715 output_ns = dynamic_cast<UserNameSpace *>(sym);
717 fprintf(stderr, "Output namespace \"%s\" is not a pure namespace.\n",
722 output_ns->import_all_recursive();
723 autorelease_pool.clean();
724 output_lang->output_root(output_ns, output_dir);
727 autorelease_pool.clean();
730 int run_idlc(int argc, const char **argv)
736 toplevel = new UserNameSpace();
737 cdl_toplevel = new UserNameSpace();
746 fprintf(stderr, "The -r (--server) option may only be used "
747 "with the -l (--lang) option.\n");
756 catch (InternalError &e) {
757 fprintf(stderr, "Internal idlc error at %s:%d\n",
762 catch (InvalidArgument) {
763 fprintf(stderr, "Internal idlc error: Uncaught InvalidArgument\n");
767 catch (SymbolNotFound) {
768 fprintf(stderr, "Internal idlc error: Uncaught SymbolNotFound\n");
772 catch (DuplicateSymbol) {
773 fprintf(stderr, "Internal idlc error: Uncaught DuplicateSymbol\n");
778 // An error message has already been displayed.
783 fprintf(stderr, "%s:%d: Internal error: Uncaught Exception\n",
784 cur_input_file, curline);
794 int main(int argc, const char **argv)
796 int ret = run_idlc(argc, argv);
797 autorelease_pool.clean();
801 extern "C" int yywrap()
808 void idl_error(char *s)
811 fprintf(stderr, "%s:%d: %s at \"%s\".\n", cur_input_file, curline, s, yytext);
813 fprintf(stderr, "%s:%d: %s at end of input.\n", cur_input_file, curline, s);
818 void yyerrorf(const char *s, ...)
822 fprintf(stderr, "%s:%d: ", cur_input_file, curline);
823 vfprintf(stderr, s, va);
824 fprintf(stderr, "\n");
830 void yyerrorfl(const char *file, int line, const char *s, ...)
834 fprintf(stderr, "%s:%d: ", file, line);
835 vfprintf(stderr, s, va);
836 fprintf(stderr, "\n");