X-Git-Url: https://git.xonotic.org/?p=xonotic%2Fgmqcc.git;a=blobdiff_plain;f=ftepp.c;h=a34b58c0e152d6077e669ac12d1215cfbf643389;hp=ed501a50a43c7b229e80afcdabbdbb46d49e4e4d;hb=b5b9559d6f57d7c5f10f0f09581e049f6fab24a0;hpb=6bc29a1601b6326dad807ac4f1d6db9e37adb3b6 diff --git a/ftepp.c b/ftepp.c index ed501a5..a34b58c 100644 --- a/ftepp.c +++ b/ftepp.c @@ -1,7 +1,7 @@ /* - * Copyright (C) 2012 + * Copyright (C) 2012, 2013 * Wolfgang Bumiller - * Dale Weiler + * 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 @@ -21,9 +21,15 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ +#include +#include +#include + #include "gmqcc.h" #include "lexer.h" +#define HT_MACROS 1024 + typedef struct { bool on; bool was_on; @@ -35,7 +41,7 @@ typedef struct { char *value; /* a copy from the lexer */ union { - vector v; + vec3_t v; int i; double f; int t; /* type */ @@ -43,42 +49,179 @@ typedef struct { } pptoken; typedef struct { - lex_ctx ctx; + lex_ctx_t ctx; char *name; 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; -typedef struct { +typedef struct ftepp_s { lex_file *lex; int token; unsigned int errors; bool output_on; ppcondition *conditions; - ppmacro **macros; - + /*ppmacro **macros;*/ + ht macros; /* hashtable */ char *output_string; char *itemname; char *includename; + bool in_macro; + + uint32_t predef_countval; + uint32_t predef_randval; } ftepp_t; +/* __DATE__ */ +static char *ftepp_predef_date(ftepp_t *context) { + const struct tm *itime = NULL; + char *value = (char*)mem_a(82); + time_t rtime; + + (void)context; + + time (&rtime); + itime = util_localtime(&rtime); + strftime(value, 82, "\"%b %d %Y\"", itime); + + return value; +} + +/* __TIME__ */ +static char *ftepp_predef_time(ftepp_t *context) { + const struct tm *itime = NULL; + char *value = (char*)mem_a(82); + time_t rtime; + + (void)context; + + time (&rtime); + itime = util_localtime(&rtime); + strftime(value, 82, "\"%X\"", itime); + + return value; +} + +/* __LINE__ */ +static char *ftepp_predef_line(ftepp_t *context) { + char *value; + + util_asprintf(&value, "%d", (int)context->lex->line); + return value; +} +/* __FILE__ */ +static char *ftepp_predef_file(ftepp_t *context) { + size_t length = strlen(context->lex->name) + 3; /* two quotes and a terminator */ + char *value = (char*)mem_a(length); + + util_snprintf(value, length, "\"%s\"", context->lex->name); + return value; +} +/* __COUNTER_LAST__ */ +static char *ftepp_predef_counterlast(ftepp_t *context) { + char *value; + util_asprintf(&value, "%u", context->predef_countval); + return value; +} +/* __COUNTER__ */ +static char *ftepp_predef_counter(ftepp_t *context) { + char *value; + + context->predef_countval ++; + util_asprintf(&value, "%u", context->predef_countval); + + return value; +} +/* __RANDOM__ */ +static char *ftepp_predef_random(ftepp_t *context) { + char *value; + + context->predef_randval = (util_rand() % 0xFF) + 1; + util_asprintf(&value, "%u", context->predef_randval); + return value; +} +/* __RANDOM_LAST__ */ +static char *ftepp_predef_randomlast(ftepp_t *context) { + char *value; + + util_asprintf(&value, "%u", context->predef_randval); + return value; +} +/* __TIMESTAMP__ */ +static char *ftepp_predef_timestamp(ftepp_t *context) { + struct stat finfo; + const char *find; + char *value; + size_t size; + + if (stat(context->lex->name, &finfo)) + return util_strdup("\"\""); + + find = util_ctime(&finfo.st_mtime); + value = (char*)mem_a(strlen(find) + 1); + memcpy(&value[1], find, (size = strlen(find)) - 1); + + value[0] = '"'; + value[size] = '"'; + + return value; +} + +typedef struct { + const char *name; + char *(*func)(ftepp_t *); +} ftepp_predef_t; + +static const ftepp_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 }, + { "__DATE__", &ftepp_predef_date }, + { "__TIME__", &ftepp_predef_time }, + { "__TIME_STAMP__", &ftepp_predef_timestamp } +}; + +static GMQCC_INLINE size_t ftepp_predef_index(const char *name) { + /* no hashtable here, we simply check for one to exist the naive way */ + size_t i; + for(i = 1; i < GMQCC_ARRAY_COUNT(ftepp_predefs) + 1; i++) + if (!strcmp(ftepp_predefs[i-1].name, name)) + return i; + return 0; +} + +bool ftepp_predef_exists(const char *name); +bool ftepp_predef_exists(const char *name) { + return ftepp_predef_index(name) != 0; +} + +/* singleton because we're allowed */ +static GMQCC_INLINE char *(*ftepp_predef(const char *name))(ftepp_t *context) { + size_t i = ftepp_predef_index(name); + return (i != 0) ? ftepp_predefs[i-1].func : NULL; +} + #define ftepp_tokval(f) ((f)->lex->tok.value) #define ftepp_ctx(f) ((f)->lex->tok.ctx) -static void ftepp_errorat(ftepp_t *ftepp, lex_ctx ctx, const char *fmt, ...) +static void ftepp_errorat(ftepp_t *ftepp, lex_ctx_t ctx, const char *fmt, ...) { va_list ap; ftepp->errors++; va_start(ap, fmt); - con_cvprintmsg((void*)&ctx, LVL_ERROR, "error", fmt, ap); + con_cvprintmsg(ctx, LVL_ERROR, "error", fmt, ap); va_end(ap); } @@ -89,7 +232,7 @@ static void ftepp_error(ftepp_t *ftepp, const char *fmt, ...) ftepp->errors++; va_start(ap, fmt); - con_cvprintmsg((void*)&ftepp->lex->tok.ctx, LVL_ERROR, "error", fmt, ap); + con_cvprintmsg(ftepp->lex->tok.ctx, LVL_ERROR, "error", fmt, ap); va_end(ap); } @@ -119,13 +262,13 @@ static pptoken *pptoken_make(ftepp_t *ftepp) return token; } -static void pptoken_delete(pptoken *self) +static GMQCC_INLINE void pptoken_delete(pptoken *self) { mem_d(self->value); mem_d(self); } -static ppmacro *ppmacro_new(lex_ctx ctx, const char *name) +static ppmacro *ppmacro_new(lex_ctx_t ctx, const char *name) { ppmacro *macro = (ppmacro*)mem_a(sizeof(ppmacro)); @@ -148,28 +291,36 @@ static void ppmacro_delete(ppmacro *self) mem_d(self); } -static ftepp_t* ftepp_new() +static ftepp_t* ftepp_new(void) { ftepp_t *ftepp; ftepp = (ftepp_t*)mem_a(sizeof(*ftepp)); memset(ftepp, 0, sizeof(*ftepp)); - ftepp->output_on = true; + ftepp->macros = util_htnew(HT_MACROS); + ftepp->output_on = true; + ftepp->predef_countval = 0; + ftepp->predef_randval = 0; return ftepp; } +static GMQCC_INLINE 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); + + util_htrem(self->macros, (void (*)(void*))&ppmacro_delete); + vec_free(self->conditions); if (self->lex) lex_close(self->lex); @@ -188,7 +339,7 @@ static void ftepp_out(ftepp_t *ftepp, const char *str, bool ignore_cond) } } -static void ftepp_update_output_condition(ftepp_t *ftepp) +static GMQCC_INLINE void ftepp_update_output_condition(ftepp_t *ftepp) { size_t i; ftepp->output_on = true; @@ -196,25 +347,14 @@ static void ftepp_update_output_condition(ftepp_t *ftepp) ftepp->output_on = ftepp->output_on && ftepp->conditions[i].on; } -static ppmacro* ftepp_macro_find(ftepp_t *ftepp, const char *name) +static GMQCC_INLINE ppmacro* ftepp_macro_find(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)) - return ftepp->macros[i]; - } - return NULL; + return (ppmacro*)util_htget(ftepp->macros, name); } -static void ftepp_macro_delete(ftepp_t *ftepp, const char *name) +static GMQCC_INLINE 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; - } - } + util_htrm(ftepp->macros, name, (void (*)(void*))&ppmacro_delete); } static GMQCC_INLINE int ftepp_next(ftepp_t *ftepp) @@ -265,16 +405,24 @@ 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"); return false; @@ -288,9 +436,59 @@ 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 = (int)strtol(ftepp_tokval(ftepp), NULL, 10); + + 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 if (macro->variadic && !strcmp(ftepp_tokval(ftepp), "__VA_COUNT__")) { + ftepp->token = TOKEN_VA_COUNT; + ptok = pptoken_make(ftepp); + vec_push(macro->output, ptok); + ftepp_next(ftepp); + } 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) { @@ -302,7 +500,7 @@ static bool ftepp_define_body(ftepp_t *ftepp, ppmacro *macro) static bool ftepp_define(ftepp_t *ftepp) { - ppmacro *macro; + ppmacro *macro = NULL; size_t l = ftepp_ctx(ftepp).line; (void)ftepp_next(ftepp); @@ -315,7 +513,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)); } @@ -330,18 +528,24 @@ static bool ftepp_define(ftepp_t *ftepp) if (ftepp->token == '(') { macro->has_params = true; - if (!ftepp_define_params(ftepp, macro)) + if (!ftepp_define_params(ftepp, macro)) { + ppmacro_delete(macro); return false; + } } - if (!ftepp_skipspace(ftepp)) + if (!ftepp_skipspace(ftepp)) { + ppmacro_delete(macro); return false; + } - if (!ftepp_define_body(ftepp, macro)) + if (!ftepp_define_body(ftepp, macro)) { + ppmacro_delete(macro); return false; + } if (ftepp->output_on) - vec_push(ftepp->macros, macro); + util_htset(ftepp->macros, macro->name, (void*)macro); else { ppmacro_delete(macro); } @@ -397,7 +601,7 @@ static bool ftepp_macro_call_params(ftepp_t *ftepp, macroparam **out_params) ptok = pptoken_make(ftepp); vec_push(mp.tokens, ptok); if (ftepp_next(ftepp) >= TOKEN_EOF) { - ftepp_error(ftepp, "unexpected EOF in macro call"); + ftepp_error(ftepp, "unexpected end of file in macro call"); goto on_error; } } @@ -410,16 +614,10 @@ static bool ftepp_macro_call_params(ftepp_t *ftepp, macroparam **out_params) goto on_error; } if (ftepp_next(ftepp) >= TOKEN_EOF) { - ftepp_error(ftepp, "unexpected EOF in macro call"); + ftepp_error(ftepp, "unexpected end of file 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; @@ -499,18 +697,43 @@ 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 *buffer = NULL; + 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; @@ -519,21 +742,47 @@ 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_VA_COUNT: + util_asprintf(&buffer, "%d", varargs); + ftepp_out(ftepp, buffer, false); + mem_d(buffer); + 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)) { @@ -574,17 +823,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]; + util_snprintf(lineno, 128, "\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; @@ -596,9 +875,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; @@ -614,18 +894,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); @@ -666,6 +950,7 @@ 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; @@ -677,6 +962,14 @@ static bool ftepp_if_value(ftepp_t *ftepp, bool *out, double *value_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: @@ -733,6 +1026,7 @@ static bool ftepp_if_value(ftepp_t *ftepp, bool *out, double *value_out) } break; case TOKEN_STRINGCONST: + *value_out = 0; *out = false; break; case TOKEN_INTCONST: @@ -756,8 +1050,12 @@ static bool ftepp_if_value(ftepp_t *ftepp, bool *out, double *value_out) default: 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 (wasneg) + *value_out = -*value_out; if (wasnot) { *out = !*out; *value_out = (*out ? 1 : 0); @@ -971,7 +1269,7 @@ static void unescape(const char *str, char *out) { static char *ftepp_include_find_path(const char *file, const char *pathfile) { - FILE *fp; + fs_file_t *fp; char *filename = NULL; const char *last_slash; size_t len; @@ -991,9 +1289,9 @@ static char *ftepp_include_find_path(const char *file, const char *pathfile) memcpy(vec_add(filename, len+1), file, len); vec_last(filename) = 0; - fp = file_open(filename, "rb"); + fp = fs_file_open(filename, "rb"); if (fp) { - file_close(fp); + fs_file_close(fp); return filename; } vec_free(filename); @@ -1010,28 +1308,35 @@ static char *ftepp_include_find(ftepp_t *ftepp, const char *file) return filename; } -static void ftepp_directive_warning(ftepp_t *ftepp) { +static bool ftepp_directive_warning(ftepp_t *ftepp) { char *message = NULL; if (!ftepp_skipspace(ftepp)) - return; + return false; /* handle the odd non string constant case so it works like C */ if (ftepp->token != TOKEN_STRINGCONST) { - vec_upload(message, "#warning", 8); + bool store = false; + vec_append(message, 8, "#warning"); ftepp_next(ftepp); while (ftepp->token != TOKEN_EOL) { - vec_upload(message, ftepp_tokval(ftepp), strlen(ftepp_tokval(ftepp))); + vec_append(message, strlen(ftepp_tokval(ftepp)), ftepp_tokval(ftepp)); ftepp_next(ftepp); } vec_push(message, '\0'); - (void)!!ftepp_warn(ftepp, WARN_CPP, message); + if (ftepp->output_on) + store = ftepp_warn(ftepp, WARN_CPP, message); + else + store = false; vec_free(message); - return; + return store; } + if (!ftepp->output_on) + return false; + unescape (ftepp_tokval(ftepp), ftepp_tokval(ftepp)); - (void)!!ftepp_warn(ftepp, WARN_CPP, "#warning %s", ftepp_tokval(ftepp)); + return ftepp_warn(ftepp, WARN_CPP, "#warning %s", ftepp_tokval(ftepp)); } static void ftepp_directive_error(ftepp_t *ftepp) { @@ -1042,22 +1347,54 @@ static void ftepp_directive_error(ftepp_t *ftepp) { /* handle the odd non string constant case so it works like C */ if (ftepp->token != TOKEN_STRINGCONST) { - vec_upload(message, "#error", 6); + vec_append(message, 6, "#error"); ftepp_next(ftepp); while (ftepp->token != TOKEN_EOL) { - vec_upload(message, ftepp_tokval(ftepp), strlen(ftepp_tokval(ftepp))); + vec_append(message, strlen(ftepp_tokval(ftepp)), ftepp_tokval(ftepp)); ftepp_next(ftepp); } vec_push(message, '\0'); - ftepp_error(ftepp, message); + if (ftepp->output_on) + ftepp_error(ftepp, message); vec_free(message); return; } + 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_append(message, 8, "#message"); + ftepp_next(ftepp); + while (ftepp->token != TOKEN_EOL) { + vec_append(message, strlen(ftepp_tokval(ftepp)), 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)); +} + /** * Include a file. * FIXME: do we need/want a -I option? @@ -1067,7 +1404,7 @@ static bool ftepp_include(ftepp_t *ftepp) { lex_file *old_lexer = ftepp->lex; lex_file *inlex; - lex_ctx ctx; + lex_ctx_t ctx; char lineno[128]; char *filename; char *old_includename; @@ -1081,6 +1418,11 @@ static bool ftepp_include(ftepp_t *ftepp) return false; } + if (!ftepp->output_on) { + ftepp_next(ftepp); + return true; + } + ctx = ftepp_ctx(ftepp); unescape(ftepp_tokval(ftepp), ftepp_tokval(ftepp)); @@ -1117,7 +1459,7 @@ static bool ftepp_include(ftepp_t *ftepp) 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)); + util_snprintf(lineno, sizeof(lineno), ")\n#pragma line(%lu)\n", (unsigned long)(ctx.line+1)); ftepp_out(ftepp, lineno, false); /* skip the line */ @@ -1147,12 +1489,17 @@ static bool ftepp_else_allowed(ftepp_t *ftepp) return true; } +static GMQCC_INLINE void ftepp_inmacro(ftepp_t *ftepp, const char *hash) { + if (ftepp->in_macro) + (void)!ftepp_warn(ftepp, WARN_DIRECTIVE_INMACRO, "`#%s` directive in macro", hash); +} + static bool ftepp_hash(ftepp_t *ftepp) { ppcondition cond; ppcondition *pc; - lex_ctx ctx = ftepp_ctx(ftepp); + lex_ctx_t ctx = ftepp_ctx(ftepp); if (!ftepp_skipspace(ftepp)) return false; @@ -1162,12 +1509,15 @@ static bool ftepp_hash(ftepp_t *ftepp) case TOKEN_IDENT: case TOKEN_TYPENAME: if (!strcmp(ftepp_tokval(ftepp), "define")) { + ftepp_inmacro(ftepp, "define"); return ftepp_define(ftepp); } else if (!strcmp(ftepp_tokval(ftepp), "undef")) { + ftepp_inmacro(ftepp, "undef"); return ftepp_undef(ftepp); } else if (!strcmp(ftepp_tokval(ftepp), "ifdef")) { + ftepp_inmacro(ftepp, "ifdef"); if (!ftepp_ifdef(ftepp, &cond)) return false; cond.was_on = cond.on; @@ -1176,6 +1526,7 @@ static bool ftepp_hash(ftepp_t *ftepp) break; } else if (!strcmp(ftepp_tokval(ftepp), "ifndef")) { + ftepp_inmacro(ftepp, "ifndef"); if (!ftepp_ifdef(ftepp, &cond)) return false; cond.on = !cond.on; @@ -1185,6 +1536,7 @@ static bool ftepp_hash(ftepp_t *ftepp) break; } else if (!strcmp(ftepp_tokval(ftepp), "elifdef")) { + ftepp_inmacro(ftepp, "elifdef"); if (!ftepp_else_allowed(ftepp)) return false; if (!ftepp_ifdef(ftepp, &cond)) @@ -1196,6 +1548,7 @@ static bool ftepp_hash(ftepp_t *ftepp) break; } else if (!strcmp(ftepp_tokval(ftepp), "elifndef")) { + ftepp_inmacro(ftepp, "elifndef"); if (!ftepp_else_allowed(ftepp)) return false; if (!ftepp_ifdef(ftepp, &cond)) @@ -1208,6 +1561,7 @@ static bool ftepp_hash(ftepp_t *ftepp) break; } else if (!strcmp(ftepp_tokval(ftepp), "elif")) { + ftepp_inmacro(ftepp, "elif"); if (!ftepp_else_allowed(ftepp)) return false; if (!ftepp_if(ftepp, &cond)) @@ -1219,6 +1573,7 @@ static bool ftepp_hash(ftepp_t *ftepp) break; } else if (!strcmp(ftepp_tokval(ftepp), "if")) { + ftepp_inmacro(ftepp, "if"); if (!ftepp_if(ftepp, &cond)) return false; cond.was_on = cond.on; @@ -1227,6 +1582,7 @@ static bool ftepp_hash(ftepp_t *ftepp) break; } else if (!strcmp(ftepp_tokval(ftepp), "else")) { + ftepp_inmacro(ftepp, "else"); if (!ftepp_else_allowed(ftepp)) return false; pc = &vec_last(ftepp->conditions); @@ -1237,6 +1593,7 @@ static bool ftepp_hash(ftepp_t *ftepp) break; } else if (!strcmp(ftepp_tokval(ftepp), "endif")) { + ftepp_inmacro(ftepp, "endif"); if (!vec_size(ftepp->conditions)) { ftepp_error(ftepp, "#endif without #if"); return false; @@ -1247,6 +1604,7 @@ static bool ftepp_hash(ftepp_t *ftepp) break; } else if (!strcmp(ftepp_tokval(ftepp), "include")) { + ftepp_inmacro(ftepp, "include"); return ftepp_include(ftepp); } else if (!strcmp(ftepp_tokval(ftepp), "pragma")) { @@ -1261,6 +1619,10 @@ static bool ftepp_hash(ftepp_t *ftepp) ftepp_directive_error(ftepp); break; } + else if (!strcmp(ftepp_tokval(ftepp), "message")) { + ftepp_directive_message(ftepp); + break; + } else { if (ftepp->output_on) { ftepp_error(ftepp, "unrecognized preprocessor directive: `%s`", ftepp_tokval(ftepp)); @@ -1297,6 +1659,9 @@ static bool ftepp_preprocess(ftepp_t *ftepp) ppmacro *macro; bool newline = true; + /* predef stuff */ + char *expand = NULL; + ftepp->lex->flags.preprocessing = true; ftepp->lex->flags.mergelines = false; ftepp->lex->flags.noops = true; @@ -1314,10 +1679,24 @@ static bool ftepp_preprocess(ftepp_t *ftepp) case TOKEN_KEYWORD: case TOKEN_IDENT: case TOKEN_TYPENAME: + /* is it a predef? */ + if (OPTS_FLAG(FTEPP_PREDEFS)) { + char *(*predef)(ftepp_t*) = ftepp_predef(ftepp_tokval(ftepp)); + if (predef) { + expand = predef(ftepp); + ftepp_out (ftepp, expand, false); + ftepp_next(ftepp); + + mem_d(expand); + 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); @@ -1349,7 +1728,7 @@ static bool ftepp_preprocess(ftepp_t *ftepp) break; case TOKEN_WHITE: /* same as default but don't set newline=false */ - ftepp_out(ftepp, ftepp_tokval(ftepp), false); + ftepp_out(ftepp, ftepp_tokval(ftepp), true); ftepp_next(ftepp); break; default: @@ -1370,9 +1749,7 @@ static bool ftepp_preprocess(ftepp_t *ftepp) /* 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() +static bool ftepp_preprocess_done(ftepp_t *ftepp) { bool retval = true; if (vec_size(ftepp->conditions)) { @@ -1388,7 +1765,7 @@ static bool ftepp_preprocess_done() return retval; } -bool ftepp_preprocess_file(const char *filename) +bool ftepp_preprocess_file(ftepp_t *ftepp, const char *filename) { ftepp->lex = lex_open(filename); ftepp->itemname = util_strdup(filename); @@ -1398,10 +1775,10 @@ bool ftepp_preprocess_file(const char *filename) } if (!ftepp_preprocess(ftepp)) return false; - return ftepp_preprocess_done(); + return ftepp_preprocess_done(ftepp); } -bool ftepp_preprocess_string(const char *name, const char *str) +bool ftepp_preprocess_string(ftepp_t *ftepp, const char *name, const char *str) { ftepp->lex = lex_open_string(str, strlen(str), name); ftepp->itemname = util_strdup(name); @@ -1411,45 +1788,46 @@ bool ftepp_preprocess_string(const char *name, const char *str) } if (!ftepp_preprocess(ftepp)) return false; - return ftepp_preprocess_done(); + return ftepp_preprocess_done(ftepp); } -void ftepp_add_macro(const char *name, const char *value) { +void ftepp_add_macro(ftepp_t *ftepp, const char *name, const char *value) { char *create = NULL; /* use saner path for empty macros */ if (!value) { - ftepp_add_define("__builtin__", name); + ftepp_add_define(ftepp, "__builtin__", name); return; } - vec_upload(create, "#define ", 8); - vec_upload(create, name, strlen(name)); + vec_append(create, 8, "#define "); + vec_append(create, strlen(name), name); vec_push (create, ' '); - vec_upload(create, value, strlen(value)); + vec_append(create, strlen(value), value); vec_push (create, 0); - ftepp_preprocess_string("__builtin__", create); + ftepp_preprocess_string(ftepp, "__builtin__", create); vec_free (create); } -bool ftepp_init() +ftepp_t *ftepp_create() { + ftepp_t *ftepp; char minor[32]; char major[32]; ftepp = ftepp_new(); if (!ftepp) - return false; + return NULL; 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__"); + ftepp_add_define(ftepp, NULL, "GMQCC"); + if (OPTS_OPTION_U32(OPTION_STANDARD) == COMPILER_FTEQCC) { + ftepp_add_define(ftepp, NULL, "__STD_FTEQCC__"); /* 1.00 */ major[0] = '"'; major[1] = '1'; @@ -1458,12 +1836,16 @@ bool ftepp_init() 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__"); + } else if (OPTS_OPTION_U32(OPTION_STANDARD) == COMPILER_GMQCC) { + ftepp_add_define(ftepp, NULL, "__STD_GMQCC__"); + util_snprintf(major, 32, "\"%d\"", GMQCC_VERSION_MAJOR); + util_snprintf(minor, 32, "\"%d\"", GMQCC_VERSION_MINOR); + } else if (OPTS_OPTION_U32(OPTION_STANDARD) == COMPILER_QCCX) { + ftepp_add_define(ftepp, NULL, "__STD_QCCX__"); + util_snprintf(major, 32, "\"%d\"", GMQCC_VERSION_MAJOR); + util_snprintf(minor, 32, "\"%d\"", GMQCC_VERSION_MINOR); + } else if (OPTS_OPTION_U32(OPTION_STANDARD) == COMPILER_QCC) { + ftepp_add_define(ftepp, NULL, "__STD_QCC__"); /* 1.0 */ major[0] = '"'; major[1] = '1'; @@ -1474,35 +1856,41 @@ bool ftepp_init() minor[2] = '"'; } - ftepp_add_macro("__STD_VERSION_MINOR__", minor); - ftepp_add_macro("__STD_VERSION_MAJOR__", major); + ftepp_add_macro(ftepp, "__STD_VERSION_MINOR__", minor); + ftepp_add_macro(ftepp, "__STD_VERSION_MAJOR__", major); - return true; + /* + * We're going to just make __NULL__ nil, which works for 60% of the + * cases of __NULL_ for fteqcc. + */ + ftepp_add_macro(ftepp, "__NULL__", "nil"); + + return ftepp; } -void ftepp_add_define(const char *source, const char *name) +void ftepp_add_define(ftepp_t *ftepp, const char *source, const char *name) { ppmacro *macro; - lex_ctx ctx = { "__builtin__", 0 }; + lex_ctx_t ctx = { "__builtin__", 0, 0 }; ctx.file = source; macro = ppmacro_new(ctx, name); - vec_push(ftepp->macros, macro); + /*vec_push(ftepp->macros, macro);*/ + util_htset(ftepp->macros, name, macro); } -const char *ftepp_get() +const char *ftepp_get(ftepp_t *ftepp) { return ftepp->output_string; } -void ftepp_flush() +void ftepp_flush(ftepp_t *ftepp) { - vec_free(ftepp->output_string); + ftepp_flush_do(ftepp); } -void ftepp_finish() +void ftepp_finish(ftepp_t *ftepp) { if (!ftepp) return; ftepp_delete(ftepp); - ftepp = NULL; }