]> git.buserror.net Git - polintos/scott/priv.git/blob - idlcomp/main.cc
Add first draft of marshalling spec
[polintos/scott/priv.git] / idlcomp / main.cc
1 // Entry point for the interface compiler
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 // 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).
20 //
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:
24 //
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).
29 //
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.
36 //
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.
40 //
41 //      This is the only pass that involves actually reading and
42 //      parsing the input file(s).
43 //
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.
48 //
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.
53 //
54 //      This is done by calling the virtual lookup_chain() method on
55 //      the root namespace, which recursively calls it on all of its
56 //      contents.
57 //
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
63 //      output. 
64 //      
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.
72 //
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
79 //      in pass 1).
80 //
81 //   5: Perform any remaining semantic analysis, now that all references
82 //      to other entities have been looked up.  
83 //
84 //      This is done by calling the virtual final_analysis() method on
85 //      the root namespace, which recursively calls it on all of its
86 //      contents.
87 //
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
91 //      contents.
92 //
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...
101
102 #include <climits>
103 #include <cstdio>
104 #include <cstdarg>
105 #include <cstring>
106 #include <cstdlib>
107 #include <cerrno>
108
109 #include <idlc.h>
110 #include <lang.h>
111 #include <targets.h>
112 #include <util.h>
113
114 int num_err;
115 unsigned int enum_pos;
116 bool no_output;
117 bool server_stubs;
118 bool makedep;
119 int current_pass;
120 int traversal;
121 Con *yylval_con;
122 String **yylval_string;
123
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;
128
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;
135
136 void print_usage()
137 {
138         fprintf(stderr,
139 "Usage: %s [options] [<input(s)>]\n\n"
140 "Options are:\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",
164 cmdname);
165 }
166
167 static void set_lang(const char *lang)
168 {
169         if (output_lang) {
170                 fprintf(stderr, "Only one language may be specified.\n");
171                 throw UserError();
172         }
173
174         for (Language *l = first_lang; l; l = l->next) {
175                 if (!strcasecmp(l->name, lang)) {
176                         output_lang = l;
177                         return;
178                 }
179         }
180         
181         fprintf(stderr, "Unsupported language \"%s\".\n", lang);
182         throw UserError();
183 }
184
185 static void set_target(const char *targ)
186 {
187         if (target != &targets[0]) {
188                 fprintf(stderr, "Only one target architecture may be specified.\n");
189                 throw UserError();
190         }
191         
192         for (int i = 1; i <= max_target; i++) {
193                 if (!strcasecmp(targ, targets[i].name)) {
194                         target = &targets[i];
195                         return;
196                 }
197         }
198
199         fprintf(stderr, "Unsupported target \"%s\".\n", targ);
200         throw UserError();
201 }
202
203 // Returns true if the argument is consumed.
204 static bool shortopt(char c, const char *arg)
205 {
206         switch (c) {
207                 case 'd':
208                         idl_debug = 1;
209                         cdl_debug = 1;
210                         break;
211
212                 case 'h':
213                         print_usage();
214                         exit(0);
215                         
216                 case 'i':
217                         if (!arg) {
218                                 fprintf(stderr, "The -i option requires an argument.\n");
219                                 throw UserError();
220                         }
221                         
222                         UserNameSpace::declare_import(arg);
223                         return true;
224                 
225                 case 'M':
226                         makedep = true;
227                         break;
228                 
229                 case 'n':
230                         no_output = true;
231                         break;
232                 
233                 case 'l':
234                         if (!arg) {
235                                 fprintf(stderr, "The -l option requires an argument.\n");
236                                 throw UserError();
237                         }
238
239                         set_lang(arg);
240                         return true;
241                 
242                 case 'o':
243                         if (output_dir) {
244                                 fprintf(stderr, "Only one output directory may be specified.\n");
245                                 throw UserError();
246                         }
247                 
248                         if (!arg) {
249                                 fprintf(stderr, "The -o option requires an argument.\n");
250                                 throw UserError();
251                         }
252
253                         output_dir = arg;
254                         return true;
255                 
256                 case 'r':
257                         server_stubs = true;
258                         break;
259
260                 case 's':
261                         if (output_ns_name) {
262                                 fprintf(stderr, "Only one output namespace may be specified.\n");
263                                 throw UserError();
264                         }
265                 
266                         output_ns_name = arg;
267                         return true;
268
269                 case 't':
270                         if (!arg) {
271                                 fprintf(stderr, "The -t option requires an argument.\n");
272                                 throw UserError();
273                         }
274                         
275                         set_target(arg);
276                         return true;
277
278                 default:
279                         fprintf(stderr, "%s: unknown option %c\n", cmdname, c);
280                         throw UserError();
281         }
282         
283         return false;
284 }
285
286 static void longopt(const char *s, const char *arg)
287 {
288         if (!strcmp(s, "help")) {
289                 print_usage();
290                 exit(0);
291         } else if (!strcmp(s, "debug")) {
292                 idl_debug = 1;
293                 cdl_debug = 1;
294         } else if (!strcmp(s, "include")) {
295                 if (!arg) {
296                         fprintf(stderr, "The --include option requires an argument.\n");
297                         throw UserError();
298                 }
299
300                 UserNameSpace::declare_import(arg);
301         } else if (!strcmp(s, "makedep")) {
302                 makedep = true;
303         } else if (!strcmp(s, "no-output")) {
304                 no_output = true;
305         } else if (!strcmp(s, "output")) {
306                 if (output_dir) {
307                         fprintf(stderr, "Only one output directory may be specified.\n");
308                         throw UserError();
309                 }
310
311                 if (!arg) {
312                         fprintf(stderr, "The --output option requires an argument.\n");
313                         throw UserError();
314                 }
315
316                 output_dir = arg;
317         } else if (!strcmp(s, "namespace")) {
318                 if (output_ns_name) {
319                         fprintf(stderr, "Only one output namespace may be specified.\n");
320                         throw UserError();
321                 }
322
323                 if (!arg) {
324                         fprintf(stderr, "The --namespace option requires an argument.\n");
325                         throw UserError();
326                 }
327         
328                 output_ns_name = arg;
329         } else if (!strcmp(s, "language")) {
330                 if (!arg) {
331                         fprintf(stderr, "The --language option requires an argument.\n");
332                         throw UserError();
333                 }
334
335                 set_lang(arg);
336         } else if (!strcmp(s, "target")) {
337                 if (!arg) {
338                         fprintf(stderr, "The --target option requires an argument.\n");
339                         throw UserError();
340                 }
341
342                 set_target(arg);
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);
347                 printf("\n");
348                 exit(0);
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);
353                 printf("\n");
354                 exit(0);
355         } else if (!strcmp(s, "server")) {
356                 server_stubs = true;
357         } else {
358                 fprintf(stderr, "%s: unknown option \"%s\"\n", cmdname, s);
359                 throw UserError();
360         }
361 }
362
363 static int global_argc;
364 static const char **global_argv;
365 static int got_dashdash;
366
367 static void process_args(void)
368 {
369         int i;
370         size_t j;
371         char *s;
372         
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];
376                 
377                         if (opt[1] == '-') {
378                                 if (!opt[2]) {
379                                         got_dashdash = 1;
380                                 } else {
381                                         s = strchr(&opt[2], '=');
382
383                                         if (!s)
384                                                 longopt(&opt[2], NULL);
385                                         else {
386                                                 *s=0;
387                                                 longopt(&opt[2], s+1);
388                                         }
389                                 }
390                         } else for (j = 1; j < strlen(opt); j++) {
391                                 if (shortopt(opt[j], global_argv[i + 1]))
392                                         i++;
393                         }
394                 } else {
395                         inputs.push_back(global_argv[i]);
396                 }
397         }
398 }
399
400 // Provide automatic closing of a file handle upon exception
401 struct FileHolder {
402         FILE *f;
403         
404         FileHolder()
405         {
406                 f = NULL;
407         }
408         
409         ~FileHolder()
410         {
411                 if (f)
412                         fclose(f);
413         }
414 };
415
416 void parse_inputs(bool cdl)
417 {
418         for (list<const char *>::iterator i = inputs.begin();
419              i != inputs.end(); ++i)
420         {
421                 FileHolder fh;
422                 cur_input_file = *i;
423
424                 curline = 1;
425                 
426                 if (strcmp(cur_input_file, "-")) {
427                         yyin = fh.f = fopen(cur_input_file, "r");
428                         if (!yyin) {
429                                 fprintf(stderr, "Cannot open input file \"%s\": %s\n",
430                                         cur_input_file, strerror(errno));
431                                 throw UserError();
432                         }
433                 } else {
434                         yyin = stdin;
435                         cur_input_file = "stdin";
436                 }
437                 
438                 if (cdl) {
439                         cur_nspace = cdl_toplevel;
440                         setup_cdlparse();
441                         
442                         if (cdl_parse())
443                                 throw UserError();
444                 } else {
445                         cur_nspace = output_ns;
446                         setup_idlparse();
447
448                         if (idl_parse())
449                                 throw UserError();
450                 }
451                 
452                 if (finish_lex() || num_err)
453                         throw UserError();
454         }
455         
456         cur_input_file = "<none>";
457         curline = 0;
458
459         if (!nspace_stack.empty())
460                 BUG();
461 }
462
463 static void init_output_ns()
464 {
465         StrList *strl;
466
467         try {
468                 String *str = new String(output_ns_name);
469                 strl = new StrList(str);
470         }
471
472         catch (InvalidArgument) {
473                 fprintf(stderr, "Output namespace \"%s\" is not vaild.\n",
474                         output_ns_name);
475                 throw UserError();
476         }
477
478         cur_nspace = toplevel;
479         NameSpace *new_ns = add_nspace(strl, false);
480
481         if (!new_ns)
482                 BUG();
483                 
484         NameSpace *conflict = check_for_imports(new_ns);
485         if (conflict) {         
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());
490
491                 throw UserError();
492         }
493
494         if (!new_ns)
495                 BUG();
496         output_ns = dynamic_cast<UserNameSpace *>(new_ns);
497         if (!output_ns)
498                 BUG();
499 }
500
501 void lookup_system_object()
502 {
503         try {
504                 StrList *sl = new StrList;
505                 
506                 sl->push_back(new String(""));
507                 sl->push_back(new String("System"));
508                 sl->push_back(new String("Object"));
509
510                 Symbol *sym = lookup_sym(toplevel, sl, toplevel);
511                 System_Object = dynamic_cast<Interface *>(sym);
512         }
513         
514         catch (UserError) {
515                 yyerrorf("Could not find System.Object.");
516                 yyerrorf("Be sure to include the file or directory containing "
517                          "the System namespace.");
518
519                 throw UserError();
520         }
521         
522         if (!System_Object) {
523                 yyerrorf("System.Object is not an interface.");
524                 throw UserError();
525         }
526 }
527
528 void lookup_system_vstruct()
529 {
530         try {
531                 StrList *sl = new StrList();
532                 
533                 sl->push_back(new String(""));
534                 sl->push_back(new String("System"));
535                 sl->push_back(new String("VStruct"));
536
537                 Symbol *sym = lookup_sym(toplevel, sl, toplevel);
538                 System_VStruct = dynamic_cast<Struct *>(sym);
539         }
540         
541         catch (UserError) {
542                 yyerrorf("Could not find System.VStruct.");
543                 yyerrorf("Be sure to include the file or directory containing "
544                          "the System namespace.");
545
546                 throw UserError();
547         }
548         
549         if (!System_VStruct) {
550                 yyerrorf("System.VStruct is not a struct.");
551                 throw UserError();
552         }
553         
554         if (!System_VStruct->is_virtual()) {
555                 yyerrorf("System.VStruct is not virtual.");
556                 throw UserError();
557         }
558 }
559
560 void normal_output()
561 {
562         if (inputs.empty()) {
563                 fprintf(stderr, "No inputs given.\n");
564                 print_usage();
565                 throw UserError();
566         }
567         
568         if (makedep) {
569                 fprintf(stderr, "Dependency generation is only supported "
570                                 "when generating language output.\n");
571                 throw UserError();
572         }
573         
574         if (!no_output && !output_dir) {
575                 fprintf(stderr, "An output directory must be specified "
576                                 "when not using -n.\n");
577                 throw UserError();
578         }
579
580         if (!output_ns_name) {
581                 fprintf(stderr, "An output namespace must be specified.\n");
582                 throw UserError();
583         }
584
585         if (!no_output && output_dir)
586                 makepath(output_dir);
587
588         init_output_ns();
589         
590         current_pass = 1;
591         autorelease_pool.clean();
592
593         parse_inputs(false);
594
595         current_pass = 2;
596         autorelease_pool.clean();
597
598         lookup_system_object();
599         lookup_system_vstruct();
600         output_ns->lookup_imports();
601         
602         current_pass = 3;
603         autorelease_pool.clean();
604         
605         output_ns->lookup_chain();
606         if (num_err)
607                 throw UserError();
608         
609         current_pass = 4;
610         autorelease_pool.clean();
611
612         output_ns->lookup_misc();
613         if (num_err)
614                 throw UserError();
615
616         current_pass = 5;
617         autorelease_pool.clean();
618
619         output_ns->final_analysis();
620         if (num_err)
621                 throw UserError();
622
623         current_pass = 6;
624         autorelease_pool.clean();
625         
626         if (!no_output)
627                 output_ns->output(output_dir);
628
629         autorelease_pool.clean();
630 }
631
632 void language_output()
633 {
634         if (server_stubs) {
635                 if (inputs.empty()) {
636                         fprintf(stderr, "No inputs given.\n");
637                         print_usage();
638                         throw UserError();
639                 }
640         } else {
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");
645                         throw UserError();
646                 }
647
648                 if (!output_ns_name) {
649                         fprintf(stderr, "An output namespace must be specified.\n");
650                         throw UserError();
651                 }
652
653                 if (makedep) {
654                         fprintf(stderr, "Dependency information is currently only supported "
655                                         "for server stubs.\n");
656                         throw UserError();
657                 }
658         }
659
660         if (no_output) {
661                 fprintf(stderr, "-n may not be used when generating "
662                                 "language-specific output.\n");
663                 throw UserError();
664         }
665
666         if (!output_dir) {
667                 fprintf(stderr, "An output directory must be specified when "
668                                 "generating language-specific output.\n");
669                 throw UserError();
670         }
671
672         if (!makedep && target == &targets[0]) {
673                 fprintf(stderr, "Target detection not yet implemented.\n"
674                                 "Please specify a target architecture.\n");
675                         throw UserError();
676         }
677
678         if (!makedep)
679                 makepath(output_dir);
680
681         if (server_stubs) {
682                 current_pass = INT_MAX;
683                 lookup_system_object();
684                 lookup_system_vstruct();
685                 parse_inputs(true);
686                 autorelease_pool.clean();
687                 output_lang->output_server(cdl_toplevel, output_dir);
688         } else {
689                 StrList *output_ns_strl;
690                 try {
691                         String *output_ns_str = new String(output_ns_name);
692                         output_ns_strl = new StrList(output_ns_str);
693                 }
694                 
695                 catch (InvalidArgument) {
696                         fprintf(stderr, "Output namespace \"%s\" is not vaild.\n",
697                                 output_ns_name);
698                         throw UserError();
699                 }
700                 
701                 cur_nspace = toplevel;
702                 current_pass = INT_MAX;
703                 lookup_system_object();
704                 lookup_system_vstruct();
705                 
706                 Symbol *sym = lookup_sym(toplevel, output_ns_strl, toplevel);
707                 output_ns = dynamic_cast<UserNameSpace *>(sym);
708                 if (!output_ns) {
709                         fprintf(stderr, "Output namespace \"%s\" is not a pure namespace.\n",
710                                 output_ns_name);
711                         throw UserError();
712                 }
713                 
714                 output_ns->import_all_recursive();
715                 autorelease_pool.clean();
716                 output_lang->output_root(output_ns, output_dir);
717         }
718
719         autorelease_pool.clean();
720 }
721
722 int run_idlc(int argc, const char **argv)
723 {
724         global_argc = argc;
725         global_argv = argv;
726         cmdname = argv[0];
727
728         toplevel = new UserNameSpace();
729         cdl_toplevel = new UserNameSpace();
730
731         try {
732                 process_args();
733                         
734                 if (output_lang)
735                         language_output();
736                 else {
737                         if (server_stubs) {
738                                 fprintf(stderr, "The -r (--server) option may only be used "
739                                                 "with the -l (--lang) option.\n");
740
741                                 throw UserError();
742                         }
743                 
744                         normal_output();
745                 }
746         }
747         
748         catch (InternalError &e) {
749                 fprintf(stderr, "Internal idlc error at %s:%d\n",
750                         e.file, e.line);
751                 return 1;
752         }
753         
754         catch (InvalidArgument) {
755                 fprintf(stderr, "Internal idlc error: Uncaught InvalidArgument\n");
756                 return 1;
757         }
758
759         catch (SymbolNotFound) {
760                 fprintf(stderr, "Internal idlc error: Uncaught SymbolNotFound\n");
761                 return 1;
762         }
763
764         catch (DuplicateSymbol) {
765                 fprintf(stderr, "Internal idlc error: Uncaught DuplicateSymbol\n");
766                 return 1;
767         }
768         
769         catch (UserError) {
770                 // An error message has already been displayed.
771                 num_err++;
772         }
773         
774         catch (...) {
775                 fprintf(stderr, "%s:%d: Internal error: Uncaught Exception\n",
776                         cur_input_file, curline);
777                 return 1;
778         }
779
780         if (num_err)
781                 return 1;
782
783         return 0;
784 }
785
786 int main(int argc, const char **argv)
787 {
788         int ret = run_idlc(argc, argv);
789         autorelease_pool.clean();
790         return ret;
791 }
792
793 extern "C" int yywrap()
794 {
795         return 1;
796 }
797
798 extern char *yytext;
799
800 void idl_error(char *s)
801 {
802         if (strlen(yytext))
803                 fprintf(stderr, "%s:%d: %s at \"%s\".\n", cur_input_file, curline, s, yytext);
804         else
805                 fprintf(stderr, "%s:%d: %s at end of input.\n", cur_input_file, curline, s);
806
807         num_err++;
808 }
809
810 void yyerrorf(const char *s, ...)
811 {
812         va_list va;
813         va_start(va, s);
814         fprintf(stderr, "%s:%d: ", cur_input_file, curline);
815         vfprintf(stderr, s, va);
816         fprintf(stderr, "\n");
817         va_end(va);
818         
819         num_err++;
820 }
821
822 void yyerrorfl(const char *file, int line, const char *s, ...)
823 {
824         va_list va;
825         va_start(va, s);
826         fprintf(stderr, "%s:%d: ", file, line);
827         vfprintf(stderr, s, va);
828         fprintf(stderr, "\n");
829         va_end(va);
830         
831         num_err++;
832 }
833
834 int idl_lex()
835 {
836         return yylex();
837 }