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