2 * Copyright (C) 2012, 2013
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:
13 * The above copyright notice and this permission notice shall be included in all
14 * copies or substantial portions of the Software.
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
32 #define HT_MACROS 1024
42 /* a copy from the lexer */
56 /* yes we need an extra flag since `#define FOO x` is not the same as `#define FOO() x` */
63 typedef struct ftepp_s {
69 ppcondition *conditions;
71 ht macros; /* hashtable<string, ppmacro*> */
80 * Implement the predef subsystem now. We can do this safely with the
81 * help of lexer contexts.
83 static uint32_t ftepp_predef_countval = 0;
84 static uint32_t ftepp_predef_randval = 0;
87 static char *ftepp_predef_date(lex_file *context) {
88 struct tm *itime = NULL;
90 char *value = (char*)mem_a(82);
91 /* 82 is enough for strftime but we also have " " in our string */
99 localtime_s(itime, &rtime);
101 itime = localtime(&rtime);
104 strftime(value, 82, "\"%b %d %Y\"", itime);
110 static char *ftepp_predef_time(lex_file *context) {
111 struct tm *itime = NULL;
113 char *value = (char*)mem_a(82);
114 /* 82 is enough for strftime but we also have " " in our string */
122 localtime_s(itime, &rtime);
124 itime = localtime(&rtime);
127 strftime(value, 82, "\"%X\"", itime);
133 static char *ftepp_predef_line(lex_file *context) {
135 util_asprintf(&value, "%d", (int)context->line);
139 static char *ftepp_predef_file(lex_file *context) {
140 size_t length = strlen(context->name) + 3; /* two quotes and a terminator */
141 char *value = (char*)mem_a(length);
142 util_snprintf(value, length, "\"%s\"", context->name);
146 /* __COUNTER_LAST__ */
147 static char *ftepp_predef_counterlast(lex_file *context) {
149 util_asprintf(&value, "%u", ftepp_predef_countval);
155 static char *ftepp_predef_counter(lex_file *context) {
157 ftepp_predef_countval ++;
158 util_asprintf(&value, "%u", ftepp_predef_countval);
164 static char *ftepp_predef_random(lex_file *context) {
166 ftepp_predef_randval = (util_rand() % 0xFF) + 1;
167 util_asprintf(&value, "%u", ftepp_predef_randval);
172 /* __RANDOM_LAST__ */
173 static char *ftepp_predef_randomlast(lex_file *context) {
175 util_asprintf(&value, "%u", ftepp_predef_randval);
181 static char *ftepp_predef_timestamp(lex_file *context) {
186 if (stat(context->name, &finfo))
187 return util_strdup("\"<failed to determine timestamp>\"");
190 * ctime and its fucking annoying newline char, no worries, we're
191 * professionals here.
193 find = ctime(&finfo.st_mtime);
194 value = (char*)mem_a(strlen(find) + 1);
195 memcpy(&value[1], find, (size = strlen(find)) - 1);
205 char *(*func)(lex_file *);
208 static const ftepp_predef_t ftepp_predefs[] = {
209 { "__LINE__", &ftepp_predef_line },
210 { "__FILE__", &ftepp_predef_file },
211 { "__COUNTER__", &ftepp_predef_counter },
212 { "__COUNTER_LAST__", &ftepp_predef_counterlast },
213 { "__RANDOM__", &ftepp_predef_random },
214 { "__RANDOM_LAST__", &ftepp_predef_randomlast },
215 { "__DATE__", &ftepp_predef_date },
216 { "__TIME__", &ftepp_predef_time },
217 { "__TIME_STAMP__", &ftepp_predef_timestamp }
220 static GMQCC_INLINE int ftepp_predef_index(const char *name) {
221 /* no hashtable here, we simply check for one to exist the naive way */
223 for(i = 0; i < (int)(sizeof(ftepp_predefs)/sizeof(*ftepp_predefs)); i++)
224 if (!strcmp(ftepp_predefs[i].name, name))
229 bool ftepp_predef_exists(const char *name);
230 bool ftepp_predef_exists(const char *name) {
231 return ftepp_predef_index(name) != -1;
234 /* singleton because we're allowed */
235 static GMQCC_INLINE char *(*ftepp_predef(const char *name))(lex_file *context) {
236 int i = ftepp_predef_index(name);
237 return (i != -1) ? ftepp_predefs[i].func : NULL;
240 #define ftepp_tokval(f) ((f)->lex->tok.value)
241 #define ftepp_ctx(f) ((f)->lex->tok.ctx)
243 static void ftepp_errorat(ftepp_t *ftepp, lex_ctx_t ctx, const char *fmt, ...)
250 con_cvprintmsg(ctx, LVL_ERROR, "error", fmt, ap);
254 static void ftepp_error(ftepp_t *ftepp, const char *fmt, ...)
261 con_cvprintmsg(ftepp->lex->tok.ctx, LVL_ERROR, "error", fmt, ap);
265 static bool GMQCC_WARN ftepp_warn(ftepp_t *ftepp, int warntype, const char *fmt, ...)
271 r = vcompile_warning(ftepp->lex->tok.ctx, warntype, fmt, ap);
276 static pptoken *pptoken_make(ftepp_t *ftepp)
278 pptoken *token = (pptoken*)mem_a(sizeof(pptoken));
279 token->token = ftepp->token;
281 if (token->token == TOKEN_WHITE)
282 token->value = util_strdup(" ");
285 token->value = util_strdup(ftepp_tokval(ftepp));
287 memcpy(&token->constval, &ftepp->lex->tok.constval, sizeof(token->constval));
291 static GMQCC_INLINE void pptoken_delete(pptoken *self)
297 static ppmacro *ppmacro_new(lex_ctx_t ctx, const char *name)
299 ppmacro *macro = (ppmacro*)mem_a(sizeof(ppmacro));
302 memset(macro, 0, sizeof(*macro));
303 macro->name = util_strdup(name);
307 static void ppmacro_delete(ppmacro *self)
310 for (i = 0; i < vec_size(self->params); ++i)
311 mem_d(self->params[i]);
312 vec_free(self->params);
313 for (i = 0; i < vec_size(self->output); ++i)
314 pptoken_delete(self->output[i]);
315 vec_free(self->output);
320 static ftepp_t* ftepp_new(void)
324 ftepp = (ftepp_t*)mem_a(sizeof(*ftepp));
325 memset(ftepp, 0, sizeof(*ftepp));
327 ftepp->macros = util_htnew(HT_MACROS);
328 ftepp->output_on = true;
333 static GMQCC_INLINE void ftepp_flush_do(ftepp_t *self)
335 vec_free(self->output_string);
338 static void ftepp_delete(ftepp_t *self)
340 ftepp_flush_do(self);
342 mem_d(self->itemname);
343 if (self->includename)
344 vec_free(self->includename);
346 util_htrem(self->macros, (void (*)(void*))&ppmacro_delete);
348 vec_free(self->conditions);
350 lex_close(self->lex);
354 static void ftepp_out(ftepp_t *ftepp, const char *str, bool ignore_cond)
356 if (ignore_cond || ftepp->output_on)
361 data = vec_add(ftepp->output_string, len);
362 memcpy(data, str, len);
366 static GMQCC_INLINE void ftepp_update_output_condition(ftepp_t *ftepp)
369 ftepp->output_on = true;
370 for (i = 0; i < vec_size(ftepp->conditions); ++i)
371 ftepp->output_on = ftepp->output_on && ftepp->conditions[i].on;
374 static GMQCC_INLINE ppmacro* ftepp_macro_find(ftepp_t *ftepp, const char *name)
376 return (ppmacro*)util_htget(ftepp->macros, name);
379 static GMQCC_INLINE void ftepp_macro_delete(ftepp_t *ftepp, const char *name)
381 util_htrm(ftepp->macros, name, (void (*)(void*))&ppmacro_delete);
384 static GMQCC_INLINE int ftepp_next(ftepp_t *ftepp)
386 return (ftepp->token = lex_do(ftepp->lex));
389 /* Important: this does not skip newlines! */
390 static bool ftepp_skipspace(ftepp_t *ftepp)
392 if (ftepp->token != TOKEN_WHITE)
394 while (ftepp_next(ftepp) == TOKEN_WHITE) {}
395 if (ftepp->token >= TOKEN_EOF) {
396 ftepp_error(ftepp, "unexpected end of preprocessor directive");
402 /* this one skips EOLs as well */
403 static bool ftepp_skipallwhite(ftepp_t *ftepp)
405 if (ftepp->token != TOKEN_WHITE && ftepp->token != TOKEN_EOL)
409 } while (ftepp->token == TOKEN_WHITE || ftepp->token == TOKEN_EOL);
410 if (ftepp->token >= TOKEN_EOF) {
411 ftepp_error(ftepp, "unexpected end of preprocessor directive");
418 * The huge macro parsing code...
420 static bool ftepp_define_params(ftepp_t *ftepp, ppmacro *macro)
424 if (!ftepp_skipspace(ftepp))
426 if (ftepp->token == ')')
428 switch (ftepp->token) {
432 vec_push(macro->params, util_strdup(ftepp_tokval(ftepp)));
435 macro->variadic = true;
438 ftepp_error(ftepp, "unexpected token in parameter list");
442 if (!ftepp_skipspace(ftepp))
444 if (macro->variadic && ftepp->token != ')') {
445 ftepp_error(ftepp, "cannot have parameters after the variadic parameters");
448 } while (ftepp->token == ',');
450 if (ftepp->token != ')') {
451 ftepp_error(ftepp, "expected closing paren after macro parameter list");
455 /* skipspace happens in ftepp_define */
459 static bool ftepp_define_body(ftepp_t *ftepp, ppmacro *macro)
462 while (ftepp->token != TOKEN_EOL && ftepp->token < TOKEN_EOF) {
463 bool subscript = false;
465 if (macro->variadic && !strcmp(ftepp_tokval(ftepp), "__VA_ARGS__")) {
466 subscript = !!(ftepp_next(ftepp) == '#');
468 if (subscript && ftepp_next(ftepp) != '#') {
469 ftepp_error(ftepp, "expected `##` in __VA_ARGS__ for subscripting");
471 } else if (subscript) {
472 if (ftepp_next(ftepp) == '[') {
473 if (ftepp_next(ftepp) != TOKEN_INTCONST) {
474 ftepp_error(ftepp, "expected index for __VA_ARGS__ subscript");
478 index = (int)strtol(ftepp_tokval(ftepp), NULL, 10);
480 if (ftepp_next(ftepp) != ']') {
481 ftepp_error(ftepp, "expected `]` in __VA_ARGS__ subscript");
486 * mark it as an array to be handled later as such and not
487 * as traditional __VA_ARGS__
489 ftepp->token = TOKEN_VA_ARGS_ARRAY;
490 ptok = pptoken_make(ftepp);
491 ptok->constval.i = index;
492 vec_push(macro->output, ptok);
495 ftepp_error(ftepp, "expected `[` for subscripting of __VA_ARGS__");
499 int old = ftepp->token;
500 ftepp->token = TOKEN_VA_ARGS;
501 ptok = pptoken_make(ftepp);
502 vec_push(macro->output, ptok);
506 else if (macro->variadic && !strcmp(ftepp_tokval(ftepp), "__VA_COUNT__")) {
507 ftepp->token = TOKEN_VA_COUNT;
508 ptok = pptoken_make(ftepp);
509 vec_push(macro->output, ptok);
512 ptok = pptoken_make(ftepp);
513 vec_push(macro->output, ptok);
517 /* recursive expansion can cause EOFs here */
518 if (ftepp->token != TOKEN_EOL && ftepp->token != TOKEN_EOF) {
519 ftepp_error(ftepp, "unexpected junk after macro or unexpected end of file");
525 static bool ftepp_define(ftepp_t *ftepp)
527 ppmacro *macro = NULL;
528 size_t l = ftepp_ctx(ftepp).line;
530 (void)ftepp_next(ftepp);
531 if (!ftepp_skipspace(ftepp))
534 switch (ftepp->token) {
538 macro = ftepp_macro_find(ftepp, ftepp_tokval(ftepp));
539 if (macro && ftepp->output_on) {
540 if (ftepp_warn(ftepp, WARN_CPP, "redefining `%s`", ftepp_tokval(ftepp)))
542 ftepp_macro_delete(ftepp, ftepp_tokval(ftepp));
544 macro = ppmacro_new(ftepp_ctx(ftepp), ftepp_tokval(ftepp));
547 ftepp_error(ftepp, "expected macro name");
551 (void)ftepp_next(ftepp);
553 if (ftepp->token == '(') {
554 macro->has_params = true;
555 if (!ftepp_define_params(ftepp, macro)) {
556 ppmacro_delete(macro);
561 if (!ftepp_skipspace(ftepp)) {
562 ppmacro_delete(macro);
566 if (!ftepp_define_body(ftepp, macro)) {
567 ppmacro_delete(macro);
571 if (ftepp->output_on)
572 util_htset(ftepp->macros, macro->name, (void*)macro);
574 ppmacro_delete(macro);
577 for (; l < ftepp_ctx(ftepp).line; ++l)
578 ftepp_out(ftepp, "\n", true);
583 * When a macro is used we have to handle parameters as well
584 * as special-concatenation via ## or stringification via #
586 * Note: parenthesis can nest, so FOO((a),b) is valid, but only
587 * this kind of parens. Curly braces or [] don't count towards the
594 static void macroparam_clean(macroparam *self)
597 for (i = 0; i < vec_size(self->tokens); ++i)
598 pptoken_delete(self->tokens[i]);
599 vec_free(self->tokens);
602 /* need to leave the last token up */
603 static bool ftepp_macro_call_params(ftepp_t *ftepp, macroparam **out_params)
605 macroparam *params = NULL;
611 if (!ftepp_skipallwhite(ftepp))
613 while (ftepp->token != ')') {
615 if (!ftepp_skipallwhite(ftepp))
617 while (parens || ftepp->token != ',') {
618 if (ftepp->token == '(')
620 else if (ftepp->token == ')') {
625 ptok = pptoken_make(ftepp);
626 vec_push(mp.tokens, ptok);
627 if (ftepp_next(ftepp) >= TOKEN_EOF) {
628 ftepp_error(ftepp, "unexpected end of file in macro call");
632 vec_push(params, mp);
634 if (ftepp->token == ')')
636 if (ftepp->token != ',') {
637 ftepp_error(ftepp, "expected closing paren or comma in macro call");
640 if (ftepp_next(ftepp) >= TOKEN_EOF) {
641 ftepp_error(ftepp, "unexpected end of file in macro call");
645 *out_params = params;
650 macroparam_clean(&mp);
651 for (i = 0; i < vec_size(params); ++i)
652 macroparam_clean(¶ms[i]);
657 static bool macro_params_find(ppmacro *macro, const char *name, size_t *idx)
660 for (i = 0; i < vec_size(macro->params); ++i) {
661 if (!strcmp(macro->params[i], name)) {
669 static void ftepp_stringify_token(ftepp_t *ftepp, pptoken *token)
674 switch (token->token) {
675 case TOKEN_STRINGCONST:
678 /* in preprocessor mode strings already are string,
679 * so we don't get actual newline bytes here.
680 * Still need to escape backslashes and quotes.
683 case '\\': ftepp_out(ftepp, "\\\\", false); break;
684 case '"': ftepp_out(ftepp, "\\\"", false); break;
687 ftepp_out(ftepp, chs, false);
694 ftepp_out(ftepp, " ", false);
697 ftepp_out(ftepp, "\\n", false);
700 ftepp_out(ftepp, token->value, false);
705 static void ftepp_stringify(ftepp_t *ftepp, macroparam *param)
708 ftepp_out(ftepp, "\"", false);
709 for (i = 0; i < vec_size(param->tokens); ++i)
710 ftepp_stringify_token(ftepp, param->tokens[i]);
711 ftepp_out(ftepp, "\"", false);
714 static void ftepp_recursion_header(ftepp_t *ftepp)
716 ftepp_out(ftepp, "\n#pragma push(line)\n", false);
719 static void ftepp_recursion_footer(ftepp_t *ftepp)
721 ftepp_out(ftepp, "\n#pragma pop(line)\n", false);
724 static void ftepp_param_out(ftepp_t *ftepp, macroparam *param)
728 for (i = 0; i < vec_size(param->tokens); ++i) {
729 out = param->tokens[i];
730 if (out->token == TOKEN_EOL)
731 ftepp_out(ftepp, "\n", false);
733 ftepp_out(ftepp, out->value, false);
737 static bool ftepp_preprocess(ftepp_t *ftepp);
738 static bool ftepp_macro_expand(ftepp_t *ftepp, ppmacro *macro, macroparam *params, bool resetline)
741 char *old_string = ftepp->output_string;
743 lex_file *old_lexer = ftepp->lex;
744 size_t vararg_start = vec_size(macro->params);
756 if (vararg_start < vec_size(params))
757 varargs = vec_size(params) - vararg_start;
762 if (!vec_size(macro->output))
765 ftepp->output_string = NULL;
766 for (o = 0; o < vec_size(macro->output); ++o) {
767 pptoken *out = macro->output[o];
768 switch (out->token) {
770 if (!macro->variadic) {
771 ftepp_error(ftepp, "internal preprocessor error: TOKEN_VA_ARGS in non-variadic macro");
772 vec_free(old_string);
779 ftepp_param_out(ftepp, ¶ms[pi + vararg_start]);
780 for (++pi; pi < varargs; ++pi) {
781 ftepp_out(ftepp, ", ", false);
782 ftepp_param_out(ftepp, ¶ms[pi + vararg_start]);
786 case TOKEN_VA_ARGS_ARRAY:
787 if ((size_t)out->constval.i >= varargs) {
788 ftepp_error(ftepp, "subscript of `[%u]` is out of bounds for `__VA_ARGS__`", out->constval.i);
789 vec_free(old_string);
793 ftepp_param_out(ftepp, ¶ms[out->constval.i + vararg_start]);
797 util_asprintf(&buffer, "%d", varargs);
798 ftepp_out(ftepp, buffer, false);
805 if (!macro_params_find(macro, out->value, &pi)) {
806 ftepp_out(ftepp, out->value, false);
809 ftepp_param_out(ftepp, ¶ms[pi]);
812 if (o + 1 < vec_size(macro->output)) {
813 nextok = macro->output[o+1]->token;
815 /* raw concatenation */
819 if ( (nextok == TOKEN_IDENT ||
820 nextok == TOKEN_KEYWORD ||
821 nextok == TOKEN_TYPENAME) &&
822 macro_params_find(macro, macro->output[o+1]->value, &pi))
825 ftepp_stringify(ftepp, ¶ms[pi]);
829 ftepp_out(ftepp, "#", false);
832 ftepp_out(ftepp, "\n", false);
835 ftepp_out(ftepp, out->value, false);
839 vec_push(ftepp->output_string, 0);
840 /* Now run the preprocessor recursively on this string buffer */
842 printf("__________\n%s\n=========\n", ftepp->output_string);
844 inlex = lex_open_string(ftepp->output_string, vec_size(ftepp->output_string)-1, ftepp->lex->name);
846 ftepp_error(ftepp, "internal error: failed to instantiate lexer");
851 inlex->line = ftepp->lex->line;
852 inlex->sline = ftepp->lex->sline;
855 old_inmacro = ftepp->in_macro;
856 ftepp->in_macro = true;
857 ftepp->output_string = NULL;
858 if (!ftepp_preprocess(ftepp)) {
859 ftepp->in_macro = old_inmacro;
860 vec_free(ftepp->lex->open_string);
861 vec_free(ftepp->output_string);
862 lex_close(ftepp->lex);
866 ftepp->in_macro = old_inmacro;
867 vec_free(ftepp->lex->open_string);
868 lex_close(ftepp->lex);
870 inner_string = ftepp->output_string;
871 ftepp->output_string = old_string;
873 has_newlines = (strchr(inner_string, '\n') != NULL);
875 if (has_newlines && !old_inmacro)
876 ftepp_recursion_header(ftepp);
878 vec_append(ftepp->output_string, vec_size(inner_string), inner_string);
879 vec_free(inner_string);
881 if (has_newlines && !old_inmacro)
882 ftepp_recursion_footer(ftepp);
884 if (resetline && !ftepp->in_macro) {
886 util_snprintf(lineno, 128, "\n#pragma line(%lu)\n", (unsigned long)(old_lexer->sline));
887 ftepp_out(ftepp, lineno, false);
890 old_string = ftepp->output_string;
892 ftepp->lex = old_lexer;
893 ftepp->output_string = old_string;
897 static bool ftepp_macro_call(ftepp_t *ftepp, ppmacro *macro)
900 macroparam *params = NULL;
904 if (!macro->has_params) {
905 if (!ftepp_macro_expand(ftepp, macro, NULL, false))
912 if (!ftepp_skipallwhite(ftepp))
915 if (ftepp->token != '(') {
916 ftepp_error(ftepp, "expected macro parameters in parenthesis");
921 paramline = ftepp->lex->sline;
922 if (!ftepp_macro_call_params(ftepp, ¶ms))
925 if ( vec_size(params) < vec_size(macro->params) ||
926 (vec_size(params) > vec_size(macro->params) && !macro->variadic) )
928 ftepp_error(ftepp, "macro %s expects%s %u paramteters, %u provided", macro->name,
929 (macro->variadic ? " at least" : ""),
930 (unsigned int)vec_size(macro->params),
931 (unsigned int)vec_size(params));
936 if (!ftepp_macro_expand(ftepp, macro, params, (paramline != ftepp->lex->sline)))
941 for (o = 0; o < vec_size(params); ++o)
942 macroparam_clean(¶ms[o]);
948 * #if - the FTEQCC way:
949 * defined(FOO) => true if FOO was #defined regardless of parameters or contents
950 * <numbers> => True if the number is not 0
951 * !<factor> => True if the factor yields false
952 * !!<factor> => ERROR on 2 or more unary nots
953 * <macro> => becomes the macro's FIRST token regardless of parameters
954 * <e> && <e> => True if both expressions are true
955 * <e> || <e> => True if either expression is true
957 * <ident> => False (remember for macros the <macro> rule applies instead)
958 * Unary + and - are weird and wrong in fteqcc so we don't allow them
959 * parenthesis in expressions are allowed
960 * parameter lists on macros are errors
961 * No mathematical calculations are executed
963 static bool ftepp_if_expr(ftepp_t *ftepp, bool *out, double *value_out);
964 static bool ftepp_if_op(ftepp_t *ftepp)
966 ftepp->lex->flags.noops = false;
968 if (!ftepp_skipspace(ftepp))
970 ftepp->lex->flags.noops = true;
973 static bool ftepp_if_value(ftepp_t *ftepp, bool *out, double *value_out)
979 if (!ftepp_skipspace(ftepp))
982 while (ftepp->token == '!') {
985 if (!ftepp_skipspace(ftepp))
989 if (ftepp->token == TOKEN_OPERATOR && !strcmp(ftepp_tokval(ftepp), "-"))
993 if (!ftepp_skipspace(ftepp))
997 switch (ftepp->token) {
1001 if (!strcmp(ftepp_tokval(ftepp), "defined")) {
1003 if (!ftepp_skipspace(ftepp))
1005 if (ftepp->token != '(') {
1006 ftepp_error(ftepp, "`defined` keyword in #if requires a macro name in parenthesis");
1010 if (!ftepp_skipspace(ftepp))
1012 if (ftepp->token != TOKEN_IDENT &&
1013 ftepp->token != TOKEN_TYPENAME &&
1014 ftepp->token != TOKEN_KEYWORD)
1016 ftepp_error(ftepp, "defined() used on an unexpected token type");
1019 macro = ftepp_macro_find(ftepp, ftepp_tokval(ftepp));
1022 if (!ftepp_skipspace(ftepp))
1024 if (ftepp->token != ')') {
1025 ftepp_error(ftepp, "expected closing paren");
1031 macro = ftepp_macro_find(ftepp, ftepp_tokval(ftepp));
1032 if (!macro || !vec_size(macro->output)) {
1036 /* This does not expand recursively! */
1037 switch (macro->output[0]->token) {
1038 case TOKEN_INTCONST:
1039 *value_out = macro->output[0]->constval.i;
1040 *out = !!(macro->output[0]->constval.i);
1042 case TOKEN_FLOATCONST:
1043 *value_out = macro->output[0]->constval.f;
1044 *out = !!(macro->output[0]->constval.f);
1052 case TOKEN_STRINGCONST:
1056 case TOKEN_INTCONST:
1057 *value_out = ftepp->lex->tok.constval.i;
1058 *out = !!(ftepp->lex->tok.constval.i);
1060 case TOKEN_FLOATCONST:
1061 *value_out = ftepp->lex->tok.constval.f;
1062 *out = !!(ftepp->lex->tok.constval.f);
1067 if (!ftepp_if_expr(ftepp, out, value_out))
1069 if (ftepp->token != ')') {
1070 ftepp_error(ftepp, "expected closing paren in #if expression");
1076 ftepp_error(ftepp, "junk in #if: `%s` ...", ftepp_tokval(ftepp));
1077 if (OPTS_OPTION_BOOL(OPTION_DEBUG))
1078 ftepp_error(ftepp, "internal: token %i\n", ftepp->token);
1082 *value_out = -*value_out;
1085 *value_out = (*out ? 1 : 0);
1091 static bool ftepp_if_nextvalue(ftepp_t *ftepp, bool *out, double *value_out)
1093 if (!ftepp_next(ftepp))
1095 return ftepp_if_value(ftepp, out, value_out);
1099 static bool ftepp_if_expr(ftepp_t *ftepp, bool *out, double *value_out)
1101 if (!ftepp_if_value(ftepp, out, value_out))
1104 if (!ftepp_if_op(ftepp))
1107 if (ftepp->token == ')' || ftepp->token != TOKEN_OPERATOR)
1110 /* FTEQCC is all right-associative and no precedence here */
1111 if (!strcmp(ftepp_tokval(ftepp), "&&") ||
1112 !strcmp(ftepp_tokval(ftepp), "||"))
1115 char opc = ftepp_tokval(ftepp)[0];
1119 if (!ftepp_next(ftepp))
1121 if (!ftepp_if_expr(ftepp, &next, &nextvalue))
1125 *out = *out && next;
1127 *out = *out || next;
1129 *value_out = (*out ? 1 : 0);
1132 else if (!strcmp(ftepp_tokval(ftepp), "==") ||
1133 !strcmp(ftepp_tokval(ftepp), "!=") ||
1134 !strcmp(ftepp_tokval(ftepp), ">=") ||
1135 !strcmp(ftepp_tokval(ftepp), "<=") ||
1136 !strcmp(ftepp_tokval(ftepp), ">") ||
1137 !strcmp(ftepp_tokval(ftepp), "<"))
1140 const char opc0 = ftepp_tokval(ftepp)[0];
1141 const char opc1 = ftepp_tokval(ftepp)[1];
1144 if (!ftepp_next(ftepp))
1146 if (!ftepp_if_expr(ftepp, &next, &other))
1150 *out = (*value_out == other);
1151 else if (opc0 == '!')
1152 *out = (*value_out != other);
1153 else if (opc0 == '>') {
1154 if (opc1 == '=') *out = (*value_out >= other);
1155 else *out = (*value_out > other);
1157 else if (opc0 == '<') {
1158 if (opc1 == '=') *out = (*value_out <= other);
1159 else *out = (*value_out < other);
1161 *value_out = (*out ? 1 : 0);
1166 ftepp_error(ftepp, "junk after #if");
1171 static bool ftepp_if(ftepp_t *ftepp, ppcondition *cond)
1173 bool result = false;
1176 memset(cond, 0, sizeof(*cond));
1177 (void)ftepp_next(ftepp);
1179 if (!ftepp_skipspace(ftepp))
1181 if (ftepp->token == TOKEN_EOL) {
1182 ftepp_error(ftepp, "expected expression for #if-directive");
1186 if (!ftepp_if_expr(ftepp, &result, &dummy))
1194 * ifdef is rather simple
1196 static bool ftepp_ifdef(ftepp_t *ftepp, ppcondition *cond)
1199 memset(cond, 0, sizeof(*cond));
1200 (void)ftepp_next(ftepp);
1201 if (!ftepp_skipspace(ftepp))
1204 switch (ftepp->token) {
1206 case TOKEN_TYPENAME:
1208 macro = ftepp_macro_find(ftepp, ftepp_tokval(ftepp));
1211 ftepp_error(ftepp, "expected macro name");
1215 (void)ftepp_next(ftepp);
1216 if (!ftepp_skipspace(ftepp))
1218 /* relaxing this condition
1219 if (ftepp->token != TOKEN_EOL && ftepp->token != TOKEN_EOF) {
1220 ftepp_error(ftepp, "stray tokens after #ifdef");
1229 * undef is also simple
1231 static bool ftepp_undef(ftepp_t *ftepp)
1233 (void)ftepp_next(ftepp);
1234 if (!ftepp_skipspace(ftepp))
1237 if (ftepp->output_on) {
1238 switch (ftepp->token) {
1240 case TOKEN_TYPENAME:
1242 ftepp_macro_delete(ftepp, ftepp_tokval(ftepp));
1245 ftepp_error(ftepp, "expected macro name");
1250 (void)ftepp_next(ftepp);
1251 if (!ftepp_skipspace(ftepp))
1253 /* relaxing this condition
1254 if (ftepp->token != TOKEN_EOL && ftepp->token != TOKEN_EOF) {
1255 ftepp_error(ftepp, "stray tokens after #ifdef");
1262 /* Special unescape-string function which skips a leading quote
1263 * and stops at a quote, not just at \0
1265 static void unescape(const char *str, char *out) {
1267 while (*str && *str != '"') {
1271 case '\\': *out++ = *str; break;
1272 case '"': *out++ = *str; break;
1273 case 'a': *out++ = '\a'; break;
1274 case 'b': *out++ = '\b'; break;
1275 case 'r': *out++ = '\r'; break;
1276 case 'n': *out++ = '\n'; break;
1277 case 't': *out++ = '\t'; break;
1278 case 'f': *out++ = '\f'; break;
1279 case 'v': *out++ = '\v'; break;
1294 static char *ftepp_include_find_path(const char *file, const char *pathfile)
1297 char *filename = NULL;
1298 const char *last_slash;
1304 last_slash = strrchr(pathfile, '/');
1307 len = last_slash - pathfile;
1308 memcpy(vec_add(filename, len), pathfile, len);
1309 vec_push(filename, '/');
1313 memcpy(vec_add(filename, len+1), file, len);
1314 vec_last(filename) = 0;
1316 fp = fs_file_open(filename, "rb");
1325 static char *ftepp_include_find(ftepp_t *ftepp, const char *file)
1327 char *filename = NULL;
1329 filename = ftepp_include_find_path(file, ftepp->includename);
1331 filename = ftepp_include_find_path(file, ftepp->itemname);
1335 static bool ftepp_directive_warning(ftepp_t *ftepp) {
1336 char *message = NULL;
1338 if (!ftepp_skipspace(ftepp))
1341 /* handle the odd non string constant case so it works like C */
1342 if (ftepp->token != TOKEN_STRINGCONST) {
1344 vec_append(message, 8, "#warning");
1346 while (ftepp->token != TOKEN_EOL) {
1347 vec_append(message, strlen(ftepp_tokval(ftepp)), ftepp_tokval(ftepp));
1350 vec_push(message, '\0');
1351 if (ftepp->output_on)
1352 store = ftepp_warn(ftepp, WARN_CPP, message);
1359 if (!ftepp->output_on)
1362 unescape (ftepp_tokval(ftepp), ftepp_tokval(ftepp));
1363 return ftepp_warn(ftepp, WARN_CPP, "#warning %s", ftepp_tokval(ftepp));
1366 static void ftepp_directive_error(ftepp_t *ftepp) {
1367 char *message = NULL;
1369 if (!ftepp_skipspace(ftepp))
1372 /* handle the odd non string constant case so it works like C */
1373 if (ftepp->token != TOKEN_STRINGCONST) {
1374 vec_append(message, 6, "#error");
1376 while (ftepp->token != TOKEN_EOL) {
1377 vec_append(message, strlen(ftepp_tokval(ftepp)), ftepp_tokval(ftepp));
1380 vec_push(message, '\0');
1381 if (ftepp->output_on)
1382 ftepp_error(ftepp, message);
1387 if (!ftepp->output_on)
1390 unescape (ftepp_tokval(ftepp), ftepp_tokval(ftepp));
1391 ftepp_error(ftepp, "#error %s", ftepp_tokval(ftepp));
1394 static void ftepp_directive_message(ftepp_t *ftepp) {
1395 char *message = NULL;
1397 if (!ftepp_skipspace(ftepp))
1400 /* handle the odd non string constant case so it works like C */
1401 if (ftepp->token != TOKEN_STRINGCONST) {
1402 vec_append(message, 8, "#message");
1404 while (ftepp->token != TOKEN_EOL) {
1405 vec_append(message, strlen(ftepp_tokval(ftepp)), ftepp_tokval(ftepp));
1408 vec_push(message, '\0');
1409 if (ftepp->output_on)
1410 con_cprintmsg(ftepp->lex->tok.ctx, LVL_MSG, "message", message);
1415 if (!ftepp->output_on)
1418 unescape (ftepp_tokval(ftepp), ftepp_tokval(ftepp));
1419 con_cprintmsg(ftepp->lex->tok.ctx, LVL_MSG, "message", ftepp_tokval(ftepp));
1424 * FIXME: do we need/want a -I option?
1425 * FIXME: what about when dealing with files in subdirectories coming from a progs.src?
1427 static bool ftepp_include(ftepp_t *ftepp)
1429 lex_file *old_lexer = ftepp->lex;
1434 char *old_includename;
1436 (void)ftepp_next(ftepp);
1437 if (!ftepp_skipspace(ftepp))
1440 if (ftepp->token != TOKEN_STRINGCONST) {
1441 ftepp_error(ftepp, "expected filename to include");
1445 if (!ftepp->output_on) {
1450 ctx = ftepp_ctx(ftepp);
1452 unescape(ftepp_tokval(ftepp), ftepp_tokval(ftepp));
1454 ftepp_out(ftepp, "\n#pragma file(", false);
1455 ftepp_out(ftepp, ftepp_tokval(ftepp), false);
1456 ftepp_out(ftepp, ")\n#pragma line(1)\n", false);
1458 filename = ftepp_include_find(ftepp, ftepp_tokval(ftepp));
1460 ftepp_error(ftepp, "failed to open include file `%s`", ftepp_tokval(ftepp));
1463 inlex = lex_open(filename);
1465 ftepp_error(ftepp, "open failed on include file `%s`", filename);
1470 old_includename = ftepp->includename;
1471 ftepp->includename = filename;
1472 if (!ftepp_preprocess(ftepp)) {
1473 vec_free(ftepp->includename);
1474 ftepp->includename = old_includename;
1475 lex_close(ftepp->lex);
1476 ftepp->lex = old_lexer;
1479 vec_free(ftepp->includename);
1480 ftepp->includename = old_includename;
1481 lex_close(ftepp->lex);
1482 ftepp->lex = old_lexer;
1484 ftepp_out(ftepp, "\n#pragma file(", false);
1485 ftepp_out(ftepp, ctx.file, false);
1486 util_snprintf(lineno, sizeof(lineno), ")\n#pragma line(%lu)\n", (unsigned long)(ctx.line+1));
1487 ftepp_out(ftepp, lineno, false);
1490 (void)ftepp_next(ftepp);
1491 if (!ftepp_skipspace(ftepp))
1493 if (ftepp->token != TOKEN_EOL) {
1494 ftepp_error(ftepp, "stray tokens after #include");
1497 (void)ftepp_next(ftepp);
1502 /* Basic structure handlers */
1503 static bool ftepp_else_allowed(ftepp_t *ftepp)
1505 if (!vec_size(ftepp->conditions)) {
1506 ftepp_error(ftepp, "#else without #if");
1509 if (vec_last(ftepp->conditions).had_else) {
1510 ftepp_error(ftepp, "multiple #else for a single #if");
1516 static bool ftepp_hash(ftepp_t *ftepp)
1521 lex_ctx_t ctx = ftepp_ctx(ftepp);
1523 if (!ftepp_skipspace(ftepp))
1526 switch (ftepp->token) {
1529 case TOKEN_TYPENAME:
1530 if (!strcmp(ftepp_tokval(ftepp), "define")) {
1531 return ftepp_define(ftepp);
1533 else if (!strcmp(ftepp_tokval(ftepp), "undef")) {
1534 return ftepp_undef(ftepp);
1536 else if (!strcmp(ftepp_tokval(ftepp), "ifdef")) {
1537 if (!ftepp_ifdef(ftepp, &cond))
1539 cond.was_on = cond.on;
1540 vec_push(ftepp->conditions, cond);
1541 ftepp->output_on = ftepp->output_on && cond.on;
1544 else if (!strcmp(ftepp_tokval(ftepp), "ifndef")) {
1545 if (!ftepp_ifdef(ftepp, &cond))
1548 cond.was_on = cond.on;
1549 vec_push(ftepp->conditions, cond);
1550 ftepp->output_on = ftepp->output_on && cond.on;
1553 else if (!strcmp(ftepp_tokval(ftepp), "elifdef")) {
1554 if (!ftepp_else_allowed(ftepp))
1556 if (!ftepp_ifdef(ftepp, &cond))
1558 pc = &vec_last(ftepp->conditions);
1559 pc->on = !pc->was_on && cond.on;
1560 pc->was_on = pc->was_on || pc->on;
1561 ftepp_update_output_condition(ftepp);
1564 else if (!strcmp(ftepp_tokval(ftepp), "elifndef")) {
1565 if (!ftepp_else_allowed(ftepp))
1567 if (!ftepp_ifdef(ftepp, &cond))
1570 pc = &vec_last(ftepp->conditions);
1571 pc->on = !pc->was_on && cond.on;
1572 pc->was_on = pc->was_on || pc->on;
1573 ftepp_update_output_condition(ftepp);
1576 else if (!strcmp(ftepp_tokval(ftepp), "elif")) {
1577 if (!ftepp_else_allowed(ftepp))
1579 if (!ftepp_if(ftepp, &cond))
1581 pc = &vec_last(ftepp->conditions);
1582 pc->on = !pc->was_on && cond.on;
1583 pc->was_on = pc->was_on || pc->on;
1584 ftepp_update_output_condition(ftepp);
1587 else if (!strcmp(ftepp_tokval(ftepp), "if")) {
1588 if (!ftepp_if(ftepp, &cond))
1590 cond.was_on = cond.on;
1591 vec_push(ftepp->conditions, cond);
1592 ftepp->output_on = ftepp->output_on && cond.on;
1595 else if (!strcmp(ftepp_tokval(ftepp), "else")) {
1596 if (!ftepp_else_allowed(ftepp))
1598 pc = &vec_last(ftepp->conditions);
1599 pc->on = !pc->was_on;
1600 pc->had_else = true;
1602 ftepp_update_output_condition(ftepp);
1605 else if (!strcmp(ftepp_tokval(ftepp), "endif")) {
1606 if (!vec_size(ftepp->conditions)) {
1607 ftepp_error(ftepp, "#endif without #if");
1610 vec_pop(ftepp->conditions);
1612 ftepp_update_output_condition(ftepp);
1615 else if (!strcmp(ftepp_tokval(ftepp), "include")) {
1616 return ftepp_include(ftepp);
1618 else if (!strcmp(ftepp_tokval(ftepp), "pragma")) {
1619 ftepp_out(ftepp, "#", false);
1622 else if (!strcmp(ftepp_tokval(ftepp), "warning")) {
1623 ftepp_directive_warning(ftepp);
1626 else if (!strcmp(ftepp_tokval(ftepp), "error")) {
1627 ftepp_directive_error(ftepp);
1630 else if (!strcmp(ftepp_tokval(ftepp), "message")) {
1631 ftepp_directive_message(ftepp);
1635 if (ftepp->output_on) {
1636 ftepp_error(ftepp, "unrecognized preprocessor directive: `%s`", ftepp_tokval(ftepp));
1643 /* break; never reached */
1645 ftepp_error(ftepp, "unexpected preprocessor token: `%s`", ftepp_tokval(ftepp));
1648 ftepp_errorat(ftepp, ctx, "empty preprocessor directive");
1651 ftepp_error(ftepp, "missing newline at end of file", ftepp_tokval(ftepp));
1654 /* Builtins! Don't forget the builtins! */
1655 case TOKEN_INTCONST:
1656 case TOKEN_FLOATCONST:
1657 ftepp_out(ftepp, "#", false);
1660 if (!ftepp_skipspace(ftepp))
1665 static bool ftepp_preprocess(ftepp_t *ftepp)
1668 bool newline = true;
1671 char *expand = NULL;
1673 ftepp->lex->flags.preprocessing = true;
1674 ftepp->lex->flags.mergelines = false;
1675 ftepp->lex->flags.noops = true;
1680 if (ftepp->token >= TOKEN_EOF)
1686 switch (ftepp->token) {
1689 case TOKEN_TYPENAME:
1690 /* is it a predef? */
1691 if (OPTS_FLAG(FTEPP_PREDEFS)) {
1692 char *(*predef)(lex_file*) = ftepp_predef(ftepp_tokval(ftepp));
1694 expand = predef(ftepp->lex);
1695 ftepp_out (ftepp, expand, false);
1703 if (ftepp->output_on)
1704 macro = ftepp_macro_find(ftepp, ftepp_tokval(ftepp));
1709 ftepp_out(ftepp, ftepp_tokval(ftepp), false);
1713 if (!ftepp_macro_call(ftepp, macro))
1714 ftepp->token = TOKEN_ERROR;
1718 ftepp_out(ftepp, ftepp_tokval(ftepp), false);
1722 ftepp->lex->flags.mergelines = true;
1723 if (ftepp_next(ftepp) >= TOKEN_EOF) {
1724 ftepp_error(ftepp, "error in preprocessor directive");
1725 ftepp->token = TOKEN_ERROR;
1728 if (!ftepp_hash(ftepp))
1729 ftepp->token = TOKEN_ERROR;
1730 ftepp->lex->flags.mergelines = false;
1734 ftepp_out(ftepp, "\n", true);
1738 /* same as default but don't set newline=false */
1739 ftepp_out(ftepp, ftepp_tokval(ftepp), true);
1744 ftepp_out(ftepp, ftepp_tokval(ftepp), false);
1748 } while (!ftepp->errors && ftepp->token < TOKEN_EOF);
1750 /* force a 0 at the end but don't count it as added to the output */
1751 vec_push(ftepp->output_string, 0);
1752 vec_shrinkby(ftepp->output_string, 1);
1754 return (ftepp->token == TOKEN_EOF);
1757 /* Like in parser.c - files keep the previous state so we have one global
1758 * preprocessor. Except here we will want to warn about dangling #ifs.
1760 static bool ftepp_preprocess_done(ftepp_t *ftepp)
1763 if (vec_size(ftepp->conditions)) {
1764 if (ftepp_warn(ftepp, WARN_MULTIFILE_IF, "#if spanning multiple files, is this intended?"))
1767 lex_close(ftepp->lex);
1769 if (ftepp->itemname) {
1770 mem_d(ftepp->itemname);
1771 ftepp->itemname = NULL;
1776 bool ftepp_preprocess_file(ftepp_t *ftepp, const char *filename)
1778 ftepp->lex = lex_open(filename);
1779 ftepp->itemname = util_strdup(filename);
1781 con_out("failed to open file \"%s\"\n", filename);
1784 if (!ftepp_preprocess(ftepp))
1786 return ftepp_preprocess_done(ftepp);
1789 bool ftepp_preprocess_string(ftepp_t *ftepp, const char *name, const char *str)
1791 ftepp->lex = lex_open_string(str, strlen(str), name);
1792 ftepp->itemname = util_strdup(name);
1794 con_out("failed to create lexer for string \"%s\"\n", name);
1797 if (!ftepp_preprocess(ftepp))
1799 return ftepp_preprocess_done(ftepp);
1803 void ftepp_add_macro(ftepp_t *ftepp, const char *name, const char *value) {
1804 char *create = NULL;
1806 /* use saner path for empty macros */
1808 ftepp_add_define(ftepp, "__builtin__", name);
1812 vec_append(create, 8, "#define ");
1813 vec_append(create, strlen(name), name);
1814 vec_push (create, ' ');
1815 vec_append(create, strlen(value), value);
1816 vec_push (create, 0);
1818 ftepp_preprocess_string(ftepp, "__builtin__", create);
1822 ftepp_t *ftepp_create()
1828 ftepp = ftepp_new();
1832 memset(minor, 0, sizeof(minor));
1833 memset(major, 0, sizeof(major));
1835 /* set the right macro based on the selected standard */
1836 ftepp_add_define(ftepp, NULL, "GMQCC");
1837 if (OPTS_OPTION_U32(OPTION_STANDARD) == COMPILER_FTEQCC) {
1838 ftepp_add_define(ftepp, NULL, "__STD_FTEQCC__");
1847 } else if (OPTS_OPTION_U32(OPTION_STANDARD) == COMPILER_GMQCC) {
1848 ftepp_add_define(ftepp, NULL, "__STD_GMQCC__");
1849 util_snprintf(major, 32, "\"%d\"", GMQCC_VERSION_MAJOR);
1850 util_snprintf(minor, 32, "\"%d\"", GMQCC_VERSION_MINOR);
1851 } else if (OPTS_OPTION_U32(OPTION_STANDARD) == COMPILER_QCCX) {
1852 ftepp_add_define(ftepp, NULL, "__STD_QCCX__");
1853 util_snprintf(major, 32, "\"%d\"", GMQCC_VERSION_MAJOR);
1854 util_snprintf(minor, 32, "\"%d\"", GMQCC_VERSION_MINOR);
1855 } else if (OPTS_OPTION_U32(OPTION_STANDARD) == COMPILER_QCC) {
1856 ftepp_add_define(ftepp, NULL, "__STD_QCC__");
1867 ftepp_add_macro(ftepp, "__STD_VERSION_MINOR__", minor);
1868 ftepp_add_macro(ftepp, "__STD_VERSION_MAJOR__", major);
1871 * We're going to just make __NULL__ nil, which works for 60% of the
1872 * cases of __NULL_ for fteqcc.
1874 ftepp_add_macro(ftepp, "__NULL__", "nil");
1879 void ftepp_add_define(ftepp_t *ftepp, const char *source, const char *name)
1882 lex_ctx_t ctx = { "__builtin__", 0, 0 };
1884 macro = ppmacro_new(ctx, name);
1885 /*vec_push(ftepp->macros, macro);*/
1886 util_htset(ftepp->macros, name, macro);
1889 const char *ftepp_get(ftepp_t *ftepp)
1891 return ftepp->output_string;
1894 void ftepp_flush(ftepp_t *ftepp)
1896 ftepp_flush_do(ftepp);
1899 void ftepp_finish(ftepp_t *ftepp)
1903 ftepp_delete(ftepp);