1 /* main.cc -- entry point for the interface compiler
3 * Written by Scott Wood <scott@buserror.net>
5 * The idlc program implements two main functions. One is to turn
6 * .idl files into binary type descriptions. The other is to turn
7 * those type descriptions into language-specific stubs (though a
8 * compiler/interpreter could also just read the binary types
9 * directly, as the ORB does).
11 * For the former function, it is necessary to do the work in multiple
12 * passes, as not all information is available the first time through due
13 * to the absence of forward declarations. The passes are as follows:
15 * 1: Parse the input text, and determine which symbols exist in
16 * which namespaces. At this point, no non-namespace-qualified
17 * symbol lookups will be done (though namespace-qualified lookups
18 * are done, such as to check for duplicate symbols).
20 * Objects will be created for each entity (Interface, Struct,
21 * Datum, Alias, etc.), but any reference to another entity will be
22 * stored as a StrList, not as an object pointer. This must be the
23 * case even if a lookup during this pass would have found
24 * something, as on the second pass it might find a "closer"
25 * matching symbol added later.
27 * The StrList will later be looked up with only the entity
28 * containing the reference as context, so namespace search rules
29 * must not depend on anything else.
31 * This is the only pass that involves actually reading and
32 * parsing the input file(s).
34 * 2: Look up the namespace names of using namespace.* statements.
35 * These are the only lookups where order of the statements in the
36 * file matters, and it alters the result of a lookup, so it must be
37 * completed before chain lookups are done.
39 * 3: Look up the symbol names recorded in pass 1 for aliases, and
40 * typedefs, and const datum initializers and types. This is done
41 * before the rest of the lookups, so there will always be a
42 * complete chain to follow in subsequent passes.
44 * This is done by calling the virtual lookup_chain() method on
45 * the root namespace, which recursively calls it on all of its
48 * 4: Look up other symbol names recorded in pass 1. At this point,
49 * unresolved alias chains may still exist and must be handled,
50 * though they should be fixed during this phase so that they do not
51 * appear in phase 4. Typedefs will remain fully chained, as the
52 * chain is semantically meaningful and must be preserved in the
55 * Const datum initializers are looked up in this pass despite being
56 * chains, since aliases aren't resolved in pass 2, and it's
57 * (slightly) easier to do the lookup in pass 3 and resolve the
58 * chain in pass 4 than to store a Symbol reference in pass 2,
59 * get the concerete sym in pass 3, and still have to either
60 * concrete-ize other Datums' initializers or wait until pass 4
61 * to resolve the chain.
63 * This is done by calling the virtual lookup_misc() method on the
64 * root namespace, which recursively calls it on all of its
65 * contents. Upon receiving this call, each entity object should
66 * call lookup_sym() or lookup_type() on the various StrLists it has
67 * stored, but should not do anything with the object pointer
68 * until pass 4 (unless the object was satisfactorily initialized
71 * 5: Perform any remaining semantic analysis, now that all references
72 * to other entities have been looked up.
74 * This is done by calling the virtual final_analysis() method on
75 * the root namespace, which recursively calls it on all of its
79 * This is done by calling the virtual output() method on
80 * the root namespace, which recursively calls it on all of its
83 * All circular dependencies must be contained within the set of files
84 * passed to the compiler in one invocation. Supporting circular
85 * dependencies over multiple invocations of the compiler would require
86 * explicitly running only certain passes, then compiling the other
87 * batch, and then finishing the original batch. It could be done with
88 * some pain (intermediate states would need to be serialized), but I
89 * only want to do it if there's a real need. Circular dependencies
90 * ought to be isolated fairly locally...
106 unsigned int enum_pos;
113 String **yylval_string;
115 UserNameSpaceRef toplevel, cdl_toplevel;
116 UserNameSpace *output_ns;
117 NameSpaceRef cur_nspace;
118 const char *cmdname, *output_dir, *cur_input_file = "<none>", *output_ns_name;
120 list<const char *> inputs;
121 list<NameSpaceRef> nspace_stack;
122 Language *first_lang, *output_lang;
123 Interface *System_Object;
124 Struct *System_VStruct;
125 AutoReleasePool autorelease_pool;
130 "Usage: %s [options] [<input(s)>]\n\n"
132 "-d (--debug) Print parser debugging information.\n"
133 "-h (--help) Print this help screen.\n"
134 "-i DIR (--include=DIR) Look in DIR for resolving external type definitions.\n"
135 " Multiple include directories can be specified, and\n"
136 " will be mounted at the --namespace specified when\n"
137 " they were created.\n"
138 "-l LANG (--lang=LANG) Generate stubs for this language.\n"
139 "-M (--makedep) Output dependency information for make.\n"
140 "-n (--no-output) Do not produce output; only check for errors.\n"
141 "-o DIR (--output=DIR) Generate output in DIR.\n"
142 "-r (--server) Generate server stubs according to the input CDL files.\n"
143 "-s NS (--namespace=NS) Input files constitute the given namespace.\n"
144 "-t TARGET (--target=T) Generate stubs for the specified target architecture.\n"
145 " By default, the current architecture is targeted.\n"
146 "--show-languages List languages for which stub generation is supported.\n"
147 "--show-targets List supported target architectures.\n"
148 "If a language is specified, language-specific stubs for the output --namespace\n"
149 "will be placed in the specified output directory. All inputs must be specified\n"
150 "as --includes (including all components of the output namespace and anything\n"
151 "referenced by them). No non-option inputs may be specified.\n\n"
152 "If a language is not specified, inputs are treated as IDL source\n"
153 "files, and are compiled into the specified output directory, as\n"
154 "the specified output --namespace.\n\n",
158 static void set_lang(const char *lang)
161 fprintf(stderr, "Only one language may be specified.\n");
165 for (Language *l = first_lang; l; l = l->next) {
166 if (!strcasecmp(l->name, lang)) {
172 fprintf(stderr, "Unsupported language \"%s\".\n", lang);
176 static void set_target(const char *targ)
178 if (target != &targets[0]) {
179 fprintf(stderr, "Only one target architecture may be specified.\n");
183 for (int i = 1; i <= max_target; i++) {
184 if (!strcasecmp(targ, targets[i].name)) {
185 target = &targets[i];
190 fprintf(stderr, "Unsupported target \"%s\".\n", targ);
194 // Returns true if the argument is consumed.
195 static bool shortopt(char c, const char *arg)
209 fprintf(stderr, "The -i option requires an argument.\n");
213 UserNameSpace::declare_import(arg);
226 fprintf(stderr, "The -l option requires an argument.\n");
235 fprintf(stderr, "Only one output directory may be specified.\n");
240 fprintf(stderr, "The -o option requires an argument.\n");
252 if (output_ns_name) {
253 fprintf(stderr, "Only one output namespace may be specified.\n");
257 output_ns_name = arg;
262 fprintf(stderr, "The -t option requires an argument.\n");
270 fprintf(stderr, "%s: unknown option %c\n", cmdname, c);
277 static void longopt(const char *s, const char *arg)
279 if (!strcmp(s, "help")) {
282 } else if (!strcmp(s, "debug")) {
285 } else if (!strcmp(s, "include")) {
287 fprintf(stderr, "The --include option requires an argument.\n");
291 UserNameSpace::declare_import(arg);
292 } else if (!strcmp(s, "makedep")) {
294 } else if (!strcmp(s, "no-output")) {
296 } else if (!strcmp(s, "output")) {
298 fprintf(stderr, "Only one output directory may be specified.\n");
303 fprintf(stderr, "The --output option requires an argument.\n");
308 } else if (!strcmp(s, "namespace")) {
309 if (output_ns_name) {
310 fprintf(stderr, "Only one output namespace may be specified.\n");
315 fprintf(stderr, "The --namespace option requires an argument.\n");
319 output_ns_name = arg;
320 } else if (!strcmp(s, "language")) {
322 fprintf(stderr, "The --language option requires an argument.\n");
327 } else if (!strcmp(s, "target")) {
329 fprintf(stderr, "The --target option requires an argument.\n");
334 } else if (!strcmp(s, "show-languages")) {
335 printf("Supported language bindings:\n");
336 for (Language *l = first_lang; l; l = l->next)
337 printf(" %s\n", l->name);
340 } else if (!strcmp(s, "show-targets")) {
341 printf("Supported target architectures:\n");
342 for (int i = 1; i <= max_target; i++)
343 printf(" %s\n", targets[i].name);
346 } else if (!strcmp(s, "server")) {
349 fprintf(stderr, "%s: unknown option \"%s\"\n", cmdname, s);
354 static int global_argc;
355 static const char **global_argv;
356 static int got_dashdash;
358 static void process_args(void)
364 for (i = 1; i < global_argc; i++) {
365 if (global_argv[i][0] == '-' && global_argv[i][1] && !got_dashdash) {
366 const char *opt = global_argv[i];
372 s = strchr(&opt[2], '=');
375 longopt(&opt[2], NULL);
378 longopt(&opt[2], s+1);
381 } else for (j = 1; j < strlen(opt); j++) {
382 if (shortopt(opt[j], global_argv[i + 1]))
386 inputs.push_back(global_argv[i]);
391 // Provide automatic closing of a file handle upon exception
407 void parse_inputs(bool cdl)
409 for (list<const char *>::iterator i = inputs.begin();
410 i != inputs.end(); ++i)
417 if (strcmp(cur_input_file, "-")) {
418 yyin = fh.f = fopen(cur_input_file, "r");
420 fprintf(stderr, "Cannot open input file \"%s\": %s\n",
421 cur_input_file, strerror(errno));
426 cur_input_file = "stdin";
430 cur_nspace = cdl_toplevel;
436 cur_nspace = output_ns;
443 if (finish_lex() || num_err)
447 cur_input_file = "<none>";
450 if (!nspace_stack.empty())
454 static void init_output_ns()
459 String *str = new String(output_ns_name);
460 strl = new StrList(str);
463 catch (InvalidArgument) {
464 fprintf(stderr, "Output namespace \"%s\" is not vaild.\n",
469 cur_nspace = toplevel;
470 NameSpace *new_ns = add_nspace(strl, false);
475 NameSpace *conflict = check_for_imports(new_ns);
477 fprintf(stderr, "Output namespace \"%s\" conflicts"
478 " with \"%s\" at \"%s\".\n",
479 output_ns_name, conflict->get_fq_name()->flatten()->c_str(),
480 conflict->get_path()->c_str());
487 output_ns = dynamic_cast<UserNameSpace *>(new_ns);
492 void lookup_system_object()
495 StrList *sl = new StrList;
497 sl->push_back(new String(""));
498 sl->push_back(new String("System"));
499 sl->push_back(new String("Object"));
501 Symbol *sym = lookup_sym(toplevel, sl, toplevel);
502 System_Object = dynamic_cast<Interface *>(sym);
506 yyerrorf("Could not find System.Object.");
507 yyerrorf("Be sure to include the file or directory containing "
508 "the System namespace.");
513 if (!System_Object) {
514 yyerrorf("System.Object is not an interface.");
519 void lookup_system_vstruct()
522 StrList *sl = new StrList();
524 sl->push_back(new String(""));
525 sl->push_back(new String("System"));
526 sl->push_back(new String("VStruct"));
528 Symbol *sym = lookup_sym(toplevel, sl, toplevel);
529 System_VStruct = dynamic_cast<Struct *>(sym);
533 yyerrorf("Could not find System.VStruct.");
534 yyerrorf("Be sure to include the file or directory containing "
535 "the System namespace.");
540 if (!System_VStruct) {
541 yyerrorf("System.VStruct is not a struct.");
545 if (!System_VStruct->is_virtual()) {
546 yyerrorf("System.VStruct is not virtual.");
553 if (inputs.empty()) {
554 fprintf(stderr, "No inputs given.\n");
560 fprintf(stderr, "Dependency generation is only supported "
561 "when generating language output.\n");
565 if (!no_output && !output_dir) {
566 fprintf(stderr, "An output directory must be specified "
567 "when not using -n.\n");
571 if (!output_ns_name) {
572 fprintf(stderr, "An output namespace must be specified.\n");
576 if (!no_output && output_dir)
577 makepath(output_dir);
582 autorelease_pool.clean();
587 autorelease_pool.clean();
589 lookup_system_object();
590 lookup_system_vstruct();
591 output_ns->lookup_imports();
594 autorelease_pool.clean();
596 output_ns->lookup_chain();
601 autorelease_pool.clean();
603 output_ns->lookup_misc();
608 autorelease_pool.clean();
610 output_ns->final_analysis();
615 autorelease_pool.clean();
618 output_ns->output(output_dir);
620 autorelease_pool.clean();
623 void language_output()
626 if (inputs.empty()) {
627 fprintf(stderr, "No inputs given.\n");
632 if (!inputs.empty()) {
633 fprintf(stderr, "Inputs may not be specified when generating "
634 "language-specific client output.\n"
635 "Use --include to specify input namespaces.\n");
639 if (!output_ns_name) {
640 fprintf(stderr, "An output namespace must be specified.\n");
645 fprintf(stderr, "Dependency information is currently only supported "
646 "for server stubs.\n");
652 fprintf(stderr, "-n may not be used when generating "
653 "language-specific output.\n");
658 fprintf(stderr, "An output directory must be specified when "
659 "generating language-specific output.\n");
663 if (!makedep && target == &targets[0]) {
664 fprintf(stderr, "Target detection not yet implemented.\n"
665 "Please specify a target architecture.\n");
670 makepath(output_dir);
673 current_pass = INT_MAX;
674 lookup_system_object();
675 lookup_system_vstruct();
677 autorelease_pool.clean();
678 output_lang->output_server(cdl_toplevel, output_dir);
680 StrList *output_ns_strl;
682 String *output_ns_str = new String(output_ns_name);
683 output_ns_strl = new StrList(output_ns_str);
686 catch (InvalidArgument) {
687 fprintf(stderr, "Output namespace \"%s\" is not vaild.\n",
692 cur_nspace = toplevel;
693 current_pass = INT_MAX;
694 lookup_system_object();
695 lookup_system_vstruct();
697 Symbol *sym = lookup_sym(toplevel, output_ns_strl, toplevel);
698 output_ns = dynamic_cast<UserNameSpace *>(sym);
700 fprintf(stderr, "Output namespace \"%s\" is not a pure namespace.\n",
705 output_ns->import_all_recursive();
706 autorelease_pool.clean();
707 output_lang->output_root(output_ns, output_dir);
710 autorelease_pool.clean();
713 int run_idlc(int argc, const char **argv)
719 toplevel = new UserNameSpace();
720 cdl_toplevel = new UserNameSpace();
729 fprintf(stderr, "The -r (--server) option may only be used "
730 "with the -l (--lang) option.\n");
739 catch (InternalError &e) {
740 fprintf(stderr, "Internal idlc error at %s:%d\n",
745 catch (InvalidArgument) {
746 fprintf(stderr, "Internal idlc error: Uncaught InvalidArgument\n");
750 catch (SymbolNotFound) {
751 fprintf(stderr, "Internal idlc error: Uncaught SymbolNotFound\n");
755 catch (DuplicateSymbol) {
756 fprintf(stderr, "Internal idlc error: Uncaught DuplicateSymbol\n");
761 // An error message has already been displayed.
766 fprintf(stderr, "%s:%d: Internal error: Uncaught Exception\n",
767 cur_input_file, curline);
777 int main(int argc, const char **argv)
779 int ret = run_idlc(argc, argv);
780 autorelease_pool.clean();
784 extern "C" int yywrap()
791 void idl_error(char *s)
794 fprintf(stderr, "%s:%d: %s at \"%s\".\n", cur_input_file, curline, s, yytext);
796 fprintf(stderr, "%s:%d: %s at end of input.\n", cur_input_file, curline, s);
801 void yyerrorf(const char *s, ...)
805 fprintf(stderr, "%s:%d: ", cur_input_file, curline);
806 vfprintf(stderr, s, va);
807 fprintf(stderr, "\n");
813 void yyerrorfl(const char *file, int line, const char *s, ...)
817 fprintf(stderr, "%s:%d: ", file, line);
818 vfprintf(stderr, s, va);
819 fprintf(stderr, "\n");