X-Git-Url: https://git.xonotic.org/?a=blobdiff_plain;f=ftepp.c;h=59ec54504402efdfdbf70c737405e5d4f73f07bd;hb=79abe3fb6d9cf6c0d5f234d64fd77181b2c2317d;hp=15702b0665daa8790ecf136f5ac9b5dd668a5c6d;hpb=c48b9dbf61d9afbc480a2fb487d8181cb077792e;p=xonotic%2Fgmqcc.git diff --git a/ftepp.c b/ftepp.c index 15702b0..59ec545 100644 --- a/ftepp.c +++ b/ftepp.c @@ -58,8 +58,12 @@ typedef struct { bool newline; unsigned int errors; + bool output_on; ppcondition *conditions; ppmacro **macros; + + bool output_string; + char *output; } ftepp_t; #define ftepp_tokval(f) ((f)->lex->tok.value) @@ -87,25 +91,47 @@ static void ftepp_error(ftepp_t *ftepp, const char *fmt, ...) va_end(ap); } -pptoken *pptoken_make(ftepp_t *ftepp) +static bool GMQCC_WARN ftepp_warn(ftepp_t *ftepp, int warntype, const char *fmt, ...) +{ + va_list ap; + int lvl = LVL_WARNING; + + if (!OPTS_WARN(warntype)) + return false; + + if (opts_werror) { + lvl = LVL_ERROR; + ftepp->errors++; + } + + va_start(ap, fmt); + con_vprintmsg(lvl, ftepp->lex->tok.ctx.file, ftepp->lex->tok.ctx.line, "error", fmt, ap); + va_end(ap); + return opts_werror; +} + +static pptoken *pptoken_make(ftepp_t *ftepp) { pptoken *token = (pptoken*)mem_a(sizeof(pptoken)); token->token = ftepp->token; +#if 0 if (token->token == TOKEN_WHITE) token->value = util_strdup(" "); else +#else token->value = util_strdup(ftepp_tokval(ftepp)); +#endif memcpy(&token->constval, &ftepp->lex->tok.constval, sizeof(token->constval)); return token; } -void pptoken_delete(pptoken *self) +static void pptoken_delete(pptoken *self) { mem_d(self->value); mem_d(self); } -ppmacro *ppmacro_new(lex_ctx ctx, const char *name) +static ppmacro *ppmacro_new(lex_ctx ctx, const char *name) { ppmacro *macro = (ppmacro*)mem_a(sizeof(ppmacro)); memset(macro, 0, sizeof(*macro)); @@ -113,7 +139,7 @@ ppmacro *ppmacro_new(lex_ctx ctx, const char *name) return macro; } -void ppmacro_delete(ppmacro *self) +static void ppmacro_delete(ppmacro *self) { size_t i; for (i = 0; i < vec_size(self->params); ++i) @@ -126,17 +152,19 @@ void ppmacro_delete(ppmacro *self) mem_d(self); } -ftepp_t* ftepp_init() +static ftepp_t* ftepp_init() { ftepp_t *ftepp; ftepp = (ftepp_t*)mem_a(sizeof(*ftepp)); memset(ftepp, 0, sizeof(*ftepp)); + ftepp->output_on = true; + return ftepp; } -void ftepp_delete(ftepp_t *self) +static void ftepp_delete(ftepp_t *self) { size_t i; for (i = 0; i < vec_size(self->macros); ++i) @@ -147,7 +175,31 @@ void ftepp_delete(ftepp_t *self) mem_d(self); } -ppmacro* ftepp_macro_find(ftepp_t *ftepp, const char *name) +static void ftepp_out(ftepp_t *ftepp, const char *str, bool ignore_cond) +{ + if (ignore_cond || ftepp->output_on) + { + size_t len; + char *data; + if (!ftepp->output_string) { + printf("%s", str); + return; + } + len = strlen(str); + data = vec_add(ftepp->output, len); + memcpy(data, str, len); + } +} + +static void ftepp_update_output_condition(ftepp_t *ftepp) +{ + size_t i; + ftepp->output_on = true; + for (i = 0; i < vec_size(ftepp->conditions); ++i) + ftepp->output_on = ftepp->output_on && ftepp->conditions[i].on; +} + +static ppmacro* ftepp_macro_find(ftepp_t *ftepp, const char *name) { size_t i; for (i = 0; i < vec_size(ftepp->macros); ++i) { @@ -157,6 +209,17 @@ ppmacro* ftepp_macro_find(ftepp_t *ftepp, const char *name) return NULL; } +static void ftepp_macro_delete(ftepp_t *ftepp, const char *name) +{ + size_t i; + for (i = 0; i < vec_size(ftepp->macros); ++i) { + if (!strcmp(name, ftepp->macros[i]->name)) { + vec_remove(ftepp->macros, i, 1); + return; + } + } +} + static inline int ftepp_next(ftepp_t *ftepp) { return (ftepp->token = lex_do(ftepp->lex)); @@ -175,6 +238,21 @@ static bool ftepp_skipspace(ftepp_t *ftepp) return true; } +/* this one skips EOLs as well */ +static bool ftepp_skipallwhite(ftepp_t *ftepp) +{ + if (ftepp->token != TOKEN_WHITE && ftepp->token != TOKEN_EOL) + return true; + do { + ftepp_next(ftepp); + } while (ftepp->token == TOKEN_WHITE || ftepp->token == TOKEN_EOL); + if (ftepp->token >= TOKEN_EOF) { + ftepp_error(ftepp, "unexpected end of preprocessor directive"); + return false; + } + return true; +} + /** * The huge macro parsing code... */ @@ -184,6 +262,8 @@ static bool ftepp_define_params(ftepp_t *ftepp, ppmacro *macro) ftepp_next(ftepp); if (!ftepp_skipspace(ftepp)) return false; + if (ftepp->token == ')') + break; switch (ftepp->token) { case TOKEN_IDENT: case TOKEN_TYPENAME: @@ -213,12 +293,10 @@ static bool ftepp_define_body(ftepp_t *ftepp, ppmacro *macro) while (ftepp->token != TOKEN_EOL && ftepp->token < TOKEN_EOF) { ptok = pptoken_make(ftepp); vec_push(macro->output, ptok); - ftepp_next(ftepp); - if (!ftepp_skipspace(ftepp)) - return false; } - if (ftepp->token != TOKEN_EOL) { + /* recursive expansion can cause EOFs here */ + if (ftepp->token != TOKEN_EOL && ftepp->token != TOKEN_EOF) { ftepp_error(ftepp, "unexpected junk after macro or unexpected end of file"); return false; } @@ -236,6 +314,12 @@ static bool ftepp_define(ftepp_t *ftepp) case TOKEN_IDENT: case TOKEN_TYPENAME: case TOKEN_KEYWORD: + macro = ftepp_macro_find(ftepp, ftepp_tokval(ftepp)); + if (macro && ftepp->output_on) { + if (ftepp_warn(ftepp, WARN_PREPROCESSOR, "redefining `%s`", ftepp_tokval(ftepp))) + return false; + ftepp_macro_delete(ftepp, ftepp_tokval(ftepp)); + } macro = ppmacro_new(ftepp_ctx(ftepp), ftepp_tokval(ftepp)); break; default: @@ -257,10 +341,230 @@ static bool ftepp_define(ftepp_t *ftepp) if (!ftepp_define_body(ftepp, macro)) return false; - vec_push(ftepp->macros, macro); + if (ftepp->output_on) + vec_push(ftepp->macros, macro); + else { + ppmacro_delete(macro); + } return true; } +/** + * When a macro is used we have to handle parameters as well + * as special-concatenation via ## or stringification via # + * + * Note: parenthesis can nest, so FOO((a),b) is valid, but only + * this kind of parens. Curly braces or [] don't count towards the + * paren-level. + */ +typedef struct { + pptoken **tokens; +} macroparam; + +static void macroparam_clean(macroparam *self) +{ + size_t i; + for (i = 0; i < vec_size(self->tokens); ++i) + pptoken_delete(self->tokens[i]); + vec_free(self->tokens); +} + +/* need to leave the last token up */ +static bool ftepp_macro_call_params(ftepp_t *ftepp, macroparam **out_params) +{ + macroparam *params = NULL; + pptoken *ptok; + macroparam mp; + size_t parens = 0; + size_t i; + + if (!ftepp_skipallwhite(ftepp)) + return false; + while (ftepp->token != ')') { + mp.tokens = NULL; + if (!ftepp_skipallwhite(ftepp)) + return false; + while (parens || ftepp->token != ',') { + if (ftepp->token == '(') + ++parens; + else if (ftepp->token == ')') { + if (!parens) + break; + --parens; + } + ptok = pptoken_make(ftepp); + vec_push(mp.tokens, ptok); + if (ftepp_next(ftepp) >= TOKEN_EOF) { + ftepp_error(ftepp, "unexpected EOF in macro call"); + goto on_error; + } + } + vec_push(params, mp); + mp.tokens = NULL; + if (ftepp->token == ')') + break; + if (ftepp->token != ',') { + ftepp_error(ftepp, "expected closing paren or comma in macro call"); + goto on_error; + } + if (ftepp_next(ftepp) >= TOKEN_EOF) { + ftepp_error(ftepp, "unexpected EOF in macro call"); + goto on_error; + } + } + /* need to leave that up + if (ftepp_next(ftepp) >= TOKEN_EOF) { + ftepp_error(ftepp, "unexpected EOF in macro call"); + goto on_error; + } + */ + *out_params = params; + return true; + +on_error: + if (mp.tokens) + macroparam_clean(&mp); + for (i = 0; i < vec_size(params); ++i) + macroparam_clean(¶ms[i]); + vec_free(params); + return false; +} + +static bool macro_params_find(ppmacro *macro, const char *name, size_t *idx) +{ + size_t i; + for (i = 0; i < vec_size(macro->params); ++i) { + if (!strcmp(macro->params[i], name)) { + *idx = i; + return true; + } + } + return false; +} + +static bool ftepp_preprocess(ftepp_t *ftepp); +static bool ftepp_macro_expand(ftepp_t *ftepp, ppmacro *macro, macroparam *params) +{ + char *old_string = ftepp->output; + bool old_string_flag = ftepp->output_string; + lex_file *old_lexer = ftepp->lex; + bool retval = true; + + size_t o, pi, pv; + lex_file *inlex; + + /* really ... */ + if (!vec_size(macro->output)) + return true; + + ftepp->output = NULL; + ftepp->output_string = true; + for (o = 0; o < vec_size(macro->output); ++o) { + pptoken *out = macro->output[o]; + switch (out->token) { + case TOKEN_IDENT: + case TOKEN_TYPENAME: + case TOKEN_KEYWORD: + if (!macro_params_find(macro, out->value, &pi)) { + ftepp_out(ftepp, out->value, false); + break; + } else { + for (pv = 0; pv < vec_size(params[pi].tokens); ++pv) { + out = params[pi].tokens[pv]; + if (out->token == TOKEN_EOL) + ftepp_out(ftepp, "\n", false); + else + ftepp_out(ftepp, out->value, false); + } + } + break; + case '#': + if (o + 1 < vec_size(macro->output) && macro->output[o+1]->token == '#') { + /* raw concatenation */ + ++o; + break; + } + ftepp_out(ftepp, "#", false); + break; + case TOKEN_EOL: + ftepp_out(ftepp, "\n", false); + break; + default: + ftepp_out(ftepp, out->value, false); + break; + } + } + vec_push(ftepp->output, 0); + /* Now run the preprocessor recursively on this string buffer */ + /* + printf("__________\n%s\n=========\n", ftepp->output); + */ + inlex = lex_open_string(ftepp->output, vec_size(ftepp->output)-1, ftepp->lex->name); + if (!inlex) { + ftepp_error(ftepp, "internal error: failed to instantiate lexer"); + retval = false; + goto cleanup; + } + ftepp->output = old_string; + ftepp->output_string = old_string_flag; + ftepp->lex = inlex; + if (!ftepp_preprocess(ftepp)) { + retval = false; + goto cleanup; + } + +cleanup: + ftepp->lex = old_lexer; + ftepp->output = old_string; + ftepp->output_string = old_string_flag; + return retval; +} + +static bool ftepp_macro_call(ftepp_t *ftepp, ppmacro *macro) +{ + size_t o; + macroparam *params = NULL; + bool retval = true; + + if (!macro->has_params) { + if (!ftepp_macro_expand(ftepp, macro, NULL)) + return false; + ftepp_next(ftepp); + return true; + } + ftepp_next(ftepp); + + if (!ftepp_skipallwhite(ftepp)) + return false; + + if (ftepp->token != '(') { + ftepp_error(ftepp, "expected macro parameters in parenthesis"); + return false; + } + + ftepp_next(ftepp); + if (!ftepp_macro_call_params(ftepp, ¶ms)) + return false; + + if (vec_size(params) != vec_size(macro->params)) { + ftepp_error(ftepp, "macro %s expects %u paramteters, %u provided", macro->name, + (unsigned int)vec_size(macro->params), + (unsigned int)vec_size(params)); + retval = false; + goto cleanup; + } + + if (!ftepp_macro_expand(ftepp, macro, params)) + retval = false; + ftepp_next(ftepp); + +cleanup: + for (o = 0; o < vec_size(params); ++o) + macroparam_clean(¶ms[o]); + vec_free(params); + return retval; +} + /** * #if - the FTEQCC way: * defined(FOO) => true if FOO was #defined regardless of parameters or contents @@ -451,14 +755,50 @@ static bool ftepp_ifdef(ftepp_t *ftepp, ppcondition *cond) (void)ftepp_next(ftepp); if (!ftepp_skipspace(ftepp)) return false; - if (ftepp->token != TOKEN_EOL) { + /* relaxing this condition + if (ftepp->token != TOKEN_EOL && ftepp->token != TOKEN_EOF) { ftepp_error(ftepp, "stray tokens after #ifdef"); return false; } + */ cond->on = !!macro; return true; } +/** + * undef is also simple + */ +static bool ftepp_undef(ftepp_t *ftepp) +{ + (void)ftepp_next(ftepp); + if (!ftepp_skipspace(ftepp)) + return false; + + if (ftepp->output_on) { + switch (ftepp->token) { + case TOKEN_IDENT: + case TOKEN_TYPENAME: + case TOKEN_KEYWORD: + ftepp_macro_delete(ftepp, ftepp_tokval(ftepp)); + break; + default: + ftepp_error(ftepp, "expected macro name"); + return false; + } + } + + (void)ftepp_next(ftepp); + if (!ftepp_skipspace(ftepp)) + return false; + /* relaxing this condition + if (ftepp->token != TOKEN_EOL && ftepp->token != TOKEN_EOF) { + ftepp_error(ftepp, "stray tokens after #ifdef"); + return false; + } + */ + return true; +} + /* Basic structure handlers */ static bool ftepp_else_allowed(ftepp_t *ftepp) { @@ -490,11 +830,15 @@ static bool ftepp_hash(ftepp_t *ftepp) if (!strcmp(ftepp_tokval(ftepp), "define")) { return ftepp_define(ftepp); } + else if (!strcmp(ftepp_tokval(ftepp), "undef")) { + return ftepp_undef(ftepp); + } else if (!strcmp(ftepp_tokval(ftepp), "ifdef")) { if (!ftepp_ifdef(ftepp, &cond)) return false; cond.was_on = cond.on; vec_push(ftepp->conditions, cond); + ftepp->output_on = ftepp->output_on && cond.on; break; } else if (!strcmp(ftepp_tokval(ftepp), "ifndef")) { @@ -503,6 +847,7 @@ static bool ftepp_hash(ftepp_t *ftepp) cond.on = !cond.on; cond.was_on = cond.on; vec_push(ftepp->conditions, cond); + ftepp->output_on = ftepp->output_on && cond.on; break; } else if (!strcmp(ftepp_tokval(ftepp), "elifdef")) { @@ -513,6 +858,7 @@ static bool ftepp_hash(ftepp_t *ftepp) pc = &vec_last(ftepp->conditions); pc->on = !pc->was_on && cond.on; pc->was_on = pc->was_on || pc->on; + ftepp_update_output_condition(ftepp); break; } else if (!strcmp(ftepp_tokval(ftepp), "elifndef")) { @@ -524,6 +870,7 @@ static bool ftepp_hash(ftepp_t *ftepp) pc = &vec_last(ftepp->conditions); pc->on = !pc->was_on && cond.on; pc->was_on = pc->was_on || pc->on; + ftepp_update_output_condition(ftepp); break; } else if (!strcmp(ftepp_tokval(ftepp), "elif")) { @@ -534,6 +881,7 @@ static bool ftepp_hash(ftepp_t *ftepp) pc = &vec_last(ftepp->conditions); pc->on = !pc->was_on && cond.on; pc->was_on = pc->was_on || pc->on; + ftepp_update_output_condition(ftepp); break; } else if (!strcmp(ftepp_tokval(ftepp), "if")) { @@ -541,6 +889,7 @@ static bool ftepp_hash(ftepp_t *ftepp) return false; cond.was_on = cond.on; vec_push(ftepp->conditions, cond); + ftepp->output_on = ftepp->output_on && cond.on; break; } else if (!strcmp(ftepp_tokval(ftepp), "else")) { @@ -550,6 +899,7 @@ static bool ftepp_hash(ftepp_t *ftepp) pc->on = !pc->was_on; pc->had_else = true; ftepp_next(ftepp); + ftepp_update_output_condition(ftepp); break; } else if (!strcmp(ftepp_tokval(ftepp), "endif")) { @@ -559,6 +909,7 @@ static bool ftepp_hash(ftepp_t *ftepp) } vec_pop(ftepp->conditions); ftepp_next(ftepp); + ftepp_update_output_condition(ftepp); break; } else { @@ -575,25 +926,22 @@ static bool ftepp_hash(ftepp_t *ftepp) case TOKEN_EOF: ftepp_error(ftepp, "missing newline at end of file", ftepp_tokval(ftepp)); return false; + + /* Builtins! Don't forget the builtins! */ + case TOKEN_INTCONST: + case TOKEN_FLOATCONST: + ftepp_out(ftepp, "#", false); + return true; } if (!ftepp_skipspace(ftepp)) return false; return true; } -static void ftepp_out(ftepp_t *ftepp, const char *str, bool ignore_cond) -{ - if (ignore_cond || - !vec_size(ftepp->conditions) || - vec_last(ftepp->conditions).on) - { - printf("%s", str); - } -} - static bool ftepp_preprocess(ftepp_t *ftepp) { - bool newline = true; + ppmacro *macro; + bool newline = true; ftepp->lex->flags.preprocessing = true; ftepp->lex->flags.mergelines = false; @@ -604,11 +952,27 @@ static bool ftepp_preprocess(ftepp_t *ftepp) { if (ftepp->token >= TOKEN_EOF) break; - +#if 0 ftepp->newline = newline; newline = false; +#else + /* For the sake of FTE compatibility... FU, really */ + ftepp->newline = newline = true; +#endif switch (ftepp->token) { + case TOKEN_KEYWORD: + case TOKEN_IDENT: + case TOKEN_TYPENAME: + macro = ftepp_macro_find(ftepp, ftepp_tokval(ftepp)); + if (!macro) { + ftepp_out(ftepp, ftepp_tokval(ftepp), false); + ftepp_next(ftepp); + break; + } + if (!ftepp_macro_call(ftepp, macro)) + ftepp->token = TOKEN_ERROR; + break; case '#': if (!ftepp->newline) { ftepp_out(ftepp, ftepp_tokval(ftepp), false); @@ -638,7 +1002,6 @@ static bool ftepp_preprocess(ftepp_t *ftepp) } while (!ftepp->errors && ftepp->token < TOKEN_EOF); newline = ftepp->token == TOKEN_EOF; - ftepp_delete(ftepp); return newline; } @@ -650,7 +1013,12 @@ bool ftepp_preprocess_file(const char *filename) con_out("failed to open file \"%s\"\n", filename); return false; } - return ftepp_preprocess(ftepp); + if (!ftepp_preprocess(ftepp)) { + ftepp_delete(ftepp); + return false; + } + ftepp_delete(ftepp); + return true; } bool ftepp_preprocess_string(const char *name, const char *str) @@ -661,5 +1029,10 @@ bool ftepp_preprocess_string(const char *name, const char *str) con_out("failed to create lexer for string \"%s\"\n", name); return false; } - return ftepp_preprocess(ftepp); + if (!ftepp_preprocess(ftepp)) { + ftepp_delete(ftepp); + return false; + } + ftepp_delete(ftepp); + return true; }