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 GMQCC_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);
1013 static void ftepp_directive_warning(ftepp_t *ftepp) {
1014 char *message = NULL;
1016 if (!ftepp_skipspace(ftepp))
1019 /* handle the odd non string constant case so it works like C */
1020 if (ftepp->token != TOKEN_STRINGCONST) {
1021 vec_upload(message, "#warning", 8);
1023 while (ftepp->token != TOKEN_EOL) {
1024 vec_upload(message, ftepp_tokval(ftepp), strlen(ftepp_tokval(ftepp)));
1027 vec_push(message, '\0');
1028 (void)!!ftepp_warn(ftepp, WARN_CPP, message);
1033 unescape (ftepp_tokval(ftepp), ftepp_tokval(ftepp));
1034 (void)!!ftepp_warn(ftepp, WARN_CPP, "#warning %s", ftepp_tokval(ftepp));
1037 static void ftepp_directive_error(ftepp_t *ftepp) {
1038 char *message = NULL;
1040 if (!ftepp_skipspace(ftepp))
1043 /* handle the odd non string constant case so it works like C */
1044 if (ftepp->token != TOKEN_STRINGCONST) {
1045 vec_upload(message, "#error", 6);
1047 while (ftepp->token != TOKEN_EOL) {
1048 vec_upload(message, ftepp_tokval(ftepp), strlen(ftepp_tokval(ftepp)));
1051 vec_push(message, '\0');
1052 ftepp_error(ftepp, message);
1057 unescape (ftepp_tokval(ftepp), ftepp_tokval(ftepp));
1058 ftepp_error(ftepp, "#error %s", ftepp_tokval(ftepp));
1063 * FIXME: do we need/want a -I option?
1064 * FIXME: what about when dealing with files in subdirectories coming from a progs.src?
1066 static bool ftepp_include(ftepp_t *ftepp)
1068 lex_file *old_lexer = ftepp->lex;
1073 char *old_includename;
1075 (void)ftepp_next(ftepp);
1076 if (!ftepp_skipspace(ftepp))
1079 if (ftepp->token != TOKEN_STRINGCONST) {
1080 ftepp_error(ftepp, "expected filename to include");
1084 ctx = ftepp_ctx(ftepp);
1086 unescape(ftepp_tokval(ftepp), ftepp_tokval(ftepp));
1088 ftepp_out(ftepp, "\n#pragma file(", false);
1089 ftepp_out(ftepp, ftepp_tokval(ftepp), false);
1090 ftepp_out(ftepp, ")\n#pragma line(1)\n", false);
1092 filename = ftepp_include_find(ftepp, ftepp_tokval(ftepp));
1094 ftepp_error(ftepp, "failed to open include file `%s`", ftepp_tokval(ftepp));
1097 inlex = lex_open(filename);
1099 ftepp_error(ftepp, "open failed on include file `%s`", filename);
1104 old_includename = ftepp->includename;
1105 ftepp->includename = filename;
1106 if (!ftepp_preprocess(ftepp)) {
1107 vec_free(ftepp->includename);
1108 ftepp->includename = old_includename;
1109 lex_close(ftepp->lex);
1110 ftepp->lex = old_lexer;
1113 vec_free(ftepp->includename);
1114 ftepp->includename = old_includename;
1115 lex_close(ftepp->lex);
1116 ftepp->lex = old_lexer;
1118 ftepp_out(ftepp, "\n#pragma file(", false);
1119 ftepp_out(ftepp, ctx.file, false);
1120 snprintf(lineno, sizeof(lineno), ")\n#pragma line(%lu)\n", (unsigned long)(ctx.line+1));
1121 ftepp_out(ftepp, lineno, false);
1124 (void)ftepp_next(ftepp);
1125 if (!ftepp_skipspace(ftepp))
1127 if (ftepp->token != TOKEN_EOL) {
1128 ftepp_error(ftepp, "stray tokens after #include");
1131 (void)ftepp_next(ftepp);
1136 /* Basic structure handlers */
1137 static bool ftepp_else_allowed(ftepp_t *ftepp)
1139 if (!vec_size(ftepp->conditions)) {
1140 ftepp_error(ftepp, "#else without #if");
1143 if (vec_last(ftepp->conditions).had_else) {
1144 ftepp_error(ftepp, "multiple #else for a single #if");
1150 static bool ftepp_hash(ftepp_t *ftepp)
1155 lex_ctx ctx = ftepp_ctx(ftepp);
1157 if (!ftepp_skipspace(ftepp))
1160 switch (ftepp->token) {
1163 case TOKEN_TYPENAME:
1164 if (!strcmp(ftepp_tokval(ftepp), "define")) {
1165 return ftepp_define(ftepp);
1167 else if (!strcmp(ftepp_tokval(ftepp), "undef")) {
1168 return ftepp_undef(ftepp);
1170 else if (!strcmp(ftepp_tokval(ftepp), "ifdef")) {
1171 if (!ftepp_ifdef(ftepp, &cond))
1173 cond.was_on = cond.on;
1174 vec_push(ftepp->conditions, cond);
1175 ftepp->output_on = ftepp->output_on && cond.on;
1178 else if (!strcmp(ftepp_tokval(ftepp), "ifndef")) {
1179 if (!ftepp_ifdef(ftepp, &cond))
1182 cond.was_on = cond.on;
1183 vec_push(ftepp->conditions, cond);
1184 ftepp->output_on = ftepp->output_on && cond.on;
1187 else if (!strcmp(ftepp_tokval(ftepp), "elifdef")) {
1188 if (!ftepp_else_allowed(ftepp))
1190 if (!ftepp_ifdef(ftepp, &cond))
1192 pc = &vec_last(ftepp->conditions);
1193 pc->on = !pc->was_on && cond.on;
1194 pc->was_on = pc->was_on || pc->on;
1195 ftepp_update_output_condition(ftepp);
1198 else if (!strcmp(ftepp_tokval(ftepp), "elifndef")) {
1199 if (!ftepp_else_allowed(ftepp))
1201 if (!ftepp_ifdef(ftepp, &cond))
1204 pc = &vec_last(ftepp->conditions);
1205 pc->on = !pc->was_on && cond.on;
1206 pc->was_on = pc->was_on || pc->on;
1207 ftepp_update_output_condition(ftepp);
1210 else if (!strcmp(ftepp_tokval(ftepp), "elif")) {
1211 if (!ftepp_else_allowed(ftepp))
1213 if (!ftepp_if(ftepp, &cond))
1215 pc = &vec_last(ftepp->conditions);
1216 pc->on = !pc->was_on && cond.on;
1217 pc->was_on = pc->was_on || pc->on;
1218 ftepp_update_output_condition(ftepp);
1221 else if (!strcmp(ftepp_tokval(ftepp), "if")) {
1222 if (!ftepp_if(ftepp, &cond))
1224 cond.was_on = cond.on;
1225 vec_push(ftepp->conditions, cond);
1226 ftepp->output_on = ftepp->output_on && cond.on;
1229 else if (!strcmp(ftepp_tokval(ftepp), "else")) {
1230 if (!ftepp_else_allowed(ftepp))
1232 pc = &vec_last(ftepp->conditions);
1233 pc->on = !pc->was_on;
1234 pc->had_else = true;
1236 ftepp_update_output_condition(ftepp);
1239 else if (!strcmp(ftepp_tokval(ftepp), "endif")) {
1240 if (!vec_size(ftepp->conditions)) {
1241 ftepp_error(ftepp, "#endif without #if");
1244 vec_pop(ftepp->conditions);
1246 ftepp_update_output_condition(ftepp);
1249 else if (!strcmp(ftepp_tokval(ftepp), "include")) {
1250 return ftepp_include(ftepp);
1252 else if (!strcmp(ftepp_tokval(ftepp), "pragma")) {
1253 ftepp_out(ftepp, "#", false);
1256 else if (!strcmp(ftepp_tokval(ftepp), "warning")) {
1257 ftepp_directive_warning(ftepp);
1260 else if (!strcmp(ftepp_tokval(ftepp), "error")) {
1261 ftepp_directive_error(ftepp);
1265 if (ftepp->output_on) {
1266 ftepp_error(ftepp, "unrecognized preprocessor directive: `%s`", ftepp_tokval(ftepp));
1273 /* break; never reached */
1275 ftepp_error(ftepp, "unexpected preprocessor token: `%s`", ftepp_tokval(ftepp));
1278 ftepp_errorat(ftepp, ctx, "empty preprocessor directive");
1281 ftepp_error(ftepp, "missing newline at end of file", ftepp_tokval(ftepp));
1284 /* Builtins! Don't forget the builtins! */
1285 case TOKEN_INTCONST:
1286 case TOKEN_FLOATCONST:
1287 ftepp_out(ftepp, "#", false);
1290 if (!ftepp_skipspace(ftepp))
1295 static bool ftepp_preprocess(ftepp_t *ftepp)
1298 bool newline = true;
1300 ftepp->lex->flags.preprocessing = true;
1301 ftepp->lex->flags.mergelines = false;
1302 ftepp->lex->flags.noops = true;
1307 if (ftepp->token >= TOKEN_EOF)
1313 switch (ftepp->token) {
1316 case TOKEN_TYPENAME:
1317 if (ftepp->output_on)
1318 macro = ftepp_macro_find(ftepp, ftepp_tokval(ftepp));
1322 ftepp_out(ftepp, ftepp_tokval(ftepp), false);
1326 if (!ftepp_macro_call(ftepp, macro))
1327 ftepp->token = TOKEN_ERROR;
1331 ftepp_out(ftepp, ftepp_tokval(ftepp), false);
1335 ftepp->lex->flags.mergelines = true;
1336 if (ftepp_next(ftepp) >= TOKEN_EOF) {
1337 ftepp_error(ftepp, "error in preprocessor directive");
1338 ftepp->token = TOKEN_ERROR;
1341 if (!ftepp_hash(ftepp))
1342 ftepp->token = TOKEN_ERROR;
1343 ftepp->lex->flags.mergelines = false;
1347 ftepp_out(ftepp, "\n", true);
1351 /* same as default but don't set newline=false */
1352 ftepp_out(ftepp, ftepp_tokval(ftepp), false);
1357 ftepp_out(ftepp, ftepp_tokval(ftepp), false);
1361 } while (!ftepp->errors && ftepp->token < TOKEN_EOF);
1363 /* force a 0 at the end but don't count it as added to the output */
1364 vec_push(ftepp->output_string, 0);
1365 vec_shrinkby(ftepp->output_string, 1);
1367 return (ftepp->token == TOKEN_EOF);
1370 /* Like in parser.c - files keep the previous state so we have one global
1371 * preprocessor. Except here we will want to warn about dangling #ifs.
1373 static ftepp_t *ftepp;
1375 static bool ftepp_preprocess_done()
1378 if (vec_size(ftepp->conditions)) {
1379 if (ftepp_warn(ftepp, WARN_MULTIFILE_IF, "#if spanning multiple files, is this intended?"))
1382 lex_close(ftepp->lex);
1384 if (ftepp->itemname) {
1385 mem_d(ftepp->itemname);
1386 ftepp->itemname = NULL;
1391 bool ftepp_preprocess_file(const char *filename)
1393 ftepp->lex = lex_open(filename);
1394 ftepp->itemname = util_strdup(filename);
1396 con_out("failed to open file \"%s\"\n", filename);
1399 if (!ftepp_preprocess(ftepp))
1401 return ftepp_preprocess_done();
1404 bool ftepp_preprocess_string(const char *name, const char *str)
1406 ftepp->lex = lex_open_string(str, strlen(str), name);
1407 ftepp->itemname = util_strdup(name);
1409 con_out("failed to create lexer for string \"%s\"\n", name);
1412 if (!ftepp_preprocess(ftepp))
1414 return ftepp_preprocess_done();
1418 void ftepp_add_macro(const char *name, const char *value) {
1419 char *create = NULL;
1421 /* use saner path for empty macros */
1423 ftepp_add_define("__builtin__", name);
1427 vec_upload(create, "#define ", 8);
1428 vec_upload(create, name, strlen(name));
1429 vec_push (create, ' ');
1430 vec_upload(create, value, strlen(value));
1431 vec_push (create, 0);
1433 ftepp_preprocess_string("__builtin__", create);
1442 ftepp = ftepp_new();
1446 memset(minor, 0, sizeof(minor));
1447 memset(major, 0, sizeof(major));
1449 /* set the right macro based on the selected standard */
1450 ftepp_add_define(NULL, "GMQCC");
1451 if (opts.standard == COMPILER_FTEQCC) {
1452 ftepp_add_define(NULL, "__STD_FTEQCC__");
1461 } else if (opts.standard == COMPILER_GMQCC) {
1462 ftepp_add_define(NULL, "__STD_GMQCC__");
1463 sprintf(major, "\"%d\"", GMQCC_VERSION_MAJOR);
1464 sprintf(minor, "\"%d\"", GMQCC_VERSION_MINOR);
1465 } else if (opts.standard == COMPILER_QCC) {
1466 ftepp_add_define(NULL, "__STD_QCC__");
1477 ftepp_add_macro("__STD_VERSION_MINOR__", minor);
1478 ftepp_add_macro("__STD_VERSION_MAJOR__", major);
1483 void ftepp_add_define(const char *source, const char *name)
1486 lex_ctx ctx = { "__builtin__", 0 };
1488 macro = ppmacro_new(ctx, name);
1489 vec_push(ftepp->macros, macro);
1492 const char *ftepp_get()
1494 return ftepp->output_string;
1499 vec_free(ftepp->output_string);
1506 ftepp_delete(ftepp);