Constant folding for string comparisons too
[xonotic/gmqcc.git] / main.cpp
1 #include <stdlib.h>
2 #include <string.h>
3
4 #include "gmqcc.h"
5 #include "lexer.h"
6
7 /* TODO: cleanup this whole file .. it's a fuckign mess */
8
9 /* set by the standard */
10 const oper_info *operators = nullptr;
11 size_t operator_count = 0;
12 static bool opts_output_wasset = false;
13 struct argitem { char *filename; int type; };
14 struct ppitem { char *name; char *value; };
15 static argitem *items = nullptr;
16 static ppitem  *ppems = nullptr;
17
18 #define TYPE_QC  0
19 #define TYPE_ASM 1
20 #define TYPE_SRC 2
21
22 static const char *app_name;
23
24 static void version(void) {
25     con_out("GMQCC %d.%d.%d Built %s %s\n" GMQCC_DEV_VERSION_STRING,
26         GMQCC_VERSION_MAJOR,
27         GMQCC_VERSION_MINOR,
28         GMQCC_VERSION_PATCH,
29         __DATE__,
30         __TIME__
31     );
32 }
33
34 static int usage(void) {
35     con_out("usage: %s [options] [files...]", app_name);
36     con_out("options:\n"
37             "  -h, --help             show this help message\n"
38             "  -debug                 turns on compiler debug messages\n");
39     con_out("  -o, --output=file      output file, defaults to progs.dat\n"
40             "  -s filename            add a progs.src file to be used\n");
41     con_out("  -E                     stop after preprocessing\n");
42     con_out("  -q, --quiet            be less verbose\n");
43     con_out("  -config file           use the specified ini file\n");
44     con_out("  -std=standard          select one of the following standards\n"
45             "       -std=qcc          original QuakeC\n"
46             "       -std=fteqcc       fteqcc QuakeC\n"
47             "       -std=gmqcc        this compiler (default)\n");
48     con_out("  -f<flag>               enable a flag\n"
49             "  -fno-<flag>            disable a flag\n"
50             "  -fhelp                 list possible flags\n");
51     con_out("  -W<warning>            enable a warning\n"
52             "  -Wno-<warning>         disable a warning\n"
53             "  -Wall                  enable all warnings\n");
54     con_out("  -Werror                treat warnings as errors\n"
55             "  -Werror-<warning>      treat a warning as error\n"
56             "  -Wno-error-<warning>   opposite of the above\n");
57     con_out("  -Whelp                 list possible warnings\n");
58     con_out("  -O<number>             optimization level\n"
59             "  -O<name>               enable specific optimization\n"
60             "  -Ono-<name>            disable specific optimization\n"
61             "  -Ohelp                 list optimizations\n");
62     con_out("  -force-crc=num         force a specific checksum into the header\n");
63     con_out("  -state-fps=num         emulate OP_STATE with the specified FPS\n");
64     con_out("  -coverage              add coverage support\n");
65     return -1;
66 }
67
68 /* command line parsing */
69 static bool options_witharg(int *argc_, char ***argv_, char **out) {
70     int  argc   = *argc_;
71     char **argv = *argv_;
72
73     if (argv[0][2]) {
74         *out = argv[0]+2;
75         return true;
76     }
77     /* eat up the next */
78     if (argc < 2) /* no parameter was provided */
79         return false;
80
81     *out = argv[1];
82     --*argc_;
83     ++*argv_;
84     return true;
85 }
86
87 static bool options_long_witharg_all(const char *optname, int *argc_, char ***argv_, char **out, int ds, bool split) {
88     int  argc   = *argc_;
89     char **argv = *argv_;
90
91     size_t len = strlen(optname);
92
93     if (strncmp(argv[0]+ds, optname, len))
94         return false;
95
96     /* it's --optname, check how the parameter is supplied */
97     if (argv[0][ds+len] == '=') {
98         /* using --opt=param */
99         *out = argv[0]+ds+len+1;
100         return true;
101     }
102
103     if (!split || argc < ds) /* no parameter was provided, or only single-arg form accepted */
104         return false;
105
106     /* using --opt param */
107     *out = argv[1];
108     --*argc_;
109     ++*argv_;
110     return true;
111 }
112 static bool options_long_witharg(const char *optname, int *argc_, char ***argv_, char **out) {
113     return options_long_witharg_all(optname, argc_, argv_, out, 2, true);
114 }
115 static bool options_long_gcc(const char *optname, int *argc_, char ***argv_, char **out) {
116     return options_long_witharg_all(optname, argc_, argv_, out, 1, false);
117 }
118
119 static bool options_parse(int argc, char **argv) {
120     bool argend = false;
121     size_t itr;
122     char buffer[1024];
123     char *config = nullptr;
124
125     while (!argend && argc > 1) {
126         char *argarg;
127         argitem item;
128         ppitem macro;
129
130         ++argv;
131         --argc;
132
133         if (argv[0][0] == '-') {
134             /* All gcc-type long options */
135             if (options_long_gcc("std", &argc, &argv, &argarg)) {
136                 if (!strcmp(argarg, "gmqcc") || !strcmp(argarg, "default")) {
137
138                     opts_set(opts.flags, ADJUST_VECTOR_FIELDS,          true);
139                     opts_set(opts.flags, CORRECT_LOGIC,                 true);
140                     opts_set(opts.flags, SHORT_LOGIC,                   true);
141                     opts_set(opts.flags, UNTYPED_NIL,                   true);
142                     opts_set(opts.flags, VARIADIC_ARGS,                 true);
143                     opts_set(opts.flags, FALSE_EMPTY_STRINGS,           false);
144                     opts_set(opts.flags, TRUE_EMPTY_STRINGS,            true);
145                     opts_set(opts.flags, LOOP_LABELS,                   true);
146                     opts_set(opts.flags, TRANSLATABLE_STRINGS,          true);
147                     opts_set(opts.flags, INITIALIZED_NONCONSTANTS,      true);
148                     opts_set(opts.werror, WARN_INVALID_PARAMETER_COUNT, true);
149                     opts_set(opts.werror, WARN_MISSING_RETURN_VALUES,   true);
150                     opts_set(opts.flags,  EXPRESSIONS_FOR_BUILTINS,     true);
151                     opts_set(opts.warn,   WARN_BREAKDEF,                true);
152
153
154
155                     OPTS_OPTION_U32(OPTION_STANDARD) = COMPILER_GMQCC;
156
157                 } else if (!strcmp(argarg, "qcc")) {
158
159                     opts_set(opts.flags, ADJUST_VECTOR_FIELDS,  false);
160                     opts_set(opts.flags, ASSIGN_FUNCTION_TYPES, true);
161
162                     OPTS_OPTION_U32(OPTION_STANDARD) = COMPILER_QCC;
163
164                 } else if (!strcmp(argarg, "fte") || !strcmp(argarg, "fteqcc")) {
165
166                     opts_set(opts.flags, FTEPP,                    true);
167                     opts_set(opts.flags, TRANSLATABLE_STRINGS,     true);
168                     opts_set(opts.flags, ADJUST_VECTOR_FIELDS,     false);
169                     opts_set(opts.flags, ASSIGN_FUNCTION_TYPES,    true);
170                     opts_set(opts.flags, CORRECT_TERNARY,          false);
171                     opts_set(opts.warn, WARN_TERNARY_PRECEDENCE,   true);
172                     opts_set(opts.warn, WARN_BREAKDEF,             true);
173
174                     OPTS_OPTION_U32(OPTION_STANDARD) = COMPILER_FTEQCC;
175
176                 } else if (!strcmp(argarg, "qccx")) {
177
178                     opts_set(opts.flags, ADJUST_VECTOR_FIELDS,  false);
179                     OPTS_OPTION_U32(OPTION_STANDARD) = COMPILER_QCCX;
180
181                 } else {
182                     con_out("Unknown standard: %s\n", argarg);
183                     return false;
184                 }
185                 continue;
186             }
187             if (options_long_gcc("force-crc", &argc, &argv, &argarg)) {
188
189                 OPTS_OPTION_BOOL(OPTION_FORCECRC)   = true;
190                 OPTS_OPTION_U16 (OPTION_FORCED_CRC) = strtol(argarg, nullptr, 0);
191                 continue;
192             }
193             if (options_long_gcc("state-fps", &argc, &argv, &argarg)) {
194                 OPTS_OPTION_U32(OPTION_STATE_FPS) = strtol(argarg, nullptr, 0);
195                 opts_set(opts.flags, EMULATE_STATE, true);
196                 continue;
197             }
198             if (options_long_gcc("config", &argc, &argv, &argarg)) {
199                 config = argarg;
200                 continue;
201             }
202             if (options_long_gcc("progsrc", &argc, &argv, &argarg)) {
203                 OPTS_OPTION_STR(OPTION_PROGSRC) = argarg;
204                 continue;
205             }
206
207             /* show defaults (like pathscale) */
208             if (!strcmp(argv[0]+1, "show-defaults")) {
209                 for (itr = 0; itr < COUNT_FLAGS; ++itr) {
210                     if (!OPTS_FLAG(itr))
211                         continue;
212
213                     memset(buffer, 0, sizeof(buffer));
214                     util_strtononcmd(opts_flag_list[itr].name, buffer, strlen(opts_flag_list[itr].name) + 1);
215
216                     con_out("-f%s ", buffer);
217                 }
218                 for (itr = 0; itr < COUNT_WARNINGS; ++itr) {
219                     if (!OPTS_WARN(itr))
220                         continue;
221
222                     memset(buffer, 0, sizeof(buffer));
223                     util_strtononcmd(opts_warn_list[itr].name, buffer, strlen(opts_warn_list[itr].name) + 1);
224                     con_out("-W%s ", buffer);
225                 }
226                 con_out("\n");
227                 exit(0);
228             }
229
230             if (!strcmp(argv[0]+1, "debug")) {
231                 OPTS_OPTION_BOOL(OPTION_DEBUG) = true;
232                 continue;
233             }
234             if (!strcmp(argv[0]+1, "dump")) {
235                 OPTS_OPTION_BOOL(OPTION_DUMP)  = true;
236                 continue;
237             }
238             if (!strcmp(argv[0]+1, "dumpfin")) {
239                 OPTS_OPTION_BOOL(OPTION_DUMPFIN) = true;
240                 continue;
241             }
242             if (!strcmp(argv[0]+1, "nocolor")) {
243                 con_color(0);
244                 continue;
245             }
246             if (!strcmp(argv[0]+1, "coverage")) {
247                 OPTS_OPTION_BOOL(OPTION_COVERAGE) = true;
248                 continue;
249             }
250
251             switch (argv[0][1]) {
252                 /* -h, show usage but exit with 0 */
253                 case 'h':
254                     usage();
255                     exit(0);
256                     /* break; never reached because of exit(0) */
257
258                 case 'v':
259                     version();
260                     exit(0);
261
262                 case 'E':
263                     OPTS_OPTION_BOOL(OPTION_PP_ONLY) = true;
264                     opts_set(opts.flags, FTEPP_PREDEFS, true); /* predefs on for -E */
265                     break;
266
267                 /* debug turns on -flno */
268                 case 'g':
269                     opts_setflag("LNO", true);
270                     OPTS_OPTION_BOOL(OPTION_G) = true;
271                     break;
272
273                 case 'q':
274                     OPTS_OPTION_BOOL(OPTION_QUIET) = true;
275                     break;
276
277                 case 'D':
278                     if (!strlen(argv[0]+2)) {
279                         con_err("expected name after -D\n");
280                         exit(0);
281                     }
282
283                     if (!(argarg = strchr(argv[0] + 2, '='))) {
284                         macro.name  = util_strdup(argv[0]+2);
285                         macro.value = nullptr;
286                     } else {
287                         *argarg='\0'; /* terminate for name */
288                         macro.name  = util_strdup(argv[0]+2);
289                         macro.value = util_strdup(argarg+1);
290                     }
291                     vec_push(ppems, macro);
292                     break;
293
294                 /* handle all -fflags */
295                 case 'f':
296                     util_strtocmd(argv[0]+2, argv[0]+2, strlen(argv[0]+2)+1);
297                     if (!strcmp(argv[0]+2, "HELP") || *(argv[0]+2) == '?') {
298                         con_out("Possible flags:\n\n");
299                         for (itr = 0; itr < COUNT_FLAGS; ++itr) {
300                             util_strtononcmd(opts_flag_list[itr].name, buffer, sizeof(buffer));
301                             con_out(" -f%s\n", buffer);
302                         }
303                         exit(0);
304                     }
305                     else if (!strncmp(argv[0]+2, "NO_", 3)) {
306                         if (!opts_setflag(argv[0]+5, false)) {
307                             con_out("unknown flag: %s\n", argv[0]+2);
308                             return false;
309                         }
310                     }
311                     else if (!opts_setflag(argv[0]+2, true)) {
312                         con_out("unknown flag: %s\n", argv[0]+2);
313                         return false;
314                     }
315                     break;
316                 case 'W':
317                     util_strtocmd(argv[0]+2, argv[0]+2, strlen(argv[0]+2)+1);
318                     if (!strcmp(argv[0]+2, "HELP") || *(argv[0]+2) == '?') {
319                         con_out("Possible warnings:\n");
320                         for (itr = 0; itr < COUNT_WARNINGS; ++itr) {
321                             util_strtononcmd(opts_warn_list[itr].name, buffer, sizeof(buffer));
322                             con_out(" -W%s\n", buffer);
323                             if (itr == WARN_DEBUG)
324                                 con_out("   Warnings included by -Wall:\n");
325                         }
326                         exit(0);
327                     }
328                     else if (!strcmp(argv[0]+2, "NO_ERROR") ||
329                              !strcmp(argv[0]+2, "NO_ERROR_ALL"))
330                     {
331                         for (itr = 0; itr < GMQCC_ARRAY_COUNT(opts.werror); ++itr)