X-Git-Url: https://git.xonotic.org/?p=xonotic%2Fgmqcc.git;a=blobdiff_plain;f=ftepp.c;h=27e3afe2ed17d4c72b94b697a48303724800d047;hp=34ca698c8702e03c65361f0bba4d7225e9c7e0a9;hb=e11a17b40805b93e1e2ae80129c9d235b6d6119b;hpb=facd89b188fd9120b8d5405b4d0a05d7d1557b07 diff --git a/ftepp.c b/ftepp.c index 34ca698..27e3afe 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 @@ -20,6 +21,7 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ +#include #include "gmqcc.h" #include "lexer.h" @@ -48,6 +50,7 @@ typedef struct { char **params; /* yes we need an extra flag since `#define FOO x` is not the same as `#define FOO() x` */ bool has_params; + bool variadic; pptoken **output; } ppmacro; @@ -55,7 +58,6 @@ typedef struct { typedef struct { lex_file *lex; int token; - bool newline; unsigned int errors; bool output_on; @@ -65,8 +67,114 @@ typedef struct { char *output_string; char *itemname; + char *includename; + bool in_macro; } ftepp_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; + +/* __DATE__ */ +char *ftepp_predef_date(lex_file *context) { + struct tm *itime; + time_t rtime; + char *value = (char*)mem_a(82); + /* 82 is enough for strftime but we also have " " in our string */ + + (void)context; + + /* get time */ + time (&rtime); + itime = localtime(&rtime); + + strftime(value, 82, "\"%b %d %Y\"", itime); + + return value; +} + +/* __TIME__ */ +char *ftepp_predef_time(lex_file *context) { + struct tm *itime; + time_t rtime; + char *value = (char*)mem_a(82); + /* 82 is enough for strftime but we also have " " in our string */ + + (void)context; + + /* get time */ + time (&rtime); + itime = localtime(&rtime); + + strftime(value, 82, "\"%X\"", itime); + + return value; +} + +/* __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; +} + +const ftepp_predef_t ftepp_predefs[FTEPP_PREDEF_COUNT] = { + { "__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 }, + { "__DATE__", &ftepp_predef_date }, + { "__TIME__", &ftepp_predef_time } +}; + #define ftepp_tokval(f) ((f)->lex->tok.value) #define ftepp_ctx(f) ((f)->lex->tok.ctx) @@ -77,7 +185,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); } @@ -88,27 +196,19 @@ 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; - 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); + r = vcompile_warning(ftepp->lex->tok.ctx, warntype, fmt, ap); va_end(ap); - return opts_werror; + return r; } static pptoken *pptoken_make(ftepp_t *ftepp) @@ -134,8 +234,9 @@ static void pptoken_delete(pptoken *self) static ppmacro *ppmacro_new(lex_ctx ctx, const char *name) { - (void)ctx; ppmacro *macro = (ppmacro*)mem_a(sizeof(ppmacro)); + + (void)ctx; memset(macro, 0, sizeof(*macro)); macro->name = util_strdup(name); return macro; @@ -166,11 +267,19 @@ static ftepp_t* ftepp_new() return ftepp; } +static void ftepp_flush_do(ftepp_t *self) +{ + vec_free(self->output_string); +} + static void ftepp_delete(ftepp_t *self) { size_t i; + ftepp_flush_do(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); @@ -221,7 +330,7 @@ static void ftepp_macro_delete(ftepp_t *ftepp, const char *name) } } -static inline int ftepp_next(ftepp_t *ftepp) +static GMQCC_INLINE int ftepp_next(ftepp_t *ftepp) { return (ftepp->token = lex_do(ftepp->lex)); } @@ -269,15 +378,22 @@ static bool ftepp_define_params(ftepp_t *ftepp, ppmacro *macro) case TOKEN_IDENT: case TOKEN_TYPENAME: case TOKEN_KEYWORD: + vec_push(macro->params, util_strdup(ftepp_tokval(ftepp))); + break; + case TOKEN_DOTS: + macro->variadic = true; break; default: ftepp_error(ftepp, "unexpected token in parameter list"); return false; } - vec_push(macro->params, util_strdup(ftepp_tokval(ftepp))); ftepp_next(ftepp); if (!ftepp_skipspace(ftepp)) return false; + if (macro->variadic && ftepp->token != ')') { + ftepp_error(ftepp, "cannot have parameters after the variadic parameters"); + return false; + } } while (ftepp->token == ','); if (ftepp->token != ')') { ftepp_error(ftepp, "expected closing paren after macro parameter list"); @@ -292,9 +408,55 @@ static bool ftepp_define_body(ftepp_t *ftepp, ppmacro *macro) { pptoken *ptok; while (ftepp->token != TOKEN_EOL && ftepp->token < TOKEN_EOF) { - ptok = pptoken_make(ftepp); - vec_push(macro->output, ptok); - ftepp_next(ftepp); + bool subscript = false; + size_t index = 0; + if (macro->variadic && !strcmp(ftepp_tokval(ftepp), "__VA_ARGS__")) { + subscript = !!(ftepp_next(ftepp) == '#'); + + if (subscript && ftepp_next(ftepp) != '#') { + ftepp_error(ftepp, "expected `##` in __VA_ARGS__ for subscripting"); + return false; + } else if (subscript) { + if (ftepp_next(ftepp) == '[') { + if (ftepp_next(ftepp) != TOKEN_INTCONST) { + ftepp_error(ftepp, "expected index for __VA_ARGS__ subscript"); + return false; + } + + index = atoi(ftepp_tokval(ftepp)); + + if (ftepp_next(ftepp) != ']') { + ftepp_error(ftepp, "expected `]` in __VA_ARGS__ subscript"); + return false; + } + + /* + * mark it as an array to be handled later as such and not + * as traditional __VA_ARGS__ + */ + ftepp->token = TOKEN_VA_ARGS_ARRAY; + ptok = pptoken_make(ftepp); + ptok->constval.i = index; + vec_push(macro->output, ptok); + ftepp_next(ftepp); + } else { + ftepp_error(ftepp, "expected `[` for subscripting of __VA_ARGS__"); + return false; + } + } else { + int old = ftepp->token; + ftepp->token = TOKEN_VA_ARGS; + ptok = pptoken_make(ftepp); + vec_push(macro->output, ptok); + ftepp->token = old; + } + } + else + { + ptok = pptoken_make(ftepp); + vec_push(macro->output, ptok); + ftepp_next(ftepp); + } } /* recursive expansion can cause EOFs here */ if (ftepp->token != TOKEN_EOL && ftepp->token != TOKEN_EOF) { @@ -307,6 +469,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; @@ -317,7 +481,7 @@ static bool ftepp_define(ftepp_t *ftepp) 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))) + if (ftepp_warn(ftepp, WARN_CPP, "redefining `%s`", ftepp_tokval(ftepp))) return false; ftepp_macro_delete(ftepp, ftepp_tokval(ftepp)); } @@ -347,6 +511,9 @@ static bool ftepp_define(ftepp_t *ftepp) else { ppmacro_delete(macro); } + + for (; l < ftepp_ctx(ftepp).line; ++l) + ftepp_out(ftepp, "\n", true); return true; } @@ -498,18 +665,42 @@ 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) +static void ftepp_param_out(ftepp_t *ftepp, macroparam *param) { - char *old_string = ftepp->output_string; - lex_file *old_lexer = ftepp->lex; - bool retval = true; + size_t i; + pptoken *out; + for (i = 0; i < vec_size(param->tokens); ++i) { + out = param->tokens[i]; + if (out->token == TOKEN_EOL) + ftepp_out(ftepp, "\n", false); + else + ftepp_out(ftepp, out->value, false); + } +} - size_t o, pi, pv; +static bool ftepp_preprocess(ftepp_t *ftepp); +static bool ftepp_macro_expand(ftepp_t *ftepp, ppmacro *macro, macroparam *params, bool resetline) +{ + char *old_string = ftepp->output_string; + char *inner_string; + lex_file *old_lexer = ftepp->lex; + size_t vararg_start = vec_size(macro->params); + bool retval = true; + bool has_newlines; + size_t varargs; + + size_t o, pi; lex_file *inlex; + bool old_inmacro; + int nextok; + if (vararg_start < vec_size(params)) + varargs = vec_size(params) - vararg_start; + else + varargs = 0; + /* really ... */ if (!vec_size(macro->output)) return true; @@ -518,21 +709,41 @@ static bool ftepp_macro_expand(ftepp_t *ftepp, ppmacro *macro, macroparam *param for (o = 0; o < vec_size(macro->output); ++o) { pptoken *out = macro->output[o]; switch (out->token) { + case TOKEN_VA_ARGS: + if (!macro->variadic) { + ftepp_error(ftepp, "internal preprocessor error: TOKEN_VA_ARGS in non-variadic macro"); + vec_free(old_string); + return false; + } + if (!varargs) + break; + + pi = 0; + ftepp_param_out(ftepp, ¶ms[pi + vararg_start]); + for (++pi; pi < varargs; ++pi) { + ftepp_out(ftepp, ", ", false); + ftepp_param_out(ftepp, ¶ms[pi + vararg_start]); + } + break; + + case TOKEN_VA_ARGS_ARRAY: + if ((size_t)out->constval.i >= varargs) { + ftepp_error(ftepp, "subscript of `[%u]` is out of bounds for `__VA_ARGS__`", out->constval.i); + vec_free(old_string); + return false; + } + + ftepp_param_out(ftepp, ¶ms[out->constval.i + vararg_start]); + break; + 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); - } - } + } else + ftepp_param_out(ftepp, ¶ms[pi]); break; case '#': if (o + 1 < vec_size(macro->output)) { @@ -573,17 +784,47 @@ static bool ftepp_macro_expand(ftepp_t *ftepp, ppmacro *macro, macroparam *param retval = false; goto cleanup; } - ftepp->output_string = old_string; - ftepp->lex = inlex; - ftepp_recursion_header(ftepp); + + inlex->line = ftepp->lex->line; + inlex->sline = ftepp->lex->sline; + ftepp->lex = inlex; + + old_inmacro = ftepp->in_macro; + ftepp->in_macro = true; + ftepp->output_string = NULL; if (!ftepp_preprocess(ftepp)) { + ftepp->in_macro = old_inmacro; + vec_free(ftepp->lex->open_string); + vec_free(ftepp->output_string); lex_close(ftepp->lex); retval = false; goto cleanup; } - ftepp_recursion_footer(ftepp); - old_string = ftepp->output_string; + ftepp->in_macro = old_inmacro; + vec_free(ftepp->lex->open_string); + lex_close(ftepp->lex); + + inner_string = ftepp->output_string; + ftepp->output_string = old_string; + + has_newlines = (strchr(inner_string, '\n') != NULL); + + if (has_newlines && !old_inmacro) + ftepp_recursion_header(ftepp); + + vec_append(ftepp->output_string, vec_size(inner_string), inner_string); + vec_free(inner_string); + + if (has_newlines && !old_inmacro) + ftepp_recursion_footer(ftepp); + if (resetline && !ftepp->in_macro) { + char lineno[128]; + sprintf(lineno, "\n#pragma line(%lu)\n", (unsigned long)(old_lexer->sline)); + ftepp_out(ftepp, lineno, false); + } + + old_string = ftepp->output_string; cleanup: ftepp->lex = old_lexer; ftepp->output_string = old_string; @@ -595,9 +836,10 @@ static bool ftepp_macro_call(ftepp_t *ftepp, ppmacro *macro) size_t o; macroparam *params = NULL; bool retval = true; + size_t paramline; if (!macro->has_params) { - if (!ftepp_macro_expand(ftepp, macro, NULL)) + if (!ftepp_macro_expand(ftepp, macro, NULL, false)) return false; ftepp_next(ftepp); return true; @@ -613,18 +855,22 @@ static bool ftepp_macro_call(ftepp_t *ftepp, ppmacro *macro) } ftepp_next(ftepp); + paramline = ftepp->lex->sline; 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, + if ( vec_size(params) < vec_size(macro->params) || + (vec_size(params) > vec_size(macro->params) && !macro->variadic) ) + { + ftepp_error(ftepp, "macro %s expects%s %u paramteters, %u provided", macro->name, + (macro->variadic ? " at least" : ""), (unsigned int)vec_size(macro->params), (unsigned int)vec_size(params)); retval = false; goto cleanup; } - if (!ftepp_macro_expand(ftepp, macro, params)) + if (!ftepp_macro_expand(ftepp, macro, params, (paramline != ftepp->lex->sline))) retval = false; ftepp_next(ftepp); @@ -651,10 +897,21 @@ 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; + bool wasneg = false; if (!ftepp_skipspace(ftepp)) return false; @@ -666,6 +923,14 @@ static bool ftepp_if_expr(ftepp_t *ftepp, bool *out) return false; } + if (ftepp->token == TOKEN_OPERATOR && !strcmp(ftepp_tokval(ftepp), "-")) + { + wasneg = true; + ftepp_next(ftepp); + if (!ftepp_skipspace(ftepp)) + return false; + } + switch (ftepp->token) { case TOKEN_IDENT: case TOKEN_TYPENAME: @@ -703,13 +968,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: @@ -719,18 +987,21 @@ static bool ftepp_if_expr(ftepp_t *ftepp, bool *out) } break; case TOKEN_STRINGCONST: + *value_out = 0; *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"); @@ -739,38 +1010,93 @@ 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)); + if (OPTS_OPTION_BOOL(OPTION_DEBUG)) + ftepp_error(ftepp, "internal: token %i\n", ftepp->token); return false; } - if (wasnot) + if (wasneg) + *value_out = -*value_out; + 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 { @@ -782,6 +1108,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); @@ -793,7 +1120,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; @@ -901,30 +1228,132 @@ static void unescape(const char *str, char *out) { *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; - size_t len; - if (ftepp->itemname) { - const char *last_slash; - last_slash = strrchr(ftepp->itemname, '/'); - if (last_slash) { - len = last_slash - ftepp->itemname; - memcpy(vec_add(filename, len), ftepp->itemname, len); - vec_push(filename, '/'); + 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); } - else { - len = strlen(ftepp->itemname); - memcpy(vec_add(filename, len), ftepp->itemname, len); - if (vec_last(filename) != '/') - vec_push(filename, '/'); + vec_push(message, '\0'); + if (ftepp->output_on) + store = ftepp_warn(ftepp, WARN_CPP, message); + else + store = false; + vec_free(message); + return store; + } + + if (!ftepp->output_on) + return false; + + 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'); + if (ftepp->output_on) + ftepp_error(ftepp, message); + vec_free(message); + return; } - len = strlen(file); - memcpy(vec_add(filename, len), file, len); - vec_push(filename, 0); - return filename; + + if (!ftepp->output_on) + return; + + unescape (ftepp_tokval(ftepp), ftepp_tokval(ftepp)); + ftepp_error(ftepp, "#error %s", ftepp_tokval(ftepp)); +} + +static void ftepp_directive_message(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, "#message", 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'); + if (ftepp->output_on) + con_cprintmsg(&ftepp->lex->tok.ctx, LVL_MSG, "message", message); + vec_free(message); + return; + } + + if (!ftepp->output_on) + return; + + unescape (ftepp_tokval(ftepp), ftepp_tokval(ftepp)); + con_cprintmsg(&ftepp->lex->tok.ctx, LVL_MSG, "message", ftepp_tokval(ftepp)); } /** @@ -939,6 +1368,7 @@ static bool ftepp_include(ftepp_t *ftepp) lex_ctx ctx; char lineno[128]; char *filename; + char *old_includename; (void)ftepp_next(ftepp); if (!ftepp_skipspace(ftepp)) @@ -958,19 +1388,28 @@ static bool ftepp_include(ftepp_t *ftepp) 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, "failed to open include file `%s`", filename); + ftepp_error(ftepp, "open failed on include file `%s`", filename); vec_free(filename); return false; } - vec_free(filename); 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; @@ -1112,9 +1551,26 @@ static bool ftepp_hash(ftepp_t *ftepp) 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 if (!strcmp(ftepp_tokval(ftepp), "message")) { + ftepp_directive_message(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; never reached */ default: @@ -1143,6 +1599,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; @@ -1153,18 +1613,32 @@ 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; + 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); @@ -1174,7 +1648,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; @@ -1194,15 +1668,24 @@ 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), true); + 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; - 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 @@ -1213,12 +1696,12 @@ static ftepp_t *ftepp; static bool ftepp_preprocess_done() { bool retval = true; - lex_close(ftepp->lex); - ftepp->lex = NULL; 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; @@ -1234,10 +1717,8 @@ bool ftepp_preprocess_file(const char *filename) con_out("failed to open file \"%s\"\n", filename); return false; } - if (!ftepp_preprocess(ftepp)) { - ftepp_delete(ftepp); + if (!ftepp_preprocess(ftepp)) return false; - } return ftepp_preprocess_done(); } @@ -1249,17 +1730,88 @@ bool ftepp_preprocess_string(const char *name, const char *str) con_out("failed to create lexer for string \"%s\"\n", name); return false; } - if (!ftepp_preprocess(ftepp)) { - ftepp_delete(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(); - return !!ftepp; + 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_OPTION_U32(OPTION_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_OPTION_U32(OPTION_STANDARD) == COMPILER_GMQCC) { + ftepp_add_define(NULL, "__STD_GMQCC__"); + sprintf(major, "\"%d\"", GMQCC_VERSION_MAJOR); + sprintf(minor, "\"%d\"", GMQCC_VERSION_MINOR); + } else if (OPTS_OPTION_U32(OPTION_STANDARD) == COMPILER_QCCX) { + ftepp_add_define(NULL, "__STD_QCCX__"); + sprintf(major, "\"%d\"", GMQCC_VERSION_MAJOR); + sprintf(minor, "\"%d\"", GMQCC_VERSION_MINOR); + } else if (OPTS_OPTION_U32(OPTION_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() @@ -1269,7 +1821,7 @@ const char *ftepp_get() void ftepp_flush() { - vec_free(ftepp->output_string); + ftepp_flush_do(ftepp); } void ftepp_finish()