5 * Permission is hereby granted, free of charge, to any person obtaining a copy of
6 * this software and associated documentation files (the "Software"), to deal in
7 * the Software without restriction, including without limitation the rights to
8 * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
9 * of the Software, and to permit persons to whom the Software is furnished to do
10 * so, subject to the following conditions:
12 * The above copyright notice and this permission notice shall be included in all
13 * copies or substantial portions of the Software.
15 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
35 /* a copy from the lexer */
49 /* yes we need an extra flag since `#define FOO x` is not the same as `#define FOO() x` */
62 ppcondition *conditions;
70 #define ftepp_tokval(f) ((f)->lex->tok.value)
71 #define ftepp_ctx(f) ((f)->lex->tok.ctx)
73 static void ftepp_errorat(ftepp_t *ftepp, lex_ctx ctx, const char *fmt, ...)
80 con_cvprintmsg((void*)&ctx, LVL_ERROR, "error", fmt, ap);
84 static void ftepp_error(ftepp_t *ftepp, const char *fmt, ...)
91 con_cvprintmsg((void*)&ftepp->lex->tok.ctx, LVL_ERROR, "error", fmt, ap);
95 static bool GMQCC_WARN ftepp_warn(ftepp_t *ftepp, int warntype, const char *fmt, ...)
98 int lvl = LVL_WARNING;
100 if (!OPTS_WARN(warntype))
109 con_cvprintmsg((void*)&ftepp->lex->tok.ctx, lvl, "error", fmt, ap);
114 static pptoken *pptoken_make(ftepp_t *ftepp)
116 pptoken *token = (pptoken*)mem_a(sizeof(pptoken));
117 token->token = ftepp->token;
119 if (token->token == TOKEN_WHITE)
120 token->value = util_strdup(" ");
123 token->value = util_strdup(ftepp_tokval(ftepp));
125 memcpy(&token->constval, &ftepp->lex->tok.constval, sizeof(token->constval));
129 static void pptoken_delete(pptoken *self)
135 static ppmacro *ppmacro_new(lex_ctx ctx, const char *name)
137 ppmacro *macro = (ppmacro*)mem_a(sizeof(ppmacro));
140 memset(macro, 0, sizeof(*macro));
141 macro->name = util_strdup(name);
145 static void ppmacro_delete(ppmacro *self)
148 for (i = 0; i < vec_size(self->params); ++i)
149 mem_d(self->params[i]);
150 vec_free(self->params);
151 for (i = 0; i < vec_size(self->output); ++i)
152 pptoken_delete(self->output[i]);
153 vec_free(self->output);
158 static ftepp_t* ftepp_new()
162 ftepp = (ftepp_t*)mem_a(sizeof(*ftepp));
163 memset(ftepp, 0, sizeof(*ftepp));
165 ftepp->output_on = true;
170 static void ftepp_delete(ftepp_t *self)
174 mem_d(self->itemname);
175 for (i = 0; i < vec_size(self->macros); ++i)
176 ppmacro_delete(self->macros[i]);
177 vec_free(self->macros);
178 vec_free(self->conditions);
180 lex_close(self->lex);
184 static void ftepp_out(ftepp_t *ftepp, const char *str, bool ignore_cond)
186 if (ignore_cond || ftepp->output_on)
191 data = vec_add(ftepp->output_string, len);
192 memcpy(data, str, len);
196 static void ftepp_update_output_condition(ftepp_t *ftepp)
199 ftepp->output_on = true;
200 for (i = 0; i < vec_size(ftepp->conditions); ++i)
201 ftepp->output_on = ftepp->output_on && ftepp->conditions[i].on;
204 static ppmacro* ftepp_macro_find(ftepp_t *ftepp, const char *name)
207 for (i = 0; i < vec_size(ftepp->macros); ++i) {
208 if (!strcmp(name, ftepp->macros[i]->name))
209 return ftepp->macros[i];
214 static void ftepp_macro_delete(ftepp_t *ftepp, const char *name)
217 for (i = 0; i < vec_size(ftepp->macros); ++i) {
218 if (!strcmp(name, ftepp->macros[i]->name)) {
219 vec_remove(ftepp->macros, i, 1);
225 static inline int ftepp_next(ftepp_t *ftepp)
227 return (ftepp->token = lex_do(ftepp->lex));
230 /* Important: this does not skip newlines! */
231 static bool ftepp_skipspace(ftepp_t *ftepp)
233 if (ftepp->token != TOKEN_WHITE)
235 while (ftepp_next(ftepp) == TOKEN_WHITE) {}
236 if (ftepp->token >= TOKEN_EOF) {
237 ftepp_error(ftepp, "unexpected end of preprocessor directive");
243 /* this one skips EOLs as well */
244 static bool ftepp_skipallwhite(ftepp_t *ftepp)
246 if (ftepp->token != TOKEN_WHITE && ftepp->token != TOKEN_EOL)
250 } while (ftepp->token == TOKEN_WHITE || ftepp->token == TOKEN_EOL);
251 if (ftepp->token >= TOKEN_EOF) {
252 ftepp_error(ftepp, "unexpected end of preprocessor directive");
259 * The huge macro parsing code...
261 static bool ftepp_define_params(ftepp_t *ftepp, ppmacro *macro)
265 if (!ftepp_skipspace(ftepp))
267 if (ftepp->token == ')')
269 switch (ftepp->token) {
275 ftepp_error(ftepp, "unexpected token in parameter list");
278 vec_push(macro->params, util_strdup(ftepp_tokval(ftepp)));
280 if (!ftepp_skipspace(ftepp))
282 } while (ftepp->token == ',');
283 if (ftepp->token != ')') {
284 ftepp_error(ftepp, "expected closing paren after macro parameter list");
288 /* skipspace happens in ftepp_define */
292 static bool ftepp_define_body(ftepp_t *ftepp, ppmacro *macro)
295 while (ftepp->token != TOKEN_EOL && ftepp->token < TOKEN_EOF) {
296 ptok = pptoken_make(ftepp);
297 vec_push(macro->output, ptok);
300 /* recursive expansion can cause EOFs here */
301 if (ftepp->token != TOKEN_EOL && ftepp->token != TOKEN_EOF) {
302 ftepp_error(ftepp, "unexpected junk after macro or unexpected end of file");
308 static bool ftepp_define(ftepp_t *ftepp)
311 (void)ftepp_next(ftepp);
312 if (!ftepp_skipspace(ftepp))
315 switch (ftepp->token) {
319 macro = ftepp_macro_find(ftepp, ftepp_tokval(ftepp));
320 if (macro && ftepp->output_on) {
321 if (ftepp_warn(ftepp, WARN_PREPROCESSOR, "redefining `%s`", ftepp_tokval(ftepp)))
323 ftepp_macro_delete(ftepp, ftepp_tokval(ftepp));
325 macro = ppmacro_new(ftepp_ctx(ftepp), ftepp_tokval(ftepp));
328 ftepp_error(ftepp, "expected macro name");
332 (void)ftepp_next(ftepp);
334 if (ftepp->token == '(') {
335 macro->has_params = true;
336 if (!ftepp_define_params(ftepp, macro))
340 if (!ftepp_skipspace(ftepp))
343 if (!ftepp_define_body(ftepp, macro))
346 if (ftepp->output_on)
347 vec_push(ftepp->macros, macro);
349 ppmacro_delete(macro);
355 * When a macro is used we have to handle parameters as well
356 * as special-concatenation via ## or stringification via #
358 * Note: parenthesis can nest, so FOO((a),b) is valid, but only
359 * this kind of parens. Curly braces or [] don't count towards the
366 static void macroparam_clean(macroparam *self)
369 for (i = 0; i < vec_size(self->tokens); ++i)
370 pptoken_delete(self->tokens[i]);
371 vec_free(self->tokens);
374 /* need to leave the last token up */
375 static bool ftepp_macro_call_params(ftepp_t *ftepp, macroparam **out_params)
377 macroparam *params = NULL;
383 if (!ftepp_skipallwhite(ftepp))
385 while (ftepp->token != ')') {
387 if (!ftepp_skipallwhite(ftepp))
389 while (parens || ftepp->token != ',') {
390 if (ftepp->token == '(')
392 else if (ftepp->token == ')') {
397 ptok = pptoken_make(ftepp);
398 vec_push(mp.tokens, ptok);
399 if (ftepp_next(ftepp) >= TOKEN_EOF) {
400 ftepp_error(ftepp, "unexpected EOF in macro call");
404 vec_push(params, mp);
406 if (ftepp->token == ')')
408 if (ftepp->token != ',') {
409 ftepp_error(ftepp, "expected closing paren or comma in macro call");
412 if (ftepp_next(ftepp) >= TOKEN_EOF) {
413 ftepp_error(ftepp, "unexpected EOF in macro call");
417 /* need to leave that up
418 if (ftepp_next(ftepp) >= TOKEN_EOF) {
419 ftepp_error(ftepp, "unexpected EOF in macro call");
423 *out_params = params;
428 macroparam_clean(&mp);
429 for (i = 0; i < vec_size(params); ++i)
430 macroparam_clean(¶ms[i]);
435 static bool macro_params_find(ppmacro *macro, const char *name, size_t *idx)
438 for (i = 0; i < vec_size(macro->params); ++i) {
439 if (!strcmp(macro->params[i], name)) {
447 static void ftepp_stringify_token(ftepp_t *ftepp, pptoken *token)
452 switch (token->token) {
453 case TOKEN_STRINGCONST:
456 /* in preprocessor mode strings already are string,
457 * so we don't get actual newline bytes here.
458 * Still need to escape backslashes and quotes.
461 case '\\': ftepp_out(ftepp, "\\\\", false); break;
462 case '"': ftepp_out(ftepp, "\\\"", false); break;
465 ftepp_out(ftepp, chs, false);
472 ftepp_out(ftepp, " ", false);
475 ftepp_out(ftepp, "\\n", false);
478 ftepp_out(ftepp, token->value, false);
483 static void ftepp_stringify(ftepp_t *ftepp, macroparam *param)
486 ftepp_out(ftepp, "\"", false);
487 for (i = 0; i < vec_size(param->tokens); ++i)
488 ftepp_stringify_token(ftepp, param->tokens[i]);
489 ftepp_out(ftepp, "\"", false);
492 static void ftepp_recursion_header(ftepp_t *ftepp)
494 ftepp_out(ftepp, "\n#pragma push(line)\n", false);
497 static void ftepp_recursion_footer(ftepp_t *ftepp)
499 ftepp_out(ftepp, "\n#pragma pop(line)\n", false);
502 static bool ftepp_preprocess(ftepp_t *ftepp);
503 static bool ftepp_macro_expand(ftepp_t *ftepp, ppmacro *macro, macroparam *params)
505 char *old_string = ftepp->output_string;
506 lex_file *old_lexer = ftepp->lex;
515 if (!vec_size(macro->output))
518 ftepp->output_string = NULL;
519 for (o = 0; o < vec_size(macro->output); ++o) {
520 pptoken *out = macro->output[o];
521 switch (out->token) {
525 if (!macro_params_find(macro, out->value, &pi)) {
526 ftepp_out(ftepp, out->value, false);
529 for (pv = 0; pv < vec_size(params[pi].tokens); ++pv) {
530 out = params[pi].tokens[pv];
531 if (out->token == TOKEN_EOL)
532 ftepp_out(ftepp, "\n", false);
534 ftepp_out(ftepp, out->value, false);
539 if (o + 1 < vec_size(macro->output)) {
540 nextok = macro->output[o+1]->token;
542 /* raw concatenation */
546 if ( (nextok == TOKEN_IDENT ||
547 nextok == TOKEN_KEYWORD ||
548 nextok == TOKEN_TYPENAME) &&
549 macro_params_find(macro, macro->output[o+1]->value, &pi))
552 ftepp_stringify(ftepp, ¶ms[pi]);
556 ftepp_out(ftepp, "#", false);
559 ftepp_out(ftepp, "\n", false);
562 ftepp_out(ftepp, out->value, false);
566 vec_push(ftepp->output_string, 0);
567 /* Now run the preprocessor recursively on this string buffer */
569 printf("__________\n%s\n=========\n", ftepp->output_string);
571 inlex = lex_open_string(ftepp->output_string, vec_size(ftepp->output_string)-1, ftepp->lex->name);
573 ftepp_error(ftepp, "internal error: failed to instantiate lexer");
577 ftepp->output_string = old_string;
579 ftepp_recursion_header(ftepp);
580 if (!ftepp_preprocess(ftepp)) {
581 lex_close(ftepp->lex);
585 ftepp_recursion_footer(ftepp);
586 old_string = ftepp->output_string;
589 ftepp->lex = old_lexer;
590 ftepp->output_string = old_string;
594 static bool ftepp_macro_call(ftepp_t *ftepp, ppmacro *macro)
597 macroparam *params = NULL;
600 if (!macro->has_params) {
601 if (!ftepp_macro_expand(ftepp, macro, NULL))
608 if (!ftepp_skipallwhite(ftepp))
611 if (ftepp->token != '(') {
612 ftepp_error(ftepp, "expected macro parameters in parenthesis");
617 if (!ftepp_macro_call_params(ftepp, ¶ms))
620 if (vec_size(params) != vec_size(macro->params)) {
621 ftepp_error(ftepp, "macro %s expects %u paramteters, %u provided", macro->name,
622 (unsigned int)vec_size(macro->params),
623 (unsigned int)vec_size(params));
628 if (!ftepp_macro_expand(ftepp, macro, params))
633 for (o = 0; o < vec_size(params); ++o)
634 macroparam_clean(¶ms[o]);
640 * #if - the FTEQCC way:
641 * defined(FOO) => true if FOO was #defined regardless of parameters or contents
642 * <numbers> => True if the number is not 0
643 * !<factor> => True if the factor yields false
644 * !!<factor> => ERROR on 2 or more unary nots
645 * <macro> => becomes the macro's FIRST token regardless of parameters
646 * <e> && <e> => True if both expressions are true
647 * <e> || <e> => True if either expression is true
649 * <ident> => False (remember for macros the <macro> rule applies instead)
650 * Unary + and - are weird and wrong in fteqcc so we don't allow them
651 * parenthesis in expressions are allowed
652 * parameter lists on macros are errors
653 * No mathematical calculations are executed
655 static bool ftepp_if_expr(ftepp_t *ftepp, bool *out)
660 if (!ftepp_skipspace(ftepp))
663 while (ftepp->token == '!') {
666 if (!ftepp_skipspace(ftepp))
670 switch (ftepp->token) {
674 if (!strcmp(ftepp_tokval(ftepp), "defined")) {
676 if (!ftepp_skipspace(ftepp))
678 if (ftepp->token != '(') {
679 ftepp_error(ftepp, "`defined` keyword in #if requires a macro name in parenthesis");
683 if (!ftepp_skipspace(ftepp))
685 if (ftepp->token != TOKEN_IDENT &&
686 ftepp->token != TOKEN_TYPENAME &&
687 ftepp->token != TOKEN_KEYWORD)
689 ftepp_error(ftepp, "defined() used on an unexpected token type");
692 macro = ftepp_macro_find(ftepp, ftepp_tokval(ftepp));
695 if (!ftepp_skipspace(ftepp))
697 if (ftepp->token != ')') {
698 ftepp_error(ftepp, "expected closing paren");
704 macro = ftepp_macro_find(ftepp, ftepp_tokval(ftepp));
705 if (!macro || !vec_size(macro->output)) {
708 /* This does not expand recursively! */
709 switch (macro->output[0]->token) {
711 *out = !!(macro->output[0]->constval.f);
713 case TOKEN_FLOATCONST:
714 *out = !!(macro->output[0]->constval.f);
722 case TOKEN_STRINGCONST:
726 *out = !!(ftepp->lex->tok.constval.i);
728 case TOKEN_FLOATCONST:
729 *out = !!(ftepp->lex->tok.constval.f);
734 if (!ftepp_if_expr(ftepp, out))
736 if (ftepp->token != ')') {
737 ftepp_error(ftepp, "expected closing paren in #if expression");
743 ftepp_error(ftepp, "junk in #if");
749 ftepp->lex->flags.noops = false;
751 if (!ftepp_skipspace(ftepp))
753 ftepp->lex->flags.noops = true;
755 if (ftepp->token == ')')
758 if (ftepp->token != TOKEN_OPERATOR)
761 if (!strcmp(ftepp_tokval(ftepp), "&&") ||
762 !strcmp(ftepp_tokval(ftepp), "||"))
765 char opc = ftepp_tokval(ftepp)[0];
768 if (!ftepp_if_expr(ftepp, &next))
778 ftepp_error(ftepp, "junk after #if");
783 static bool ftepp_if(ftepp_t *ftepp, ppcondition *cond)
787 memset(cond, 0, sizeof(*cond));
788 (void)ftepp_next(ftepp);
790 if (!ftepp_skipspace(ftepp))
792 if (ftepp->token == TOKEN_EOL) {
793 ftepp_error(ftepp, "expected expression for #if-directive");
797 if (!ftepp_if_expr(ftepp, &result))
805 * ifdef is rather simple
807 static bool ftepp_ifdef(ftepp_t *ftepp, ppcondition *cond)
810 memset(cond, 0, sizeof(*cond));
811 (void)ftepp_next(ftepp);
812 if (!ftepp_skipspace(ftepp))
815 switch (ftepp->token) {
819 macro = ftepp_macro_find(ftepp, ftepp_tokval(ftepp));
822 ftepp_error(ftepp, "expected macro name");
826 (void)ftepp_next(ftepp);
827 if (!ftepp_skipspace(ftepp))
829 /* relaxing this condition
830 if (ftepp->token != TOKEN_EOL && ftepp->token != TOKEN_EOF) {
831 ftepp_error(ftepp, "stray tokens after #ifdef");
840 * undef is also simple
842 static bool ftepp_undef(ftepp_t *ftepp)
844 (void)ftepp_next(ftepp);
845 if (!ftepp_skipspace(ftepp))
848 if (ftepp->output_on) {
849 switch (ftepp->token) {
853 ftepp_macro_delete(ftepp, ftepp_tokval(ftepp));
856 ftepp_error(ftepp, "expected macro name");
861 (void)ftepp_next(ftepp);
862 if (!ftepp_skipspace(ftepp))
864 /* relaxing this condition
865 if (ftepp->token != TOKEN_EOL && ftepp->token != TOKEN_EOF) {
866 ftepp_error(ftepp, "stray tokens after #ifdef");
873 /* Special unescape-string function which skips a leading quote
874 * and stops at a quote, not just at \0
876 static void unescape(const char *str, char *out) {
878 while (*str && *str != '"') {
882 case '\\': *out++ = *str; break;
883 case '"': *out++ = *str; break;
884 case 'a': *out++ = '\a'; break;
885 case 'b': *out++ = '\b'; break;
886 case 'r': *out++ = '\r'; break;
887 case 'n': *out++ = '\n'; break;
888 case 't': *out++ = '\t'; break;
889 case 'f': *out++ = '\f'; break;
890 case 'v': *out++ = '\v'; break;
905 static char *ftepp_include_find(ftepp_t *ftepp, const char *file)
907 char *filename = NULL;
910 if (ftepp->itemname) {
911 const char *last_slash;
912 last_slash = strrchr(ftepp->itemname, '/');
914 len = last_slash - ftepp->itemname;
915 memcpy(vec_add(filename, len), ftepp->itemname, len);
916 vec_push(filename, '/');
919 len = strlen(ftepp->itemname);
920 memcpy(vec_add(filename, len), ftepp->itemname, len);
921 if (vec_last(filename) != '/')
922 vec_push(filename, '/');
926 memcpy(vec_add(filename, len), file, len);
927 vec_push(filename, 0);
933 * FIXME: do we need/want a -I option?
934 * FIXME: what about when dealing with files in subdirectories coming from a progs.src?
936 static bool ftepp_include(ftepp_t *ftepp)
938 lex_file *old_lexer = ftepp->lex;
944 (void)ftepp_next(ftepp);
945 if (!ftepp_skipspace(ftepp))
948 if (ftepp->token != TOKEN_STRINGCONST) {
949 ftepp_error(ftepp, "expected filename to include");
953 ctx = ftepp_ctx(ftepp);
955 unescape(ftepp_tokval(ftepp), ftepp_tokval(ftepp));
957 ftepp_out(ftepp, "\n#pragma file(", false);
958 ftepp_out(ftepp, ftepp_tokval(ftepp), false);
959 ftepp_out(ftepp, ")\n#pragma line(1)\n", false);
961 filename = ftepp_include_find(ftepp, ftepp_tokval(ftepp));
962 inlex = lex_open(filename);
964 ftepp_error(ftepp, "failed to open include file `%s`", filename);
970 if (!ftepp_preprocess(ftepp)) {
971 lex_close(ftepp->lex);
972 ftepp->lex = old_lexer;
975 lex_close(ftepp->lex);
976 ftepp->lex = old_lexer;
978 ftepp_out(ftepp, "\n#pragma file(", false);
979 ftepp_out(ftepp, ctx.file, false);
980 snprintf(lineno, sizeof(lineno), ")\n#pragma line(%lu)\n", (unsigned long)(ctx.line+1));
981 ftepp_out(ftepp, lineno, false);
984 (void)ftepp_next(ftepp);
985 if (!ftepp_skipspace(ftepp))
987 if (ftepp->token != TOKEN_EOL) {
988 ftepp_error(ftepp, "stray tokens after #include");
991 (void)ftepp_next(ftepp);
996 /* Basic structure handlers */
997 static bool ftepp_else_allowed(ftepp_t *ftepp)
999 if (!vec_size(ftepp->conditions)) {
1000 ftepp_error(ftepp, "#else without #if");
1003 if (vec_last(ftepp->conditions).had_else) {
1004 ftepp_error(ftepp, "multiple #else for a single #if");
1010 static bool ftepp_hash(ftepp_t *ftepp)
1015 lex_ctx ctx = ftepp_ctx(ftepp);
1017 if (!ftepp_skipspace(ftepp))
1020 switch (ftepp->token) {
1023 case TOKEN_TYPENAME:
1024 if (!strcmp(ftepp_tokval(ftepp), "define")) {
1025 return ftepp_define(ftepp);
1027 else if (!strcmp(ftepp_tokval(ftepp), "undef")) {
1028 return ftepp_undef(ftepp);
1030 else if (!strcmp(ftepp_tokval(ftepp), "ifdef")) {
1031 if (!ftepp_ifdef(ftepp, &cond))
1033 cond.was_on = cond.on;
1034 vec_push(ftepp->conditions, cond);
1035 ftepp->output_on = ftepp->output_on && cond.on;
1038 else if (!strcmp(ftepp_tokval(ftepp), "ifndef")) {
1039 if (!ftepp_ifdef(ftepp, &cond))
1042 cond.was_on = cond.on;
1043 vec_push(ftepp->conditions, cond);
1044 ftepp->output_on = ftepp->output_on && cond.on;
1047 else if (!strcmp(ftepp_tokval(ftepp), "elifdef")) {
1048 if (!ftepp_else_allowed(ftepp))
1050 if (!ftepp_ifdef(ftepp, &cond))
1052 pc = &vec_last(ftepp->conditions);
1053 pc->on = !pc->was_on && cond.on;
1054 pc->was_on = pc->was_on || pc->on;
1055 ftepp_update_output_condition(ftepp);
1058 else if (!strcmp(ftepp_tokval(ftepp), "elifndef")) {
1059 if (!ftepp_else_allowed(ftepp))
1061 if (!ftepp_ifdef(ftepp, &cond))
1064 pc = &vec_last(ftepp->conditions);
1065 pc->on = !pc->was_on && cond.on;
1066 pc->was_on = pc->was_on || pc->on;
1067 ftepp_update_output_condition(ftepp);
1070 else if (!strcmp(ftepp_tokval(ftepp), "elif")) {
1071 if (!ftepp_else_allowed(ftepp))
1073 if (!ftepp_if(ftepp, &cond))
1075 pc = &vec_last(ftepp->conditions);
1076 pc->on = !pc->was_on && cond.on;
1077 pc->was_on = pc->was_on || pc->on;
1078 ftepp_update_output_condition(ftepp);
1081 else if (!strcmp(ftepp_tokval(ftepp), "if")) {
1082 if (!ftepp_if(ftepp, &cond))
1084 cond.was_on = cond.on;
1085 vec_push(ftepp->conditions, cond);
1086 ftepp->output_on = ftepp->output_on && cond.on;
1089 else if (!strcmp(ftepp_tokval(ftepp), "else")) {
1090 if (!ftepp_else_allowed(ftepp))
1092 pc = &vec_last(ftepp->conditions);
1093 pc->on = !pc->was_on;
1094 pc->had_else = true;
1096 ftepp_update_output_condition(ftepp);
1099 else if (!strcmp(ftepp_tokval(ftepp), "endif")) {
1100 if (!vec_size(ftepp->conditions)) {
1101 ftepp_error(ftepp, "#endif without #if");
1104 vec_pop(ftepp->conditions);
1106 ftepp_update_output_condition(ftepp);
1109 else if (!strcmp(ftepp_tokval(ftepp), "include")) {
1110 return ftepp_include(ftepp);
1112 else if (!strcmp(ftepp_tokval(ftepp), "pragma")) {
1113 ftepp_out(ftepp, "#", false);
1117 ftepp_error(ftepp, "unrecognized preprocessor directive: `%s`", ftepp_tokval(ftepp));
1120 /* break; never reached */
1122 ftepp_error(ftepp, "unexpected preprocessor token: `%s`", ftepp_tokval(ftepp));
1125 ftepp_errorat(ftepp, ctx, "empty preprocessor directive");
1128 ftepp_error(ftepp, "missing newline at end of file", ftepp_tokval(ftepp));
1131 /* Builtins! Don't forget the builtins! */
1132 case TOKEN_INTCONST:
1133 case TOKEN_FLOATCONST:
1134 ftepp_out(ftepp, "#", false);
1137 if (!ftepp_skipspace(ftepp))
1142 static bool ftepp_preprocess(ftepp_t *ftepp)
1145 bool newline = true;
1147 ftepp->lex->flags.preprocessing = true;
1148 ftepp->lex->flags.mergelines = false;
1149 ftepp->lex->flags.noops = true;
1154 if (ftepp->token >= TOKEN_EOF)
1157 ftepp->newline = newline;
1160 /* For the sake of FTE compatibility... FU, really */
1161 ftepp->newline = newline = true;
1164 switch (ftepp->token) {
1167 case TOKEN_TYPENAME:
1168 macro = ftepp_macro_find(ftepp, ftepp_tokval(ftepp));
1170 ftepp_out(ftepp, ftepp_tokval(ftepp), false);
1174 if (!ftepp_macro_call(ftepp, macro))
1175 ftepp->token = TOKEN_ERROR;
1178 if (!ftepp->newline) {
1179 ftepp_out(ftepp, ftepp_tokval(ftepp), false);
1183 ftepp->lex->flags.mergelines = true;
1184 if (ftepp_next(ftepp) >= TOKEN_EOF) {
1185 ftepp_error(ftepp, "error in preprocessor directive");
1186 ftepp->token = TOKEN_ERROR;
1189 if (!ftepp_hash(ftepp))
1190 ftepp->token = TOKEN_ERROR;
1191 ftepp->lex->flags.mergelines = false;
1195 ftepp_out(ftepp, "\n", true);
1199 ftepp_out(ftepp, ftepp_tokval(ftepp), false);
1203 } while (!ftepp->errors && ftepp->token < TOKEN_EOF);
1205 newline = ftepp->token == TOKEN_EOF;
1209 /* Like in parser.c - files keep the previous state so we have one global
1210 * preprocessor. Except here we will want to warn about dangling #ifs.
1212 static ftepp_t *ftepp;
1214 static bool ftepp_preprocess_done()
1217 lex_close(ftepp->lex);
1219 if (vec_size(ftepp->conditions)) {
1220 if (ftepp_warn(ftepp, WARN_MULTIFILE_IF, "#if spanning multiple files, is this intended?"))
1223 if (ftepp->itemname) {
1224 mem_d(ftepp->itemname);
1225 ftepp->itemname = NULL;
1230 bool ftepp_preprocess_file(const char *filename)
1232 ftepp->lex = lex_open(filename);
1233 ftepp->itemname = util_strdup(filename);
1235 con_out("failed to open file \"%s\"\n", filename);
1238 if (!ftepp_preprocess(ftepp)) {
1239 ftepp_delete(ftepp);
1242 return ftepp_preprocess_done();
1245 bool ftepp_preprocess_string(const char *name, const char *str)
1247 ftepp->lex = lex_open_string(str, strlen(str), name);
1248 ftepp->itemname = util_strdup(name);
1250 con_out("failed to create lexer for string \"%s\"\n", name);
1253 if (!ftepp_preprocess(ftepp)) {
1254 ftepp_delete(ftepp);
1257 return ftepp_preprocess_done();
1262 ftepp = ftepp_new();
1266 const char *ftepp_get()
1268 return ftepp->output_string;
1273 vec_free(ftepp->output_string);
1280 ftepp_delete(ftepp);