X-Git-Url: https://de.git.xonotic.org/?p=xonotic%2Fgmqcc.git;a=blobdiff_plain;f=ftepp.c;h=21669a8aa6a492910c23b12dcb3d8482c4fb09a8;hp=ec5c3f648af3eb89ceb2f4743f3e3c0b6f59b2d5;hb=c7280fca5285463dd68395f322004d249cfbbdb3;hpb=d6888205186c1663e89119cc0443078550558d5e diff --git a/ftepp.c b/ftepp.c index ec5c3f6..21669a8 100644 --- a/ftepp.c +++ b/ftepp.c @@ -1,6 +1,7 @@ /* - * Copyright (C) 2012 + * Copyright (C) 2012, 2013 * Wolfgang Bumiller + * Dale Weiler * * Permission is hereby granted, free of charge, to any person obtaining a copy of * this software and associated documentation files (the "Software"), to deal in @@ -55,13 +56,89 @@ typedef struct { typedef struct { lex_file *lex; int token; - bool newline; unsigned int errors; + bool output_on; ppcondition *conditions; ppmacro **macros; + + char *output_string; + + char *itemname; + char *includename; } ftepp_t; +typedef struct { + const char *name; + char *(*func)(lex_file *); +} predef_t; + +/* + * Implement the predef subsystem now. We can do this safely with the + * help of lexer contexts. + */ +static uint32_t ftepp_predef_countval = 0; +static uint32_t ftepp_predef_randval = 0; + +/* __LINE__ */ +char *ftepp_predef_line(lex_file *context) { + char *value; + util_asprintf(&value, "%d", (int)context->line); + return value; +} +/* __FILE__ */ +char *ftepp_predef_file(lex_file *context) { + size_t length = strlen(context->name) + 3; /* two quotes and a terminator */ + char *value = (char*)mem_a(length); + memset (value, 0, length); + sprintf(value, "\"%s\"", context->name); + + return value; +} +/* __COUNTER_LAST__ */ +char *ftepp_predef_counterlast(lex_file *context) { + char *value; + util_asprintf(&value, "%u", ftepp_predef_countval); + + (void)context; + return value; +} +/* __COUNTER__ */ +char *ftepp_predef_counter(lex_file *context) { + char *value; + ftepp_predef_countval ++; + util_asprintf(&value, "%u", ftepp_predef_countval); + (void)context; + + return value; +} +/* __RANDOM__ */ +char *ftepp_predef_random(lex_file *context) { + char *value; + ftepp_predef_randval = (util_rand() % 0xFF) + 1; + util_asprintf(&value, "%u", ftepp_predef_randval); + + (void)context; + return value; +} +/* __RANDOM_LAST__ */ +char *ftepp_predef_randomlast(lex_file *context) { + char *value; + util_asprintf(&value, "%u", ftepp_predef_randval); + + (void)context; + return value; +} + +static const predef_t ftepp_predefs[] = { + { "__LINE__", &ftepp_predef_line }, + { "__FILE__", &ftepp_predef_file }, + { "__COUNTER__", &ftepp_predef_counter }, + { "__COUNTER_LAST__", &ftepp_predef_counterlast }, + { "__RANDOM__", &ftepp_predef_random }, + { "__RANDOM_LAST__", &ftepp_predef_randomlast }, +}; + #define ftepp_tokval(f) ((f)->lex->tok.value) #define ftepp_ctx(f) ((f)->lex->tok.ctx) @@ -72,7 +149,7 @@ static void ftepp_errorat(ftepp_t *ftepp, lex_ctx ctx, const char *fmt, ...) ftepp->errors++; va_start(ap, fmt); - con_vprintmsg(LVL_ERROR, ctx.file, ctx.line, "error", fmt, ap); + con_cvprintmsg((void*)&ctx, LVL_ERROR, "error", fmt, ap); va_end(ap); } @@ -83,18 +160,32 @@ static void ftepp_error(ftepp_t *ftepp, const char *fmt, ...) ftepp->errors++; va_start(ap, fmt); - con_vprintmsg(LVL_ERROR, ftepp->lex->tok.ctx.file, ftepp->lex->tok.ctx.line, "error", fmt, ap); + con_cvprintmsg((void*)&ftepp->lex->tok.ctx, LVL_ERROR, "error", fmt, ap); + va_end(ap); +} + +static bool GMQCC_WARN ftepp_warn(ftepp_t *ftepp, int warntype, const char *fmt, ...) +{ + bool r; + va_list ap; + + va_start(ap, fmt); + r = vcompile_warning(ftepp->lex->tok.ctx, warntype, fmt, ap); va_end(ap); + return r; } 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; } @@ -108,6 +199,8 @@ static void pptoken_delete(pptoken *self) static ppmacro *ppmacro_new(lex_ctx ctx, const char *name) { ppmacro *macro = (ppmacro*)mem_a(sizeof(ppmacro)); + + (void)ctx; memset(macro, 0, sizeof(*macro)); macro->name = util_strdup(name); return macro; @@ -126,38 +219,56 @@ static void ppmacro_delete(ppmacro *self) mem_d(self); } -static ftepp_t* ftepp_init() +static ftepp_t* ftepp_new() { ftepp_t *ftepp; ftepp = (ftepp_t*)mem_a(sizeof(*ftepp)); memset(ftepp, 0, sizeof(*ftepp)); + ftepp->output_on = true; + return ftepp; } static void ftepp_delete(ftepp_t *self) { size_t i; + ftepp_flush(self); + if (self->itemname) + mem_d(self->itemname); + if (self->includename) + vec_free(self->includename); for (i = 0; i < vec_size(self->macros); ++i) ppmacro_delete(self->macros[i]); vec_free(self->macros); vec_free(self->conditions); - lex_close(self->lex); + if (self->lex) + lex_close(self->lex); mem_d(self); } 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) + if (ignore_cond || ftepp->output_on) { - printf("%s", str); + size_t len; + char *data; + len = strlen(str); + data = vec_add(ftepp->output_string, len); + memcpy(data, str, len); } } -ppmacro* ftepp_macro_find(ftepp_t *ftepp, const char *name) +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) { @@ -167,7 +278,18 @@ ppmacro* ftepp_macro_find(ftepp_t *ftepp, const char *name) return NULL; } -static inline int ftepp_next(ftepp_t *ftepp) +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 GMQCC_INLINE int ftepp_next(ftepp_t *ftepp) { return (ftepp->token = lex_do(ftepp->lex)); } @@ -242,7 +364,8 @@ static bool ftepp_define_body(ftepp_t *ftepp, ppmacro *macro) vec_push(macro->output, ptok); ftepp_next(ftepp); } - 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; } @@ -252,6 +375,8 @@ static bool ftepp_define_body(ftepp_t *ftepp, ppmacro *macro) static bool ftepp_define(ftepp_t *ftepp) { ppmacro *macro; + size_t l = ftepp_ctx(ftepp).line; + (void)ftepp_next(ftepp); if (!ftepp_skipspace(ftepp)) return false; @@ -260,6 +385,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: @@ -281,7 +412,14 @@ 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); + } + + for (; l < ftepp_ctx(ftepp).line; ++l) + ftepp_out(ftepp, "\n", true); return true; } @@ -305,6 +443,7 @@ static void macroparam_clean(macroparam *self) 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; @@ -313,8 +452,12 @@ static bool ftepp_macro_call_params(ftepp_t *ftepp, macroparam **out_params) size_t parens = 0; size_t i; - while (true) { + 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; @@ -343,10 +486,12 @@ static bool ftepp_macro_call_params(ftepp_t *ftepp, macroparam **out_params) 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; @@ -359,20 +504,180 @@ on_error: 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 void ftepp_stringify_token(ftepp_t *ftepp, pptoken *token) +{ + char chs[2]; + const char *ch; + chs[1] = 0; + switch (token->token) { + case TOKEN_STRINGCONST: + ch = token->value; + while (*ch) { + /* in preprocessor mode strings already are string, + * so we don't get actual newline bytes here. + * Still need to escape backslashes and quotes. + */ + switch (*ch) { + case '\\': ftepp_out(ftepp, "\\\\", false); break; + case '"': ftepp_out(ftepp, "\\\"", false); break; + default: + chs[0] = *ch; + ftepp_out(ftepp, chs, false); + break; + } + ++ch; + } + break; + case TOKEN_WHITE: + ftepp_out(ftepp, " ", false); + break; + case TOKEN_EOL: + ftepp_out(ftepp, "\\n", false); + break; + default: + ftepp_out(ftepp, token->value, false); + break; + } +} + +static void ftepp_stringify(ftepp_t *ftepp, macroparam *param) +{ + size_t i; + ftepp_out(ftepp, "\"", false); + for (i = 0; i < vec_size(param->tokens); ++i) + ftepp_stringify_token(ftepp, param->tokens[i]); + ftepp_out(ftepp, "\"", false); +} + +static void ftepp_recursion_header(ftepp_t *ftepp) +{ + ftepp_out(ftepp, "\n#pragma push(line)\n", false); +} + +static void ftepp_recursion_footer(ftepp_t *ftepp) +{ + ftepp_out(ftepp, "\n#pragma pop(line)\n", 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_string; + lex_file *old_lexer = ftepp->lex; + bool retval = true; + + size_t o, pi, pv; + lex_file *inlex; + + int nextok; + + /* really ... */ + if (!vec_size(macro->output)) + return true; + + ftepp->output_string = NULL; + 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)) { + nextok = macro->output[o+1]->token; + if (nextok == '#') { + /* raw concatenation */ + ++o; + break; + } + if ( (nextok == TOKEN_IDENT || + nextok == TOKEN_KEYWORD || + nextok == TOKEN_TYPENAME) && + macro_params_find(macro, macro->output[o+1]->value, &pi)) + { + ++o; + ftepp_stringify(ftepp, ¶ms[pi]); + 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_string, 0); + /* Now run the preprocessor recursively on this string buffer */ + /* + printf("__________\n%s\n=========\n", ftepp->output_string); + */ + inlex = lex_open_string(ftepp->output_string, vec_size(ftepp->output_string)-1, ftepp->lex->name); + if (!inlex) { + ftepp_error(ftepp, "internal error: failed to instantiate lexer"); + retval = false; + goto cleanup; + } + ftepp->output_string = old_string; + inlex->line = ftepp->lex->line; + inlex->sline = ftepp->lex->sline; + ftepp->lex = inlex; + ftepp_recursion_header(ftepp); + if (!ftepp_preprocess(ftepp)) { + lex_close(ftepp->lex); + retval = false; + goto cleanup; + } + ftepp_recursion_footer(ftepp); + old_string = ftepp->output_string; + +cleanup: + ftepp->lex = old_lexer; + ftepp->output_string = old_string; + return retval; +} + static bool ftepp_macro_call(ftepp_t *ftepp, ppmacro *macro) { size_t o; macroparam *params = NULL; bool retval = true; - ftepp_next(ftepp); - if (!macro->has_params) { - for (o = 0; o < vec_size(macro->output); ++o) { - ftepp_out(ftepp, macro->output[o]->value, false); - } + if (!ftepp_macro_expand(ftepp, macro, NULL)) + return false; + ftepp_next(ftepp); return true; } + ftepp_next(ftepp); if (!ftepp_skipallwhite(ftepp)) return false; @@ -386,8 +691,17 @@ static bool ftepp_macro_call(ftepp_t *ftepp, ppmacro *macro) if (!ftepp_macro_call_params(ftepp, ¶ms)) return false; - ftepp_out(ftepp, "Parsed macro parameters", false); - goto cleanup; + 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) @@ -412,7 +726,17 @@ cleanup: * parameter lists on macros are errors * No mathematical calculations are executed */ -static bool ftepp_if_expr(ftepp_t *ftepp, bool *out) +static bool ftepp_if_expr(ftepp_t *ftepp, bool *out, double *value_out); +static bool ftepp_if_op(ftepp_t *ftepp) +{ + ftepp->lex->flags.noops = false; + ftepp_next(ftepp); + if (!ftepp_skipspace(ftepp)) + return false; + ftepp->lex->flags.noops = true; + return true; +} +static bool ftepp_if_value(ftepp_t *ftepp, bool *out, double *value_out) { ppmacro *macro; bool wasnot = false; @@ -464,13 +788,16 @@ static bool ftepp_if_expr(ftepp_t *ftepp, bool *out) macro = ftepp_macro_find(ftepp, ftepp_tokval(ftepp)); if (!macro || !vec_size(macro->output)) { *out = false; + *value_out = 0; } else { /* This does not expand recursively! */ switch (macro->output[0]->token) { case TOKEN_INTCONST: - *out = !!(macro->output[0]->constval.f); + *value_out = macro->output[0]->constval.i; + *out = !!(macro->output[0]->constval.i); break; case TOKEN_FLOATCONST: + *value_out = macro->output[0]->constval.f; *out = !!(macro->output[0]->constval.f); break; default: @@ -483,15 +810,17 @@ static bool ftepp_if_expr(ftepp_t *ftepp, bool *out) *out = false; break; case TOKEN_INTCONST: + *value_out = ftepp->lex->tok.constval.i; *out = !!(ftepp->lex->tok.constval.i); break; case TOKEN_FLOATCONST: + *value_out = ftepp->lex->tok.constval.f; *out = !!(ftepp->lex->tok.constval.f); break; case '(': ftepp_next(ftepp); - if (!ftepp_if_expr(ftepp, out)) + if (!ftepp_if_expr(ftepp, out, value_out)) return false; if (ftepp->token != ')') { ftepp_error(ftepp, "expected closing paren in #if expression"); @@ -500,38 +829,89 @@ static bool ftepp_if_expr(ftepp_t *ftepp, bool *out) break; default: - ftepp_error(ftepp, "junk in #if"); + ftepp_error(ftepp, "junk in #if: `%s` ...", ftepp_tokval(ftepp)); return false; } - if (wasnot) + if (wasnot) { *out = !*out; + *value_out = (*out ? 1 : 0); + } + return true; +} - ftepp->lex->flags.noops = false; - ftepp_next(ftepp); - if (!ftepp_skipspace(ftepp)) +/* +static bool ftepp_if_nextvalue(ftepp_t *ftepp, bool *out, double *value_out) +{ + if (!ftepp_next(ftepp)) return false; - ftepp->lex->flags.noops = true; + return ftepp_if_value(ftepp, out, value_out); +} +*/ - if (ftepp->token == ')') - return true; +static bool ftepp_if_expr(ftepp_t *ftepp, bool *out, double *value_out) +{ + if (!ftepp_if_value(ftepp, out, value_out)) + return false; - if (ftepp->token != TOKEN_OPERATOR) + if (!ftepp_if_op(ftepp)) + return false; + + if (ftepp->token == ')' || ftepp->token != TOKEN_OPERATOR) return true; + /* FTEQCC is all right-associative and no precedence here */ if (!strcmp(ftepp_tokval(ftepp), "&&") || !strcmp(ftepp_tokval(ftepp), "||")) { bool next = false; char opc = ftepp_tokval(ftepp)[0]; + double nextvalue; - ftepp_next(ftepp); - if (!ftepp_if_expr(ftepp, &next)) + (void)nextvalue; + if (!ftepp_next(ftepp)) + return false; + if (!ftepp_if_expr(ftepp, &next, &nextvalue)) return false; if (opc == '&') *out = *out && next; else *out = *out || next; + + *value_out = (*out ? 1 : 0); + return true; + } + else if (!strcmp(ftepp_tokval(ftepp), "==") || + !strcmp(ftepp_tokval(ftepp), "!=") || + !strcmp(ftepp_tokval(ftepp), ">=") || + !strcmp(ftepp_tokval(ftepp), "<=") || + !strcmp(ftepp_tokval(ftepp), ">") || + !strcmp(ftepp_tokval(ftepp), "<")) + { + bool next = false; + const char opc0 = ftepp_tokval(ftepp)[0]; + const char opc1 = ftepp_tokval(ftepp)[1]; + double other; + + if (!ftepp_next(ftepp)) + return false; + if (!ftepp_if_expr(ftepp, &next, &other)) + return false; + + if (opc0 == '=') + *out = (*value_out == other); + else if (opc0 == '!') + *out = (*value_out != other); + else if (opc0 == '>') { + if (opc1 == '=') *out = (*value_out >= other); + else *out = (*value_out > other); + } + else if (opc0 == '<') { + if (opc1 == '=') *out = (*value_out <= other); + else *out = (*value_out < other); + } + *value_out = (*out ? 1 : 0); + return true; } else { @@ -543,6 +923,7 @@ static bool ftepp_if_expr(ftepp_t *ftepp, bool *out) static bool ftepp_if(ftepp_t *ftepp, ppcondition *cond) { bool result = false; + double dummy = 0; memset(cond, 0, sizeof(*cond)); (void)ftepp_next(ftepp); @@ -554,7 +935,7 @@ static bool ftepp_if(ftepp_t *ftepp, ppcondition *cond) return false; } - if (!ftepp_if_expr(ftepp, &result)) + if (!ftepp_if_expr(ftepp, &result, &dummy)) return false; cond->on = result; @@ -586,14 +967,247 @@ 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; +} + +/* Special unescape-string function which skips a leading quote + * and stops at a quote, not just at \0 + */ +static void unescape(const char *str, char *out) { + ++str; + while (*str && *str != '"') { + if (*str == '\\') { + ++str; + switch (*str) { + case '\\': *out++ = *str; break; + case '"': *out++ = *str; break; + case 'a': *out++ = '\a'; break; + case 'b': *out++ = '\b'; break; + case 'r': *out++ = '\r'; break; + case 'n': *out++ = '\n'; break; + case 't': *out++ = '\t'; break; + case 'f': *out++ = '\f'; break; + case 'v': *out++ = '\v'; break; + default: + *out++ = '\\'; + *out++ = *str; + break; + } + ++str; + continue; + } + + *out++ = *str++; + } + *out = 0; +} + +static char *ftepp_include_find_path(const char *file, const char *pathfile) +{ + FILE *fp; + char *filename = NULL; + const char *last_slash; + size_t len; + + if (!pathfile) + return NULL; + + last_slash = strrchr(pathfile, '/'); + + if (last_slash) { + len = last_slash - pathfile; + memcpy(vec_add(filename, len), pathfile, len); + vec_push(filename, '/'); + } + + len = strlen(file); + memcpy(vec_add(filename, len+1), file, len); + vec_last(filename) = 0; + + fp = file_open(filename, "rb"); + if (fp) { + file_close(fp); + return filename; + } + vec_free(filename); + return NULL; +} + +static char *ftepp_include_find(ftepp_t *ftepp, const char *file) +{ + char *filename = NULL; + + filename = ftepp_include_find_path(file, ftepp->includename); + if (!filename) + filename = ftepp_include_find_path(file, ftepp->itemname); + return filename; +} + +static bool ftepp_directive_warning(ftepp_t *ftepp) { + char *message = NULL; + + if (!ftepp_skipspace(ftepp)) + return false; + + /* handle the odd non string constant case so it works like C */ + if (ftepp->token != TOKEN_STRINGCONST) { + bool store = false; + vec_upload(message, "#warning", 8); + ftepp_next(ftepp); + while (ftepp->token != TOKEN_EOL) { + vec_upload(message, ftepp_tokval(ftepp), strlen(ftepp_tokval(ftepp))); + ftepp_next(ftepp); + } + vec_push(message, '\0'); + store = ftepp_warn(ftepp, WARN_CPP, message); + vec_free(message); + return store; + } + + unescape (ftepp_tokval(ftepp), ftepp_tokval(ftepp)); + return ftepp_warn(ftepp, WARN_CPP, "#warning %s", ftepp_tokval(ftepp)); +} + +static void ftepp_directive_error(ftepp_t *ftepp) { + char *message = NULL; + + if (!ftepp_skipspace(ftepp)) + return; + + /* handle the odd non string constant case so it works like C */ + if (ftepp->token != TOKEN_STRINGCONST) { + vec_upload(message, "#error", 6); + ftepp_next(ftepp); + while (ftepp->token != TOKEN_EOL) { + vec_upload(message, ftepp_tokval(ftepp), strlen(ftepp_tokval(ftepp))); + ftepp_next(ftepp); + } + vec_push(message, '\0'); + ftepp_error(ftepp, message); + vec_free(message); + return; + } + + unescape (ftepp_tokval(ftepp), ftepp_tokval(ftepp)); + ftepp_error(ftepp, "#error %s", ftepp_tokval(ftepp)); +} + +/** + * Include a file. + * FIXME: do we need/want a -I option? + * FIXME: what about when dealing with files in subdirectories coming from a progs.src? + */ +static bool ftepp_include(ftepp_t *ftepp) +{ + lex_file *old_lexer = ftepp->lex; + lex_file *inlex; + lex_ctx ctx; + char lineno[128]; + char *filename; + char *old_includename; + + (void)ftepp_next(ftepp); + if (!ftepp_skipspace(ftepp)) + return false; + + if (ftepp->token != TOKEN_STRINGCONST) { + ftepp_error(ftepp, "expected filename to include"); + return false; + } + + ctx = ftepp_ctx(ftepp); + + unescape(ftepp_tokval(ftepp), ftepp_tokval(ftepp)); + + ftepp_out(ftepp, "\n#pragma file(", false); + ftepp_out(ftepp, ftepp_tokval(ftepp), false); + ftepp_out(ftepp, ")\n#pragma line(1)\n", false); + + filename = ftepp_include_find(ftepp, ftepp_tokval(ftepp)); + if (!filename) { + ftepp_error(ftepp, "failed to open include file `%s`", ftepp_tokval(ftepp)); + return false; + } + inlex = lex_open(filename); + if (!inlex) { + ftepp_error(ftepp, "open failed on include file `%s`", filename); + vec_free(filename); + return false; + } + ftepp->lex = inlex; + old_includename = ftepp->includename; + ftepp->includename = filename; + if (!ftepp_preprocess(ftepp)) { + vec_free(ftepp->includename); + ftepp->includename = old_includename; + lex_close(ftepp->lex); + ftepp->lex = old_lexer; + return false; + } + vec_free(ftepp->includename); + ftepp->includename = old_includename; + lex_close(ftepp->lex); + ftepp->lex = old_lexer; + + ftepp_out(ftepp, "\n#pragma file(", false); + ftepp_out(ftepp, ctx.file, false); + snprintf(lineno, sizeof(lineno), ")\n#pragma line(%lu)\n", (unsigned long)(ctx.line+1)); + ftepp_out(ftepp, lineno, false); + + /* skip the line */ + (void)ftepp_next(ftepp); + if (!ftepp_skipspace(ftepp)) + return false; + if (ftepp->token != TOKEN_EOL) { + ftepp_error(ftepp, "stray tokens after #include"); + return false; + } + (void)ftepp_next(ftepp); + + return true; +} + /* Basic structure handlers */ static bool ftepp_else_allowed(ftepp_t *ftepp) { @@ -625,11 +1239,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")) { @@ -638,6 +1256,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")) { @@ -648,6 +1267,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")) { @@ -659,6 +1279,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")) { @@ -669,6 +1290,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")) { @@ -676,6 +1298,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")) { @@ -685,6 +1308,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")) { @@ -694,13 +1318,34 @@ static bool ftepp_hash(ftepp_t *ftepp) } vec_pop(ftepp->conditions); ftepp_next(ftepp); + ftepp_update_output_condition(ftepp); + break; + } + else if (!strcmp(ftepp_tokval(ftepp), "include")) { + return ftepp_include(ftepp); + } + else if (!strcmp(ftepp_tokval(ftepp), "pragma")) { + ftepp_out(ftepp, "#", false); + break; + } + else if (!strcmp(ftepp_tokval(ftepp), "warning")) { + ftepp_directive_warning(ftepp); + break; + } + else if (!strcmp(ftepp_tokval(ftepp), "error")) { + ftepp_directive_error(ftepp); break; } else { - ftepp_error(ftepp, "unrecognized preprocessor directive: `%s`", ftepp_tokval(ftepp)); - return false; + if (ftepp->output_on) { + ftepp_error(ftepp, "unrecognized preprocessor directive: `%s`", ftepp_tokval(ftepp)); + return false; + } else { + ftepp_next(ftepp); + break; + } } - break; + /* break; never reached */ default: ftepp_error(ftepp, "unexpected preprocessor token: `%s`", ftepp_tokval(ftepp)); return false; @@ -710,6 +1355,12 @@ 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; @@ -721,6 +1372,10 @@ static bool ftepp_preprocess(ftepp_t *ftepp) ppmacro *macro; bool newline = true; + /* predef stuff */ + char *expand = NULL; + size_t i; + ftepp->lex->flags.preprocessing = true; ftepp->lex->flags.mergelines = false; ftepp->lex->flags.noops = true; @@ -730,15 +1385,33 @@ static bool ftepp_preprocess(ftepp_t *ftepp) { if (ftepp->token >= TOKEN_EOF) break; - - ftepp->newline = newline; - newline = false; +#if 0 + newline = true; +#endif switch (ftepp->token) { case TOKEN_KEYWORD: case TOKEN_IDENT: case TOKEN_TYPENAME: - macro = ftepp_macro_find(ftepp, ftepp_tokval(ftepp)); + /* is it a predef? */ + if (OPTS_FLAG(FTEPP_PREDEFS)) { + for (i = 0; i < sizeof(ftepp_predefs) / sizeof (*ftepp_predefs); i++) { + if (!strcmp(ftepp_predefs[i].name, ftepp_tokval(ftepp))) { + expand = ftepp_predefs[i].func(ftepp->lex); + ftepp_out(ftepp, expand, false); + ftepp_next(ftepp); /* skip */ + + mem_d(expand); /* free memory */ + break; + } + } + } + + if (ftepp->output_on) + macro = ftepp_macro_find(ftepp, ftepp_tokval(ftepp)); + else + macro = NULL; + if (!macro) { ftepp_out(ftepp, ftepp_tokval(ftepp), false); ftepp_next(ftepp); @@ -748,7 +1421,7 @@ static bool ftepp_preprocess(ftepp_t *ftepp) ftepp->token = TOKEN_ERROR; break; case '#': - if (!ftepp->newline) { + if (!newline) { ftepp_out(ftepp, ftepp_tokval(ftepp), false); ftepp_next(ftepp); break; @@ -768,36 +1441,162 @@ static bool ftepp_preprocess(ftepp_t *ftepp) ftepp_out(ftepp, "\n", true); ftepp_next(ftepp); break; + case TOKEN_WHITE: + /* same as default but don't set newline=false */ + ftepp_out(ftepp, ftepp_tokval(ftepp), false); + ftepp_next(ftepp); + break; default: + newline = false; ftepp_out(ftepp, ftepp_tokval(ftepp), false); ftepp_next(ftepp); break; } } while (!ftepp->errors && ftepp->token < TOKEN_EOF); - newline = ftepp->token == TOKEN_EOF; - ftepp_delete(ftepp); - return newline; + /* force a 0 at the end but don't count it as added to the output */ + vec_push(ftepp->output_string, 0); + vec_shrinkby(ftepp->output_string, 1); + + return (ftepp->token == TOKEN_EOF); +} + +/* Like in parser.c - files keep the previous state so we have one global + * preprocessor. Except here we will want to warn about dangling #ifs. + */ +static ftepp_t *ftepp; + +static bool ftepp_preprocess_done() +{ + bool retval = true; + if (vec_size(ftepp->conditions)) { + if (ftepp_warn(ftepp, WARN_MULTIFILE_IF, "#if spanning multiple files, is this intended?")) + retval = false; + } + lex_close(ftepp->lex); + ftepp->lex = NULL; + if (ftepp->itemname) { + mem_d(ftepp->itemname); + ftepp->itemname = NULL; + } + return retval; } bool ftepp_preprocess_file(const char *filename) { - ftepp_t *ftepp = ftepp_init(); ftepp->lex = lex_open(filename); + ftepp->itemname = util_strdup(filename); if (!ftepp->lex) { con_out("failed to open file \"%s\"\n", filename); return false; } - return ftepp_preprocess(ftepp); + if (!ftepp_preprocess(ftepp)) + return false; + return ftepp_preprocess_done(); } bool ftepp_preprocess_string(const char *name, const char *str) { - ftepp_t *ftepp = ftepp_init(); ftepp->lex = lex_open_string(str, strlen(str), name); + ftepp->itemname = util_strdup(name); if (!ftepp->lex) { con_out("failed to create lexer for string \"%s\"\n", name); return false; } - return ftepp_preprocess(ftepp); + if (!ftepp_preprocess(ftepp)) + return false; + return ftepp_preprocess_done(); +} + + +void ftepp_add_macro(const char *name, const char *value) { + char *create = NULL; + + /* use saner path for empty macros */ + if (!value) { + ftepp_add_define("__builtin__", name); + return; + } + + vec_upload(create, "#define ", 8); + vec_upload(create, name, strlen(name)); + vec_push (create, ' '); + vec_upload(create, value, strlen(value)); + vec_push (create, 0); + + ftepp_preprocess_string("__builtin__", create); + vec_free (create); +} + +bool ftepp_init() +{ + char minor[32]; + char major[32]; + + ftepp = ftepp_new(); + if (!ftepp) + return false; + + memset(minor, 0, sizeof(minor)); + memset(major, 0, sizeof(major)); + + /* set the right macro based on the selected standard */ + ftepp_add_define(NULL, "GMQCC"); + if (opts.standard == COMPILER_FTEQCC) { + ftepp_add_define(NULL, "__STD_FTEQCC__"); + /* 1.00 */ + major[0] = '"'; + major[1] = '1'; + major[2] = '"'; + + minor[0] = '"'; + minor[1] = '0'; + minor[2] = '"'; + } else if (opts.standard == COMPILER_GMQCC) { + ftepp_add_define(NULL, "__STD_GMQCC__"); + sprintf(major, "\"%d\"", GMQCC_VERSION_MAJOR); + sprintf(minor, "\"%d\"", GMQCC_VERSION_MINOR); + } else if (opts.standard == COMPILER_QCC) { + ftepp_add_define(NULL, "__STD_QCC__"); + /* 1.0 */ + major[0] = '"'; + major[1] = '1'; + major[2] = '"'; + + minor[0] = '"'; + minor[1] = '0'; + minor[2] = '"'; + } + + ftepp_add_macro("__STD_VERSION_MINOR__", minor); + ftepp_add_macro("__STD_VERSION_MAJOR__", major); + + return true; +} + +void ftepp_add_define(const char *source, const char *name) +{ + ppmacro *macro; + lex_ctx ctx = { "__builtin__", 0 }; + ctx.file = source; + macro = ppmacro_new(ctx, name); + vec_push(ftepp->macros, macro); +} + +const char *ftepp_get() +{ + return ftepp->output_string; +} + +void ftepp_flush() +{ + vec_free(ftepp->output_string); +} + +void ftepp_finish() +{ + if (!ftepp) + return; + ftepp_delete(ftepp); + ftepp = NULL; }