6 * Permission is hereby granted, free of charge, to any person obtaining a copy of
7 * this software and associated documentation files (the "Software"), to deal in
8 * the Software without restriction, including without limitation the rights to
9 * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
10 * of the Software, and to permit persons to whom the Software is furnished to do
11 * so, subject to the following conditions:
13 * The above copyright notice and this permission notice shall be included in all
14 * copies or substantial portions of the Software.
16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
36 /* a copy from the lexer */
50 /* yes we need an extra flag since `#define FOO x` is not the same as `#define FOO() x` */
62 ppcondition *conditions;
71 #define ftepp_tokval(f) ((f)->lex->tok.value)
72 #define ftepp_ctx(f) ((f)->lex->tok.ctx)
74 static void ftepp_errorat(ftepp_t *ftepp, lex_ctx ctx, const char *fmt, ...)
81 con_cvprintmsg((void*)&ctx, LVL_ERROR, "error", fmt, ap);
85 static void ftepp_error(ftepp_t *ftepp, const char *fmt, ...)
92 con_cvprintmsg((void*)&ftepp->lex->tok.ctx, LVL_ERROR, "error", fmt, ap);
96 static bool GMQCC_WARN ftepp_warn(ftepp_t *ftepp, int warntype, const char *fmt, ...)
102 r = vcompile_warning(ftepp->lex->tok.ctx, warntype, fmt, ap);
107 static pptoken *pptoken_make(ftepp_t *ftepp)
109 pptoken *token = (pptoken*)mem_a(sizeof(pptoken));
110 token->token = ftepp->token;
112 if (token->token == TOKEN_WHITE)
113 token->value = util_strdup(" ");
116 token->value = util_strdup(ftepp_tokval(ftepp));
118 memcpy(&token->constval, &ftepp->lex->tok.constval, sizeof(token->constval));
122 static void pptoken_delete(pptoken *self)
128 static ppmacro *ppmacro_new(lex_ctx ctx, const char *name)
130 ppmacro *macro = (ppmacro*)mem_a(sizeof(ppmacro));
133 memset(macro, 0, sizeof(*macro));
134 macro->name = util_strdup(name);
138 static void ppmacro_delete(ppmacro *self)
141 for (i = 0; i < vec_size(self->params); ++i)
142 mem_d(self->params[i]);
143 vec_free(self->params);
144 for (i = 0; i < vec_size(self->output); ++i)
145 pptoken_delete(self->output[i]);
146 vec_free(self->output);
151 static ftepp_t* ftepp_new()
155 ftepp = (ftepp_t*)mem_a(sizeof(*ftepp));
156 memset(ftepp, 0, sizeof(*ftepp));
158 ftepp->output_on = true;
163 static void ftepp_delete(ftepp_t *self)
167 mem_d(self->itemname);
168 if (self->includename)
169 vec_free(self->includename);
170 for (i = 0; i < vec_size(self->macros); ++i)
171 ppmacro_delete(self->macros[i]);
172 vec_free(self->macros);
173 vec_free(self->conditions);
175 lex_close(self->lex);
179 static void ftepp_out(ftepp_t *ftepp, const char *str, bool ignore_cond)
181 if (ignore_cond || ftepp->output_on)
186 data = vec_add(ftepp->output_string, len);
187 memcpy(data, str, len);
191 static void ftepp_update_output_condition(ftepp_t *ftepp)
194 ftepp->output_on = true;
195 for (i = 0; i < vec_size(ftepp->conditions); ++i)
196 ftepp->output_on = ftepp->output_on && ftepp->conditions[i].on;
199 static ppmacro* ftepp_macro_find(ftepp_t *ftepp, const char *name)
202 for (i = 0; i < vec_size(ftepp->macros); ++i) {
203 if (!strcmp(name, ftepp->macros[i]->name))
204 return ftepp->macros[i];
209 static void ftepp_macro_delete(ftepp_t *ftepp, const char *name)
212 for (i = 0; i < vec_size(ftepp->macros); ++i) {
213 if (!strcmp(name, ftepp->macros[i]->name)) {
214 vec_remove(ftepp->macros, i, 1);
220 static inline int ftepp_next(ftepp_t *ftepp)
222 return (ftepp->token = lex_do(ftepp->lex));
225 /* Important: this does not skip newlines! */
226 static bool ftepp_skipspace(ftepp_t *ftepp)
228 if (ftepp->token != TOKEN_WHITE)
230 while (ftepp_next(ftepp) == TOKEN_WHITE) {}
231 if (ftepp->token >= TOKEN_EOF) {
232 ftepp_error(ftepp, "unexpected end of preprocessor directive");
238 /* this one skips EOLs as well */
239 static bool ftepp_skipallwhite(ftepp_t *ftepp)
241 if (ftepp->token != TOKEN_WHITE && ftepp->token != TOKEN_EOL)
245 } while (ftepp->token == TOKEN_WHITE || ftepp->token == TOKEN_EOL);
246 if (ftepp->token >= TOKEN_EOF) {
247 ftepp_error(ftepp, "unexpected end of preprocessor directive");
254 * The huge macro parsing code...
256 static bool ftepp_define_params(ftepp_t *ftepp, ppmacro *macro)
260 if (!ftepp_skipspace(ftepp))
262 if (ftepp->token == ')')
264 switch (ftepp->token) {
270 ftepp_error(ftepp, "unexpected token in parameter list");
273 vec_push(macro->params, util_strdup(ftepp_tokval(ftepp)));
275 if (!ftepp_skipspace(ftepp))
277 } while (ftepp->token == ',');
278 if (ftepp->token != ')') {
279 ftepp_error(ftepp, "expected closing paren after macro parameter list");
283 /* skipspace happens in ftepp_define */
287 static bool ftepp_define_body(ftepp_t *ftepp, ppmacro *macro)
290 while (ftepp->token != TOKEN_EOL && ftepp->token < TOKEN_EOF) {
291 ptok = pptoken_make(ftepp);
292 vec_push(macro->output, ptok);
295 /* recursive expansion can cause EOFs here */
296 if (ftepp->token != TOKEN_EOL && ftepp->token != TOKEN_EOF) {
297 ftepp_error(ftepp, "unexpected junk after macro or unexpected end of file");
303 static bool ftepp_define(ftepp_t *ftepp)
306 size_t l = ftepp_ctx(ftepp).line;
308 (void)ftepp_next(ftepp);
309 if (!ftepp_skipspace(ftepp))
312 switch (ftepp->token) {
316 macro = ftepp_macro_find(ftepp, ftepp_tokval(ftepp));
317 if (macro && ftepp->output_on) {
318 if (ftepp_warn(ftepp, WARN_PREPROCESSOR, "redefining `%s`", ftepp_tokval(ftepp)))
320 ftepp_macro_delete(ftepp, ftepp_tokval(ftepp));
322 macro = ppmacro_new(ftepp_ctx(ftepp), ftepp_tokval(ftepp));
325 ftepp_error(ftepp, "expected macro name");
329 (void)ftepp_next(ftepp);
331 if (ftepp->token == '(') {
332 macro->has_params = true;
333 if (!ftepp_define_params(ftepp, macro))
337 if (!ftepp_skipspace(ftepp))
340 if (!ftepp_define_body(ftepp, macro))
343 if (ftepp->output_on)
344 vec_push(ftepp->macros, macro);
346 ppmacro_delete(macro);
349 for (; l < ftepp_ctx(ftepp).line; ++l)
350 ftepp_out(ftepp, "\n", true);
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, double *value_out);
656 static bool ftepp_if_op(ftepp_t *ftepp)
658 ftepp->lex->flags.noops = false;
660 if (!ftepp_skipspace(ftepp))
662 ftepp->lex->flags.noops = true;
665 static bool ftepp_if_value(ftepp_t *ftepp, bool *out, double *value_out)
670 if (!ftepp_skipspace(ftepp))
673 while (ftepp->token == '!') {
676 if (!ftepp_skipspace(ftepp))
680 switch (ftepp->token) {
684 if (!strcmp(ftepp_tokval(ftepp), "defined")) {
686 if (!ftepp_skipspace(ftepp))
688 if (ftepp->token != '(') {
689 ftepp_error(ftepp, "`defined` keyword in #if requires a macro name in parenthesis");
693 if (!ftepp_skipspace(ftepp))
695 if (ftepp->token != TOKEN_IDENT &&
696 ftepp->token != TOKEN_TYPENAME &&
697 ftepp->token != TOKEN_KEYWORD)
699 ftepp_error(ftepp, "defined() used on an unexpected token type");
702 macro = ftepp_macro_find(ftepp, ftepp_tokval(ftepp));
705 if (!ftepp_skipspace(ftepp))
707 if (ftepp->token != ')') {
708 ftepp_error(ftepp, "expected closing paren");
714 macro = ftepp_macro_find(ftepp, ftepp_tokval(ftepp));
715 if (!macro || !vec_size(macro->output)) {
719 /* This does not expand recursively! */
720 switch (macro->output[0]->token) {
722 *value_out = macro->output[0]->constval.i;
723 *out = !!(macro->output[0]->constval.i);
725 case TOKEN_FLOATCONST:
726 *value_out = macro->output[0]->constval.f;
727 *out = !!(macro->output[0]->constval.f);
735 case TOKEN_STRINGCONST:
739 *value_out = ftepp->lex->tok.constval.i;
740 *out = !!(ftepp->lex->tok.constval.i);
742 case TOKEN_FLOATCONST:
743 *value_out = ftepp->lex->tok.constval.f;
744 *out = !!(ftepp->lex->tok.constval.f);
749 if (!ftepp_if_expr(ftepp, out, value_out))
751 if (ftepp->token != ')') {
752 ftepp_error(ftepp, "expected closing paren in #if expression");
758 ftepp_error(ftepp, "junk in #if: `%s` ...", ftepp_tokval(ftepp));
763 *value_out = (*out ? 1 : 0);
769 static bool ftepp_if_nextvalue(ftepp_t *ftepp, bool *out, double *value_out)
771 if (!ftepp_next(ftepp))
773 return ftepp_if_value(ftepp, out, value_out);
777 static bool ftepp_if_expr(ftepp_t *ftepp, bool *out, double *value_out)
779 if (!ftepp_if_value(ftepp, out, value_out))
782 if (!ftepp_if_op(ftepp))
785 if (ftepp->token == ')' || ftepp->token != TOKEN_OPERATOR)
788 /* FTEQCC is all right-associative and no precedence here */
789 if (!strcmp(ftepp_tokval(ftepp), "&&") ||
790 !strcmp(ftepp_tokval(ftepp), "||"))
793 char opc = ftepp_tokval(ftepp)[0];
797 if (!ftepp_next(ftepp))
799 if (!ftepp_if_expr(ftepp, &next, &nextvalue))
807 *value_out = (*out ? 1 : 0);
810 else if (!strcmp(ftepp_tokval(ftepp), "==") ||
811 !strcmp(ftepp_tokval(ftepp), "!=") ||
812 !strcmp(ftepp_tokval(ftepp), ">=") ||
813 !strcmp(ftepp_tokval(ftepp), "<=") ||
814 !strcmp(ftepp_tokval(ftepp), ">") ||
815 !strcmp(ftepp_tokval(ftepp), "<"))
818 const char opc0 = ftepp_tokval(ftepp)[0];
819 const char opc1 = ftepp_tokval(ftepp)[1];
822 if (!ftepp_next(ftepp))
824 if (!ftepp_if_expr(ftepp, &next, &other))
828 *out = (*value_out == other);
829 else if (opc0 == '!')
830 *out = (*value_out != other);
831 else if (opc0 == '>') {
832 if (opc1 == '=') *out = (*value_out >= other);
833 else *out = (*value_out > other);
835 else if (opc0 == '<') {
836 if (opc1 == '=') *out = (*value_out <= other);
837 else *out = (*value_out < other);
839 *value_out = (*out ? 1 : 0);
844 ftepp_error(ftepp, "junk after #if");
849 static bool ftepp_if(ftepp_t *ftepp, ppcondition *cond)
854 memset(cond, 0, sizeof(*cond));
855 (void)ftepp_next(ftepp);
857 if (!ftepp_skipspace(ftepp))
859 if (ftepp->token == TOKEN_EOL) {
860 ftepp_error(ftepp, "expected expression for #if-directive");
864 if (!ftepp_if_expr(ftepp, &result, &dummy))
872 * ifdef is rather simple
874 static bool ftepp_ifdef(ftepp_t *ftepp, ppcondition *cond)
877 memset(cond, 0, sizeof(*cond));
878 (void)ftepp_next(ftepp);
879 if (!ftepp_skipspace(ftepp))
882 switch (ftepp->token) {
886 macro = ftepp_macro_find(ftepp, ftepp_tokval(ftepp));
889 ftepp_error(ftepp, "expected macro name");
893 (void)ftepp_next(ftepp);
894 if (!ftepp_skipspace(ftepp))
896 /* relaxing this condition
897 if (ftepp->token != TOKEN_EOL && ftepp->token != TOKEN_EOF) {
898 ftepp_error(ftepp, "stray tokens after #ifdef");
907 * undef is also simple
909 static bool ftepp_undef(ftepp_t *ftepp)
911 (void)ftepp_next(ftepp);
912 if (!ftepp_skipspace(ftepp))
915 if (ftepp->output_on) {
916 switch (ftepp->token) {
920 ftepp_macro_delete(ftepp, ftepp_tokval(ftepp));
923 ftepp_error(ftepp, "expected macro name");
928 (void)ftepp_next(ftepp);
929 if (!ftepp_skipspace(ftepp))
931 /* relaxing this condition
932 if (ftepp->token != TOKEN_EOL && ftepp->token != TOKEN_EOF) {
933 ftepp_error(ftepp, "stray tokens after #ifdef");
940 /* Special unescape-string function which skips a leading quote
941 * and stops at a quote, not just at \0
943 static void unescape(const char *str, char *out) {
945 while (*str && *str != '"') {
949 case '\\': *out++ = *str; break;
950 case '"': *out++ = *str; break;
951 case 'a': *out++ = '\a'; break;
952 case 'b': *out++ = '\b'; break;
953 case 'r': *out++ = '\r'; break;
954 case 'n': *out++ = '\n'; break;
955 case 't': *out++ = '\t'; break;
956 case 'f': *out++ = '\f'; break;
957 case 'v': *out++ = '\v'; break;
972 static char *ftepp_include_find_path(const char *file, const char *pathfile)
975 char *filename = NULL;
976 const char *last_slash;
982 last_slash = strrchr(pathfile, '/');
985 len = last_slash - pathfile;
986 memcpy(vec_add(filename, len), pathfile, len);
987 vec_push(filename, '/');
991 memcpy(vec_add(filename, len+1), file, len);
992 vec_last(filename) = 0;
994 fp = util_fopen(filename, "rb");
1003 static char *ftepp_include_find(ftepp_t *ftepp, const char *file)
1005 char *filename = NULL;
1007 filename = ftepp_include_find_path(file, ftepp->includename);
1009 filename = ftepp_include_find_path(file, ftepp->itemname);
1015 * FIXME: do we need/want a -I option?
1016 * FIXME: what about when dealing with files in subdirectories coming from a progs.src?
1018 static bool ftepp_include(ftepp_t *ftepp)
1020 lex_file *old_lexer = ftepp->lex;
1025 char *old_includename;
1027 (void)ftepp_next(ftepp);
1028 if (!ftepp_skipspace(ftepp))
1031 if (ftepp->token != TOKEN_STRINGCONST) {
1032 ftepp_error(ftepp, "expected filename to include");
1036 ctx = ftepp_ctx(ftepp);
1038 unescape(ftepp_tokval(ftepp), ftepp_tokval(ftepp));
1040 ftepp_out(ftepp, "\n#pragma file(", false);
1041 ftepp_out(ftepp, ftepp_tokval(ftepp), false);
1042 ftepp_out(ftepp, ")\n#pragma line(1)\n", false);
1044 filename = ftepp_include_find(ftepp, ftepp_tokval(ftepp));
1046 ftepp_error(ftepp, "failed to open include file `%s`", ftepp_tokval(ftepp));
1049 inlex = lex_open(filename);
1051 ftepp_error(ftepp, "open failed on include file `%s`", filename);
1056 old_includename = ftepp->includename;
1057 ftepp->includename = filename;
1058 if (!ftepp_preprocess(ftepp)) {
1059 vec_free(ftepp->includename);
1060 ftepp->includename = old_includename;
1061 lex_close(ftepp->lex);
1062 ftepp->lex = old_lexer;
1065 vec_free(ftepp->includename);
1066 ftepp->includename = old_includename;
1067 lex_close(ftepp->lex);
1068 ftepp->lex = old_lexer;
1070 ftepp_out(ftepp, "\n#pragma file(", false);
1071 ftepp_out(ftepp, ctx.file, false);
1072 snprintf(lineno, sizeof(lineno), ")\n#pragma line(%lu)\n", (unsigned long)(ctx.line+1));
1073 ftepp_out(ftepp, lineno, false);
1076 (void)ftepp_next(ftepp);
1077 if (!ftepp_skipspace(ftepp))
1079 if (ftepp->token != TOKEN_EOL) {
1080 ftepp_error(ftepp, "stray tokens after #include");
1083 (void)ftepp_next(ftepp);
1088 /* Basic structure handlers */
1089 static bool ftepp_else_allowed(ftepp_t *ftepp)
1091 if (!vec_size(ftepp->conditions)) {
1092 ftepp_error(ftepp, "#else without #if");
1095 if (vec_last(ftepp->conditions).had_else) {
1096 ftepp_error(ftepp, "multiple #else for a single #if");
1102 static bool ftepp_hash(ftepp_t *ftepp)
1107 lex_ctx ctx = ftepp_ctx(ftepp);
1109 if (!ftepp_skipspace(ftepp))
1112 switch (ftepp->token) {
1115 case TOKEN_TYPENAME:
1116 if (!strcmp(ftepp_tokval(ftepp), "define")) {
1117 return ftepp_define(ftepp);
1119 else if (!strcmp(ftepp_tokval(ftepp), "undef")) {
1120 return ftepp_undef(ftepp);
1122 else if (!strcmp(ftepp_tokval(ftepp), "ifdef")) {
1123 if (!ftepp_ifdef(ftepp, &cond))
1125 cond.was_on = cond.on;
1126 vec_push(ftepp->conditions, cond);
1127 ftepp->output_on = ftepp->output_on && cond.on;
1130 else if (!strcmp(ftepp_tokval(ftepp), "ifndef")) {
1131 if (!ftepp_ifdef(ftepp, &cond))
1134 cond.was_on = cond.on;
1135 vec_push(ftepp->conditions, cond);
1136 ftepp->output_on = ftepp->output_on && cond.on;
1139 else if (!strcmp(ftepp_tokval(ftepp), "elifdef")) {
1140 if (!ftepp_else_allowed(ftepp))
1142 if (!ftepp_ifdef(ftepp, &cond))
1144 pc = &vec_last(ftepp->conditions);
1145 pc->on = !pc->was_on && cond.on;
1146 pc->was_on = pc->was_on || pc->on;
1147 ftepp_update_output_condition(ftepp);
1150 else if (!strcmp(ftepp_tokval(ftepp), "elifndef")) {
1151 if (!ftepp_else_allowed(ftepp))
1153 if (!ftepp_ifdef(ftepp, &cond))
1156 pc = &vec_last(ftepp->conditions);
1157 pc->on = !pc->was_on && cond.on;
1158 pc->was_on = pc->was_on || pc->on;
1159 ftepp_update_output_condition(ftepp);
1162 else if (!strcmp(ftepp_tokval(ftepp), "elif")) {
1163 if (!ftepp_else_allowed(ftepp))
1165 if (!ftepp_if(ftepp, &cond))
1167 pc = &vec_last(ftepp->conditions);
1168 pc->on = !pc->was_on && cond.on;
1169 pc->was_on = pc->was_on || pc->on;
1170 ftepp_update_output_condition(ftepp);
1173 else if (!strcmp(ftepp_tokval(ftepp), "if")) {
1174 if (!ftepp_if(ftepp, &cond))
1176 cond.was_on = cond.on;
1177 vec_push(ftepp->conditions, cond);
1178 ftepp->output_on = ftepp->output_on && cond.on;
1181 else if (!strcmp(ftepp_tokval(ftepp), "else")) {
1182 if (!ftepp_else_allowed(ftepp))
1184 pc = &vec_last(ftepp->conditions);
1185 pc->on = !pc->was_on;
1186 pc->had_else = true;
1188 ftepp_update_output_condition(ftepp);
1191 else if (!strcmp(ftepp_tokval(ftepp), "endif")) {
1192 if (!vec_size(ftepp->conditions)) {
1193 ftepp_error(ftepp, "#endif without #if");
1196 vec_pop(ftepp->conditions);
1198 ftepp_update_output_condition(ftepp);
1201 else if (!strcmp(ftepp_tokval(ftepp), "include")) {
1202 return ftepp_include(ftepp);
1204 else if (!strcmp(ftepp_tokval(ftepp), "pragma")) {
1205 ftepp_out(ftepp, "#", false);
1209 if (ftepp->output_on) {
1210 ftepp_error(ftepp, "unrecognized preprocessor directive: `%s`", ftepp_tokval(ftepp));
1217 /* break; never reached */
1219 ftepp_error(ftepp, "unexpected preprocessor token: `%s`", ftepp_tokval(ftepp));
1222 ftepp_errorat(ftepp, ctx, "empty preprocessor directive");
1225 ftepp_error(ftepp, "missing newline at end of file", ftepp_tokval(ftepp));
1228 /* Builtins! Don't forget the builtins! */
1229 case TOKEN_INTCONST:
1230 case TOKEN_FLOATCONST:
1231 ftepp_out(ftepp, "#", false);
1234 if (!ftepp_skipspace(ftepp))
1239 static bool ftepp_preprocess(ftepp_t *ftepp)
1242 bool newline = true;
1244 ftepp->lex->flags.preprocessing = true;
1245 ftepp->lex->flags.mergelines = false;
1246 ftepp->lex->flags.noops = true;
1251 if (ftepp->token >= TOKEN_EOF)
1257 switch (ftepp->token) {
1260 case TOKEN_TYPENAME:
1261 if (ftepp->output_on)
1262 macro = ftepp_macro_find(ftepp, ftepp_tokval(ftepp));
1266 ftepp_out(ftepp, ftepp_tokval(ftepp), false);
1270 if (!ftepp_macro_call(ftepp, macro))
1271 ftepp->token = TOKEN_ERROR;
1275 ftepp_out(ftepp, ftepp_tokval(ftepp), false);
1279 ftepp->lex->flags.mergelines = true;
1280 if (ftepp_next(ftepp) >= TOKEN_EOF) {
1281 ftepp_error(ftepp, "error in preprocessor directive");
1282 ftepp->token = TOKEN_ERROR;
1285 if (!ftepp_hash(ftepp))
1286 ftepp->token = TOKEN_ERROR;
1287 ftepp->lex->flags.mergelines = false;
1291 ftepp_out(ftepp, "\n", true);
1295 /* same as default but don't set newline=false */
1296 ftepp_out(ftepp, ftepp_tokval(ftepp), false);
1301 ftepp_out(ftepp, ftepp_tokval(ftepp), false);
1305 } while (!ftepp->errors && ftepp->token < TOKEN_EOF);
1307 /* force a 0 at the end but don't count it as added to the output */
1308 vec_push(ftepp->output_string, 0);
1309 vec_shrinkby(ftepp->output_string, 1);
1311 return (ftepp->token == TOKEN_EOF);
1314 /* Like in parser.c - files keep the previous state so we have one global
1315 * preprocessor. Except here we will want to warn about dangling #ifs.
1317 static ftepp_t *ftepp;
1319 static bool ftepp_preprocess_done()
1322 if (vec_size(ftepp->conditions)) {
1323 if (ftepp_warn(ftepp, WARN_MULTIFILE_IF, "#if spanning multiple files, is this intended?"))
1326 lex_close(ftepp->lex);
1328 if (ftepp->itemname) {
1329 mem_d(ftepp->itemname);
1330 ftepp->itemname = NULL;
1335 bool ftepp_preprocess_file(const char *filename)
1337 ftepp->lex = lex_open(filename);
1338 ftepp->itemname = util_strdup(filename);
1340 con_out("failed to open file \"%s\"\n", filename);
1343 if (!ftepp_preprocess(ftepp))
1345 return ftepp_preprocess_done();
1348 bool ftepp_preprocess_string(const char *name, const char *str)
1350 ftepp->lex = lex_open_string(str, strlen(str), name);
1351 ftepp->itemname = util_strdup(name);
1353 con_out("failed to create lexer for string \"%s\"\n", name);
1356 if (!ftepp_preprocess(ftepp))
1358 return ftepp_preprocess_done();
1362 void ftepp_add_macro(const char *name, const char *value) {
1363 char *create = NULL;
1365 /* use saner path for empty macros */
1367 ftepp_add_define("__builtin__", name);
1371 vec_upload(create, "#define ", 8);
1372 vec_upload(create, name, strlen(name));
1373 vec_push (create, ' ');
1374 vec_upload(create, value, strlen(value));
1375 vec_push (create, 0);
1377 ftepp_preprocess_string("__builtin__", create);
1386 ftepp = ftepp_new();
1390 memset(minor, 0, sizeof(minor));
1391 memset(major, 0, sizeof(major));
1393 /* set the right macro based on the selected standard */
1394 ftepp_add_define(NULL, "GMQCC");
1395 if (opts.standard == COMPILER_FTEQCC) {
1396 ftepp_add_define(NULL, "__STD_FTEQCC__");
1405 } else if (opts.standard == COMPILER_GMQCC) {
1406 ftepp_add_define(NULL, "__STD_GMQCC__");
1407 sprintf(major, "\"%d\"", GMQCC_VERSION_MAJOR);
1408 sprintf(minor, "\"%d\"", GMQCC_VERSION_MINOR);
1409 } else if (opts.standard == COMPILER_QCC) {
1410 ftepp_add_define(NULL, "__STD_QCC__");
1421 ftepp_add_macro("__STD_VERSION_MINOR__", minor);
1422 ftepp_add_macro("__STD_VERSION_MAJOR__", major);
1427 void ftepp_add_define(const char *source, const char *name)
1430 lex_ctx ctx = { "__builtin__", 0 };
1432 macro = ppmacro_new(ctx, name);
1433 vec_push(ftepp->macros, macro);
1436 const char *ftepp_get()
1438 return ftepp->output_string;
1443 vec_free(ftepp->output_string);
1450 ftepp_delete(ftepp);