19 /* a copy from the lexer */
32 /* yes we need an extra flag since `#define FOO x` is not the same as `#define FOO() x` */
43 ppcondition *conditions;
44 ht macros; /* hashtable<string, ppmacro*> */
49 uint32_t predef_countval;
50 uint32_t predef_randval;
54 static char *ftepp_predef_date(ftepp_t *context) {
55 const struct tm *itime = NULL;
56 char *value = (char*)mem_a(82);
62 itime = util_localtime(&rtime);
63 strftime(value, 82, "\"%b %d %Y\"", itime);
69 static char *ftepp_predef_time(ftepp_t *context) {
70 const struct tm *itime = NULL;
71 char *value = (char*)mem_a(82);
77 itime = util_localtime(&rtime);
78 strftime(value, 82, "\"%X\"", itime);
84 static char *ftepp_predef_line(ftepp_t *context) {
87 util_asprintf(&value, "%d", (int)context->lex->line);
91 static char *ftepp_predef_file(ftepp_t *context) {
92 size_t length = strlen(context->lex->name) + 3; /* two quotes and a terminator */
93 char *value = (char*)mem_a(length);
95 util_snprintf(value, length, "\"%s\"", context->lex->name);
98 /* __COUNTER_LAST__ */
99 static char *ftepp_predef_counterlast(ftepp_t *context) {
101 util_asprintf(&value, "%u", context->predef_countval);
105 static char *ftepp_predef_counter(ftepp_t *context) {
108 context->predef_countval ++;
109 util_asprintf(&value, "%u", context->predef_countval);
114 static char *ftepp_predef_random(ftepp_t *context) {
117 context->predef_randval = (util_rand() % 0xFF) + 1;
118 util_asprintf(&value, "%u", context->predef_randval);
121 /* __RANDOM_LAST__ */
122 static char *ftepp_predef_randomlast(ftepp_t *context) {
125 util_asprintf(&value, "%u", context->predef_randval);
129 static char *ftepp_predef_timestamp(ftepp_t *context) {
135 if (stat(context->lex->name, &finfo))
136 return util_strdup("\"<failed to determine timestamp>\"");
138 find = util_ctime(&finfo.st_mtime);
139 value = (char*)mem_a(strlen(find) + 1);
140 memcpy(&value[1], find, (size = strlen(find)) - 1);
150 char *(*func)(ftepp_t *);
153 static const ftepp_predef_t ftepp_predefs[] = {
154 { "__LINE__", &ftepp_predef_line },
155 { "__FILE__", &ftepp_predef_file },
156 { "__COUNTER__", &ftepp_predef_counter },
157 { "__COUNTER_LAST__", &ftepp_predef_counterlast },
158 { "__RANDOM__", &ftepp_predef_random },
159 { "__RANDOM_LAST__", &ftepp_predef_randomlast },
160 { "__DATE__", &ftepp_predef_date },
161 { "__TIME__", &ftepp_predef_time },
162 { "__TIME_STAMP__", &ftepp_predef_timestamp }
165 static GMQCC_INLINE size_t ftepp_predef_index(const char *name) {
166 /* no hashtable here, we simply check for one to exist the naive way */
168 for(i = 1; i < GMQCC_ARRAY_COUNT(ftepp_predefs) + 1; i++)
169 if (!strcmp(ftepp_predefs[i-1].name, name))
174 bool ftepp_predef_exists(const char *name);
175 bool ftepp_predef_exists(const char *name) {
176 return ftepp_predef_index(name) != 0;
179 /* singleton because we're allowed */
180 static GMQCC_INLINE char *(*ftepp_predef(const char *name))(ftepp_t *context) {
181 size_t i = ftepp_predef_index(name);
182 return (i != 0) ? ftepp_predefs[i-1].func : NULL;
185 #define ftepp_tokval(f) ((f)->lex->tok.value)
186 #define ftepp_ctx(f) ((f)->lex->tok.ctx)
188 static void ftepp_errorat(ftepp_t *ftepp, lex_ctx_t ctx, const char *fmt, ...)
195 con_cvprintmsg(ctx, LVL_ERROR, "error", fmt, ap);
199 static void ftepp_error(ftepp_t *ftepp, const char *fmt, ...)
206 con_cvprintmsg(ftepp->lex->tok.ctx, LVL_ERROR, "error", fmt, ap);
210 static bool GMQCC_WARN ftepp_warn(ftepp_t *ftepp, int warntype, const char *fmt, ...)
216 r = vcompile_warning(ftepp->lex->tok.ctx, warntype, fmt, ap);
221 static pptoken *pptoken_make(ftepp_t *ftepp)
223 pptoken *token = (pptoken*)mem_a(sizeof(pptoken));
224 token->token = ftepp->token;
225 token->value = util_strdup(ftepp_tokval(ftepp));
226 memcpy(&token->constval, &ftepp->lex->tok.constval, sizeof(token->constval));
230 static GMQCC_INLINE void pptoken_delete(pptoken *self)
236 static ppmacro *ppmacro_new(lex_ctx_t ctx, const char *name)
238 ppmacro *macro = (ppmacro*)mem_a(sizeof(ppmacro));
241 memset(macro, 0, sizeof(*macro));
242 macro->name = util_strdup(name);
246 static void ppmacro_delete(ppmacro *self)
249 for (i = 0; i < vec_size(self->params); ++i)
250 mem_d(self->params[i]);
251 vec_free(self->params);
252 for (i = 0; i < vec_size(self->output); ++i)
253 pptoken_delete(self->output[i]);
254 vec_free(self->output);
259 static ftepp_t* ftepp_new(void)
263 ftepp = (ftepp_t*)mem_a(sizeof(*ftepp));
264 memset(ftepp, 0, sizeof(*ftepp));
266 ftepp->macros = util_htnew(HT_MACROS);
267 ftepp->output_on = true;
268 ftepp->predef_countval = 0;
269 ftepp->predef_randval = 0;
274 static GMQCC_INLINE void ftepp_flush_do(ftepp_t *self)
276 vec_free(self->output_string);
279 static void ftepp_delete(ftepp_t *self)
281 ftepp_flush_do(self);
283 mem_d(self->itemname);
284 if (self->includename)
285 vec_free(self->includename);
287 util_htrem(self->macros, (void (*)(void*))&ppmacro_delete);
289 vec_free(self->conditions);
291 lex_close(self->lex);
295 static void ftepp_out(ftepp_t *ftepp, const char *str, bool ignore_cond)
297 if (ignore_cond || ftepp->output_on)
302 data = vec_add(ftepp->output_string, len);
303 memcpy(data, str, len);
307 static GMQCC_INLINE void ftepp_update_output_condition(ftepp_t *ftepp)
310 ftepp->output_on = true;
311 for (i = 0; i < vec_size(ftepp->conditions); ++i)
312 ftepp->output_on = ftepp->output_on && ftepp->conditions[i].on;
315 static GMQCC_INLINE ppmacro* ftepp_macro_find(ftepp_t *ftepp, const char *name)
317 return (ppmacro*)util_htget(ftepp->macros, name);
320 static GMQCC_INLINE void ftepp_macro_delete(ftepp_t *ftepp, const char *name)
322 util_htrm(ftepp->macros, name, (void (*)(void*))&ppmacro_delete);
325 static GMQCC_INLINE int ftepp_next(ftepp_t *ftepp)
327 return (ftepp->token = lex_do(ftepp->lex));
330 /* Important: this does not skip newlines! */
331 static bool ftepp_skipspace(ftepp_t *ftepp)
333 if (ftepp->token != TOKEN_WHITE)
335 while (ftepp_next(ftepp) == TOKEN_WHITE) {}
336 if (ftepp->token >= TOKEN_EOF) {
337 ftepp_error(ftepp, "unexpected end of preprocessor directive");
343 /* this one skips EOLs as well */
344 static bool ftepp_skipallwhite(ftepp_t *ftepp)
346 if (ftepp->token != TOKEN_WHITE && ftepp->token != TOKEN_EOL)
350 } while (ftepp->token == TOKEN_WHITE || ftepp->token == TOKEN_EOL);
351 if (ftepp->token >= TOKEN_EOF) {
352 ftepp_error(ftepp, "unexpected end of preprocessor directive");
359 * The huge macro parsing code...
361 static bool ftepp_define_params(ftepp_t *ftepp, ppmacro *macro)
365 if (!ftepp_skipspace(ftepp))
367 if (ftepp->token == ')')
369 switch (ftepp->token) {
373 vec_push(macro->params, util_strdup(ftepp_tokval(ftepp)));
376 macro->variadic = true;
379 ftepp_error(ftepp, "unexpected token in parameter list");
383 if (!ftepp_skipspace(ftepp))
385 if (macro->variadic && ftepp->token != ')') {
386 ftepp_error(ftepp, "cannot have parameters after the variadic parameters");
389 } while (ftepp->token == ',');
391 if (ftepp->token != ')') {
392 ftepp_error(ftepp, "expected closing paren after macro parameter list");
396 /* skipspace happens in ftepp_define */
400 static bool ftepp_define_body(ftepp_t *ftepp, ppmacro *macro)
403 while (ftepp->token != TOKEN_EOL && ftepp->token < TOKEN_EOF) {
404 bool subscript = false;
406 if (macro->variadic && !strcmp(ftepp_tokval(ftepp), "__VA_ARGS__")) {
407 subscript = !!(ftepp_next(ftepp) == '#');
409 if (subscript && ftepp_next(ftepp) != '#') {
410 ftepp_error(ftepp, "expected `##` in __VA_ARGS__ for subscripting");
412 } else if (subscript) {
413 if (ftepp_next(ftepp) == '[') {
414 if (ftepp_next(ftepp) != TOKEN_INTCONST) {
415 ftepp_error(ftepp, "expected index for __VA_ARGS__ subscript");
419 index = (int)strtol(ftepp_tokval(ftepp), NULL, 10);
421 if (ftepp_next(ftepp) != ']') {
422 ftepp_error(ftepp, "expected `]` in __VA_ARGS__ subscript");
427 * mark it as an array to be handled later as such and not
428 * as traditional __VA_ARGS__
430 ftepp->token = TOKEN_VA_ARGS_ARRAY;
431 ptok = pptoken_make(ftepp);
432 ptok->constval.i = index;
433 vec_push(macro->output, ptok);
436 ftepp_error(ftepp, "expected `[` for subscripting of __VA_ARGS__");
440 int old = ftepp->token;
441 ftepp->token = TOKEN_VA_ARGS;
442 ptok = pptoken_make(ftepp);
443 vec_push(macro->output, ptok);
447 else if (macro->variadic && !strcmp(ftepp_tokval(ftepp), "__VA_COUNT__")) {
448 ftepp->token = TOKEN_VA_COUNT;
449 ptok = pptoken_make(ftepp);
450 vec_push(macro->output, ptok);
453 ptok = pptoken_make(ftepp);
454 vec_push(macro->output, ptok);
458 /* recursive expansion can cause EOFs here */
459 if (ftepp->token != TOKEN_EOL && ftepp->token != TOKEN_EOF) {
460 ftepp_error(ftepp, "unexpected junk after macro or unexpected end of file");
466 static const char *ftepp_math_constants[][2] = {
467 { "M_E", "2.7182818284590452354" }, /* e */
468 { "M_LOG2E", "1.4426950408889634074" }, /* log_2 e */
469 { "M_LOG10E", "0.43429448190325182765" }, /* log_10 e */
470 { "M_LN2", "0.69314718055994530942" }, /* log_e 2 */
471 { "M_LN10", "2.30258509299404568402" }, /* log_e 10 */
472 { "M_PI", "3.14159265358979323846" }, /* pi */
473 { "M_PI_2", "1.57079632679489661923" }, /* pi/2 */
474 { "M_PI_4", "0.78539816339744830962" }, /* pi/4 */
475 { "M_1_PI", "0.31830988618379067154" }, /* 1/pi */
476 { "M_2_PI", "0.63661977236758134308" }, /* 2/pi */
477 { "M_2_SQRTPI", "1.12837916709551257390" }, /* 2/sqrt(pi) */
478 { "M_SQRT2", "1.41421356237309504880" }, /* sqrt(2) */
479 { "M_SQRT1_2", "0.70710678118654752440" }, /* 1/sqrt(2) */
480 { "M_TAU", "6.28318530717958647692" } /* pi*2 */
483 static bool ftepp_define(ftepp_t *ftepp)
485 ppmacro *macro = NULL;
486 size_t l = ftepp_ctx(ftepp).line;
488 bool mathconstant = false;
490 (void)ftepp_next(ftepp);
491 if (!ftepp_skipspace(ftepp))
494 switch (ftepp->token) {
498 if (OPTS_FLAG(FTEPP_MATHDEFS)) {
499 for (i = 0; i < GMQCC_ARRAY_COUNT(ftepp_math_constants); i++) {
500 if (!strcmp(ftepp_math_constants[i][0], ftepp_tokval(ftepp))) {
507 macro = ftepp_macro_find(ftepp, ftepp_tokval(ftepp));
509 if (OPTS_FLAG(FTEPP_MATHDEFS)) {
510 /* user defined ones take precedence */
511 if (macro && mathconstant) {
512 ftepp_macro_delete(ftepp, ftepp_tokval(ftepp));
517 if (macro && ftepp->output_on) {
518 if (ftepp_warn(ftepp, WARN_CPP, "redefining `%s`", ftepp_tokval(ftepp)))
520 ftepp_macro_delete(ftepp, ftepp_tokval(ftepp));
522 macro = ppmacro_new(ftepp_ctx(ftepp), ftepp_tokval(ftepp));
525 ftepp_error(ftepp, "expected macro name");
529 (void)ftepp_next(ftepp);
531 if (ftepp->token == '(') {
532 macro->has_params = true;
533 if (!ftepp_define_params(ftepp, macro)) {
534 ppmacro_delete(macro);
539 if (!ftepp_skipspace(ftepp)) {
540 ppmacro_delete(macro);
544 if (!ftepp_define_body(ftepp, macro)) {
545 ppmacro_delete(macro);
549 if (ftepp->output_on)
550 util_htset(ftepp->macros, macro->name, (void*)macro);
552 ppmacro_delete(macro);
555 for (; l < ftepp_ctx(ftepp).line; ++l)
556 ftepp_out(ftepp, "\n", true);
561 * When a macro is used we have to handle parameters as well
562 * as special-concatenation via ## or stringification via #
564 * Note: parenthesis can nest, so FOO((a),b) is valid, but only
565 * this kind of parens. Curly braces or [] don't count towards the
572 static void macroparam_clean(macroparam *self)
575 for (i = 0; i < vec_size(self->tokens); ++i)
576 pptoken_delete(self->tokens[i]);
577 vec_free(self->tokens);
580 /* need to leave the last token up */
581 static bool ftepp_macro_call_params(ftepp_t *ftepp, macroparam **out_params)
583 macroparam *params = NULL;
589 if (!ftepp_skipallwhite(ftepp))
591 while (ftepp->token != ')') {
593 if (!ftepp_skipallwhite(ftepp))
595 while (parens || ftepp->token != ',') {
596 if (ftepp->token == '(')
598 else if (ftepp->token == ')') {
603 ptok = pptoken_make(ftepp);
604 vec_push(mp.tokens, ptok);
605 if (ftepp_next(ftepp) >= TOKEN_EOF) {
606 ftepp_error(ftepp, "unexpected end of file in macro call");
610 vec_push(params, mp);
612 if (ftepp->token == ')')
614 if (ftepp->token != ',') {
615 ftepp_error(ftepp, "expected closing paren or comma in macro call");
618 if (ftepp_next(ftepp) >= TOKEN_EOF) {
619 ftepp_error(ftepp, "unexpected end of file in macro call");
623 *out_params = params;
628 macroparam_clean(&mp);
629 for (i = 0; i < vec_size(params); ++i)
630 macroparam_clean(¶ms[i]);
635 static bool macro_params_find(ppmacro *macro, const char *name, size_t *idx)
638 for (i = 0; i < vec_size(macro->params); ++i) {
639 if (!strcmp(macro->params[i], name)) {
647 static void ftepp_stringify_token(ftepp_t *ftepp, pptoken *token)
652 switch (token->token) {
653 case TOKEN_STRINGCONST:
656 /* in preprocessor mode strings already are string,
657 * so we don't get actual newline bytes here.
658 * Still need to escape backslashes and quotes.
661 case '\\': ftepp_out(ftepp, "\\\\", false); break;
662 case '"': ftepp_out(ftepp, "\\\"", false); break;
665 ftepp_out(ftepp, chs, false);
672 ftepp_out(ftepp, " ", false);
675 ftepp_out(ftepp, "\\n", false);
678 ftepp_out(ftepp, token->value, false);
683 static void ftepp_stringify(ftepp_t *ftepp, macroparam *param)
686 ftepp_out(ftepp, "\"", false);
687 for (i = 0; i < vec_size(param->tokens); ++i)
688 ftepp_stringify_token(ftepp, param->tokens[i]);
689 ftepp_out(ftepp, "\"", false);
692 static void ftepp_recursion_header(ftepp_t *ftepp)
694 ftepp_out(ftepp, "\n#pragma push(line)\n", false);
697 static void ftepp_recursion_footer(ftepp_t *ftepp)
699 ftepp_out(ftepp, "\n#pragma pop(line)\n", false);
702 static bool ftepp_macro_expand(ftepp_t *ftepp, ppmacro *macro, macroparam *params, bool resetline);
703 static void ftepp_param_out(ftepp_t *ftepp, macroparam *param)
707 for (i = 0; i < vec_size(param->tokens); ++i) {
708 out = param->tokens[i];
709 if (out->token == TOKEN_EOL)
710 ftepp_out(ftepp, "\n", false);
712 ppmacro *find = ftepp_macro_find(ftepp, out->value);
713 if (OPTS_FLAG(FTEPP_INDIRECT_EXPANSION) && find && !find->has_params)
714 ftepp_macro_expand(ftepp, find, NULL, false);
716 ftepp_out(ftepp, out->value, false);
721 static bool ftepp_preprocess(ftepp_t *ftepp);
722 static bool ftepp_macro_expand(ftepp_t *ftepp, ppmacro *macro, macroparam *params, bool resetline)
725 char *old_string = ftepp->output_string;
727 lex_file *old_lexer = ftepp->lex;
728 size_t vararg_start = vec_size(macro->params);
741 if (vararg_start < vec_size(params))
742 varargs = vec_size(params) - vararg_start;
747 if (!vec_size(macro->output))
750 ftepp->output_string = NULL;
751 for (o = 0; o < vec_size(macro->output); ++o) {
752 pptoken *out = macro->output[o];
753 switch (out->token) {
755 if (!macro->variadic) {
756 ftepp_error(ftepp, "internal preprocessor error: TOKEN_VA_ARGS in non-variadic macro");
757 vec_free(old_string);
764 ftepp_param_out(ftepp, ¶ms[pi + vararg_start]);
765 for (++pi; pi < varargs; ++pi) {
766 ftepp_out(ftepp, ", ", false);
767 ftepp_param_out(ftepp, ¶ms[pi + vararg_start]);
771 case TOKEN_VA_ARGS_ARRAY:
772 if ((size_t)out->constval.i >= varargs) {
773 ftepp_error(ftepp, "subscript of `[%u]` is out of bounds for `__VA_ARGS__`", out->constval.i);
774 vec_free(old_string);
778 ftepp_param_out(ftepp, ¶ms[out->constval.i + vararg_start]);
782 util_asprintf(&buffer, "%d", varargs);
783 ftepp_out(ftepp, buffer, false);
790 if (!macro_params_find(macro, out->value, &pi)) {
791 ftepp_out(ftepp, out->value, false);
794 ftepp_param_out(ftepp, ¶ms[pi]);
797 if (o + 1 < vec_size(macro->output)) {
798 nextok = macro->output[o+1]->token;
800 /* raw concatenation */
805 if ( (nextok == TOKEN_IDENT ||
806 nextok == TOKEN_KEYWORD ||
807 nextok == TOKEN_TYPENAME) &&
808 macro_params_find(macro, macro->output[o+1]->value, &pi))
812 ftepp_stringify(ftepp, ¶ms[pi]);
816 ftepp_out(ftepp, "#", false);
819 ftepp_out(ftepp, "\n", false);
823 #define buffer_stripable(X) ((X) == ' ' || (X) == '\t')
824 if (vec_size(macro->output) > o + 1 && macro->output[o+1]->token == '#' && buffer_stripable(*buffer))
827 while (buffer_stripable(*buffer)) buffer++;
830 ftepp_out(ftepp, buffer, false);
834 vec_push(ftepp->output_string, 0);
835 /* Now run the preprocessor recursively on this string buffer */
837 printf("__________\n%s\n=========\n", ftepp->output_string);
839 inlex = lex_open_string(ftepp->output_string, vec_size(ftepp->output_string)-1, ftepp->lex->name);
841 ftepp_error(ftepp, "internal error: failed to instantiate lexer");
846 inlex->line = ftepp->lex->line;
847 inlex->sline = ftepp->lex->sline;
850 old_inmacro = ftepp->in_macro;
851 ftepp->in_macro = true;
852 ftepp->output_string = NULL;
853 if (!ftepp_preprocess(ftepp)) {
854 ftepp->in_macro = old_inmacro;
855 vec_free(ftepp->lex->open_string);
856 vec_free(ftepp->output_string);
857 lex_close(ftepp->lex);
861 ftepp->in_macro = old_inmacro;
862 vec_free(ftepp->lex->open_string);
863 lex_close(ftepp->lex);
865 inner_string = ftepp->output_string;
866 ftepp->output_string = old_string;
868 has_newlines = (strchr(inner_string, '\n') != NULL);
870 if (has_newlines && !old_inmacro)
871 ftepp_recursion_header(ftepp);
873 vec_append(ftepp->output_string, vec_size(inner_string), inner_string);
874 vec_free(inner_string);
876 if (has_newlines && !old_inmacro)
877 ftepp_recursion_footer(ftepp);
879 if (resetline && !ftepp->in_macro) {
881 util_snprintf(lineno, 128, "\n#pragma line(%lu)\n", (unsigned long)(old_lexer->sline));
882 ftepp_out(ftepp, lineno, false);
885 old_string = ftepp->output_string;
887 ftepp->lex = old_lexer;
888 ftepp->output_string = old_string;
892 static bool ftepp_macro_call(ftepp_t *ftepp, ppmacro *macro)
895 macroparam *params = NULL;
899 if (!macro->has_params) {
900 if (!ftepp_macro_expand(ftepp, macro, NULL, false))
907 if (!ftepp_skipallwhite(ftepp))
910 if (ftepp->token != '(') {
911 ftepp_error(ftepp, "expected macro parameters in parenthesis");
916 paramline = ftepp->lex->sline;
917 if (!ftepp_macro_call_params(ftepp, ¶ms))
920 if ( vec_size(params) < vec_size(macro->params) ||
921 (vec_size(params) > vec_size(macro->params) && !macro->variadic) )
923 ftepp_error(ftepp, "macro %s expects%s %u paramteters, %u provided", macro->name,
924 (macro->variadic ? " at least" : ""),
925 (unsigned int)vec_size(macro->params),
926 (unsigned int)vec_size(params));
931 if (!ftepp_macro_expand(ftepp, macro, params, (paramline != ftepp->lex->sline)))
936 for (o = 0; o < vec_size(params); ++o)
937 macroparam_clean(¶ms[o]);
943 * #if - the FTEQCC way:
944 * defined(FOO) => true if FOO was #defined regardless of parameters or contents
945 * <numbers> => True if the number is not 0
946 * !<factor> => True if the factor yields false
947 * !!<factor> => ERROR on 2 or more unary nots
948 * <macro> => becomes the macro's FIRST token regardless of parameters
949 * <e> && <e> => True if both expressions are true
950 * <e> || <e> => True if either expression is true
952 * <ident> => False (remember for macros the <macro> rule applies instead)
953 * Unary + and - are weird and wrong in fteqcc so we don't allow them
954 * parenthesis in expressions are allowed
955 * parameter lists on macros are errors
956 * No mathematical calculations are executed
958 static bool ftepp_if_expr(ftepp_t *ftepp, bool *out, double *value_out);
959 static bool ftepp_if_op(ftepp_t *ftepp)
961 ftepp->lex->flags.noops = false;
963 if (!ftepp_skipspace(ftepp))
965 ftepp->lex->flags.noops = true;
968 static bool ftepp_if_value(ftepp_t *ftepp, bool *out, double *value_out)
974 if (!ftepp_skipspace(ftepp))
977 while (ftepp->token == '!') {
980 if (!ftepp_skipspace(ftepp))
984 if (ftepp->token == TOKEN_OPERATOR && !strcmp(ftepp_tokval(ftepp), "-"))
988 if (!ftepp_skipspace(ftepp))
992 switch (ftepp->token) {
996 if (!strcmp(ftepp_tokval(ftepp), "defined")) {
998 if (!ftepp_skipspace(ftepp))
1000 if (ftepp->token != '(') {
1001 ftepp_error(ftepp, "`defined` keyword in #if requires a macro name in parenthesis");
1005 if (!ftepp_skipspace(ftepp))
1007 if (ftepp->token != TOKEN_IDENT &&
1008 ftepp->token != TOKEN_TYPENAME &&
1009 ftepp->token != TOKEN_KEYWORD)
1011 ftepp_error(ftepp, "defined() used on an unexpected token type");
1014 macro = ftepp_macro_find(ftepp, ftepp_tokval(ftepp));
1017 if (!ftepp_skipspace(ftepp))
1019 if (ftepp->token != ')') {
1020 ftepp_error(ftepp, "expected closing paren");
1026 macro = ftepp_macro_find(ftepp, ftepp_tokval(ftepp));
1027 if (!macro || !vec_size(macro->output)) {
1031 /* This does not expand recursively! */
1032 switch (macro->output[0]->token) {
1033 case TOKEN_INTCONST:
1034 *value_out = macro->output[0]->constval.i;
1035 *out = !!(macro->output[0]->constval.i);
1037 case TOKEN_FLOATCONST:
1038 *value_out = macro->output[0]->constval.f;
1039 *out = !!(macro->output[0]->constval.f);
1047 case TOKEN_STRINGCONST:
1051 case TOKEN_INTCONST:
1052 *value_out = ftepp->lex->tok.constval.i;
1053 *out = !!(ftepp->lex->tok.constval.i);
1055 case TOKEN_FLOATCONST:
1056 *value_out = ftepp->lex->tok.constval.f;
1057 *out = !!(ftepp->lex->tok.constval.f);
1062 if (!ftepp_if_expr(ftepp, out, value_out))
1064 if (ftepp->token != ')') {
1065 ftepp_error(ftepp, "expected closing paren in #if expression");
1071 ftepp_error(ftepp, "junk in #if: `%s` ...", ftepp_tokval(ftepp));
1072 if (OPTS_OPTION_BOOL(OPTION_DEBUG))
1073 ftepp_error(ftepp, "internal: token %i\n", ftepp->token);
1077 *value_out = -*value_out;
1080 *value_out = (*out ? 1 : 0);
1086 static bool ftepp_if_nextvalue(ftepp_t *ftepp, bool *out, double *value_out)
1088 if (!ftepp_next(ftepp))
1090 return ftepp_if_value(ftepp, out, value_out);
1094 static bool ftepp_if_expr(ftepp_t *ftepp, bool *out, double *value_out)
1096 if (!ftepp_if_value(ftepp, out, value_out))
1099 if (!ftepp_if_op(ftepp))
1102 if (ftepp->token == ')' || ftepp->token != TOKEN_OPERATOR)
1105 /* FTEQCC is all right-associative and no precedence here */
1106 if (!strcmp(ftepp_tokval(ftepp), "&&") ||
1107 !strcmp(ftepp_tokval(ftepp), "||"))
1110 char opc = ftepp_tokval(ftepp)[0];
1114 if (!ftepp_next(ftepp))
1116 if (!ftepp_if_expr(ftepp, &next, &nextvalue))
1120 *out = *out && next;
1122 *out = *out || next;
1124 *value_out = (*out ? 1 : 0);
1127 else if (!strcmp(ftepp_tokval(ftepp), "==") ||
1128 !strcmp(ftepp_tokval(ftepp), "!=") ||
1129 !strcmp(ftepp_tokval(ftepp), ">=") ||
1130 !strcmp(ftepp_tokval(ftepp), "<=") ||
1131 !strcmp(ftepp_tokval(ftepp), ">") ||
1132 !strcmp(ftepp_tokval(ftepp), "<"))
1135 const char opc0 = ftepp_tokval(ftepp)[0];
1136 const char opc1 = ftepp_tokval(ftepp)[1];
1139 if (!ftepp_next(ftepp))
1141 if (!ftepp_if_expr(ftepp, &next, &other))
1145 *out = (*value_out == other);
1146 else if (opc0 == '!')
1147 *out = (*value_out != other);
1148 else if (opc0 == '>') {
1149 if (opc1 == '=') *out = (*value_out >= other);
1150 else *out = (*value_out > other);
1152 else if (opc0 == '<') {
1153 if (opc1 == '=') *out = (*value_out <= other);
1154 else *out = (*value_out < other);
1156 *value_out = (*out ? 1 : 0);
1161 ftepp_error(ftepp, "junk after #if");
1166 static bool ftepp_if(ftepp_t *ftepp, ppcondition *cond)
1168 bool result = false;
1171 memset(cond, 0, sizeof(*cond));
1172 (void)ftepp_next(ftepp);
1174 if (!ftepp_skipspace(ftepp))
1176 if (ftepp->token == TOKEN_EOL) {
1177 ftepp_error(ftepp, "expected expression for #if-directive");
1181 if (!ftepp_if_expr(ftepp, &result, &dummy))
1189 * ifdef is rather simple
1191 static bool ftepp_ifdef(ftepp_t *ftepp, ppcondition *cond)
1194 memset(cond, 0, sizeof(*cond));
1195 (void)ftepp_next(ftepp);
1196 if (!ftepp_skipspace(ftepp))
1199 switch (ftepp->token) {
1201 case TOKEN_TYPENAME:
1203 macro = ftepp_macro_find(ftepp, ftepp_tokval(ftepp));
1206 ftepp_error(ftepp, "expected macro name");
1210 (void)ftepp_next(ftepp);
1211 if (!ftepp_skipspace(ftepp))
1213 /* relaxing this condition
1214 if (ftepp->token != TOKEN_EOL && ftepp->token != TOKEN_EOF) {
1215 ftepp_error(ftepp, "stray tokens after #ifdef");
1224 * undef is also simple
1226 static bool ftepp_undef(ftepp_t *ftepp)
1228 (void)ftepp_next(ftepp);
1229 if (!ftepp_skipspace(ftepp))
1232 if (ftepp->output_on) {
1233 switch (ftepp->token) {
1235 case TOKEN_TYPENAME:
1237 ftepp_macro_delete(ftepp, ftepp_tokval(ftepp));
1240 ftepp_error(ftepp, "expected macro name");
1245 (void)ftepp_next(ftepp);
1246 if (!ftepp_skipspace(ftepp))
1248 /* relaxing this condition
1249 if (ftepp->token != TOKEN_EOL && ftepp->token != TOKEN_EOF) {
1250 ftepp_error(ftepp, "stray tokens after #ifdef");
1257 /* Special unescape-string function which skips a leading quote
1258 * and stops at a quote, not just at \0
1260 static void unescape(const char *str, char *out) {
1262 while (*str && *str != '"') {
1266 case '\\': *out++ = *str; break;
1267 case '"': *out++ = *str; break;
1268 case 'a': *out++ = '\a'; break;
1269 case 'b': *out++ = '\b'; break;
1270 case 'r': *out++ = '\r'; break;
1271 case 'n': *out++ = '\n'; break;
1272 case 't': *out++ = '\t'; break;
1273 case 'f': *out++ = '\f'; break;
1274 case 'v': *out++ = '\v'; break;
1289 static char *ftepp_include_find_path(const char *file, const char *pathfile)
1292 char *filename = NULL;
1293 const char *last_slash;
1299 last_slash = strrchr(pathfile, '/');
1302 len = last_slash - pathfile;
1303 memcpy(vec_add(filename, len), pathfile, len);
1304 vec_push(filename, '/');
1308 memcpy(vec_add(filename, len+1), file, len);
1309 vec_last(filename) = 0;
1311 fp = fopen(filename, "rb");
1320 static char *ftepp_include_find(ftepp_t *ftepp, const char *file)
1322 char *filename = NULL;
1324 filename = ftepp_include_find_path(file, ftepp->includename);
1326 filename = ftepp_include_find_path(file, ftepp->itemname);
1330 static bool ftepp_directive_warning(ftepp_t *ftepp) {
1331 char *message = NULL;
1333 if (!ftepp_skipspace(ftepp))
1336 /* handle the odd non string constant case so it works like C */
1337 if (ftepp->token != TOKEN_STRINGCONST) {
1339 vec_append(message, 8, "#warning");
1341 while (ftepp->token != TOKEN_EOL) {
1342 vec_append(message, strlen(ftepp_tokval(ftepp)), ftepp_tokval(ftepp));
1345 vec_push(message, '\0');
1346 if (ftepp->output_on)
1347 store = ftepp_warn(ftepp, WARN_CPP, message);
1354 if (!ftepp->output_on)
1357 unescape (ftepp_tokval(ftepp), ftepp_tokval(ftepp));
1358 return ftepp_warn(ftepp, WARN_CPP, "#warning %s", ftepp_tokval(ftepp));
1361 static void ftepp_directive_error(ftepp_t *ftepp) {
1362 char *message = NULL;
1364 if (!ftepp_skipspace(ftepp))
1367 /* handle the odd non string constant case so it works like C */
1368 if (ftepp->token != TOKEN_STRINGCONST) {
1369 vec_append(message, 6, "#error");
1371 while (ftepp->token != TOKEN_EOL) {
1372 vec_append(message, strlen(ftepp_tokval(ftepp)), ftepp_tokval(ftepp));
1375 vec_push(message, '\0');
1376 if (ftepp->output_on)
1377 ftepp_error(ftepp, message);
1382 if (!ftepp->output_on)
1385 unescape (ftepp_tokval(ftepp), ftepp_tokval(ftepp));
1386 ftepp_error(ftepp, "#error %s", ftepp_tokval(ftepp));
1389 static void ftepp_directive_message(ftepp_t *ftepp) {
1390 char *message = NULL;
1392 if (!ftepp_skipspace(ftepp))
1395 /* handle the odd non string constant case so it works like C */
1396 if (ftepp->token != TOKEN_STRINGCONST) {
1397 vec_append(message, 8, "#message");
1399 while (ftepp->token != TOKEN_EOL) {
1400 vec_append(message, strlen(ftepp_tokval(ftepp)), ftepp_tokval(ftepp));
1403 vec_push(message, '\0');
1404 if (ftepp->output_on)
1405 con_cprintmsg(ftepp->lex->tok.ctx, LVL_MSG, "message", message);
1410 if (!ftepp->output_on)
1413 unescape (ftepp_tokval(ftepp), ftepp_tokval(ftepp));
1414 con_cprintmsg(ftepp->lex->tok.ctx, LVL_MSG, "message", ftepp_tokval(ftepp));
1419 * FIXME: do we need/want a -I option?
1420 * FIXME: what about when dealing with files in subdirectories coming from a progs.src?
1422 static bool ftepp_include(ftepp_t *ftepp)
1424 lex_file *old_lexer = ftepp->lex;
1429 char *parsename = NULL;
1430 char *old_includename;
1432 (void)ftepp_next(ftepp);
1433 if (!ftepp_skipspace(ftepp))
1436 if (ftepp->token != TOKEN_STRINGCONST) {
1437 ppmacro *macro = ftepp_macro_find(ftepp, ftepp_tokval(ftepp));
1439 char *backup = ftepp->output_string;
1440 ftepp->output_string = NULL;
1441 if (ftepp_macro_expand(ftepp, macro, NULL, true)) {
1442 parsename = util_strdup(ftepp->output_string);
1443 vec_free(ftepp->output_string);
1444 ftepp->output_string = backup;
1446 ftepp->output_string = backup;
1447 ftepp_error(ftepp, "expected filename to include");
1450 } else if (OPTS_FLAG(FTEPP_PREDEFS)) {
1451 /* Well it could be a predefine like __LINE__ */
1452 char *(*predef)(ftepp_t*) = ftepp_predef(ftepp_tokval(ftepp));
1454 parsename = predef(ftepp);
1456 ftepp_error(ftepp, "expected filename to include");
1462 if (!ftepp->output_on) {
1463 (void)ftepp_next(ftepp);
1468 unescape(parsename, parsename);
1470 char *tokval = ftepp_tokval(ftepp);
1471 unescape(tokval, tokval);
1472 parsename = util_strdup(tokval);
1475 ctx = ftepp_ctx(ftepp);
1476 ftepp_out(ftepp, "\n#pragma file(", false);
1477 ftepp_out(ftepp, parsename, false);
1478 ftepp_out(ftepp, ")\n#pragma line(1)\n", false);
1480 filename = ftepp_include_find(ftepp, parsename);
1482 ftepp_error(ftepp, "failed to open include file `%s`", parsename);
1487 inlex = lex_open(filename);
1489 ftepp_error(ftepp, "open failed on include file `%s`", filename);
1494 old_includename = ftepp->includename;
1495 ftepp->includename = filename;
1496 if (!ftepp_preprocess(ftepp)) {
1497 vec_free(ftepp->includename);
1498 ftepp->includename = old_includename;
1499 lex_close(ftepp->lex);
1500 ftepp->lex = old_lexer;
1503 vec_free(ftepp->includename);
1504 ftepp->includename = old_includename;
1505 lex_close(ftepp->lex);
1506 ftepp->lex = old_lexer;
1508 ftepp_out(ftepp, "\n#pragma file(", false);
1509 ftepp_out(ftepp, ctx.file, false);
1510 util_snprintf(lineno, sizeof(lineno), ")\n#pragma line(%lu)\n", (unsigned long)(ctx.line+1));
1511 ftepp_out(ftepp, lineno, false);
1514 (void)ftepp_next(ftepp);
1515 if (!ftepp_skipspace(ftepp))
1517 if (ftepp->token != TOKEN_EOL) {
1518 ftepp_error(ftepp, "stray tokens after #include");
1521 (void)ftepp_next(ftepp);
1526 /* Basic structure handlers */
1527 static bool ftepp_else_allowed(ftepp_t *ftepp)
1529 if (!vec_size(ftepp->conditions)) {
1530 ftepp_error(ftepp, "#else without #if");
1533 if (vec_last(ftepp->conditions).had_else) {
1534 ftepp_error(ftepp, "multiple #else for a single #if");
1540 static GMQCC_INLINE void ftepp_inmacro(ftepp_t *ftepp, const char *hash) {
1541 if (ftepp->in_macro)
1542 (void)!ftepp_warn(ftepp, WARN_DIRECTIVE_INMACRO, "`#%s` directive in macro", hash);
1545 static bool ftepp_hash(ftepp_t *ftepp)
1550 lex_ctx_t ctx = ftepp_ctx(ftepp);
1552 if (!ftepp_skipspace(ftepp))
1555 switch (ftepp->token) {
1558 case TOKEN_TYPENAME:
1559 if (!strcmp(ftepp_tokval(ftepp), "define")) {
1560 ftepp_inmacro(ftepp, "define");
1561 return ftepp_define(ftepp);
1563 else if (!strcmp(ftepp_tokval(ftepp), "undef")) {
1564 ftepp_inmacro(ftepp, "undef");
1565 return ftepp_undef(ftepp);
1567 else if (!strcmp(ftepp_tokval(ftepp), "ifdef")) {
1568 ftepp_inmacro(ftepp, "ifdef");
1569 if (!ftepp_ifdef(ftepp, &cond))
1571 cond.was_on = cond.on;
1572 vec_push(ftepp->conditions, cond);
1573 ftepp->output_on = ftepp->output_on && cond.on;
1576 else if (!strcmp(ftepp_tokval(ftepp), "ifndef")) {
1577 ftepp_inmacro(ftepp, "ifndef");
1578 if (!ftepp_ifdef(ftepp, &cond))
1581 cond.was_on = cond.on;
1582 vec_push(ftepp->conditions, cond);
1583 ftepp->output_on = ftepp->output_on && cond.on;
1586 else if (!strcmp(ftepp_tokval(ftepp), "elifdef")) {
1587 ftepp_inmacro(ftepp, "elifdef");
1588 if (!ftepp_else_allowed(ftepp))
1590 if (!ftepp_ifdef(ftepp, &cond))
1592 pc = &vec_last(ftepp->conditions);
1593 pc->on = !pc->was_on && cond.on;
1594 pc->was_on = pc->was_on || pc->on;
1595 ftepp_update_output_condition(ftepp);
1598 else if (!strcmp(ftepp_tokval(ftepp), "elifndef")) {
1599 ftepp_inmacro(ftepp, "elifndef");
1600 if (!ftepp_else_allowed(ftepp))
1602 if (!ftepp_ifdef(ftepp, &cond))
1605 pc = &vec_last(ftepp->conditions);
1606 pc->on = !pc->was_on && cond.on;
1607 pc->was_on = pc->was_on || pc->on;
1608 ftepp_update_output_condition(ftepp);
1611 else if (!strcmp(ftepp_tokval(ftepp), "elif")) {
1612 ftepp_inmacro(ftepp, "elif");
1613 if (!ftepp_else_allowed(ftepp))
1615 if (!ftepp_if(ftepp, &cond))
1617 pc = &vec_last(ftepp->conditions);
1618 pc->on = !pc->was_on && cond.on;
1619 pc->was_on = pc->was_on || pc->on;
1620 ftepp_update_output_condition(ftepp);
1623 else if (!strcmp(ftepp_tokval(ftepp), "if")) {
1624 ftepp_inmacro(ftepp, "if");
1625 if (!ftepp_if(ftepp, &cond))
1627 cond.was_on = cond.on;
1628 vec_push(ftepp->conditions, cond);
1629 ftepp->output_on = ftepp->output_on && cond.on;
1632 else if (!strcmp(ftepp_tokval(ftepp), "else")) {
1633 ftepp_inmacro(ftepp, "else");
1634 if (!ftepp_else_allowed(ftepp))
1636 pc = &vec_last(ftepp->conditions);
1637 pc->on = !pc->was_on;
1638 pc->had_else = true;
1640 ftepp_update_output_condition(ftepp);
1643 else if (!strcmp(ftepp_tokval(ftepp), "endif")) {
1644 ftepp_inmacro(ftepp, "endif");
1645 if (!vec_size(ftepp->conditions)) {
1646 ftepp_error(ftepp, "#endif without #if");
1649 vec_pop(ftepp->conditions);
1651 ftepp_update_output_condition(ftepp);
1654 else if (!strcmp(ftepp_tokval(ftepp), "include")) {
1655 ftepp_inmacro(ftepp, "include");
1656 return ftepp_include(ftepp);
1658 else if (!strcmp(ftepp_tokval(ftepp), "pragma")) {
1659 ftepp_out(ftepp, "#", false);
1662 else if (!strcmp(ftepp_tokval(ftepp), "warning")) {
1663 ftepp_directive_warning(ftepp);
1666 else if (!strcmp(ftepp_tokval(ftepp), "error")) {
1667 ftepp_directive_error(ftepp);
1670 else if (!strcmp(ftepp_tokval(ftepp), "message")) {
1671 ftepp_directive_message(ftepp);
1675 if (ftepp->output_on) {
1676 ftepp_error(ftepp, "unrecognized preprocessor directive: `%s`", ftepp_tokval(ftepp));
1683 /* break; never reached */
1685 ftepp_error(ftepp, "unexpected preprocessor token: `%s`", ftepp_tokval(ftepp));
1688 ftepp_errorat(ftepp, ctx, "empty preprocessor directive");
1691 ftepp_error(ftepp, "missing newline at end of file", ftepp_tokval(ftepp));
1694 /* Builtins! Don't forget the builtins! */
1695 case TOKEN_INTCONST:
1696 case TOKEN_FLOATCONST:
1697 ftepp_out(ftepp, "#", false);
1700 if (!ftepp_skipspace(ftepp))
1705 static bool ftepp_preprocess(ftepp_t *ftepp)
1708 bool newline = true;
1711 char *expand = NULL;
1713 ftepp->lex->flags.preprocessing = true;
1714 ftepp->lex->flags.mergelines = false;
1715 ftepp->lex->flags.noops = true;
1720 if (ftepp->token >= TOKEN_EOF)
1722 switch (ftepp->token) {
1725 case TOKEN_TYPENAME:
1726 /* is it a predef? */
1727 if (OPTS_FLAG(FTEPP_PREDEFS)) {
1728 char *(*predef)(ftepp_t*) = ftepp_predef(ftepp_tokval(ftepp));
1730 expand = predef(ftepp);
1731 ftepp_out (ftepp, expand, false);
1739 if (ftepp->output_on)
1740 macro = ftepp_macro_find(ftepp, ftepp_tokval(ftepp));
1745 ftepp_out(ftepp, ftepp_tokval(ftepp), false);
1749 if (!ftepp_macro_call(ftepp, macro))
1750 ftepp->token = TOKEN_ERROR;
1754 ftepp_out(ftepp, ftepp_tokval(ftepp), false);
1758 ftepp->lex->flags.mergelines = true;
1759 if (ftepp_next(ftepp) >= TOKEN_EOF) {
1760 ftepp_error(ftepp, "error in preprocessor directive");
1761 ftepp->token = TOKEN_ERROR;
1764 if (!ftepp_hash(ftepp))
1765 ftepp->token = TOKEN_ERROR;
1766 ftepp->lex->flags.mergelines = false;
1770 ftepp_out(ftepp, "\n", true);
1774 /* same as default but don't set newline=false */
1775 ftepp_out(ftepp, ftepp_tokval(ftepp), true);
1780 ftepp_out(ftepp, ftepp_tokval(ftepp), false);
1784 } while (!ftepp->errors && ftepp->token < TOKEN_EOF);
1786 /* force a 0 at the end but don't count it as added to the output */
1787 vec_push(ftepp->output_string, 0);
1788 vec_shrinkby(ftepp->output_string, 1);
1790 return (ftepp->token == TOKEN_EOF);
1793 /* Like in parser.c - files keep the previous state so we have one global
1794 * preprocessor. Except here we will want to warn about dangling #ifs.
1796 static bool ftepp_preprocess_done(ftepp_t *ftepp)
1799 if (vec_size(ftepp->conditions)) {
1800 if (ftepp_warn(ftepp, WARN_MULTIFILE_IF, "#if spanning multiple files, is this intended?"))
1803 lex_close(ftepp->lex);
1805 if (ftepp->itemname) {
1806 mem_d(ftepp->itemname);
1807 ftepp->itemname = NULL;
1812 bool ftepp_preprocess_file(ftepp_t *ftepp, const char *filename)
1814 ftepp->lex = lex_open(filename);
1815 ftepp->itemname = util_strdup(filename);
1817 con_out("failed to open file \"%s\"\n", filename);
1820 if (!ftepp_preprocess(ftepp))
1822 return ftepp_preprocess_done(ftepp);
1825 bool ftepp_preprocess_string(ftepp_t *ftepp, const char *name, const char *str)
1827 ftepp->lex = lex_open_string(str, strlen(str), name);
1828 ftepp->itemname = util_strdup(name);
1830 con_out("failed to create lexer for string \"%s\"\n", name);
1833 if (!ftepp_preprocess(ftepp))
1835 return ftepp_preprocess_done(ftepp);
1839 void ftepp_add_macro(ftepp_t *ftepp, const char *name, const char *value) {
1840 char *create = NULL;
1842 /* use saner path for empty macros */
1844 ftepp_add_define(ftepp, "__builtin__", name);
1848 vec_append(create, 8, "#define ");
1849 vec_append(create, strlen(name), name);
1850 vec_push (create, ' ');
1851 vec_append(create, strlen(value), value);
1852 vec_push (create, 0);
1854 ftepp_preprocess_string(ftepp, "__builtin__", create);
1858 ftepp_t *ftepp_create()
1865 ftepp = ftepp_new();
1869 memset(minor, 0, sizeof(minor));
1870 memset(major, 0, sizeof(major));
1872 /* set the right macro based on the selected standard */
1873 ftepp_add_define(ftepp, NULL, "GMQCC");
1874 if (OPTS_OPTION_U32(OPTION_STANDARD) == COMPILER_FTEQCC) {
1875 ftepp_add_define(ftepp, NULL, "__STD_FTEQCC__");
1884 } else if (OPTS_OPTION_U32(OPTION_STANDARD) == COMPILER_GMQCC) {
1885 ftepp_add_define(ftepp, NULL, "__STD_GMQCC__");
1886 util_snprintf(major, 32, "\"%d\"", GMQCC_VERSION_MAJOR);
1887 util_snprintf(minor, 32, "\"%d\"", GMQCC_VERSION_MINOR);
1888 } else if (OPTS_OPTION_U32(OPTION_STANDARD) == COMPILER_QCCX) {
1889 ftepp_add_define(ftepp, NULL, "__STD_QCCX__");
1890 util_snprintf(major, 32, "\"%d\"", GMQCC_VERSION_MAJOR);
1891 util_snprintf(minor, 32, "\"%d\"", GMQCC_VERSION_MINOR);
1892 } else if (OPTS_OPTION_U32(OPTION_STANDARD) == COMPILER_QCC) {
1893 ftepp_add_define(ftepp, NULL, "__STD_QCC__");
1904 ftepp_add_macro(ftepp, "__STD_VERSION_MINOR__", minor);
1905 ftepp_add_macro(ftepp, "__STD_VERSION_MAJOR__", major);
1908 * We're going to just make __NULL__ nil, which works for 60% of the
1909 * cases of __NULL_ for fteqcc.
1911 ftepp_add_macro(ftepp, "__NULL__", "nil");
1913 /* add all the math constants if they can be */
1914 if (OPTS_FLAG(FTEPP_MATHDEFS)) {
1915 for (i = 0; i < GMQCC_ARRAY_COUNT(ftepp_math_constants); i++)
1916 if (!ftepp_macro_find(ftepp, ftepp_math_constants[i][0]))
1917 ftepp_add_macro(ftepp, ftepp_math_constants[i][0], ftepp_math_constants[i][1]);
1923 void ftepp_add_define(ftepp_t *ftepp, const char *source, const char *name)
1926 lex_ctx_t ctx = { "__builtin__", 0, 0 };
1928 macro = ppmacro_new(ctx, name);
1929 /*vec_push(ftepp->macros, macro);*/
1930 util_htset(ftepp->macros, name, macro);
1933 const char *ftepp_get(ftepp_t *ftepp)
1935 return ftepp->output_string;
1938 void ftepp_flush(ftepp_t *ftepp)
1940 ftepp_flush_do(ftepp);
1943 void ftepp_finish(ftepp_t *ftepp)
1947 ftepp_delete(ftepp);