1 // Entry point for the interface compiler
3 // This software is copyright (c) 2006 Scott Wood <scott@buserror.net>.
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.
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.
15 // The idlc program implements two main functions. One is to turn
16 // .idl files into binary type descriptions. The other is to turn
17 // those type descriptions into language-specific stubs (though a
18 // compiler/interpreter could also just read the binary types
19 // directly, as the ORB does).
21 // For the former function, it is necessary to do the work in multiple
22 // passes, as not all information is available the first time through due
23 // to the absence of forward declarations. The passes are as follows:
25 // 1: Parse the input text, and determine which symbols exist in
26 // which namespaces. At this point, no non-namespace-qualified
27 // symbol lookups will be done (though namespace-qualified lookups
28 // are done, such as to check for duplicate symbols).
30 // Objects will be created for each entity (Interface, Struct,
31 // Datum, Alias, etc.), but any reference to another entity will be
32 // stored as a StrList, not as an object pointer. This must be the
33 // case even if a lookup during this pass would have found
34 // something, as on the second pass it might find a "closer"
35 // matching symbol added later.
37 // The StrList will later be looked up with only the entity
38 // containing the reference as context, so namespace search rules
39 // must not depend on anything else.
41 // This is the only pass that involves actually reading and
42 // parsing the input file(s).
44 // 2: Look up the namespace names of using namespace.* statements.
45 // These are the only lookups where order of the statements in the
46 // file matters, and it alters the result of a lookup, so it must be
47 // completed before chain lookups are done.
49 // 3: Look up the symbol names recorded in pass 1 for aliases, and
50 // typedefs, and const datum initializers and types. This is done
51 // before the rest of the lookups, so there will always be a
52 // complete chain to follow in subsequent passes.
54 // This is done by calling the virtual lookup_chain() method on
55 // the root namespace, which recursively calls it on all of its
58 // 4: Look up other symbol names recorded in pass 1. At this point,
59 // unresolved alias chains may still exist and must be handled,
60 // though they should be fixed during this phase so that they do not
61 // appear in phase 4. Typedefs will remain fully chained, as the
62 // chain is semantically meaningful and must be preserved in the
65 // Const datum initializers are looked up in this pass despite being
66 // chains, since aliases aren't resolved in pass 2, and it's
67 // (slightly) easier to do the lookup in pass 3 and resolve the
68 // chain in pass 4 than to store a Symbol reference in pass 2,
69 // get the concerete sym in pass 3, and still have to either
70 // concrete-ize other Datums' initializers or wait until pass 4
71 // to resolve the chain.
73 // This is done by calling the virtual lookup_misc() method on the
74 // root namespace, which recursively calls it on all of its
75 // contents. Upon receiving this call, each entity object should
76 // call lookup_sym() or lookup_type() on the various StrLists it has
77 // stored, but should not do anything with the object pointer
78 // until pass 4 (unless the object was satisfactorily initialized
81 // 5: Perform any remaining semantic analysis, now that all references
82 // to other entities have been looked up.
84 // This is done by calling the virtual final_analysis() method on
85 // the root namespace, which recursively calls it on all of its
88 // 6: Generate output.
89 // This is done by calling the virtual output() method on
90 // the root namespace, which recursively calls it on all of its
93 // All circular dependencies must be contained within the set of files
94 // passed to the compiler in one invocation. Supporting circular
95 // dependencies over multiple invocations of the compiler would require
96 // explicitly running only certain passes, then compiling the other
97 // batch, and then finishing the original batch. It could be done with
98 // some pain (intermediate states would need to be serialized), but I
99 // only want to do it if there's a real need. Circular dependencies
100 // ought to be isolated fairly locally...
115 unsigned int enum_pos;
122 String **yylval_string;
124 UserNameSpaceRef toplevel, cdl_toplevel;
125 UserNameSpace *output_ns;
126 NameSpaceRef cur_nspace;
127 const char *cmdname, *output_dir, *cur_input_file = "<none>", *output_ns_name;
129 list<const char *> inputs;
130 list<NameSpaceRef> nspace_stack;
131 Language *first_lang, *output_lang;
132 Interface *System_Object;
133 Struct *System_VStruct;
134 AutoReleasePool autorelease_pool;
139 "Usage: %s [options] [<input(s)>]\n\n"
141 "-d (--debug) Print parser debugging information.\n"
142 "-h (--help) Print this help screen.\n"
143 "-i DIR (--include=DIR) Look in DIR for resolving external type definitions.\n"
144 " Multiple include directories can be specified, and\n"
145 " will be mounted at the --namespace specified when\n"
146 " they were created.\n"
147 "-l LANG (--lang=LANG) Generate stubs for this language.\n"
148 "-M (--makedep) Output dependency information for make.\n"
149 "-n (--no-output) Do not produce output; only check for errors.\n"
150 "-o DIR (--output=DIR) Generate output in DIR.\n"
151 "-r (--server) Generate server stubs according to the input CDL files.\n"
152 "-s NS (--namespace=NS) Input files constitute the given namespace.\n"
153 "-t TARGET (--target=T) Generate stubs for the specified target architecture.\n"
154 " By default, the current architecture is targeted.\n"
155 "--show-languages List languages for which stub generation is supported.\n"
156 "--show-targets List supported target architectures.\n"
157 "If a language is specified, language-specific stubs for the output --namespace\n"
158 "will be placed in the specified output directory. All inputs must be specified\n"
159 "as --includes (including all components of the output namespace and anything\n"
160 "referenced by them). No non-option inputs may be specified.\n\n"
161 "If a language is not specified, inputs are treated as IDL source\n"
162 "files, and are compiled into the specified output directory, as\n"
163 "the specified output --namespace.\n\n",
167 static void set_lang(const char *lang)
170 fprintf(stderr, "Only one language may be specified.\n");
174 for (Language *l = first_lang; l; l = l->next) {
175 if (!strcasecmp(l->name, lang)) {
181 fprintf(stderr, "Unsupported language \"%s\".\n", lang);
185 static void set_target(const char *targ)
187 if (target != &targets[0]) {
188 fprintf(stderr, "Only one target architecture may be specified.\n");
192 for (int i = 1; i <= max_target; i++) {
193 if (!strcasecmp(targ, targets[i].name)) {
194 target = &targets[i];
199 fprintf(stderr, "Unsupported target \"%s\".\n", targ);
203 // Returns true if the argument is consumed.
204 static bool shortopt(char c, const char *arg)
218 fprintf(stderr, "The -i option requires an argument.\n");
222 UserNameSpace::declare_import(arg);
235 fprintf(stderr, "The -l option requires an argument.\n");
244 fprintf(stderr, "Only one output directory may be specified.\n");
249 fprintf(stderr, "The -o option requires an argument.\n");
261 if (output_ns_name) {
262 fprintf(stderr, "Only one output namespace may be specified.\n");
266 output_ns_name = arg;
271 fprintf(stderr, "The -t option requires an argument.\n");
279 fprintf(stderr, "%s: unknown option %c\n", cmdname, c);
286 static void longopt(const char *s, const char *arg)
288 if (!strcmp(s, "help")) {
291 } else if (!strcmp(s, "debug")) {
294 } else if (!strcmp(s, "include")) {
296 fprintf(stderr, "The --include option requires an argument.\n");
300 UserNameSpace::declare_import(arg);
301 } else if (!strcmp(s, "makedep")) {
303 } else if (!strcmp(s, "no-output")) {
305 } else if (!strcmp(s, "output")) {
307 fprintf(stderr, "Only one output directory may be specified.\n");
312 fprintf(stderr, "The --output option requires an argument.\n");
317 } else if (!strcmp(s, "namespace")) {
318 if (output_ns_name) {
319 fprintf(stderr, "Only one output namespace may be specified.\n");
324 fprintf(stderr, "The --namespace option requires an argument.\n");
328 output_ns_name = arg;
329 } else if (!strcmp(s, "language")) {
331 fprintf(stderr, "The --language option requires an argument.\n");
336 } else if (!strcmp(s, "target")) {
338 fprintf(stderr, "The --target option requires an argument.\n");
343 } else if (!strcmp(s, "show-languages")) {
344 printf("Supported language bindings:\n");
345 for (Language *l = first_lang; l; l = l->next)
346 printf(" %s\n", l->name);
349 } else if (!strcmp(s, "show-targets")) {
350 printf("Supported target architectures:\n");
351 for (int i = 1; i <= max_target; i++)
352 printf(" %s\n", targets[i].name);
355 } else if (!strcmp(s, "server")) {
358 fprintf(stderr, "%s: unknown option \"%s\"\n", cmdname, s);
363 static int global_argc;
364 static const char **global_argv;
365 static int got_dashdash;
367 static void process_args(void)
373 for (i = 1; i < global_argc; i++) {
374 if (global_argv[i][0] == '-' && global_argv[i][1] && !got_dashdash) {
375 const char *opt = global_argv[i];
381 s = strchr(&opt[2], '=');
384 longopt(&opt[2], NULL);
387 longopt(&opt[2], s+1);
390 } else for (j = 1; j < strlen(opt); j++) {
391 if (shortopt(opt[j], global_argv[i + 1]))
395 inputs.push_back(global_argv[i]);
400 // Provide automatic closing of a file handle upon exception
416 void parse_inputs(bool cdl)
418 for (list<const char *>::iterator i = inputs.begin();
419 i != inputs.end(); ++i)
426 if (strcmp(cur_input_file, "-")) {
427 yyin = fh.f = fopen(cur_input_file, "r");
429 fprintf(stderr, "Cannot open input file \"%s\": %s\n",
430 cur_input_file, strerror(errno));
435 cur_input_file = "stdin";
439 cur_nspace = cdl_toplevel;
445 cur_nspace = output_ns;
452 if (finish_lex() || num_err)
456 cur_input_file = "<none>";
459 if (!nspace_stack.empty())
463 static void init_output_ns()
468 String *str = new String(output_ns_name);
469 strl = new StrList(str);
472 catch (InvalidArgument) {
473 fprintf(stderr, "Output namespace \"%s\" is not vaild.\n",
478 cur_nspace = toplevel;
479 NameSpace *new_ns = add_nspace(strl, false);
484 NameSpace *conflict = check_for_imports(new_ns);
486 fprintf(stderr, "Output namespace \"%s\" conflicts"
487 " with \"%s\" at \"%s\".\n",
488 output_ns_name, conflict->get_fq_name()->flatten()->c_str(),
489 conflict->get_path()->c_str());
496 output_ns = dynamic_cast<UserNameSpace *>(new_ns);
501 void lookup_system_object()
504 StrList *sl = new StrList;
506 sl->push_back(new String(""));
507 sl->push_back(new String("System"));
508 sl->push_back(new String("Object"));
510 Symbol *sym = lookup_sym(toplevel, sl, toplevel);
511 System_Object = dynamic_cast<Interface *>(sym);
515 yyerrorf("Could not find System.Object.");
516 yyerrorf("Be sure to include the file or directory containing "
517 "the System namespace.");
522 if (!System_Object) {
523 yyerrorf("System.Object is not an interface.");
528 void lookup_system_vstruct()
531 StrList *sl = new StrList();
533 sl->push_back(new String(""));
534 sl->push_back(new String("System"));
535 sl->push_back(new String("VStruct"));
537 Symbol *sym = lookup_sym(toplevel, sl, toplevel);
538 System_VStruct = dynamic_cast<Struct *>(sym);
542 yyerrorf("Could not find System.VStruct.");
543 yyerrorf("Be sure to include the file or directory containing "
544 "the System namespace.");
549 if (!System_VStruct) {
550 yyerrorf("System.VStruct is not a struct.");
554 if (!System_VStruct->is_virtual()) {
555 yyerrorf("System.VStruct is not virtual.");
562 if (inputs.empty()) {
563 fprintf(stderr, "No inputs given.\n");
569 fprintf(stderr, "Dependency generation is only supported "
570 "when generating language output.\n");
574 if (!no_output && !output_dir) {
575 fprintf(stderr, "An output directory must be specified "
576 "when not using -n.\n");
580 if (!output_ns_name) {
581 fprintf(stderr, "An output namespace must be specified.\n");
585 if (!no_output && output_dir)
586 makepath(output_dir);
591 autorelease_pool.clean();
596 autorelease_pool.clean();
598 lookup_system_object();
599 lookup_system_vstruct();
600 output_ns->lookup_imports();
603 autorelease_pool.clean();
605 output_ns->lookup_chain();
610 autorelease_pool.clean();
612 output_ns->lookup_misc();
617 autorelease_pool.clean();
619 output_ns->final_analysis();
624 autorelease_pool.clean();
627 output_ns->output(output_dir);
629 autorelease_pool.clean();
632 void language_output()
635 if (inputs.empty()) {
636 fprintf(stderr, "No inputs given.\n");
641 if (!inputs.empty()) {
642 fprintf(stderr, "Inputs may not be specified when generating "
643 "language-specific client output.\n"
644 "Use --include to specify input namespaces.\n");
648 if (!output_ns_name) {
649 fprintf(stderr, "An output namespace must be specified.\n");
654 fprintf(stderr, "Dependency information is currently only supported "
655 "for server stubs.\n");
661 fprintf(stderr, "-n may not be used when generating "
662 "language-specific output.\n");
667 fprintf(stderr, "An output directory must be specified when "
668 "generating language-specific output.\n");
672 if (!makedep && target == &targets[0]) {
673 fprintf(stderr, "Target detection not yet implemented.\n"
674 "Please specify a target architecture.\n");
679 makepath(output_dir);
682 current_pass = INT_MAX;
683 lookup_system_object();
684 lookup_system_vstruct();
686 autorelease_pool.clean();
687 output_lang->output_server(cdl_toplevel, output_dir);
689 StrList *output_ns_strl;
691 String *output_ns_str = new String(output_ns_name);
692 output_ns_strl = new StrList(output_ns_str);
695 catch (InvalidArgument) {
696 fprintf(stderr, "Output namespace \"%s\" is not vaild.\n",
701 cur_nspace = toplevel;
702 current_pass = INT_MAX;
703 lookup_system_object();
704 lookup_system_vstruct();
706 Symbol *sym = lookup_sym(toplevel, output_ns_strl, toplevel);
707 output_ns = dynamic_cast<UserNameSpace *>(sym);
709 fprintf(stderr, "Output namespace \"%s\" is not a pure namespace.\n",
714 output_ns->import_all_recursive();
715 autorelease_pool.clean();
716 output_lang->output_root(output_ns, output_dir);
719 autorelease_pool.clean();
722 int run_idlc(int argc, const char **argv)
728 toplevel = new UserNameSpace();
729 cdl_toplevel = new UserNameSpace();
738 fprintf(stderr, "The -r (--server) option may only be used "
739 "with the -l (--lang) option.\n");
748 catch (InternalError &e) {
749 fprintf(stderr, "Internal idlc error at %s:%d\n",
754 catch (InvalidArgument) {
755 fprintf(stderr, "Internal idlc error: Uncaught InvalidArgument\n");
759 catch (SymbolNotFound) {
760 fprintf(stderr, "Internal idlc error: Uncaught SymbolNotFound\n");
764 catch (DuplicateSymbol) {
765 fprintf(stderr, "Internal idlc error: Uncaught DuplicateSymbol\n");
770 // An error message has already been displayed.
775 fprintf(stderr, "%s:%d: Internal error: Uncaught Exception\n",
776 cur_input_file, curline);
786 int main(int argc, const char **argv)
788 int ret = run_idlc(argc, argv);
789 autorelease_pool.clean();
793 extern "C" int yywrap()
800 void idl_error(char *s)
803 fprintf(stderr, "%s:%d: %s at \"%s\".\n", cur_input_file, curline, s, yytext);
805 fprintf(stderr, "%s:%d: %s at end of input.\n", cur_input_file, curline, s);
810 void yyerrorf(const char *s, ...)
814 fprintf(stderr, "%s:%d: ", cur_input_file, curline);
815 vfprintf(stderr, s, va);
816 fprintf(stderr, "\n");
822 void yyerrorfl(const char *file, int line, const char *s, ...)
826 fprintf(stderr, "%s:%d: ", file, line);
827 vfprintf(stderr, s, va);
828 fprintf(stderr, "\n");