19 /* a copy from the lexer */
33 /* yes we need an extra flag since `#define FOO x` is not the same as `#define FOO() x` */
40 typedef struct ftepp_s {
46 ppcondition *conditions;
48 ht macros; /* hashtable<string, ppmacro*> */
55 uint32_t predef_countval;
56 uint32_t predef_randval;
60 static char *ftepp_predef_date(ftepp_t *context) {
61 const struct tm *itime = NULL;
62 char *value = (char*)mem_a(82);
68 itime = util_localtime(&rtime);
69 strftime(value, 82, "\"%b %d %Y\"", itime);
75 static char *ftepp_predef_time(ftepp_t *context) {
76 const struct tm *itime = NULL;
77 char *value = (char*)mem_a(82);
83 itime = util_localtime(&rtime);
84 strftime(value, 82, "\"%X\"", itime);
90 static char *ftepp_predef_line(ftepp_t *context) {
93 util_asprintf(&value, "%d", (int)context->lex->line);
97 static char *ftepp_predef_file(ftepp_t *context) {
98 size_t length = strlen(context->lex->name) + 3; /* two quotes and a terminator */
99 char *value = (char*)mem_a(length);
101 util_snprintf(value, length, "\"%s\"", context->lex->name);
104 /* __COUNTER_LAST__ */
105 static char *ftepp_predef_counterlast(ftepp_t *context) {
107 util_asprintf(&value, "%u", context->predef_countval);
111 static char *ftepp_predef_counter(ftepp_t *context) {
114 context->predef_countval ++;
115 util_asprintf(&value, "%u", context->predef_countval);
120 static char *ftepp_predef_random(ftepp_t *context) {
123 context->predef_randval = (util_rand() % 0xFF) + 1;
124 util_asprintf(&value, "%u", context->predef_randval);
127 /* __RANDOM_LAST__ */
128 static char *ftepp_predef_randomlast(ftepp_t *context) {
131 util_asprintf(&value, "%u", context->predef_randval);
135 static char *ftepp_predef_timestamp(ftepp_t *context) {
141 if (stat(context->lex->name, &finfo))
142 return util_strdup("\"<failed to determine timestamp>\"");
144 find = util_ctime(&finfo.st_mtime);
145 value = (char*)mem_a(strlen(find) + 1);
146 memcpy(&value[1], find, (size = strlen(find)) - 1);
156 char *(*func)(ftepp_t *);
159 static const ftepp_predef_t ftepp_predefs[] = {
160 { "__LINE__", &ftepp_predef_line },
161 { "__FILE__", &ftepp_predef_file },
162 { "__COUNTER__", &ftepp_predef_counter },
163 { "__COUNTER_LAST__", &ftepp_predef_counterlast },
164 { "__RANDOM__", &ftepp_predef_random },
165 { "__RANDOM_LAST__", &ftepp_predef_randomlast },
166 { "__DATE__", &ftepp_predef_date },
167 { "__TIME__", &ftepp_predef_time },
168 { "__TIME_STAMP__", &ftepp_predef_timestamp }
171 static GMQCC_INLINE size_t ftepp_predef_index(const char *name) {
172 /* no hashtable here, we simply check for one to exist the naive way */
174 for(i = 1; i < GMQCC_ARRAY_COUNT(ftepp_predefs) + 1; i++)
175 if (!strcmp(ftepp_predefs[i-1].name, name))
180 bool ftepp_predef_exists(const char *name);
181 bool ftepp_predef_exists(const char *name) {
182 return ftepp_predef_index(name) != 0;
185 /* singleton because we're allowed */
186 static GMQCC_INLINE char *(*ftepp_predef(const char *name))(ftepp_t *context) {
187 size_t i = ftepp_predef_index(name);
188 return (i != 0) ? ftepp_predefs[i-1].func : NULL;
191 #define ftepp_tokval(f) ((f)->lex->tok.value)
192 #define ftepp_ctx(f) ((f)->lex->tok.ctx)
194 static void ftepp_errorat(ftepp_t *ftepp, lex_ctx_t ctx, const char *fmt, ...)
201 con_cvprintmsg(ctx, LVL_ERROR, "error", fmt, ap);
205 static void ftepp_error(ftepp_t *ftepp, const char *fmt, ...)
212 con_cvprintmsg(ftepp->lex->tok.ctx, LVL_ERROR, "error", fmt, ap);
216 static bool GMQCC_WARN ftepp_warn(ftepp_t *ftepp, int warntype, const char *fmt, ...)
222 r = vcompile_warning(ftepp->lex->tok.ctx, warntype, fmt, ap);
227 static pptoken *pptoken_make(ftepp_t *ftepp)
229 pptoken *token = (pptoken*)mem_a(sizeof(pptoken));
230 token->token = ftepp->token;
231 token->value = util_strdup(ftepp_tokval(ftepp));
232 memcpy(&token->constval, &ftepp->lex->tok.constval, sizeof(token->constval));
236 static GMQCC_INLINE void pptoken_delete(pptoken *self)
242 static ppmacro *ppmacro_new(lex_ctx_t ctx, const char *name)
244 ppmacro *macro = (ppmacro*)mem_a(sizeof(ppmacro));
247 memset(macro, 0, sizeof(*macro));
248 macro->name = util_strdup(name);
252 static void ppmacro_delete(ppmacro *self)
255 for (i = 0; i < vec_size(self->params); ++i)
256 mem_d(self->params[i]);
257 vec_free(self->params);
258 for (i = 0; i < vec_size(self->output); ++i)
259 pptoken_delete(self->output[i]);
260 vec_free(self->output);
265 static ftepp_t* ftepp_new(void)
269 ftepp = (ftepp_t*)mem_a(sizeof(*ftepp));
270 memset(ftepp, 0, sizeof(*ftepp));
272 ftepp->macros = util_htnew(HT_MACROS);
273 ftepp->output_on = true;
274 ftepp->predef_countval = 0;
275 ftepp->predef_randval = 0;
280 static GMQCC_INLINE void ftepp_flush_do(ftepp_t *self)
282 vec_free(self->output_string);
285 static void ftepp_delete(ftepp_t *self)
287 ftepp_flush_do(self);
289 mem_d(self->itemname);
290 if (self->includename)
291 vec_free(self->includename);
293 util_htrem(self->macros, (void (*)(void*))&ppmacro_delete);
295 vec_free(self->conditions);
297 lex_close(self->lex);
301 static void ftepp_out(ftepp_t *ftepp, const char *str, bool ignore_cond)
303 if (ignore_cond || ftepp->output_on)
308 data = vec_add(ftepp->output_string, len);
309 memcpy(data, str, len);
313 static GMQCC_INLINE void ftepp_update_output_condition(ftepp_t *ftepp)
316 ftepp->output_on = true;
317 for (i = 0; i < vec_size(ftepp->conditions); ++i)
318 ftepp->output_on = ftepp->output_on && ftepp->conditions[i].on;
321 static GMQCC_INLINE ppmacro* ftepp_macro_find(ftepp_t *ftepp, const char *name)
323 return (ppmacro*)util_htget(ftepp->macros, name);
326 static GMQCC_INLINE void ftepp_macro_delete(ftepp_t *ftepp, const char *name)
328 util_htrm(ftepp->macros, name, (void (*)(void*))&ppmacro_delete);
331 static GMQCC_INLINE int ftepp_next(ftepp_t *ftepp)
333 return (ftepp->token = lex_do(ftepp->lex));
336 /* Important: this does not skip newlines! */
337 static bool ftepp_skipspace(ftepp_t *ftepp)
339 if (ftepp->token != TOKEN_WHITE)
341 while (ftepp_next(ftepp) == TOKEN_WHITE) {}
342 if (ftepp->token >= TOKEN_EOF) {
343 ftepp_error(ftepp, "unexpected end of preprocessor directive");
349 /* this one skips EOLs as well */
350 static bool ftepp_skipallwhite(ftepp_t *ftepp)
352 if (ftepp->token != TOKEN_WHITE && ftepp->token != TOKEN_EOL)
356 } while (ftepp->token == TOKEN_WHITE || ftepp->token == TOKEN_EOL);
357 if (ftepp->token >= TOKEN_EOF) {
358 ftepp_error(ftepp, "unexpected end of preprocessor directive");
365 * The huge macro parsing code...
367 static bool ftepp_define_params(ftepp_t *ftepp, ppmacro *macro)
371 if (!ftepp_skipspace(ftepp))
373 if (ftepp->token == ')')
375 switch (ftepp->token) {
379 vec_push(macro->params, util_strdup(ftepp_tokval(ftepp)));
382 macro->variadic = true;
385 ftepp_error(ftepp, "unexpected token in parameter list");
389 if (!ftepp_skipspace(ftepp))
391 if (macro->variadic && ftepp->token != ')') {
392 ftepp_error(ftepp, "cannot have parameters after the variadic parameters");
395 } while (ftepp->token == ',');
397 if (ftepp->token != ')') {
398 ftepp_error(ftepp, "expected closing paren after macro parameter list");
402 /* skipspace happens in ftepp_define */
406 static bool ftepp_define_body(ftepp_t *ftepp, ppmacro *macro)
409 while (ftepp->token != TOKEN_EOL && ftepp->token < TOKEN_EOF) {
410 bool subscript = false;
412 if (macro->variadic && !strcmp(ftepp_tokval(ftepp), "__VA_ARGS__")) {
413 subscript = !!(ftepp_next(ftepp) == '#');
415 if (subscript && ftepp_next(ftepp) != '#') {
416 ftepp_error(ftepp, "expected `##` in __VA_ARGS__ for subscripting");
418 } else if (subscript) {
419 if (ftepp_next(ftepp) == '[') {
420 if (ftepp_next(ftepp) != TOKEN_INTCONST) {
421 ftepp_error(ftepp, "expected index for __VA_ARGS__ subscript");
425 index = (int)strtol(ftepp_tokval(ftepp), NULL, 10);
427 if (ftepp_next(ftepp) != ']') {
428 ftepp_error(ftepp, "expected `]` in __VA_ARGS__ subscript");
433 * mark it as an array to be handled later as such and not
434 * as traditional __VA_ARGS__
436 ftepp->token = TOKEN_VA_ARGS_ARRAY;
437 ptok = pptoken_make(ftepp);
438 ptok->constval.i = index;
439 vec_push(macro->output, ptok);
442 ftepp_error(ftepp, "expected `[` for subscripting of __VA_ARGS__");
446 int old = ftepp->token;
447 ftepp->token = TOKEN_VA_ARGS;
448 ptok = pptoken_make(ftepp);
449 vec_push(macro->output, ptok);
453 else if (macro->variadic && !strcmp(ftepp_tokval(ftepp), "__VA_COUNT__")) {
454 ftepp->token = TOKEN_VA_COUNT;
455 ptok = pptoken_make(ftepp);
456 vec_push(macro->output, ptok);
459 ptok = pptoken_make(ftepp);
460 vec_push(macro->output, ptok);
464 /* recursive expansion can cause EOFs here */
465 if (ftepp->token != TOKEN_EOL && ftepp->token != TOKEN_EOF) {
466 ftepp_error(ftepp, "unexpected junk after macro or unexpected end of file");
472 static const char *ftepp_math_constants[][2] = {
473 { "M_E", "2.7182818284590452354" }, /* e */
474 { "M_LOG2E", "1.4426950408889634074" }, /* log_2 e */
475 { "M_LOG10E", "0.43429448190325182765" }, /* log_10 e */
476 { "M_LN2", "0.69314718055994530942" }, /* log_e 2 */
477 { "M_LN10", "2.30258509299404568402" }, /* log_e 10 */
478 { "M_PI", "3.14159265358979323846" }, /* pi */
479 { "M_PI_2", "1.57079632679489661923" }, /* pi/2 */
480 { "M_PI_4", "0.78539816339744830962" }, /* pi/4 */
481 { "M_1_PI", "0.31830988618379067154" }, /* 1/pi */
482 { "M_2_PI", "0.63661977236758134308" }, /* 2/pi */
483 { "M_2_SQRTPI", "1.12837916709551257390" }, /* 2/sqrt(pi) */
484 { "M_SQRT2", "1.41421356237309504880" }, /* sqrt(2) */
485 { "M_SQRT1_2", "0.70710678118654752440" }, /* 1/sqrt(2) */
486 { "M_TAU", "6.28318530717958647692" } /* pi*2 */
489 static bool ftepp_define(ftepp_t *ftepp)
491 ppmacro *macro = NULL;
492 size_t l = ftepp_ctx(ftepp).line;
494 bool mathconstant = false;
496 (void)ftepp_next(ftepp);
497 if (!ftepp_skipspace(ftepp))
500 switch (ftepp->token) {
504 if (OPTS_FLAG(FTEPP_MATHDEFS)) {
505 for (i = 0; i < GMQCC_ARRAY_COUNT(ftepp_math_constants); i++) {
506 if (!strcmp(ftepp_math_constants[i][0], ftepp_tokval(ftepp))) {
513 macro = ftepp_macro_find(ftepp, ftepp_tokval(ftepp));
515 if (OPTS_FLAG(FTEPP_MATHDEFS)) {
516 /* user defined ones take precedence */
517 if (macro && mathconstant) {
518 ftepp_macro_delete(ftepp, ftepp_tokval(ftepp));
523 if (macro && ftepp->output_on) {
524 if (ftepp_warn(ftepp, WARN_CPP, "redefining `%s`", ftepp_tokval(ftepp)))
526 ftepp_macro_delete(ftepp, ftepp_tokval(ftepp));
528 macro = ppmacro_new(ftepp_ctx(ftepp), ftepp_tokval(ftepp));
531 ftepp_error(ftepp, "expected macro name");
535 (void)ftepp_next(ftepp);
537 if (ftepp->token == '(') {
538 macro->has_params = true;
539 if (!ftepp_define_params(ftepp, macro)) {
540 ppmacro_delete(macro);
545 if (!ftepp_skipspace(ftepp)) {
546 ppmacro_delete(macro);
550 if (!ftepp_define_body(ftepp, macro)) {
551 ppmacro_delete(macro);
555 if (ftepp->output_on)
556 util_htset(ftepp->macros, macro->name, (void*)macro);
558 ppmacro_delete(macro);
561 for (; l < ftepp_ctx(ftepp).line; ++l)
562 ftepp_out(ftepp, "\n", true);
567 * When a macro is used we have to handle parameters as well
568 * as special-concatenation via ## or stringification via #
570 * Note: parenthesis can nest, so FOO((a),b) is valid, but only
571 * this kind of parens. Curly braces or [] don't count towards the
578 static void macroparam_clean(macroparam *self)
581 for (i = 0; i < vec_size(self->tokens); ++i)
582 pptoken_delete(self->tokens[i]);
583 vec_free(self->tokens);
586 /* need to leave the last token up */
587 static bool ftepp_macro_call_params(ftepp_t *ftepp, macroparam **out_params)
589 macroparam *params = NULL;
595 if (!ftepp_skipallwhite(ftepp))
597 while (ftepp->token != ')') {
599 if (!ftepp_skipallwhite(ftepp))
601 while (parens || ftepp->token != ',') {
602 if (ftepp->token == '(')
604 else if (ftepp->token == ')') {
609 ptok = pptoken_make(ftepp);
610 vec_push(mp.tokens, ptok);
611 if (ftepp_next(ftepp) >= TOKEN_EOF) {
612 ftepp_error(ftepp, "unexpected end of file in macro call");
616 vec_push(params, mp);
618 if (ftepp->token == ')')
620 if (ftepp->token != ',') {
621 ftepp_error(ftepp, "expected closing paren or comma in macro call");
624 if (ftepp_next(ftepp) >= TOKEN_EOF) {
625 ftepp_error(ftepp, "unexpected end of file in macro call");
629 *out_params = params;
634 macroparam_clean(&mp);
635 for (i = 0; i < vec_size(params); ++i)
636 macroparam_clean(¶ms[i]);
641 static bool macro_params_find(ppmacro *macro, const char *name, size_t *idx)
644 for (i = 0; i < vec_size(macro->params); ++i) {
645 if (!strcmp(macro->params[i], name)) {
653 static void ftepp_stringify_token(ftepp_t *ftepp, pptoken *token)
658 switch (token->token) {
659 case TOKEN_STRINGCONST:
662 /* in preprocessor mode strings already are string,
663 * so we don't get actual newline bytes here.
664 * Still need to escape backslashes and quotes.
667 case '\\': ftepp_out(ftepp, "\\\\", false); break;
668 case '"': ftepp_out(ftepp, "\\\"", false); break;
671 ftepp_out(ftepp, chs, false);
678 ftepp_out(ftepp, " ", false);
681 ftepp_out(ftepp, "\\n", false);
684 ftepp_out(ftepp, token->value, false);
689 static void ftepp_stringify(ftepp_t *ftepp, macroparam *param)
692 ftepp_out(ftepp, "\"", false);
693 for (i = 0; i < vec_size(param->tokens); ++i)
694 ftepp_stringify_token(ftepp, param->tokens[i]);
695 ftepp_out(ftepp, "\"", false);
698 static void ftepp_recursion_header(ftepp_t *ftepp)
700 ftepp_out(ftepp, "\n#pragma push(line)\n", false);
703 static void ftepp_recursion_footer(ftepp_t *ftepp)
705 ftepp_out(ftepp, "\n#pragma pop(line)\n", false);
708 static bool ftepp_macro_expand(ftepp_t *ftepp, ppmacro *macro, macroparam *params, bool resetline);
709 static void ftepp_param_out(ftepp_t *ftepp, macroparam *param)
713 for (i = 0; i < vec_size(param->tokens); ++i) {
714 out = param->tokens[i];
715 if (out->token == TOKEN_EOL)
716 ftepp_out(ftepp, "\n", false);
718 ppmacro *find = ftepp_macro_find(ftepp, out->value);
719 if (OPTS_FLAG(FTEPP_INDIRECT_EXPANSION) && find && !find->has_params)
720 ftepp_macro_expand(ftepp, find, NULL, false);
722 ftepp_out(ftepp, out->value, false);
727 static bool ftepp_preprocess(ftepp_t *ftepp);
728 static bool ftepp_macro_expand(ftepp_t *ftepp, ppmacro *macro, macroparam *params, bool resetline)
731 char *old_string = ftepp->output_string;
733 lex_file *old_lexer = ftepp->lex;
734 size_t vararg_start = vec_size(macro->params);
747 if (vararg_start < vec_size(params))
748 varargs = vec_size(params) - vararg_start;
753 if (!vec_size(macro->output))
756 ftepp->output_string = NULL;
757 for (o = 0; o < vec_size(macro->output); ++o) {
758 pptoken *out = macro->output[o];
759 switch (out->token) {
761 if (!macro->variadic) {
762 ftepp_error(ftepp, "internal preprocessor error: TOKEN_VA_ARGS in non-variadic macro");
763 vec_free(old_string);
770 ftepp_param_out(ftepp, ¶ms[pi + vararg_start]);
771 for (++pi; pi < varargs; ++pi) {
772 ftepp_out(ftepp, ", ", false);
773 ftepp_param_out(ftepp, ¶ms[pi + vararg_start]);
777 case TOKEN_VA_ARGS_ARRAY:
778 if ((size_t)out->constval.i >= varargs) {
779 ftepp_error(ftepp, "subscript of `[%u]` is out of bounds for `__VA_ARGS__`", out->constval.i);
780 vec_free(old_string);
784 ftepp_param_out(ftepp, ¶ms[out->constval.i + vararg_start]);
788 util_asprintf(&buffer, "%d", varargs);
789 ftepp_out(ftepp, buffer, false);
796 if (!macro_params_find(macro, out->value, &pi)) {
797 ftepp_out(ftepp, out->value, false);
800 ftepp_param_out(ftepp, ¶ms[pi]);
803 if (o + 1 < vec_size(macro->output)) {
804 nextok = macro->output[o+1]->token;
806 /* raw concatenation */
811 if ( (nextok == TOKEN_IDENT ||
812 nextok == TOKEN_KEYWORD ||
813 nextok == TOKEN_TYPENAME) &&
814 macro_params_find(macro, macro->output[o+1]->value, &pi))
818 ftepp_stringify(ftepp, ¶ms[pi]);
822 ftepp_out(ftepp, "#", false);
825 ftepp_out(ftepp, "\n", false);
829 #define buffer_stripable(X) ((X) == ' ' || (X) == '\t')
830 if (vec_size(macro->output) > o + 1 && macro->output[o+1]->token == '#' && buffer_stripable(*buffer))
833 while (buffer_stripable(*buffer)) buffer++;
836 ftepp_out(ftepp, buffer, false);
840 vec_push(ftepp->output_string, 0);
841 /* Now run the preprocessor recursively on this string buffer */
843 printf("__________\n%s\n=========\n", ftepp->output_string);
845 inlex = lex_open_string(ftepp->output_string, vec_size(ftepp->output_string)-1, ftepp->lex->name);
847 ftepp_error(ftepp, "internal error: failed to instantiate lexer");
852 inlex->line = ftepp->lex->line;
853 inlex->sline = ftepp->lex->sline;
856 old_inmacro = ftepp->in_macro;
857 ftepp->in_macro = true;
858 ftepp->output_string = NULL;
859 if (!ftepp_preprocess(ftepp)) {
860 ftepp->in_macro = old_inmacro;
861 vec_free(ftepp->lex->open_string);
862 vec_free(ftepp->output_string);
863 lex_close(ftepp->lex);
867 ftepp->in_macro = old_inmacro;
868 vec_free(ftepp->lex->open_string);
869 lex_close(ftepp->lex);
871 inner_string = ftepp->output_string;
872 ftepp->output_string = old_string;
874 has_newlines = (strchr(inner_string, '\n') != NULL);
876 if (has_newlines && !old_inmacro)
877 ftepp_recursion_header(ftepp);
879 vec_append(ftepp->output_string, vec_size(inner_string), inner_string);
880 vec_free(inner_string);
882 if (has_newlines && !old_inmacro)
883 ftepp_recursion_footer(ftepp);
885 if (resetline && !ftepp->in_macro) {
887 util_snprintf(lineno, 128, "\n#pragma line(%lu)\n", (unsigned long)(old_lexer->sline));
888 ftepp_out(ftepp, lineno, false);
891 old_string = ftepp->output_string;
893 ftepp->lex = old_lexer;
894 ftepp->output_string = old_string;
898 static bool ftepp_macro_call(ftepp_t *ftepp, ppmacro *macro)
901 macroparam *params = NULL;
905 if (!macro->has_params) {
906 if (!ftepp_macro_expand(ftepp, macro, NULL, false))
913 if (!ftepp_skipallwhite(ftepp))
916 if (ftepp->token != '(') {
917 ftepp_error(ftepp, "expected macro parameters in parenthesis");
922 paramline = ftepp->lex->sline;
923 if (!ftepp_macro_call_params(ftepp, ¶ms))
926 if ( vec_size(params) < vec_size(macro->params) ||
927 (vec_size(params) > vec_size(macro->params) && !macro->variadic) )
929 ftepp_error(ftepp, "macro %s expects%s %u paramteters, %u provided", macro->name,
930 (macro->variadic ? " at least" : ""),
931 (unsigned int)vec_size(macro->params),
932 (unsigned int)vec_size(params));
937 if (!ftepp_macro_expand(ftepp, macro, params, (paramline != ftepp->lex->sline)))
942 for (o = 0; o < vec_size(params); ++o)
943 macroparam_clean(¶ms[o]);
949 * #if - the FTEQCC way:
950 * defined(FOO) => true if FOO was #defined regardless of parameters or contents
951 * <numbers> => True if the number is not 0
952 * !<factor> => True if the factor yields false
953 * !!<factor> => ERROR on 2 or more unary nots
954 * <macro> => becomes the macro's FIRST token regardless of parameters
955 * <e> && <e> => True if both expressions are true
956 * <e> || <e> => True if either expression is true
958 * <ident> => False (remember for macros the <macro> rule applies instead)
959 * Unary + and - are weird and wrong in fteqcc so we don't allow them
960 * parenthesis in expressions are allowed
961 * parameter lists on macros are errors
962 * No mathematical calculations are executed
964 static bool ftepp_if_expr(ftepp_t *ftepp, bool *out, double *value_out);
965 static bool ftepp_if_op(ftepp_t *ftepp)
967 ftepp->lex->flags.noops = false;
969 if (!ftepp_skipspace(ftepp))
971 ftepp->lex->flags.noops = true;
974 static bool ftepp_if_value(ftepp_t *ftepp, bool *out, double *value_out)
980 if (!ftepp_skipspace(ftepp))
983 while (ftepp->token == '!') {
986 if (!ftepp_skipspace(ftepp))
990 if (ftepp->token == TOKEN_OPERATOR && !strcmp(ftepp_tokval(ftepp), "-"))
994 if (!ftepp_skipspace(ftepp))
998 switch (ftepp->token) {
1000 case TOKEN_TYPENAME:
1002 if (!strcmp(ftepp_tokval(ftepp), "defined")) {
1004 if (!ftepp_skipspace(ftepp))
1006 if (ftepp->token != '(') {
1007 ftepp_error(ftepp, "`defined` keyword in #if requires a macro name in parenthesis");
1011 if (!ftepp_skipspace(ftepp))
1013 if (ftepp->token != TOKEN_IDENT &&
1014 ftepp->token != TOKEN_TYPENAME &&
1015 ftepp->token != TOKEN_KEYWORD)
1017 ftepp_error(ftepp, "defined() used on an unexpected token type");
1020 macro = ftepp_macro_find(ftepp, ftepp_tokval(ftepp));
1023 if (!ftepp_skipspace(ftepp))
1025 if (ftepp->token != ')') {
1026 ftepp_error(ftepp, "expected closing paren");
1032 macro = ftepp_macro_find(ftepp, ftepp_tokval(ftepp));
1033 if (!macro || !vec_size(macro->output)) {
1037 /* This does not expand recursively! */
1038 switch (macro->output[0]->token) {
1039 case TOKEN_INTCONST:
1040 *value_out = macro->output[0]->constval.i;
1041 *out = !!(macro->output[0]->constval.i);
1043 case TOKEN_FLOATCONST:
1044 *value_out = macro->output[0]->constval.f;
1045 *out = !!(macro->output[0]->constval.f);
1053 case TOKEN_STRINGCONST:
1057 case TOKEN_INTCONST:
1058 *value_out = ftepp->lex->tok.constval.i;
1059 *out = !!(ftepp->lex->tok.constval.i);
1061 case TOKEN_FLOATCONST:
1062 *value_out = ftepp->lex->tok.constval.f;
1063 *out = !!(ftepp->lex->tok.constval.f);
1068 if (!ftepp_if_expr(ftepp, out, value_out))
1070 if (ftepp->token != ')') {
1071 ftepp_error(ftepp, "expected closing paren in #if expression");
1077 ftepp_error(ftepp, "junk in #if: `%s` ...", ftepp_tokval(ftepp));
1078 if (OPTS_OPTION_BOOL(OPTION_DEBUG))
1079 ftepp_error(ftepp, "internal: token %i\n", ftepp->token);
1083 *value_out = -*value_out;
1086 *value_out = (*out ? 1 : 0);
1092 static bool ftepp_if_nextvalue(ftepp_t *ftepp, bool *out, double *value_out)
1094 if (!ftepp_next(ftepp))
1096 return ftepp_if_value(ftepp, out, value_out);
1100 static bool ftepp_if_expr(ftepp_t *ftepp, bool *out, double *value_out)
1102 if (!ftepp_if_value(ftepp, out, value_out))
1105 if (!ftepp_if_op(ftepp))
1108 if (ftepp->token == ')' || ftepp->token != TOKEN_OPERATOR)
1111 /* FTEQCC is all right-associative and no precedence here */
1112 if (!strcmp(ftepp_tokval(ftepp), "&&") ||
1113 !strcmp(ftepp_tokval(ftepp), "||"))
1116 char opc = ftepp_tokval(ftepp)[0];
1120 if (!ftepp_next(ftepp))
1122 if (!ftepp_if_expr(ftepp, &next, &nextvalue))
1126 *out = *out && next;
1128 *out = *out || next;
1130 *value_out = (*out ? 1 : 0);
1133 else if (!strcmp(ftepp_tokval(ftepp), "==") ||
1134 !strcmp(ftepp_tokval(ftepp), "!=") ||
1135 !strcmp(ftepp_tokval(ftepp), ">=") ||
1136 !strcmp(ftepp_tokval(ftepp), "<=") ||
1137 !strcmp(ftepp_tokval(ftepp), ">") ||
1138 !strcmp(ftepp_tokval(ftepp), "<"))
1141 const char opc0 = ftepp_tokval(ftepp)[0];
1142 const char opc1 = ftepp_tokval(ftepp)[1];
1145 if (!ftepp_next(ftepp))
1147 if (!ftepp_if_expr(ftepp, &next, &other))
1151 *out = (*value_out == other);
1152 else if (opc0 == '!')
1153 *out = (*value_out != other);
1154 else if (opc0 == '>') {
1155 if (opc1 == '=') *out = (*value_out >= other);
1156 else *out = (*value_out > other);
1158 else if (opc0 == '<') {
1159 if (opc1 == '=') *out = (*value_out <= other);
1160 else *out = (*value_out < other);
1162 *value_out = (*out ? 1 : 0);
1167 ftepp_error(ftepp, "junk after #if");
1172 static bool ftepp_if(ftepp_t *ftepp, ppcondition *cond)
1174 bool result = false;
1177 memset(cond, 0, sizeof(*cond));
1178 (void)ftepp_next(ftepp);
1180 if (!ftepp_skipspace(ftepp))
1182 if (ftepp->token == TOKEN_EOL) {
1183 ftepp_error(ftepp, "expected expression for #if-directive");
1187 if (!ftepp_if_expr(ftepp, &result, &dummy))
1195 * ifdef is rather simple
1197 static bool ftepp_ifdef(ftepp_t *ftepp, ppcondition *cond)
1200 memset(cond, 0, sizeof(*cond));
1201 (void)ftepp_next(ftepp);
1202 if (!ftepp_skipspace(ftepp))
1205 switch (ftepp->token) {
1207 case TOKEN_TYPENAME:
1209 macro = ftepp_macro_find(ftepp, ftepp_tokval(ftepp));
1212 ftepp_error(ftepp, "expected macro name");
1216 (void)ftepp_next(ftepp);
1217 if (!ftepp_skipspace(ftepp))
1219 /* relaxing this condition
1220 if (ftepp->token != TOKEN_EOL && ftepp->token != TOKEN_EOF) {
1221 ftepp_error(ftepp, "stray tokens after #ifdef");
1230 * undef is also simple
1232 static bool ftepp_undef(ftepp_t *ftepp)
1234 (void)ftepp_next(ftepp);
1235 if (!ftepp_skipspace(ftepp))
1238 if (ftepp->output_on) {
1239 switch (ftepp->token) {
1241 case TOKEN_TYPENAME:
1243 ftepp_macro_delete(ftepp, ftepp_tokval(ftepp));
1246 ftepp_error(ftepp, "expected macro name");
1251 (void)ftepp_next(ftepp);
1252 if (!ftepp_skipspace(ftepp))
1254 /* relaxing this condition
1255 if (ftepp->token != TOKEN_EOL && ftepp->token != TOKEN_EOF) {
1256 ftepp_error(ftepp, "stray tokens after #ifdef");
1263 /* Special unescape-string function which skips a leading quote
1264 * and stops at a quote, not just at \0
1266 static void unescape(const char *str, char *out) {
1268 while (*str && *str != '"') {
1272 case '\\': *out++ = *str; break;
1273 case '"': *out++ = *str; break;
1274 case 'a': *out++ = '\a'; break;
1275 case 'b': *out++ = '\b'; break;
1276 case 'r': *out++ = '\r'; break;
1277 case 'n': *out++ = '\n'; break;
1278 case 't': *out++ = '\t'; break;
1279 case 'f': *out++ = '\f'; break;
1280 case 'v': *out++ = '\v'; break;
1295 static char *ftepp_include_find_path(const char *file, const char *pathfile)
1298 char *filename = NULL;
1299 const char *last_slash;
1305 last_slash = strrchr(pathfile, '/');
1308 len = last_slash - pathfile;
1309 memcpy(vec_add(filename, len), pathfile, len);
1310 vec_push(filename, '/');
1314 memcpy(vec_add(filename, len+1), file, len);
1315 vec_last(filename) = 0;
1317 fp = fopen(filename, "rb");
1326 static char *ftepp_include_find(ftepp_t *ftepp, const char *file)
1328 char *filename = NULL;
1330 filename = ftepp_include_find_path(file, ftepp->includename);
1332 filename = ftepp_include_find_path(file, ftepp->itemname);
1336 static bool ftepp_directive_warning(ftepp_t *ftepp) {
1337 char *message = NULL;
1339 if (!ftepp_skipspace(ftepp))
1342 /* handle the odd non string constant case so it works like C */
1343 if (ftepp->token != TOKEN_STRINGCONST) {
1345 vec_append(message, 8, "#warning");
1347 while (ftepp->token != TOKEN_EOL) {
1348 vec_append(message, strlen(ftepp_tokval(ftepp)), ftepp_tokval(ftepp));
1351 vec_push(message, '\0');
1352 if (ftepp->output_on)
1353 store = ftepp_warn(ftepp, WARN_CPP, message);
1360 if (!ftepp->output_on)
1363 unescape (ftepp_tokval(ftepp), ftepp_tokval(ftepp));
1364 return ftepp_warn(ftepp, WARN_CPP, "#warning %s", ftepp_tokval(ftepp));
1367 static void ftepp_directive_error(ftepp_t *ftepp) {
1368 char *message = NULL;
1370 if (!ftepp_skipspace(ftepp))
1373 /* handle the odd non string constant case so it works like C */
1374 if (ftepp->token != TOKEN_STRINGCONST) {
1375 vec_append(message, 6, "#error");
1377 while (ftepp->token != TOKEN_EOL) {
1378 vec_append(message, strlen(ftepp_tokval(ftepp)), ftepp_tokval(ftepp));
1381 vec_push(message, '\0');
1382 if (ftepp->output_on)
1383 ftepp_error(ftepp, message);
1388 if (!ftepp->output_on)
1391 unescape (ftepp_tokval(ftepp), ftepp_tokval(ftepp));
1392 ftepp_error(ftepp, "#error %s", ftepp_tokval(ftepp));
1395 static void ftepp_directive_message(ftepp_t *ftepp) {
1396 char *message = NULL;
1398 if (!ftepp_skipspace(ftepp))
1401 /* handle the odd non string constant case so it works like C */
1402 if (ftepp->token != TOKEN_STRINGCONST) {
1403 vec_append(message, 8, "#message");
1405 while (ftepp->token != TOKEN_EOL) {
1406 vec_append(message, strlen(ftepp_tokval(ftepp)), ftepp_tokval(ftepp));
1409 vec_push(message, '\0');
1410 if (ftepp->output_on)
1411 con_cprintmsg(ftepp->lex->tok.ctx, LVL_MSG, "message", message);
1416 if (!ftepp->output_on)
1419 unescape (ftepp_tokval(ftepp), ftepp_tokval(ftepp));
1420 con_cprintmsg(ftepp->lex->tok.ctx, LVL_MSG, "message", ftepp_tokval(ftepp));
1425 * FIXME: do we need/want a -I option?
1426 * FIXME: what about when dealing with files in subdirectories coming from a progs.src?
1428 static bool ftepp_include(ftepp_t *ftepp)
1430 lex_file *old_lexer = ftepp->lex;
1435 char *parsename = NULL;
1436 char *old_includename;
1438 (void)ftepp_next(ftepp);
1439 if (!ftepp_skipspace(ftepp))
1442 if (ftepp->token != TOKEN_STRINGCONST) {
1443 ppmacro *macro = ftepp_macro_find(ftepp, ftepp_tokval(ftepp));
1445 char *backup = ftepp->output_string;
1446 ftepp->output_string = NULL;
1447 if (ftepp_macro_expand(ftepp, macro, NULL, true)) {
1448 parsename = util_strdup(ftepp->output_string);
1449 vec_free(ftepp->output_string);
1450 ftepp->output_string = backup;
1452 ftepp->output_string = backup;
1453 ftepp_error(ftepp, "expected filename to include");
1456 } else if (OPTS_FLAG(FTEPP_PREDEFS)) {
1457 /* Well it could be a predefine like __LINE__ */
1458 char *(*predef)(ftepp_t*) = ftepp_predef(ftepp_tokval(ftepp));
1460 parsename = predef(ftepp);
1462 ftepp_error(ftepp, "expected filename to include");
1468 if (!ftepp->output_on) {
1469 (void)ftepp_next(ftepp);
1474 unescape(parsename, parsename);
1476 char *tokval = ftepp_tokval(ftepp);
1477 unescape(tokval, tokval);
1478 parsename = util_strdup(tokval);
1481 ctx = ftepp_ctx(ftepp);
1482 ftepp_out(ftepp, "\n#pragma file(", false);
1483 ftepp_out(ftepp, parsename, false);
1484 ftepp_out(ftepp, ")\n#pragma line(1)\n", false);
1486 filename = ftepp_include_find(ftepp, parsename);
1488 ftepp_error(ftepp, "failed to open include file `%s`", parsename);
1493 inlex = lex_open(filename);
1495 ftepp_error(ftepp, "open failed on include file `%s`", filename);
1500 old_includename = ftepp->includename;
1501 ftepp->includename = filename;
1502 if (!ftepp_preprocess(ftepp)) {
1503 vec_free(ftepp->includename);
1504 ftepp->includename = old_includename;
1505 lex_close(ftepp->lex);
1506 ftepp->lex = old_lexer;
1509 vec_free(ftepp->includename);
1510 ftepp->includename = old_includename;
1511 lex_close(ftepp->lex);
1512 ftepp->lex = old_lexer;
1514 ftepp_out(ftepp, "\n#pragma file(", false);
1515 ftepp_out(ftepp, ctx.file, false);
1516 util_snprintf(lineno, sizeof(lineno), ")\n#pragma line(%lu)\n", (unsigned long)(ctx.line+1));
1517 ftepp_out(ftepp, lineno, false);
1520 (void)ftepp_next(ftepp);
1521 if (!ftepp_skipspace(ftepp))
1523 if (ftepp->token != TOKEN_EOL) {
1524 ftepp_error(ftepp, "stray tokens after #include");
1527 (void)ftepp_next(ftepp);
1532 /* Basic structure handlers */
1533 static bool ftepp_else_allowed(ftepp_t *ftepp)
1535 if (!vec_size(ftepp->conditions)) {
1536 ftepp_error(ftepp, "#else without #if");
1539 if (vec_last(ftepp->conditions).had_else) {
1540 ftepp_error(ftepp, "multiple #else for a single #if");
1546 static GMQCC_INLINE void ftepp_inmacro(ftepp_t *ftepp, const char *hash) {
1547 if (ftepp->in_macro)
1548 (void)!ftepp_warn(ftepp, WARN_DIRECTIVE_INMACRO, "`#%s` directive in macro", hash);
1551 static bool ftepp_hash(ftepp_t *ftepp)
1556 lex_ctx_t ctx = ftepp_ctx(ftepp);
1558 if (!ftepp_skipspace(ftepp))
1561 switch (ftepp->token) {
1564 case TOKEN_TYPENAME:
1565 if (!strcmp(ftepp_tokval(ftepp), "define")) {
1566 ftepp_inmacro(ftepp, "define");
1567 return ftepp_define(ftepp);
1569 else if (!strcmp(ftepp_tokval(ftepp), "undef")) {
1570 ftepp_inmacro(ftepp, "undef");
1571 return ftepp_undef(ftepp);
1573 else if (!strcmp(ftepp_tokval(ftepp), "ifdef")) {
1574 ftepp_inmacro(ftepp, "ifdef");
1575 if (!ftepp_ifdef(ftepp, &cond))
1577 cond.was_on = cond.on;
1578 vec_push(ftepp->conditions, cond);
1579 ftepp->output_on = ftepp->output_on && cond.on;
1582 else if (!strcmp(ftepp_tokval(ftepp), "ifndef")) {
1583 ftepp_inmacro(ftepp, "ifndef");
1584 if (!ftepp_ifdef(ftepp, &cond))
1587 cond.was_on = cond.on;
1588 vec_push(ftepp->conditions, cond);
1589 ftepp->output_on = ftepp->output_on && cond.on;
1592 else if (!strcmp(ftepp_tokval(ftepp), "elifdef")) {
1593 ftepp_inmacro(ftepp, "elifdef");
1594 if (!ftepp_else_allowed(ftepp))
1596 if (!ftepp_ifdef(ftepp, &cond))
1598 pc = &vec_last(ftepp->conditions);
1599 pc->on = !pc->was_on && cond.on;
1600 pc->was_on = pc->was_on || pc->on;
1601 ftepp_update_output_condition(ftepp);
1604 else if (!strcmp(ftepp_tokval(ftepp), "elifndef")) {
1605 ftepp_inmacro(ftepp, "elifndef");
1606 if (!ftepp_else_allowed(ftepp))
1608 if (!ftepp_ifdef(ftepp, &cond))
1611 pc = &vec_last(ftepp->conditions);
1612 pc->on = !pc->was_on && cond.on;
1613 pc->was_on = pc->was_on || pc->on;
1614 ftepp_update_output_condition(ftepp);
1617 else if (!strcmp(ftepp_tokval(ftepp), "elif")) {
1618 ftepp_inmacro(ftepp, "elif");
1619 if (!ftepp_else_allowed(ftepp))
1621 if (!ftepp_if(ftepp, &cond))
1623 pc = &vec_last(ftepp->conditions);
1624 pc->on = !pc->was_on && cond.on;
1625 pc->was_on = pc->was_on || pc->on;
1626 ftepp_update_output_condition(ftepp);
1629 else if (!strcmp(ftepp_tokval(ftepp), "if")) {
1630 ftepp_inmacro(ftepp, "if");
1631 if (!ftepp_if(ftepp, &cond))
1633 cond.was_on = cond.on;
1634 vec_push(ftepp->conditions, cond);
1635 ftepp->output_on = ftepp->output_on && cond.on;
1638 else if (!strcmp(ftepp_tokval(ftepp), "else")) {
1639 ftepp_inmacro(ftepp, "else");
1640 if (!ftepp_else_allowed(ftepp))
1642 pc = &vec_last(ftepp->conditions);
1643 pc->on = !pc->was_on;
1644 pc->had_else = true;
1646 ftepp_update_output_condition(ftepp);
1649 else if (!strcmp(ftepp_tokval(ftepp), "endif")) {
1650 ftepp_inmacro(ftepp, "endif");
1651 if (!vec_size(ftepp->conditions)) {
1652 ftepp_error(ftepp, "#endif without #if");
1655 vec_pop(ftepp->conditions);
1657 ftepp_update_output_condition(ftepp);
1660 else if (!strcmp(ftepp_tokval(ftepp), "include")) {
1661 ftepp_inmacro(ftepp, "include");
1662 return ftepp_include(ftepp);
1664 else if (!strcmp(ftepp_tokval(ftepp), "pragma")) {
1665 ftepp_out(ftepp, "#", false);
1668 else if (!strcmp(ftepp_tokval(ftepp), "warning")) {
1669 ftepp_directive_warning(ftepp);
1672 else if (!strcmp(ftepp_tokval(ftepp), "error")) {
1673 ftepp_directive_error(ftepp);
1676 else if (!strcmp(ftepp_tokval(ftepp), "message")) {
1677 ftepp_directive_message(ftepp);
1681 if (ftepp->output_on) {
1682 ftepp_error(ftepp, "unrecognized preprocessor directive: `%s`", ftepp_tokval(ftepp));
1689 /* break; never reached */
1691 ftepp_error(ftepp, "unexpected preprocessor token: `%s`", ftepp_tokval(ftepp));
1694 ftepp_errorat(ftepp, ctx, "empty preprocessor directive");
1697 ftepp_error(ftepp, "missing newline at end of file", ftepp_tokval(ftepp));
1700 /* Builtins! Don't forget the builtins! */
1701 case TOKEN_INTCONST:
1702 case TOKEN_FLOATCONST:
1703 ftepp_out(ftepp, "#", false);
1706 if (!ftepp_skipspace(ftepp))
1711 static bool ftepp_preprocess(ftepp_t *ftepp)
1714 bool newline = true;
1717 char *expand = NULL;
1719 ftepp->lex->flags.preprocessing = true;
1720 ftepp->lex->flags.mergelines = false;
1721 ftepp->lex->flags.noops = true;
1726 if (ftepp->token >= TOKEN_EOF)
1728 switch (ftepp->token) {
1731 case TOKEN_TYPENAME:
1732 /* is it a predef? */
1733 if (OPTS_FLAG(FTEPP_PREDEFS)) {
1734 char *(*predef)(ftepp_t*) = ftepp_predef(ftepp_tokval(ftepp));
1736 expand = predef(ftepp);
1737 ftepp_out (ftepp, expand, false);
1745 if (ftepp->output_on)
1746 macro = ftepp_macro_find(ftepp, ftepp_tokval(ftepp));
1751 ftepp_out(ftepp, ftepp_tokval(ftepp), false);
1755 if (!ftepp_macro_call(ftepp, macro))
1756 ftepp->token = TOKEN_ERROR;
1760 ftepp_out(ftepp, ftepp_tokval(ftepp), false);
1764 ftepp->lex->flags.mergelines = true;
1765 if (ftepp_next(ftepp) >= TOKEN_EOF) {
1766 ftepp_error(ftepp, "error in preprocessor directive");
1767 ftepp->token = TOKEN_ERROR;
1770 if (!ftepp_hash(ftepp))
1771 ftepp->token = TOKEN_ERROR;
1772 ftepp->lex->flags.mergelines = false;
1776 ftepp_out(ftepp, "\n", true);
1780 /* same as default but don't set newline=false */
1781 ftepp_out(ftepp, ftepp_tokval(ftepp), true);
1786 ftepp_out(ftepp, ftepp_tokval(ftepp), false);
1790 } while (!ftepp->errors && ftepp->token < TOKEN_EOF);
1792 /* force a 0 at the end but don't count it as added to the output */
1793 vec_push(ftepp->output_string, 0);
1794 vec_shrinkby(ftepp->output_string, 1);
1796 return (ftepp->token == TOKEN_EOF);
1799 /* Like in parser.c - files keep the previous state so we have one global
1800 * preprocessor. Except here we will want to warn about dangling #ifs.
1802 static bool ftepp_preprocess_done(ftepp_t *ftepp)
1805 if (vec_size(ftepp->conditions)) {
1806 if (ftepp_warn(ftepp, WARN_MULTIFILE_IF, "#if spanning multiple files, is this intended?"))
1809 lex_close(ftepp->lex);
1811 if (ftepp->itemname) {
1812 mem_d(ftepp->itemname);
1813 ftepp->itemname = NULL;
1818 bool ftepp_preprocess_file(ftepp_t *ftepp, const char *filename)
1820 ftepp->lex = lex_open(filename);
1821 ftepp->itemname = util_strdup(filename);
1823 con_out("failed to open file \"%s\"\n", filename);
1826 if (!ftepp_preprocess(ftepp))
1828 return ftepp_preprocess_done(ftepp);
1831 bool ftepp_preprocess_string(ftepp_t *ftepp, const char *name, const char *str)
1833 ftepp->lex = lex_open_string(str, strlen(str), name);
1834 ftepp->itemname = util_strdup(name);
1836 con_out("failed to create lexer for string \"%s\"\n", name);
1839 if (!ftepp_preprocess(ftepp))
1841 return ftepp_preprocess_done(ftepp);
1845 void ftepp_add_macro(ftepp_t *ftepp, const char *name, const char *value) {
1846 char *create = NULL;
1848 /* use saner path for empty macros */
1850 ftepp_add_define(ftepp, "__builtin__", name);
1854 vec_append(create, 8, "#define ");
1855 vec_append(create, strlen(name), name);
1856 vec_push (create, ' ');
1857 vec_append(create, strlen(value), value);
1858 vec_push (create, 0);
1860 ftepp_preprocess_string(ftepp, "__builtin__", create);
1864 ftepp_t *ftepp_create()
1871 ftepp = ftepp_new();
1875 memset(minor, 0, sizeof(minor));
1876 memset(major, 0, sizeof(major));
1878 /* set the right macro based on the selected standard */
1879 ftepp_add_define(ftepp, NULL, "GMQCC");
1880 if (OPTS_OPTION_U32(OPTION_STANDARD) == COMPILER_FTEQCC) {
1881 ftepp_add_define(ftepp, NULL, "__STD_FTEQCC__");
1890 } else if (OPTS_OPTION_U32(OPTION_STANDARD) == COMPILER_GMQCC) {
1891 ftepp_add_define(ftepp, NULL, "__STD_GMQCC__");
1892 util_snprintf(major, 32, "\"%d\"", GMQCC_VERSION_MAJOR);
1893 util_snprintf(minor, 32, "\"%d\"", GMQCC_VERSION_MINOR);
1894 } else if (OPTS_OPTION_U32(OPTION_STANDARD) == COMPILER_QCCX) {
1895 ftepp_add_define(ftepp, NULL, "__STD_QCCX__");
1896 util_snprintf(major, 32, "\"%d\"", GMQCC_VERSION_MAJOR);
1897 util_snprintf(minor, 32, "\"%d\"", GMQCC_VERSION_MINOR);
1898 } else if (OPTS_OPTION_U32(OPTION_STANDARD) == COMPILER_QCC) {
1899 ftepp_add_define(ftepp, NULL, "__STD_QCC__");
1910 ftepp_add_macro(ftepp, "__STD_VERSION_MINOR__", minor);
1911 ftepp_add_macro(ftepp, "__STD_VERSION_MAJOR__", major);
1914 * We're going to just make __NULL__ nil, which works for 60% of the
1915 * cases of __NULL_ for fteqcc.
1917 ftepp_add_macro(ftepp, "__NULL__", "nil");
1919 /* add all the math constants if they can be */
1920 if (OPTS_FLAG(FTEPP_MATHDEFS)) {
1921 for (i = 0; i < GMQCC_ARRAY_COUNT(ftepp_math_constants); i++)
1922 if (!ftepp_macro_find(ftepp, ftepp_math_constants[i][0]))
1923 ftepp_add_macro(ftepp, ftepp_math_constants[i][0], ftepp_math_constants[i][1]);
1929 void ftepp_add_define(ftepp_t *ftepp, const char *source, const char *name)
1932 lex_ctx_t ctx = { "__builtin__", 0, 0 };
1934 macro = ppmacro_new(ctx, name);
1935 /*vec_push(ftepp->macros, macro);*/
1936 util_htset(ftepp->macros, name, macro);
1939 const char *ftepp_get(ftepp_t *ftepp)
1941 return ftepp->output_string;
1944 void ftepp_flush(ftepp_t *ftepp)
1946 ftepp_flush_do(ftepp);
1949 void ftepp_finish(ftepp_t *ftepp)
1953 ftepp_delete(ftepp);