]> git.xonotic.org Git - xonotic/gmqcc.git/blob - main.c
Allow setting configuration file via commandline
[xonotic/gmqcc.git] / main.c
1 /*
2  * Copyright (C) 2012
3  *     Dale Weiler
4  *     Wolfgang Bumiller
5  *
6  * Permission is hereby granted, free of charge, to any person obtaining a copy of
7  * this software and associated documentation files (the "Software"), to deal in
8  * the Software without restriction, including without limitation the rights to
9  * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
10  * of the Software, and to permit persons to whom the Software is furnished to do
11  * so, subject to the following conditions:
12  *
13  * The above copyright notice and this permission notice shall be included in all
14  * copies or substantial portions of the Software.
15  *
16  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22  * SOFTWARE.
23  */
24 #include "gmqcc.h"
25 #include "lexer.h"
26
27 /* TODO: cleanup this whole file .. it's a fuckign mess */
28
29 /* set by the standard */
30 const oper_info *operators      = NULL;
31 size_t           operator_count = 0;
32 static bool      opts_output_wasset = false;
33
34 typedef struct { char *filename; int   type;  } argitem;
35 typedef struct { char *name;     char *value; } ppitem;
36 static argitem *items = NULL;
37 static ppitem  *ppems = NULL;
38
39 #define TYPE_QC  0
40 #define TYPE_ASM 1
41 #define TYPE_SRC 2
42
43 static const char *app_name;
44
45 static void version() {
46     con_out("GMQCC %d.%d.%d Built %s %s\n",
47         GMQCC_VERSION_MINOR,
48         GMQCC_VERSION_MAJOR,
49         GMQCC_VERSION_PATCH,
50         __DATE__,
51         __TIME__
52     );
53 }
54
55 static int usage() {
56     con_out("usage: %s [options] [files...]", app_name);
57     con_out("options:\n"
58             "  -h, --help             show this help message\n"
59             "  -debug                 turns on compiler debug messages\n"
60             "  -memchk                turns on compiler memory leak check\n");
61     con_out("  -o, --output=file      output file, defaults to progs.dat\n"
62             "  -s filename            add a progs.src file to be used\n");
63     con_out("  -E                     stop after preprocessing\n");
64     con_out("  -std=standard          select one of the following standards\n"
65             "       -std=qcc          original QuakeC\n"
66             "       -std=fteqcc       fteqcc QuakeC\n"
67             "       -std=gmqcc        this compiler (default)\n");
68     con_out("  -f<flag>               enable a flag\n"
69             "  -fno-<flag>            disable a flag\n"
70             "  -fhelp                 list possible flags\n");
71     con_out("  -W<warning>            enable a warning\n"
72             "  -Wno-<warning>         disable a warning\n"
73             "  -Wall                  enable all warnings\n"
74             "  -Werror                treat warnings as errors\n");
75     con_out("  -Whelp                 list possible warnings\n");
76     con_out("  -O<number>             optimization level\n"
77             "  -O<name>               enable specific optimization\n"
78             "  -Ono-<name>            disable specific optimization\n"
79             "  -Ohelp                 list optimizations\n");
80     con_out("  -force-crc=num         force a specific checksum into the header\n");
81     return -1;
82 }
83
84 /* command line parsing */
85 static bool options_witharg(int *argc_, char ***argv_, char **out) {
86     int  argc   = *argc_;
87     char **argv = *argv_;
88
89     if (argv[0][2]) {
90         *out = argv[0]+2;
91         return true;
92     }
93     /* eat up the next */
94     if (argc < 2) /* no parameter was provided */
95         return false;
96
97     *out = argv[1];
98     --*argc_;
99     ++*argv_;
100     return true;
101 }
102
103 static bool options_long_witharg_all(const char *optname, int *argc_, char ***argv_, char **out, int ds, bool split) {
104     int  argc   = *argc_;
105     char **argv = *argv_;
106
107     size_t len = strlen(optname);
108
109     if (strncmp(argv[0]+ds, optname, len))
110         return false;
111
112     /* it's --optname, check how the parameter is supplied */
113     if (argv[0][ds+len] == '=') {
114         /* using --opt=param */
115         *out = argv[0]+ds+len+1;
116         return true;
117     }
118
119     if (!split || argc < ds) /* no parameter was provided, or only single-arg form accepted */
120         return false;
121
122     /* using --opt param */
123     *out = argv[1];
124     --*argc_;
125     ++*argv_;
126     return true;
127 }
128 static bool options_long_witharg(const char *optname, int *argc_, char ***argv_, char **out) {
129     return options_long_witharg_all(optname, argc_, argv_, out, 2, true);
130 }
131 static bool options_long_gcc(const char *optname, int *argc_, char ***argv_, char **out) {
132     return options_long_witharg_all(optname, argc_, argv_, out, 1, false);
133 }
134
135 static bool options_parse(int argc, char **argv) {
136     bool argend = false;
137     size_t itr;
138     char  buffer[1024];
139     char *redirout = (char*)stdout;
140     char *redirerr = (char*)stderr;
141     char *config   = NULL;
142
143     while (!argend && argc > 1) {
144         char *argarg;
145         argitem item;
146         ppitem  macro;
147
148         ++argv;
149         --argc;
150
151         if (argv[0][0] == '-') {
152             /* All gcc-type long options */
153             if (options_long_gcc("std", &argc, &argv, &argarg)) {
154                 if (!strcmp(argarg, "gmqcc") || !strcmp(argarg, "default")) {
155
156                     opts_set(opts.flags, ADJUST_VECTOR_FIELDS,  true);
157                     opts.standard = COMPILER_GMQCC;
158
159                 } else if (!strcmp(argarg, "qcc")) {
160
161                     opts_set(opts.flags, ADJUST_VECTOR_FIELDS,  false);
162                     opts_set(opts.flags, ASSIGN_FUNCTION_TYPES, true);
163                     opts.standard = COMPILER_QCC;
164
165                 } else if (!strcmp(argarg, "fte") || !strcmp(argarg, "fteqcc")) {
166
167                     opts_set(opts.flags, FTEPP,                    true);
168                     opts_set(opts.flags, TRANSLATABLE_STRINGS,     true);
169                     opts_set(opts.flags, ADJUST_VECTOR_FIELDS,     false);
170                     opts_set(opts.flags, ASSIGN_FUNCTION_TYPES,    true);
171                     opts_set(opts.warn, WARN_TERNARY_PRECEDENCE,   true);
172                     opts_set(opts.flags, CORRECT_TERNARY,          false);
173                     opts.standard = COMPILER_FTEQCC;
174
175                 } else if (!strcmp(argarg, "qccx")) {
176
177                     opts_set(opts.flags, ADJUST_VECTOR_FIELDS,  false);
178                     opts.standard = COMPILER_QCCX;
179
180                 } else {
181                     con_out("Unknown standard: %s\n", argarg);
182                     return false;
183                 }
184                 continue;
185             }
186             if (options_long_gcc("force-crc", &argc, &argv, &argarg)) {
187                 opts.forcecrc   = true;
188                 opts.forced_crc = strtol(argarg, NULL, 0);
189                 continue;
190             }
191             if (options_long_gcc("redirout", &argc, &argv, &redirout)) {
192                 con_change(redirout, redirerr);
193                 continue;
194             }
195             if (options_long_gcc("redirerr", &argc, &argv, &redirerr)) {
196                 con_change(redirout, redirerr);
197                 continue;
198             }
199             if (options_long_gcc("config", &argc, &argv, &argarg)) {
200                 config = argarg;
201                 continue;
202             }
203
204             /* show defaults (like pathscale) */
205             if (!strcmp(argv[0]+1, "show-defaults")) {
206                 size_t itr;
207                 char   buffer[1024];
208                 for (itr = 0; itr < COUNT_FLAGS; ++itr) {
209                     if (!OPTS_FLAG(itr))
210                         continue;
211
212                     memset(buffer, 0, sizeof(buffer));
213                     util_strtononcmd(opts_flag_list[itr].name, buffer, strlen(opts_flag_list[itr].name) + 1);
214
215                     con_out("-f%s ", buffer);
216                 }
217                 for (itr = 0; itr < COUNT_WARNINGS; ++itr) {
218                     if (!OPTS_WARN(itr))
219                         continue;
220
221                     memset(buffer, 0, sizeof(buffer));
222                     util_strtononcmd(opts_warn_list[itr].name, buffer, strlen(opts_warn_list[itr].name) + 1);
223                     con_out("-W%s ", buffer);
224                 }
225                 con_out("\n");
226                 exit(0);
227             }
228
229             if (!strcmp(argv[0]+1, "debug")) {
230                 opts.debug = true;
231                 continue;
232             }
233             if (!strcmp(argv[0]+1, "dump")) {
234                 opts.dump = true;
235                 continue;
236             }
237             if (!strcmp(argv[0]+1, "dumpfin")) {
238                 opts.dumpfin = true;
239                 continue;
240             }
241             if (!strcmp(argv[0]+1, "memchk")) {
242                 opts.memchk = true;
243                 continue;
244             }
245             if (!strcmp(argv[0]+1, "nocolor")) {
246                 con_color(0);
247                 continue;
248             }
249
250             switch (argv[0][1]) {
251                 /* -h, show usage but exit with 0 */
252                 case 'h':
253                     usage();
254                     exit(0);
255                     /* break; never reached because of exit(0) */
256
257                 case 'v':
258                     version();
259                     exit(0);
260
261                 case 'E':
262                     opts.pp_only = true;
263                     break;
264
265                 /* debug turns on -flno */
266                 case 'g':
267                     opts_setflag("LNO", true);
268                     break;
269
270                 case 'D':
271                     if (!strlen(argv[0]+2)) {
272                         con_err("expected name after -D\n");
273                         exit(0);
274                     }
275
276                     if (!(argarg = strchr(argv[0] + 2, '='))) {
277                         macro.name  = util_strdup(argv[0]+2);
278                         macro.value = NULL;
279                     } else {
280                         *argarg='\0'; /* terminate for name */
281                         macro.name  = util_strdup(argv[0]+2);
282                         macro.value = util_strdup(argarg+1);
283                     }
284                     vec_push(ppems, macro);
285                     break;
286
287                 /* handle all -fflags */
288                 case 'f':
289                     util_strtocmd(argv[0]+2, argv[0]+2, strlen(argv[0]+2)+1);
290                     if (!strcmp(argv[0]+2, "HELP") || *(argv[0]+2) == '?') {
291                         con_out("Possible flags:\n");
292                         for (itr = 0; itr < COUNT_FLAGS; ++itr) {
293                             util_strtononcmd(opts_flag_list[itr].name, buffer, sizeof(buffer));
294                             con_out(" -f%s\n", buffer);
295                         }
296                         exit(0);
297                     }
298                     else if (!strncmp(argv[0]+2, "NO_", 3)) {
299                         if (!opts_setflag(argv[0]+5, false)) {
300                             con_out("unknown flag: %s\n", argv[0]+2);
301                             return false;
302                         }
303                     }
304                     else if (!opts_setflag(argv[0]+2, true)) {
305                         con_out("unknown flag: %s\n", argv[0]+2);
306                         return false;
307                     }
308                     break;
309                 case 'W':
310                     util_strtocmd(argv[0]+2, argv[0]+2, strlen(argv[0]+2)+1);
311                     if (!strcmp(argv[0]+2, "HELP") || *(argv[0]+2) == '?') {
312                         con_out("Possible warnings:\n");
313                         for (itr = 0; itr < COUNT_WARNINGS; ++itr) {
314                             util_strtononcmd(opts_warn_list[itr].name, buffer, sizeof(buffer));
315                             con_out(" -W%s\n", buffer);
316                         }
317                         exit(0);
318                     }
319                     else if (!strcmp(argv[0]+2, "NO_ERROR")) {
320                         opts.werror = false;
321                         break;
322                     }
323                     else if (!strcmp(argv[0]+2, "ERROR")) {
324                         opts.werror = true;
325                         break;
326                     }
327                     else if (!strcmp(argv[0]+2, "NONE")) {
328                         for (itr = 0; itr < sizeof(opts.warn)/sizeof(opts.warn[0]); ++itr)
329                             opts.warn[itr] = 0;
330                         break;
331                     }
332                     else if (!strcmp(argv[0]+2, "ALL")) {
333                         for (itr = 0; itr < sizeof(opts.warn)/sizeof(opts.warn[0]); ++itr)
334                             opts.warn[itr] = 0xFFFFFFFFL;
335                         break;
336                     }
337                     if (!strncmp(argv[0]+2, "NO_", 3)) {
338                         if (!opts_setwarn(argv[0]+5, false)) {
339                             con_out("unknown warning: %s\n", argv[0]+2);
340                             return false;
341                         }
342                     }
343                     else if (!opts_setwarn(argv[0]+2, true)) {
344                         con_out("unknown warning: %s\n", argv[0]+2);
345                         return false;
346                     }
347                     break;
348
349                 case 'O':
350                     if (!options_witharg(&argc, &argv, &argarg)) {
351                         con_out("option -O requires a numerical argument, or optimization name with an optional 'no-' prefix\n");
352                         return false;
353                     }
354                     if (isdigit(argarg[0])) {
355                         opts.O = atoi(argarg);
356                         opts_setoptimlevel(opts.O);
357                     } else {
358                         util_strtocmd(argarg, argarg, strlen(argarg)+1);
359                         if (!strcmp(argarg, "HELP")) {
360                             con_out("Possible optimizations:\n");
361                             for (itr = 0; itr < COUNT_OPTIMIZATIONS; ++itr) {
362                                 util_strtononcmd(opts_opt_list[itr].name, buffer, sizeof(buffer));
363                                 con_out(" -O%-20s (-O%u)\n", buffer, opts_opt_oflag[itr]);
364                             }
365                             exit(0);
366                         }
367                         else if (!strcmp(argarg, "ALL"))
368                             opts_setoptimlevel(opts.O = 9999);
369                         else if (!strncmp(argarg, "NO_", 3)) {
370                             if (!opts_setoptim(argarg+3, false)) {
371                                 con_out("unknown optimization: %s\n", argarg+3);
372                                 return false;
373                             }
374                         }
375                         else {
376                             if (!opts_setoptim(argarg, true)) {
377                                 con_out("unknown optimization: %s\n", argarg);
378                                 return false;
379                             }
380                         }
381                     }
382                     break;
383
384                 case 'o':
385                     if (!options_witharg(&argc, &argv, &argarg)) {
386                         con_out("option -o requires an argument: the output file name\n");
387                         return false;
388                     }
389                     opts.output = argarg;
390                     opts_output_wasset = true;
391                     break;
392
393                 case 'a':
394                 case 's':
395                     item.type = argv[0][1] == 'a' ? TYPE_ASM : TYPE_SRC;
396                     if (!options_witharg(&argc, &argv, &argarg)) {
397                         con_out("option -a requires a filename %s\n",
398                                 (argv[0][1] == 'a' ? "containing QC-asm" : "containing a progs.src formatted list"));
399                         return false;
400                     }
401                     item.filename = argarg;
402                     vec_push(items, item);
403                     break;
404
405                 case '-':
406                     if (!argv[0][2]) {
407                         /* anything following -- is considered a non-option argument */
408                         argend = true;
409                         break;
410                     }
411             /* All long options without arguments */
412                     else if (!strcmp(argv[0]+2, "help")) {
413                         usage();
414                         exit(0);
415                     }
416                     else if (!strcmp(argv[0]+2, "version")) {
417                         version();
418                         exit(0);
419                     }
420                     else {
421             /* All long options with arguments */
422                         if (options_long_witharg("output", &argc, &argv, &argarg)) {
423                             opts.output = argarg;
424                             opts_output_wasset = true;
425                         } else {
426                             con_out("Unknown parameter: %s\n", argv[0]);
427                             return false;
428                         }
429                     }
430                     break;
431
432                 default:
433                     con_out("Unknown parameter: %s\n", argv[0]);
434                     return false;
435             }
436         }
437         else
438         {
439             /* it's a QC filename */
440             item.filename = argv[0];
441             item.type     = TYPE_QC;
442             vec_push(items, item);
443         }
444     }
445     opts_ini_init(config);
446     return true;
447 }
448
449 /* returns the line number, or -1 on error */
450 static bool progs_nextline(char **out, size_t *alen,FILE *src) {
451     int    len;
452     char  *line;
453     char  *start;
454     char  *end;
455
456     line = *out;
457     len = util_getline(&line, alen, src);
458     if (len == -1)
459         return false;
460
461     /* start at first non-blank */
462     for (start = line; isspace(*start); ++start) {}
463     /* end at the first non-blank */
464     for (end = start;  *end && !isspace(*end);  ++end)   {}
465
466     *out = line;
467     /* move the actual filename to the beginning */
468     while (start != end) {
469         *line++ = *start++;
470     }
471     *line = 0;
472     return true;
473 }
474
475 int main(int argc, char **argv) {
476     size_t itr;
477     int    retval           = 0;
478     bool   opts_output_free = false;
479     bool   operators_free   = false;
480     bool   progs_src        = false;
481     FILE  *outfile          = NULL;
482
483     app_name = argv[0];
484     con_init ();
485     opts_init("progs.dat", COMPILER_GMQCC, (1024 << 3));
486
487     if (!options_parse(argc, argv)) {
488         return usage();
489     }
490
491     /* the standard decides which set of operators to use */
492     if (opts.standard == COMPILER_GMQCC) {
493         operators      = c_operators;
494         operator_count = c_operator_count;
495     } else if (opts.standard == COMPILER_FTEQCC) {
496         operators      = fte_operators;
497         operator_count = fte_operator_count;
498     } else {
499         operators      = qcc_operators;
500         operator_count = qcc_operator_count;
501     }
502
503     if (operators == fte_operators) {
504         /* fix ternary? */
505         if (OPTS_FLAG(CORRECT_TERNARY)) {
506             oper_info *newops;
507             if (operators[operator_count-2].id != opid1(',') ||
508                 operators[operator_count-1].id != opid2(':','?'))
509             {
510                 con_err("internal error: operator precedence table wasn't updated correctly!\n");
511                 exit(1);
512             }
513             operators_free = true;
514             newops = mem_a(sizeof(operators[0]) * operator_count);
515             memcpy(newops, operators, sizeof(operators[0]) * operator_count);
516             memcpy(&newops[operator_count-2], &operators[operator_count-1], sizeof(newops[0]));
517             memcpy(&newops[operator_count-1], &operators[operator_count-2], sizeof(newops[0]));
518             newops[operator_count-2].prec = newops[operator_count-1].prec+1;
519             operators = newops;
520         }
521     }
522
523     if (opts.dump) {
524         for (itr = 0; itr < COUNT_FLAGS; ++itr)
525             con_out("Flag %s = %i\n",    opts_flag_list[itr].name, OPTS_FLAG(itr));
526         for (itr = 0; itr < COUNT_WARNINGS; ++itr)
527             con_out("Warning %s = %i\n", opts_warn_list[itr].name, OPTS_WARN(itr));
528         
529         con_out("output             = %s\n", opts.output);
530         con_out("optimization level = %d\n", opts.O);
531         con_out("standard           = %i\n", opts.standard);
532     }
533
534     if (opts.pp_only) {
535         if (opts_output_wasset) {
536             outfile = util_fopen(opts.output, "wb");
537             if (!outfile) {
538                 con_err("failed to open `%s` for writing\n", opts.output);
539                 retval = 1;
540                 goto cleanup;
541             }
542         }
543         else
544             outfile = stdout;
545     }
546
547     if (!opts.pp_only) {
548         if (!parser_init()) {
549             con_err("failed to initialize parser\n");
550             retval = 1;
551             goto cleanup;
552         }
553     }
554
555     if (opts.pp_only || OPTS_FLAG(FTEPP)) {
556         if (!ftepp_init()) {
557             con_err("failed to initialize parser\n");
558             retval = 1;
559             goto cleanup;
560         }
561     }
562
563     util_debug("COM", "starting ...\n");
564
565     /* add macros */
566     if (opts.pp_only || OPTS_FLAG(FTEPP)) {
567         for (itr = 0; itr < vec_size(ppems); itr++) {
568             ftepp_add_macro(ppems[itr].name, ppems[itr].value);
569             mem_d(ppems[itr].name);
570
571             /* can be null */
572             if (ppems[itr].value)
573                 mem_d(ppems[itr].value);
574         }
575     }
576
577     if (!vec_size(items)) {
578         FILE *src;
579         char *line;
580         size_t linelen = 0;
581
582         progs_src = true;
583
584         src = util_fopen("progs.src", "rb");
585         if (!src) {
586             con_err("failed to open `progs.src` for reading\n");
587             retval = 1;
588             goto cleanup;
589         }
590
591         line = NULL;
592         if (!progs_nextline(&line, &linelen, src) || !line[0]) {
593             con_err("illformatted progs.src file: expected output filename in first line\n");
594             retval = 1;
595             goto srcdone;
596         }
597
598         if (!opts_output_wasset) {
599             opts.output = util_strdup(line);
600             opts_output_free = true;
601         }
602
603         while (progs_nextline(&line, &linelen, src)) {
604             argitem item;
605             if (!line[0] || (line[0] == '/' && line[1] == '/'))
606                 continue;
607             item.filename = util_strdup(line);
608             item.type     = TYPE_QC;
609             vec_push(items, item);
610         }
611
612 srcdone:
613         fclose(src);
614         mem_d(line);
615     }
616
617     if (retval)
618         goto cleanup;
619
620     if (vec_size(items)) {
621         if (!opts.pp_only) {
622             con_out("Mode: %s\n", (progs_src ? "progs.src" : "manual"));
623             con_out("There are %lu items to compile:\n", (unsigned long)vec_size(items));
624         }
625         for (itr = 0; itr < vec_size(items); ++itr) {
626             if (!opts.pp_only) {
627                 con_out("  item: %s (%s)\n",
628                        items[itr].filename,
629                        ( (items[itr].type == TYPE_QC ? "qc" :
630                          (items[itr].type == TYPE_ASM ? "asm" :
631                          (items[itr].type == TYPE_SRC ? "progs.src" :
632                          ("unknown"))))));
633             }
634
635             if (opts.pp_only) {
636                 const char *out;
637                 if (!ftepp_preprocess_file(items[itr].filename)) {
638                     retval = 1;
639                     goto cleanup;
640                 }
641                 out = ftepp_get();
642                 if (out)
643                     fprintf(outfile, "%s", out);
644                 ftepp_flush();
645             }
646             else {
647                 if (OPTS_FLAG(FTEPP)) {
648                     const char *data;
649                     if (!ftepp_preprocess_file(items[itr].filename)) {
650                         retval = 1;
651                         goto cleanup;
652                     }
653                     data = ftepp_get();
654                     if (vec_size(data)) {
655                         if (!parser_compile_string_len(items[itr].filename, data, vec_size(data))) {
656                             retval = 1;
657                             goto cleanup;
658                         }
659                     }
660                     ftepp_flush();
661                 }
662                 else {
663                     if (!parser_compile_file(items[itr].filename)) {
664                         retval = 1;
665                         goto cleanup;
666                     }
667                 }
668             }
669
670             if (progs_src) {
671                 mem_d(items[itr].filename);
672                 items[itr].filename = NULL;
673             }
674         }
675
676         ftepp_finish();
677         if (!opts.pp_only) {
678             if (!parser_finish(opts.output)) {
679                 retval = 1;
680                 goto cleanup;
681             }
682         }
683     }
684
685     /* stuff */
686     if (!opts.pp_only) {
687         for (itr = 0; itr < COUNT_OPTIMIZATIONS; ++itr) {
688             if (opts_optimizationcount[itr]) {
689                 con_out("%s: %u\n", opts_opt_list[itr].name, (unsigned int)opts_optimizationcount[itr]);
690             }
691         }
692     }
693
694 cleanup:
695     util_debug("COM", "cleaning ...\n");
696     ftepp_finish();
697     con_close();
698     vec_free(items);
699     vec_free(ppems);
700
701     if (!opts.pp_only)
702         parser_cleanup();
703     if (opts_output_free)
704         mem_d((char*)opts.output);
705     if (operators_free)
706         mem_d((void*)operators);
707
708     lex_cleanup();
709     util_meminfo();
710     return retval;
711 }