From 07ca2e640752ae8fecce307e1c743823f81f37c1 Mon Sep 17 00:00:00 2001 From: "Wolfgang (Blub) Bumiller" Date: Fri, 16 Nov 2012 16:57:39 +0100 Subject: [PATCH 01/59] importing initial ftepp.c; -E option now executes the preprocessor --- Makefile | 3 ++- ftepp.c | 81 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ gmqcc.h | 6 +++++ main.c | 39 +++++++++++++++------------ 4 files changed, 111 insertions(+), 18 deletions(-) create mode 100644 ftepp.c diff --git a/Makefile b/Makefile index 6b2ef6c..76439cb 100644 --- a/Makefile +++ b/Makefile @@ -24,7 +24,8 @@ OBJ = \ code.o \ ast.o \ ir.o \ - con.o + con.o \ + ftepp.o OBJ_C = main.o lexer.o parser.o OBJ_X = exec-standalone.o util.o con.o diff --git a/ftepp.c b/ftepp.c new file mode 100644 index 0000000..b32c7aa --- /dev/null +++ b/ftepp.c @@ -0,0 +1,81 @@ +/* + * Copyright (C) 2012 + * Wolfgang Bumiller + * + * 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 + * the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished to do + * so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +#include "gmqcc.h" +#include "lexer.h" + +typedef struct { + lex_file *lex; +} ftepp_t; + +#define ftepp_tokval(f) ((f)->lex->tok.value) + +ftepp_t* ftepp_init() +{ + ftepp_t *ftepp; + + ftepp = (ftepp_t*)mem_a(sizeof(*ftepp)); + memset(ftepp, 0, sizeof(*ftepp)); + + return ftepp; +} + +static bool ftepp_preprocess(ftepp_t *ftepp) +{ + int token; + + ftepp->lex->flags.preprocessing = true; + + for (token = lex_do(ftepp->lex); token < TOKEN_EOF; token = lex_do(ftepp->lex)) + { + switch (token) { + case TOKEN_EOL: printf("\n"); break; + default: + printf("%s", ftepp_tokval(ftepp)); + break; + } + } + + return (token == TOKEN_EOF); +} + +bool ftepp_preprocess_file(const char *filename) +{ + ftepp_t *ftepp = ftepp_init(); + ftepp->lex = lex_open(filename); + if (!ftepp->lex) { + con_out("failed to open file \"%s\"\n", filename); + return false; + } + return ftepp_preprocess(ftepp); +} + +bool ftepp_preprocess_string(const char *name, const char *str) +{ + ftepp_t *ftepp = ftepp_init(); + ftepp->lex = lex_open_string(str, strlen(str), name); + if (!ftepp->lex) { + con_out("failed to create lexer for string \"%s\"\n", name); + return false; + } + return ftepp_preprocess(ftepp); +} diff --git a/gmqcc.h b/gmqcc.h index b88df48..4f92eaf 100644 --- a/gmqcc.h +++ b/gmqcc.h @@ -752,6 +752,12 @@ bool parser_compile_string(const char *name, const char *str); bool parser_finish (const char *output); void parser_cleanup (); +/*===================================================================*/ +/*====================== ftepp.c commandline ========================*/ +/*===================================================================*/ +bool ftepp_preprocess_file (const char *filename); +bool ftepp_preprocess_string(const char *name, const char *str); + /*===================================================================*/ /*======================= main.c commandline ========================*/ /*===================================================================*/ diff --git a/main.c b/main.c index 2c14249..ba4cba0 100644 --- a/main.c +++ b/main.c @@ -57,28 +57,28 @@ static const char *app_name; static int usage() { con_out("usage: %s [options] [files...]", app_name); con_out("options:\n" - " -h, --help show this help message\n" - " -debug turns on compiler debug messages\n" - " -memchk turns on compiler memory leak check\n"); + " -h, --help show this help message\n" + " -debug turns on compiler debug messages\n" + " -memchk turns on compiler memory leak check\n"); con_out(" -o, --output=file output file, defaults to progs.dat\n" - " -a filename add an asm file to be assembled\n" - " -s filename add a progs.src file to be used\n"); + " -a filename add an asm file to be assembled\n" + " -s filename add a progs.src file to be used\n"); con_out(" -E stop after preprocessing\n"); con_out(" -f enable a flag\n" - " -fno- disable a flag\n" - " -std standard select one of the following standards\n" - " -std=qcc original QuakeC\n" - " -std=fteqcc fteqcc QuakeC\n" - " -std=gmqcc this compiler (default)\n"); + " -fno- disable a flag\n" + " -std standard select one of the following standards\n" + " -std=qcc original QuakeC\n" + " -std=fteqcc fteqcc QuakeC\n" + " -std=gmqcc this compiler (default)\n"); con_out(" -W enable a warning\n" - " -Wno- disable a warning\n" - " -Wall enable all warnings\n" - " -Werror treat warnings as errors\n"); + " -Wno- disable a warning\n" + " -Wall enable all warnings\n" + " -Werror treat warnings as errors\n"); con_out(" -force-crc=num force a specific checksum into the header\n"); con_out("\n"); con_out("flags:\n" - " -fadjust-vector-fields\n" - " when assigning a vector field, its _y and _z fields also get assigned\n" + " -fadjust-vector-fields\n" + " when assigning a vector field, its _y and _z fields also get assigned\n" ); return -1; } @@ -482,10 +482,15 @@ int main(int argc, char **argv) { (items[itr].type == TYPE_SRC ? "progs.src" : ("unknown")))))); - if (!parser_compile_file(items[itr].filename)) - { + if (opts_pp_only) { + if (!ftepp_preprocess_file(items[itr].filename)) { retval = 1; goto cleanup; + } + } + else if (!parser_compile_file(items[itr].filename)) { + retval = 1; + goto cleanup; } } From 4bee1bdb6e2b7f25ac7fdd9e55cdb9d052c430a4 Mon Sep 17 00:00:00 2001 From: "Wolfgang (Blub) Bumiller" Date: Fri, 16 Nov 2012 17:46:16 +0100 Subject: [PATCH 02/59] Basic structure of ftepp --- ftepp.c | 217 ++++++++++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 210 insertions(+), 7 deletions(-) diff --git a/ftepp.c b/ftepp.c index b32c7aa..877fee3 100644 --- a/ftepp.c +++ b/ftepp.c @@ -24,10 +24,44 @@ #include "lexer.h" typedef struct { - lex_file *lex; + bool on; + bool was_on; + bool had_else; +} ppcondition; + +typedef struct { + lex_file *lex; + int token; + bool newline; + unsigned int errors; + + ppcondition *conditions; } ftepp_t; #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, ...) +{ + va_list ap; + + ftepp->errors++; + + va_start(ap, fmt); + con_vprintmsg(LVL_ERROR, ctx.file, ctx.line, "error", fmt, ap); + va_end(ap); +} + +static void ftepp_error(ftepp_t *ftepp, const char *fmt, ...) +{ + va_list ap; + + ftepp->errors++; + + va_start(ap, fmt); + con_vprintmsg(LVL_ERROR, ftepp->lex->tok.ctx.file, ftepp->lex->tok.ctx.line, "error", fmt, ap); + va_end(ap); +} ftepp_t* ftepp_init() { @@ -39,23 +73,192 @@ ftepp_t* ftepp_init() return ftepp; } +static inline int ftepp_next(ftepp_t *ftepp) +{ + return (ftepp->token = lex_do(ftepp->lex)); +} + +/* Important: this does not skip newlines! */ +static bool ftepp_skipspace(ftepp_t *ftepp) +{ + while (ftepp_next(ftepp) == TOKEN_WHITE) {} + return (ftepp->token < TOKEN_EOF); +} + +static bool ftepp_if(ftepp_t *ftepp, ppcondition *cond) +{ + ftepp_error(ftepp, "TODO: #if"); + return false; +} + +static bool ftepp_ifdef(ftepp_t *ftepp, ppcondition *cond) +{ + ftepp_error(ftepp, "TODO: #ifdef"); + return false; +} + +static bool ftepp_define(ftepp_t *ftepp) +{ + ftepp_error(ftepp, "TODO: #define"); + return false; +} + +static bool ftepp_else_allowed(ftepp_t *ftepp) +{ + if (!vec_size(ftepp->conditions)) { + ftepp_error(ftepp, "#else without #if"); + return false; + } + if (vec_last(ftepp->conditions).had_else) { + ftepp_error(ftepp, "multiple #else for a single #if"); + return false; + } + return true; +} + +static bool ftepp_hash(ftepp_t *ftepp) +{ + ppcondition cond; + ppcondition *pc; + + lex_ctx ctx = ftepp_ctx(ftepp); + + if (!ftepp_skipspace(ftepp)) + return false; + + switch (ftepp->token) { + case TOKEN_IDENT: + if (!strcmp(ftepp_tokval(ftepp), "define")) { + return ftepp_define(ftepp); + } + else if (!strcmp(ftepp_tokval(ftepp), "ifdef")) { + if (!ftepp_ifdef(ftepp, &cond)) + return false; + vec_push(ftepp->conditions, cond); + return true; + } + else if (!strcmp(ftepp_tokval(ftepp), "ifndef")) { + if (!ftepp_ifdef(ftepp, &cond)) + return false; + cond.on = !cond.on; + vec_push(ftepp->conditions, cond); + return true; + } + else if (!strcmp(ftepp_tokval(ftepp), "elifdef")) { + if (!ftepp_else_allowed(ftepp)) + return false; + if (!ftepp_ifdef(ftepp, &cond)) + return false; + pc = &vec_last(ftepp->conditions); + pc->on = !pc->was_on && cond.on; + pc->was_on = pc->was_on || pc->on; + return true; + } + else if (!strcmp(ftepp_tokval(ftepp), "elifndef")) { + if (!ftepp_else_allowed(ftepp)) + return false; + if (!ftepp_ifdef(ftepp, &cond)) + return false; + cond.on = !cond.on; + pc = &vec_last(ftepp->conditions); + pc->on = !pc->was_on && cond.on; + pc->was_on = pc->was_on || pc->on; + return true; + } + else if (!strcmp(ftepp_tokval(ftepp), "elif")) { + if (!ftepp_else_allowed(ftepp)) + return false; + if (!ftepp_if(ftepp, &cond)) + return false; + pc = &vec_last(ftepp->conditions); + pc->on = !pc->was_on && cond.on; + pc->was_on = pc->was_on || pc->on; + return true; + } + else if (!strcmp(ftepp_tokval(ftepp), "if")) { + if (!ftepp_if(ftepp, &cond)) + return false; + vec_push(ftepp->conditions, cond); + return true; + } + else if (!strcmp(ftepp_tokval(ftepp), "else")) { + if (!ftepp_else_allowed(ftepp)) + return false; + pc = &vec_last(ftepp->conditions); + pc->on = !pc->was_on; + pc->had_else = true; + return true; + } + else if (!strcmp(ftepp_tokval(ftepp), "endif")) { + if (!vec_size(ftepp->conditions)) { + ftepp_error(ftepp, "#endif without #if"); + return false; + } + vec_pop(ftepp->conditions); + break; + } + else { + ftepp_error(ftepp, "unrecognized preprocessor directive: `%s`", ftepp_tokval(ftepp)); + return false; + } + break; + case TOKEN_KEYWORD: + if (!strcmp(ftepp_tokval(ftepp), "if")) { + if (!ftepp_if(ftepp, &cond)) + return false; + vec_push(ftepp->conditions, cond); + return true; + } + /* fall through */ + default: + ftepp_error(ftepp, "unexpected preprocessor token: `%s`", ftepp_tokval(ftepp)); + return false; + case TOKEN_EOL: + ftepp_errorat(ftepp, ctx, "empty preprocessor directive"); + return false; + case TOKEN_EOF: + ftepp_error(ftepp, "missing newline at end of file", ftepp_tokval(ftepp)); + return false; + } + return true; +} + static bool ftepp_preprocess(ftepp_t *ftepp) { - int token; + bool newline = true; ftepp->lex->flags.preprocessing = true; - for (token = lex_do(ftepp->lex); token < TOKEN_EOF; token = lex_do(ftepp->lex)) + do { - switch (token) { - case TOKEN_EOL: printf("\n"); break; + ftepp_next(ftepp); + + if (ftepp->token >= TOKEN_EOF) + break; + + ftepp->newline = newline; + newline = false; + + switch (ftepp->token) { + case '#': + if (!ftepp->newline) { + printf("%s", ftepp_tokval(ftepp)); + break; + } + if (!ftepp_hash(ftepp)) + return false; + break; + case TOKEN_EOL: + newline = true; + printf("\n"); + break; default: printf("%s", ftepp_tokval(ftepp)); break; } - } + } while (!ftepp->errors && ftepp->token < TOKEN_EOF); - return (token == TOKEN_EOF); + return (ftepp->token == TOKEN_EOF); } bool ftepp_preprocess_file(const char *filename) From cc1c197fc6c80721ae7c12e316cd06bcbc1a57a6 Mon Sep 17 00:00:00 2001 From: "Wolfgang (Blub) Bumiller" Date: Fri, 16 Nov 2012 18:27:32 +0100 Subject: [PATCH 03/59] macro structure, parsing ifdefs and non-macro defines --- ftepp.c | 130 +++++++++++++++++++++++++++++++++++++++++++++++++------- 1 file changed, 115 insertions(+), 15 deletions(-) diff --git a/ftepp.c b/ftepp.c index 877fee3..7b31a60 100644 --- a/ftepp.c +++ b/ftepp.c @@ -29,6 +29,22 @@ typedef struct { bool had_else; } ppcondition; +typedef struct { + int token; + char *value; +} pptoken; + +typedef struct { + lex_ctx 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; + + pptoken *output; +} ppmacro; + typedef struct { lex_file *lex; int token; @@ -36,6 +52,7 @@ typedef struct { unsigned int errors; ppcondition *conditions; + ppmacro **macros; } ftepp_t; #define ftepp_tokval(f) ((f)->lex->tok.value) @@ -63,6 +80,22 @@ static void ftepp_error(ftepp_t *ftepp, const char *fmt, ...) va_end(ap); } +ppmacro *ppmacro_new(lex_ctx ctx, const char *name) +{ + ppmacro *macro = (ppmacro*)mem_a(sizeof(ppmacro)); + memset(macro, 0, sizeof(*macro)); + macro->name = util_strdup(name); + return macro; +} + +void ppmacro_delete(ppmacro *self) +{ + vec_free(self->params); + vec_free(self->output); + mem_d(self->name); + mem_d(self); +} + ftepp_t* ftepp_init() { ftepp_t *ftepp; @@ -73,6 +106,23 @@ ftepp_t* ftepp_init() return ftepp; } +void ftepp_delete(ftepp_t *self) +{ + vec_free(self->macros); + vec_free(self->conditions); + mem_d(self); +} + +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; +} + static inline int ftepp_next(ftepp_t *ftepp) { return (ftepp->token = lex_do(ftepp->lex)); @@ -81,10 +131,14 @@ static inline int ftepp_next(ftepp_t *ftepp) /* Important: this does not skip newlines! */ static bool ftepp_skipspace(ftepp_t *ftepp) { + if (ftepp->token != TOKEN_WHITE) + return true; while (ftepp_next(ftepp) == TOKEN_WHITE) {} return (ftepp->token < TOKEN_EOF); } +/* if/ifdef/define handlers */ + static bool ftepp_if(ftepp_t *ftepp, ppcondition *cond) { ftepp_error(ftepp, "TODO: #if"); @@ -93,16 +147,61 @@ static bool ftepp_if(ftepp_t *ftepp, ppcondition *cond) static bool ftepp_ifdef(ftepp_t *ftepp, ppcondition *cond) { - ftepp_error(ftepp, "TODO: #ifdef"); - return false; + ppmacro *macro; + (void)ftepp_next(ftepp); + if (!ftepp_skipspace(ftepp)) + return false; + + switch (ftepp->token) { + case TOKEN_IDENT: + case TOKEN_KEYWORD: + macro = ftepp_macro_find(ftepp, ftepp_tokval(ftepp)); + break; + default: + ftepp_error(ftepp, "expected macro name"); + return false; + } + + (void)ftepp_next(ftepp); + if (!ftepp_skipspace(ftepp)) + return false; + if (ftepp->token != TOKEN_EOL) { + ftepp_error(ftepp, "stray tokens after #ifdef"); + return false; + } + cond->on = !!macro; + return true; } static bool ftepp_define(ftepp_t *ftepp) { - ftepp_error(ftepp, "TODO: #define"); - return false; + ppmacro *macro; + (void)ftepp_next(ftepp); + if (!ftepp_skipspace(ftepp)) + return false; + + switch (ftepp->token) { + case TOKEN_IDENT: + case TOKEN_KEYWORD: + macro = ppmacro_new(ftepp_ctx(ftepp), ftepp_tokval(ftepp)); + break; + default: + ftepp_error(ftepp, "expected macro name"); + return false; + } + + (void)ftepp_next(ftepp); + if (!ftepp_skipspace(ftepp)) + return false; + if (ftepp->token != TOKEN_EOL) { + ftepp_error(ftepp, "stray tokens after macro"); + return false; + } + vec_push(ftepp->macros, macro); + return true; } +/* Basic structure handlers */ static bool ftepp_else_allowed(ftepp_t *ftepp) { if (!vec_size(ftepp->conditions)) { @@ -127,6 +226,7 @@ static bool ftepp_hash(ftepp_t *ftepp) return false; switch (ftepp->token) { + case TOKEN_KEYWORD: case TOKEN_IDENT: if (!strcmp(ftepp_tokval(ftepp), "define")) { return ftepp_define(ftepp); @@ -202,14 +302,6 @@ static bool ftepp_hash(ftepp_t *ftepp) return false; } break; - case TOKEN_KEYWORD: - if (!strcmp(ftepp_tokval(ftepp), "if")) { - if (!ftepp_if(ftepp, &cond)) - return false; - vec_push(ftepp->conditions, cond); - return true; - } - /* fall through */ default: ftepp_error(ftepp, "unexpected preprocessor token: `%s`", ftepp_tokval(ftepp)); return false; @@ -229,10 +321,9 @@ static bool ftepp_preprocess(ftepp_t *ftepp) ftepp->lex->flags.preprocessing = true; + ftepp_next(ftepp); do { - ftepp_next(ftepp); - if (ftepp->token >= TOKEN_EOF) break; @@ -243,21 +334,30 @@ static bool ftepp_preprocess(ftepp_t *ftepp) case '#': if (!ftepp->newline) { printf("%s", ftepp_tokval(ftepp)); + ftepp_next(ftepp); + break; + } + if (ftepp_next(ftepp) >= TOKEN_EOF) { + ftepp_error(ftepp, "error in preprocessor directive"); + ftepp->token = TOKEN_ERROR; break; } if (!ftepp_hash(ftepp)) - return false; + ftepp->token = TOKEN_ERROR; break; case TOKEN_EOL: newline = true; printf("\n"); + ftepp_next(ftepp); break; default: printf("%s", ftepp_tokval(ftepp)); + ftepp_next(ftepp); break; } } while (!ftepp->errors && ftepp->token < TOKEN_EOF); + ftepp_delete(ftepp); return (ftepp->token == TOKEN_EOF); } From 0473fd84d1f79e55c87b9cb1a8b13d995a6bc141 Mon Sep 17 00:00:00 2001 From: "Wolfgang (Blub) Bumiller" Date: Fri, 16 Nov 2012 18:29:46 +0100 Subject: [PATCH 04/59] using ftepp_out which will honor conditions --- ftepp.c | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/ftepp.c b/ftepp.c index 7b31a60..769310b 100644 --- a/ftepp.c +++ b/ftepp.c @@ -315,6 +315,15 @@ static bool ftepp_hash(ftepp_t *ftepp) return true; } +static void ftepp_out(ftepp_t *ftepp, const char *str) +{ + if (!vec_size(ftepp->conditions) || + vec_last(ftepp->conditions).on) + { + printf("%s", str); + } +} + static bool ftepp_preprocess(ftepp_t *ftepp) { bool newline = true; @@ -333,7 +342,7 @@ static bool ftepp_preprocess(ftepp_t *ftepp) switch (ftepp->token) { case '#': if (!ftepp->newline) { - printf("%s", ftepp_tokval(ftepp)); + ftepp_out(ftepp, ftepp_tokval(ftepp)); ftepp_next(ftepp); break; } @@ -347,11 +356,11 @@ static bool ftepp_preprocess(ftepp_t *ftepp) break; case TOKEN_EOL: newline = true; - printf("\n"); + ftepp_out(ftepp, "\n"); ftepp_next(ftepp); break; default: - printf("%s", ftepp_tokval(ftepp)); + ftepp_out(ftepp, ftepp_tokval(ftepp)); ftepp_next(ftepp); break; } From 0c75182aabcc4d93a7216defede6e8cba5a46690 Mon Sep 17 00:00:00 2001 From: "Wolfgang (Blub) Bumiller" Date: Fri, 16 Nov 2012 19:01:44 +0100 Subject: [PATCH 05/59] Some #if parsing --- ftepp.c | 164 ++++++++++++++++++++++++++++++++++++++++++++------------ 1 file changed, 129 insertions(+), 35 deletions(-) diff --git a/ftepp.c b/ftepp.c index 769310b..c487058 100644 --- a/ftepp.c +++ b/ftepp.c @@ -32,6 +32,13 @@ typedef struct { typedef struct { int token; char *value; + /* a copy from the lexer */ + union { + vector v; + int i; + double f; + int t; /* type */ + } constval; } pptoken; typedef struct { @@ -42,7 +49,7 @@ typedef struct { /* yes we need an extra flag since `#define FOO x` is not the same as `#define FOO() x` */ bool has_params; - pptoken *output; + pptoken **output; } ppmacro; typedef struct { @@ -134,45 +141,16 @@ static bool ftepp_skipspace(ftepp_t *ftepp) if (ftepp->token != TOKEN_WHITE) return true; while (ftepp_next(ftepp) == TOKEN_WHITE) {} - return (ftepp->token < TOKEN_EOF); -} - -/* if/ifdef/define handlers */ - -static bool ftepp_if(ftepp_t *ftepp, ppcondition *cond) -{ - ftepp_error(ftepp, "TODO: #if"); - return false; -} - -static bool ftepp_ifdef(ftepp_t *ftepp, ppcondition *cond) -{ - ppmacro *macro; - (void)ftepp_next(ftepp); - if (!ftepp_skipspace(ftepp)) - return false; - - switch (ftepp->token) { - case TOKEN_IDENT: - case TOKEN_KEYWORD: - macro = ftepp_macro_find(ftepp, ftepp_tokval(ftepp)); - break; - default: - ftepp_error(ftepp, "expected macro name"); - return false; - } - - (void)ftepp_next(ftepp); - if (!ftepp_skipspace(ftepp)) - return false; - if (ftepp->token != TOKEN_EOL) { - ftepp_error(ftepp, "stray tokens after #ifdef"); + if (ftepp->token >= TOKEN_EOF) { + ftepp_error(ftepp, "unexpected end of preprocessor directive"); return false; } - cond->on = !!macro; return true; } +/** + * The huge macro parsing code... + */ static bool ftepp_define(ftepp_t *ftepp) { ppmacro *macro; @@ -182,6 +160,7 @@ static bool ftepp_define(ftepp_t *ftepp) switch (ftepp->token) { case TOKEN_IDENT: + case TOKEN_TYPENAME: case TOKEN_KEYWORD: macro = ppmacro_new(ftepp_ctx(ftepp), ftepp_tokval(ftepp)); break; @@ -201,6 +180,120 @@ static bool ftepp_define(ftepp_t *ftepp) return true; } +/** + * #if - the FTEQCC way: + * defined(FOO) => true if FOO was #defined regardless of parameters or contents + * => True if the number is not 0 + * ! => True if the factor yields false + * => becomes the macro's FIRST token regardless of parameters + * && => True if both expressions are true + * || => True if either expression is true + * => False + * => False (remember for macros the rule applies instead) + * Unary + and - are skipped + * parenthesis in expressions are allowed + * parameter lists on macros are errors + * No mathematical calculations are executed + */ +static bool ftepp_if_expr(ftepp_t *ftepp, bool *out) +{ + ppmacro *macro; + while (ftepp->token != TOKEN_EOL) { + switch (ftepp->token) { + case TOKEN_IDENT: + case TOKEN_TYPENAME: + case TOKEN_KEYWORD: + macro = ftepp_macro_find(ftepp, ftepp_tokval(ftepp)); + if (!macro || !vec_size(macro->output)) { + *out = false; + } else { + /* This does not expand recursively! */ + switch (macro->output[0]->token) { + case TOKEN_INTCONST: + *out = !!(macro->output[0]->constval.f); + break; + case TOKEN_FLOATCONST: + *out = !!(macro->output[0]->constval.f); + break; + default: + *out = false; + break; + } + } + break; + case TOKEN_STRINGCONST: + *out = false; + break; + case TOKEN_INTCONST: + *out = !!(ftepp->lex->tok.constval.i); + break; + case TOKEN_FLOATCONST: + *out = !!(ftepp->lex->tok.constval.f); + break; + + default: + ftepp_error(ftepp, "junk in #if"); + return false; + } + } + (void)ftepp_next(ftepp); + return true; +} + +static bool ftepp_if(ftepp_t *ftepp, ppcondition *cond) +{ + bool result = false; + + memset(cond, 0, sizeof(*cond)); + (void)ftepp_next(ftepp); + + if (!ftepp_skipspace(ftepp)) + return false; + if (ftepp->token == TOKEN_EOL) { + ftepp_error(ftepp, "expected expression for #if-directive"); + return false; + } + + if (!ftepp_if_expr(ftepp, &result)) + return false; + + cond->on = result; + return true; +} + +/** + * ifdef is rather simple + */ +static bool ftepp_ifdef(ftepp_t *ftepp, ppcondition *cond) +{ + ppmacro *macro; + memset(cond, 0, sizeof(*cond)); + (void)ftepp_next(ftepp); + if (!ftepp_skipspace(ftepp)) + return false; + + switch (ftepp->token) { + case TOKEN_IDENT: + case TOKEN_TYPENAME: + case TOKEN_KEYWORD: + macro = ftepp_macro_find(ftepp, ftepp_tokval(ftepp)); + break; + default: + ftepp_error(ftepp, "expected macro name"); + return false; + } + + (void)ftepp_next(ftepp); + if (!ftepp_skipspace(ftepp)) + return false; + if (ftepp->token != TOKEN_EOL) { + ftepp_error(ftepp, "stray tokens after #ifdef"); + return false; + } + cond->on = !!macro; + return true; +} + /* Basic structure handlers */ static bool ftepp_else_allowed(ftepp_t *ftepp) { @@ -228,6 +321,7 @@ static bool ftepp_hash(ftepp_t *ftepp) switch (ftepp->token) { case TOKEN_KEYWORD: case TOKEN_IDENT: + case TOKEN_TYPENAME: if (!strcmp(ftepp_tokval(ftepp), "define")) { return ftepp_define(ftepp); } From bd5b2a8b58bac34c4234fbdc7b8d3e9d61d71a54 Mon Sep 17 00:00:00 2001 From: "Wolfgang (Blub) Bumiller" Date: Fri, 16 Nov 2012 19:07:23 +0100 Subject: [PATCH 06/59] Fixing indentation --- ftepp.c | 628 ++++++++++++++++++++++++++++---------------------------- 1 file changed, 319 insertions(+), 309 deletions(-) diff --git a/ftepp.c b/ftepp.c index c487058..c1355f0 100644 --- a/ftepp.c +++ b/ftepp.c @@ -24,42 +24,42 @@ #include "lexer.h" typedef struct { - bool on; - bool was_on; - bool had_else; + bool on; + bool was_on; + bool had_else; } ppcondition; typedef struct { - int token; - char *value; - /* a copy from the lexer */ - union { - vector v; - int i; - double f; - int t; /* type */ - } constval; + int token; + char *value; + /* a copy from the lexer */ + union { + vector v; + int i; + double f; + int t; /* type */ + } constval; } pptoken; typedef struct { - lex_ctx ctx; + lex_ctx 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; + 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; - pptoken **output; + pptoken **output; } ppmacro; typedef struct { - lex_file *lex; - int token; - bool newline; - unsigned int errors; + lex_file *lex; + int token; + bool newline; + unsigned int errors; - ppcondition *conditions; - ppmacro **macros; + ppcondition *conditions; + ppmacro **macros; } ftepp_t; #define ftepp_tokval(f) ((f)->lex->tok.value) @@ -67,85 +67,85 @@ typedef struct { static void ftepp_errorat(ftepp_t *ftepp, lex_ctx ctx, const char *fmt, ...) { - va_list ap; + va_list ap; - ftepp->errors++; + ftepp->errors++; - va_start(ap, fmt); + va_start(ap, fmt); con_vprintmsg(LVL_ERROR, ctx.file, ctx.line, "error", fmt, ap); - va_end(ap); + va_end(ap); } static void ftepp_error(ftepp_t *ftepp, const char *fmt, ...) { - va_list ap; + va_list ap; - ftepp->errors++; + ftepp->errors++; - va_start(ap, fmt); + va_start(ap, fmt); con_vprintmsg(LVL_ERROR, ftepp->lex->tok.ctx.file, ftepp->lex->tok.ctx.line, "error", fmt, ap); - va_end(ap); + va_end(ap); } ppmacro *ppmacro_new(lex_ctx ctx, const char *name) { - ppmacro *macro = (ppmacro*)mem_a(sizeof(ppmacro)); - memset(macro, 0, sizeof(*macro)); - macro->name = util_strdup(name); - return macro; + ppmacro *macro = (ppmacro*)mem_a(sizeof(ppmacro)); + memset(macro, 0, sizeof(*macro)); + macro->name = util_strdup(name); + return macro; } void ppmacro_delete(ppmacro *self) { - vec_free(self->params); - vec_free(self->output); - mem_d(self->name); - mem_d(self); + vec_free(self->params); + vec_free(self->output); + mem_d(self->name); + mem_d(self); } ftepp_t* ftepp_init() { - ftepp_t *ftepp; + ftepp_t *ftepp; - ftepp = (ftepp_t*)mem_a(sizeof(*ftepp)); - memset(ftepp, 0, sizeof(*ftepp)); + ftepp = (ftepp_t*)mem_a(sizeof(*ftepp)); + memset(ftepp, 0, sizeof(*ftepp)); - return ftepp; + return ftepp; } void ftepp_delete(ftepp_t *self) { - vec_free(self->macros); - vec_free(self->conditions); - mem_d(self); + vec_free(self->macros); + vec_free(self->conditions); + mem_d(self); } 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; + 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; } static inline int ftepp_next(ftepp_t *ftepp) { - return (ftepp->token = lex_do(ftepp->lex)); + return (ftepp->token = lex_do(ftepp->lex)); } /* Important: this does not skip newlines! */ static bool ftepp_skipspace(ftepp_t *ftepp) { - if (ftepp->token != TOKEN_WHITE) - return true; - while (ftepp_next(ftepp) == TOKEN_WHITE) {} - if (ftepp->token >= TOKEN_EOF) { - ftepp_error(ftepp, "unexpected end of preprocessor directive"); - return false; - } - return true; + if (ftepp->token != TOKEN_WHITE) + return true; + while (ftepp_next(ftepp) == TOKEN_WHITE) {} + if (ftepp->token >= TOKEN_EOF) { + ftepp_error(ftepp, "unexpected end of preprocessor directive"); + return false; + } + return true; } /** @@ -153,31 +153,31 @@ static bool ftepp_skipspace(ftepp_t *ftepp) */ static bool ftepp_define(ftepp_t *ftepp) { - ppmacro *macro; - (void)ftepp_next(ftepp); - if (!ftepp_skipspace(ftepp)) - return false; + ppmacro *macro; + (void)ftepp_next(ftepp); + if (!ftepp_skipspace(ftepp)) + return false; - switch (ftepp->token) { - case TOKEN_IDENT: - case TOKEN_TYPENAME: - case TOKEN_KEYWORD: - macro = ppmacro_new(ftepp_ctx(ftepp), ftepp_tokval(ftepp)); - break; - default: - ftepp_error(ftepp, "expected macro name"); - return false; - } + switch (ftepp->token) { + case TOKEN_IDENT: + case TOKEN_TYPENAME: + case TOKEN_KEYWORD: + macro = ppmacro_new(ftepp_ctx(ftepp), ftepp_tokval(ftepp)); + break; + default: + ftepp_error(ftepp, "expected macro name"); + return false; + } - (void)ftepp_next(ftepp); - if (!ftepp_skipspace(ftepp)) - return false; - if (ftepp->token != TOKEN_EOL) { - ftepp_error(ftepp, "stray tokens after macro"); - return false; - } - vec_push(ftepp->macros, macro); - return true; + (void)ftepp_next(ftepp); + if (!ftepp_skipspace(ftepp)) + return false; + if (ftepp->token != TOKEN_EOL) { + ftepp_error(ftepp, "stray tokens after macro"); + return false; + } + vec_push(ftepp->macros, macro); + return true; } /** @@ -197,68 +197,78 @@ static bool ftepp_define(ftepp_t *ftepp) */ static bool ftepp_if_expr(ftepp_t *ftepp, bool *out) { - ppmacro *macro; - while (ftepp->token != TOKEN_EOL) { - switch (ftepp->token) { - case TOKEN_IDENT: - case TOKEN_TYPENAME: - case TOKEN_KEYWORD: - macro = ftepp_macro_find(ftepp, ftepp_tokval(ftepp)); - if (!macro || !vec_size(macro->output)) { - *out = false; - } else { - /* This does not expand recursively! */ - switch (macro->output[0]->token) { - case TOKEN_INTCONST: - *out = !!(macro->output[0]->constval.f); - break; - case TOKEN_FLOATCONST: - *out = !!(macro->output[0]->constval.f); - break; - default: - *out = false; - break; - } - } - break; - case TOKEN_STRINGCONST: - *out = false; - break; - case TOKEN_INTCONST: - *out = !!(ftepp->lex->tok.constval.i); - break; - case TOKEN_FLOATCONST: - *out = !!(ftepp->lex->tok.constval.f); - break; + ppmacro *macro; + while (ftepp->token != TOKEN_EOL) { + switch (ftepp->token) { + case TOKEN_IDENT: + case TOKEN_TYPENAME: + case TOKEN_KEYWORD: + macro = ftepp_macro_find(ftepp, ftepp_tokval(ftepp)); + if (!macro || !vec_size(macro->output)) { + *out = false; + } else { + /* This does not expand recursively! */ + switch (macro->output[0]->token) { + case TOKEN_INTCONST: + *out = !!(macro->output[0]->constval.f); + break; + case TOKEN_FLOATCONST: + *out = !!(macro->output[0]->constval.f); + break; + default: + *out = false; + break; + } + } + break; + case TOKEN_STRINGCONST: + *out = false; + break; + case TOKEN_INTCONST: + *out = !!(ftepp->lex->tok.constval.i); + break; + case TOKEN_FLOATCONST: + *out = !!(ftepp->lex->tok.constval.f); + break; - default: - ftepp_error(ftepp, "junk in #if"); - return false; - } - } - (void)ftepp_next(ftepp); - return true; + default: + ftepp_error(ftepp, "junk in #if"); + return false; + } + + ftepp_next(ftepp); + if (!ftepp_skipspace(ftepp)) + return false; + + switch (ftepp->token) { + } + } + (void)ftepp_next(ftepp); + return true; } static bool ftepp_if(ftepp_t *ftepp, ppcondition *cond) { - bool result = false; + bool result = false; - memset(cond, 0, sizeof(*cond)); - (void)ftepp_next(ftepp); + ftepp->lex->flags.noops = false; - if (!ftepp_skipspace(ftepp)) - return false; - if (ftepp->token == TOKEN_EOL) { - ftepp_error(ftepp, "expected expression for #if-directive"); - return false; - } + memset(cond, 0, sizeof(*cond)); + (void)ftepp_next(ftepp); - if (!ftepp_if_expr(ftepp, &result)) - return false; + if (!ftepp_skipspace(ftepp)) + return false; + if (ftepp->token == TOKEN_EOL) { + ftepp_error(ftepp, "expected expression for #if-directive"); + return false; + } - cond->on = result; - return true; + if (!ftepp_if_expr(ftepp, &result)) + return false; + ftepp->lex->flags.noops = true; + + cond->on = result; + return true; } /** @@ -266,207 +276,207 @@ static bool ftepp_if(ftepp_t *ftepp, ppcondition *cond) */ static bool ftepp_ifdef(ftepp_t *ftepp, ppcondition *cond) { - ppmacro *macro; - memset(cond, 0, sizeof(*cond)); - (void)ftepp_next(ftepp); - if (!ftepp_skipspace(ftepp)) - return false; + ppmacro *macro; + memset(cond, 0, sizeof(*cond)); + (void)ftepp_next(ftepp); + if (!ftepp_skipspace(ftepp)) + return false; - switch (ftepp->token) { - case TOKEN_IDENT: - case TOKEN_TYPENAME: - case TOKEN_KEYWORD: - macro = ftepp_macro_find(ftepp, ftepp_tokval(ftepp)); - break; - default: - ftepp_error(ftepp, "expected macro name"); - return false; - } + switch (ftepp->token) { + case TOKEN_IDENT: + case TOKEN_TYPENAME: + case TOKEN_KEYWORD: + macro = ftepp_macro_find(ftepp, ftepp_tokval(ftepp)); + break; + default: + ftepp_error(ftepp, "expected macro name"); + return false; + } - (void)ftepp_next(ftepp); - if (!ftepp_skipspace(ftepp)) - return false; - if (ftepp->token != TOKEN_EOL) { - ftepp_error(ftepp, "stray tokens after #ifdef"); - return false; - } - cond->on = !!macro; - return true; + (void)ftepp_next(ftepp); + if (!ftepp_skipspace(ftepp)) + return false; + if (ftepp->token != TOKEN_EOL) { + ftepp_error(ftepp, "stray tokens after #ifdef"); + return false; + } + cond->on = !!macro; + return true; } /* Basic structure handlers */ static bool ftepp_else_allowed(ftepp_t *ftepp) { - if (!vec_size(ftepp->conditions)) { - ftepp_error(ftepp, "#else without #if"); - return false; - } - if (vec_last(ftepp->conditions).had_else) { - ftepp_error(ftepp, "multiple #else for a single #if"); - return false; - } - return true; + if (!vec_size(ftepp->conditions)) { + ftepp_error(ftepp, "#else without #if"); + return false; + } + if (vec_last(ftepp->conditions).had_else) { + ftepp_error(ftepp, "multiple #else for a single #if"); + return false; + } + return true; } static bool ftepp_hash(ftepp_t *ftepp) { - ppcondition cond; - ppcondition *pc; + ppcondition cond; + ppcondition *pc; - lex_ctx ctx = ftepp_ctx(ftepp); + lex_ctx ctx = ftepp_ctx(ftepp); - if (!ftepp_skipspace(ftepp)) - return false; + if (!ftepp_skipspace(ftepp)) + return false; - switch (ftepp->token) { - case TOKEN_KEYWORD: - case TOKEN_IDENT: - case TOKEN_TYPENAME: - if (!strcmp(ftepp_tokval(ftepp), "define")) { - return ftepp_define(ftepp); - } - else if (!strcmp(ftepp_tokval(ftepp), "ifdef")) { - if (!ftepp_ifdef(ftepp, &cond)) - return false; - vec_push(ftepp->conditions, cond); - return true; - } - else if (!strcmp(ftepp_tokval(ftepp), "ifndef")) { - if (!ftepp_ifdef(ftepp, &cond)) - return false; - cond.on = !cond.on; - vec_push(ftepp->conditions, cond); - return true; - } - else if (!strcmp(ftepp_tokval(ftepp), "elifdef")) { - if (!ftepp_else_allowed(ftepp)) - return false; - if (!ftepp_ifdef(ftepp, &cond)) - return false; - pc = &vec_last(ftepp->conditions); - pc->on = !pc->was_on && cond.on; - pc->was_on = pc->was_on || pc->on; - return true; - } - else if (!strcmp(ftepp_tokval(ftepp), "elifndef")) { - if (!ftepp_else_allowed(ftepp)) - return false; - if (!ftepp_ifdef(ftepp, &cond)) - return false; - cond.on = !cond.on; - pc = &vec_last(ftepp->conditions); - pc->on = !pc->was_on && cond.on; - pc->was_on = pc->was_on || pc->on; - return true; - } - else if (!strcmp(ftepp_tokval(ftepp), "elif")) { - if (!ftepp_else_allowed(ftepp)) - return false; - if (!ftepp_if(ftepp, &cond)) - return false; - pc = &vec_last(ftepp->conditions); - pc->on = !pc->was_on && cond.on; - pc->was_on = pc->was_on || pc->on; - return true; - } - else if (!strcmp(ftepp_tokval(ftepp), "if")) { - if (!ftepp_if(ftepp, &cond)) - return false; - vec_push(ftepp->conditions, cond); - return true; - } - else if (!strcmp(ftepp_tokval(ftepp), "else")) { - if (!ftepp_else_allowed(ftepp)) - return false; - pc = &vec_last(ftepp->conditions); - pc->on = !pc->was_on; - pc->had_else = true; - return true; - } - else if (!strcmp(ftepp_tokval(ftepp), "endif")) { - if (!vec_size(ftepp->conditions)) { - ftepp_error(ftepp, "#endif without #if"); - return false; - } - vec_pop(ftepp->conditions); - break; - } - else { - ftepp_error(ftepp, "unrecognized preprocessor directive: `%s`", ftepp_tokval(ftepp)); - return false; - } - break; - default: - ftepp_error(ftepp, "unexpected preprocessor token: `%s`", ftepp_tokval(ftepp)); - return false; - case TOKEN_EOL: - ftepp_errorat(ftepp, ctx, "empty preprocessor directive"); - return false; - case TOKEN_EOF: - ftepp_error(ftepp, "missing newline at end of file", ftepp_tokval(ftepp)); - return false; - } - return true; + switch (ftepp->token) { + case TOKEN_KEYWORD: + case TOKEN_IDENT: + case TOKEN_TYPENAME: + if (!strcmp(ftepp_tokval(ftepp), "define")) { + return ftepp_define(ftepp); + } + else if (!strcmp(ftepp_tokval(ftepp), "ifdef")) { + if (!ftepp_ifdef(ftepp, &cond)) + return false; + vec_push(ftepp->conditions, cond); + return true; + } + else if (!strcmp(ftepp_tokval(ftepp), "ifndef")) { + if (!ftepp_ifdef(ftepp, &cond)) + return false; + cond.on = !cond.on; + vec_push(ftepp->conditions, cond); + return true; + } + else if (!strcmp(ftepp_tokval(ftepp), "elifdef")) { + if (!ftepp_else_allowed(ftepp)) + return false; + if (!ftepp_ifdef(ftepp, &cond)) + return false; + pc = &vec_last(ftepp->conditions); + pc->on = !pc->was_on && cond.on; + pc->was_on = pc->was_on || pc->on; + return true; + } + else if (!strcmp(ftepp_tokval(ftepp), "elifndef")) { + if (!ftepp_else_allowed(ftepp)) + return false; + if (!ftepp_ifdef(ftepp, &cond)) + return false; + cond.on = !cond.on; + pc = &vec_last(ftepp->conditions); + pc->on = !pc->was_on && cond.on; + pc->was_on = pc->was_on || pc->on; + return true; + } + else if (!strcmp(ftepp_tokval(ftepp), "elif")) { + if (!ftepp_else_allowed(ftepp)) + return false; + if (!ftepp_if(ftepp, &cond)) + return false; + pc = &vec_last(ftepp->conditions); + pc->on = !pc->was_on && cond.on; + pc->was_on = pc->was_on || pc->on; + return true; + } + else if (!strcmp(ftepp_tokval(ftepp), "if")) { + if (!ftepp_if(ftepp, &cond)) + return false; + vec_push(ftepp->conditions, cond); + return true; + } + else if (!strcmp(ftepp_tokval(ftepp), "else")) { + if (!ftepp_else_allowed(ftepp)) + return false; + pc = &vec_last(ftepp->conditions); + pc->on = !pc->was_on; + pc->had_else = true; + return true; + } + else if (!strcmp(ftepp_tokval(ftepp), "endif")) { + if (!vec_size(ftepp->conditions)) { + ftepp_error(ftepp, "#endif without #if"); + return false; + } + vec_pop(ftepp->conditions); + break; + } + else { + ftepp_error(ftepp, "unrecognized preprocessor directive: `%s`", ftepp_tokval(ftepp)); + return false; + } + break; + default: + ftepp_error(ftepp, "unexpected preprocessor token: `%s`", ftepp_tokval(ftepp)); + return false; + case TOKEN_EOL: + ftepp_errorat(ftepp, ctx, "empty preprocessor directive"); + return false; + case TOKEN_EOF: + ftepp_error(ftepp, "missing newline at end of file", ftepp_tokval(ftepp)); + return false; + } + return true; } static void ftepp_out(ftepp_t *ftepp, const char *str) { - if (!vec_size(ftepp->conditions) || - vec_last(ftepp->conditions).on) - { - printf("%s", str); - } + if (!vec_size(ftepp->conditions) || + vec_last(ftepp->conditions).on) + { + printf("%s", str); + } } static bool ftepp_preprocess(ftepp_t *ftepp) { - bool newline = true; + bool newline = true; - ftepp->lex->flags.preprocessing = true; + ftepp->lex->flags.preprocessing = true; - ftepp_next(ftepp); - do - { - if (ftepp->token >= TOKEN_EOF) - break; + ftepp_next(ftepp); + do + { + if (ftepp->token >= TOKEN_EOF) + break; - ftepp->newline = newline; - newline = false; + ftepp->newline = newline; + newline = false; - switch (ftepp->token) { - case '#': - if (!ftepp->newline) { - ftepp_out(ftepp, ftepp_tokval(ftepp)); - ftepp_next(ftepp); - break; - } - if (ftepp_next(ftepp) >= TOKEN_EOF) { - ftepp_error(ftepp, "error in preprocessor directive"); - ftepp->token = TOKEN_ERROR; - break; - } - if (!ftepp_hash(ftepp)) - ftepp->token = TOKEN_ERROR; - break; - case TOKEN_EOL: - newline = true; - ftepp_out(ftepp, "\n"); - ftepp_next(ftepp); - break; - default: - ftepp_out(ftepp, ftepp_tokval(ftepp)); - ftepp_next(ftepp); - break; - } - } while (!ftepp->errors && ftepp->token < TOKEN_EOF); + switch (ftepp->token) { + case '#': + if (!ftepp->newline) { + ftepp_out(ftepp, ftepp_tokval(ftepp)); + ftepp_next(ftepp); + break; + } + if (ftepp_next(ftepp) >= TOKEN_EOF) { + ftepp_error(ftepp, "error in preprocessor directive"); + ftepp->token = TOKEN_ERROR; + break; + } + if (!ftepp_hash(ftepp)) + ftepp->token = TOKEN_ERROR; + break; + case TOKEN_EOL: + newline = true; + ftepp_out(ftepp, "\n"); + ftepp_next(ftepp); + break; + default: + ftepp_out(ftepp, ftepp_tokval(ftepp)); + ftepp_next(ftepp); + break; + } + } while (!ftepp->errors && ftepp->token < TOKEN_EOF); - ftepp_delete(ftepp); - return (ftepp->token == TOKEN_EOF); + ftepp_delete(ftepp); + return (ftepp->token == TOKEN_EOF); } bool ftepp_preprocess_file(const char *filename) { - ftepp_t *ftepp = ftepp_init(); + ftepp_t *ftepp = ftepp_init(); ftepp->lex = lex_open(filename); if (!ftepp->lex) { con_out("failed to open file \"%s\"\n", filename); @@ -477,7 +487,7 @@ bool ftepp_preprocess_file(const char *filename) bool ftepp_preprocess_string(const char *name, const char *str) { - ftepp_t *ftepp = ftepp_init(); + ftepp_t *ftepp = ftepp_init(); ftepp->lex = lex_open_string(str, strlen(str), name); if (!ftepp->lex) { con_out("failed to create lexer for string \"%s\"\n", name); From 6ab09ef3bf650b69559429f2a2dd4da2706bc046 Mon Sep 17 00:00:00 2001 From: "Wolfgang (Blub) Bumiller" Date: Fri, 16 Nov 2012 19:28:20 +0100 Subject: [PATCH 07/59] Parsing some basic #ifs with defined() --- ftepp.c | 172 ++++++++++++++++++++++++++++++++++++++++---------------- 1 file changed, 123 insertions(+), 49 deletions(-) diff --git a/ftepp.c b/ftepp.c index c1355f0..51a83a1 100644 --- a/ftepp.c +++ b/ftepp.c @@ -198,53 +198,119 @@ static bool ftepp_define(ftepp_t *ftepp) static bool ftepp_if_expr(ftepp_t *ftepp, bool *out) { ppmacro *macro; - while (ftepp->token != TOKEN_EOL) { - switch (ftepp->token) { - case TOKEN_IDENT: - case TOKEN_TYPENAME: - case TOKEN_KEYWORD: + + if (!ftepp_skipspace(ftepp)) + return false; + + switch (ftepp->token) { + case TOKEN_IDENT: + case TOKEN_TYPENAME: + case TOKEN_KEYWORD: + if (!strcmp(ftepp_tokval(ftepp), "defined")) { + ftepp->lex->flags.noops = true; + ftepp_next(ftepp); + if (!ftepp_skipspace(ftepp)) + return false; + if (ftepp->token != '(') { + ftepp_error(ftepp, "`defined` keyword in #if requires a macro name in parenthesis"); + return false; + } + ftepp->lex->flags.noops = false; + ftepp_next(ftepp); + if (!ftepp_skipspace(ftepp)) + return false; + if (ftepp->token != TOKEN_IDENT && + ftepp->token != TOKEN_TYPENAME && + ftepp->token != TOKEN_KEYWORD) + { + ftepp_error(ftepp, "defined() used on an unexpected token type"); + return false; + } macro = ftepp_macro_find(ftepp, ftepp_tokval(ftepp)); - if (!macro || !vec_size(macro->output)) { - *out = false; - } else { - /* This does not expand recursively! */ - switch (macro->output[0]->token) { - case TOKEN_INTCONST: - *out = !!(macro->output[0]->constval.f); - break; - case TOKEN_FLOATCONST: - *out = !!(macro->output[0]->constval.f); - break; - default: - *out = false; - break; - } + *out = !!macro; + ftepp_next(ftepp); + if (!ftepp_skipspace(ftepp)) + return false; + if (ftepp->token != ')') { + ftepp_error(ftepp, "expected closing paren"); + return false; } break; - case TOKEN_STRINGCONST: - *out = false; - break; - case TOKEN_INTCONST: - *out = !!(ftepp->lex->tok.constval.i); - break; - case TOKEN_FLOATCONST: - *out = !!(ftepp->lex->tok.constval.f); - break; + } - default: - ftepp_error(ftepp, "junk in #if"); + macro = ftepp_macro_find(ftepp, ftepp_tokval(ftepp)); + if (!macro || !vec_size(macro->output)) { + *out = false; + } else { + /* This does not expand recursively! */ + switch (macro->output[0]->token) { + case TOKEN_INTCONST: + *out = !!(macro->output[0]->constval.f); + break; + case TOKEN_FLOATCONST: + *out = !!(macro->output[0]->constval.f); + break; + default: + *out = false; + break; + } + } + break; + case TOKEN_STRINGCONST: + *out = false; + break; + case TOKEN_INTCONST: + *out = !!(ftepp->lex->tok.constval.i); + break; + case TOKEN_FLOATCONST: + *out = !!(ftepp->lex->tok.constval.f); + break; + + case '(': + ftepp_next(ftepp); + if (!ftepp_if_expr(ftepp, out)) return false; - } + if (ftepp->token != ')') { + ftepp_error(ftepp, "expected closing paren in #if expression"); + return false; + } + break; + + default: + ftepp_error(ftepp, "junk in #if"); + return false; + } + + ftepp_next(ftepp); + if (!ftepp_skipspace(ftepp)) + return false; + + if (ftepp->token == ')') + return true; + + if (ftepp->token != TOKEN_OPERATOR) + return true; + + if (!strcmp(ftepp_tokval(ftepp), "&&") || + !strcmp(ftepp_tokval(ftepp), "||")) + { + bool next = false; + char opc = ftepp_tokval(ftepp)[0]; ftepp_next(ftepp); - if (!ftepp_skipspace(ftepp)) + if (!ftepp_if_expr(ftepp, &next)) return false; - switch (ftepp->token) { - } + if (opc == '&') + *out = *out && next; + else + *out = *out || next; + return true; + } + else { + ftepp_error(ftepp, "junk after #if"); + return false; } - (void)ftepp_next(ftepp); - return true; } static bool ftepp_if(ftepp_t *ftepp, ppcondition *cond) @@ -338,15 +404,17 @@ static bool ftepp_hash(ftepp_t *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); - return true; + break; } else if (!strcmp(ftepp_tokval(ftepp), "ifndef")) { if (!ftepp_ifdef(ftepp, &cond)) return false; cond.on = !cond.on; + cond.was_on = cond.on; vec_push(ftepp->conditions, cond); - return true; + break; } else if (!strcmp(ftepp_tokval(ftepp), "elifdef")) { if (!ftepp_else_allowed(ftepp)) @@ -356,7 +424,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; - return true; + break; } else if (!strcmp(ftepp_tokval(ftepp), "elifndef")) { if (!ftepp_else_allowed(ftepp)) @@ -367,7 +435,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; - return true; + break; } else if (!strcmp(ftepp_tokval(ftepp), "elif")) { if (!ftepp_else_allowed(ftepp)) @@ -377,13 +445,14 @@ 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; - return true; + break; } else if (!strcmp(ftepp_tokval(ftepp), "if")) { if (!ftepp_if(ftepp, &cond)) return false; + cond.was_on = cond.on; vec_push(ftepp->conditions, cond); - return true; + break; } else if (!strcmp(ftepp_tokval(ftepp), "else")) { if (!ftepp_else_allowed(ftepp)) @@ -391,7 +460,8 @@ static bool ftepp_hash(ftepp_t *ftepp) pc = &vec_last(ftepp->conditions); pc->on = !pc->was_on; pc->had_else = true; - return true; + ftepp_next(ftepp); + break; } else if (!strcmp(ftepp_tokval(ftepp), "endif")) { if (!vec_size(ftepp->conditions)) { @@ -399,6 +469,7 @@ static bool ftepp_hash(ftepp_t *ftepp) return false; } vec_pop(ftepp->conditions); + ftepp_next(ftepp); break; } else { @@ -416,12 +487,15 @@ static bool ftepp_hash(ftepp_t *ftepp) ftepp_error(ftepp, "missing newline at end of file", ftepp_tokval(ftepp)); return false; } + if (!ftepp_skipspace(ftepp)) + return false; return true; } -static void ftepp_out(ftepp_t *ftepp, const char *str) +static void ftepp_out(ftepp_t *ftepp, const char *str, bool ignore_cond) { - if (!vec_size(ftepp->conditions) || + if (ignore_cond || + !vec_size(ftepp->conditions) || vec_last(ftepp->conditions).on) { printf("%s", str); @@ -446,7 +520,7 @@ static bool ftepp_preprocess(ftepp_t *ftepp) switch (ftepp->token) { case '#': if (!ftepp->newline) { - ftepp_out(ftepp, ftepp_tokval(ftepp)); + ftepp_out(ftepp, ftepp_tokval(ftepp), false); ftepp_next(ftepp); break; } @@ -460,11 +534,11 @@ static bool ftepp_preprocess(ftepp_t *ftepp) break; case TOKEN_EOL: newline = true; - ftepp_out(ftepp, "\n"); + ftepp_out(ftepp, "\n", true); ftepp_next(ftepp); break; default: - ftepp_out(ftepp, ftepp_tokval(ftepp)); + ftepp_out(ftepp, ftepp_tokval(ftepp), false); ftepp_next(ftepp); break; } From bdd8b8275ee9f3cca6fd02500cc7f8627890e105 Mon Sep 17 00:00:00 2001 From: "Wolfgang (Blub) Bumiller" Date: Fri, 16 Nov 2012 19:32:02 +0100 Subject: [PATCH 08/59] handling the noops flag better now --- ftepp.c | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/ftepp.c b/ftepp.c index 51a83a1..6645166 100644 --- a/ftepp.c +++ b/ftepp.c @@ -207,7 +207,6 @@ static bool ftepp_if_expr(ftepp_t *ftepp, bool *out) case TOKEN_TYPENAME: case TOKEN_KEYWORD: if (!strcmp(ftepp_tokval(ftepp), "defined")) { - ftepp->lex->flags.noops = true; ftepp_next(ftepp); if (!ftepp_skipspace(ftepp)) return false; @@ -215,7 +214,6 @@ static bool ftepp_if_expr(ftepp_t *ftepp, bool *out) ftepp_error(ftepp, "`defined` keyword in #if requires a macro name in parenthesis"); return false; } - ftepp->lex->flags.noops = false; ftepp_next(ftepp); if (!ftepp_skipspace(ftepp)) return false; @@ -281,9 +279,11 @@ static bool ftepp_if_expr(ftepp_t *ftepp, bool *out) return false; } + ftepp->lex->flags.noops = false; ftepp_next(ftepp); if (!ftepp_skipspace(ftepp)) return false; + ftepp->lex->flags.noops = true; if (ftepp->token == ')') return true; @@ -317,8 +317,6 @@ static bool ftepp_if(ftepp_t *ftepp, ppcondition *cond) { bool result = false; - ftepp->lex->flags.noops = false; - memset(cond, 0, sizeof(*cond)); (void)ftepp_next(ftepp); @@ -331,7 +329,6 @@ static bool ftepp_if(ftepp_t *ftepp, ppcondition *cond) if (!ftepp_if_expr(ftepp, &result)) return false; - ftepp->lex->flags.noops = true; cond->on = result; return true; @@ -507,6 +504,7 @@ static bool ftepp_preprocess(ftepp_t *ftepp) bool newline = true; ftepp->lex->flags.preprocessing = true; + ftepp->lex->flags.noops = true; ftepp_next(ftepp); do From 45a354d664b8862e3054a825d88af6d6ecf6a2c3 Mon Sep 17 00:00:00 2001 From: "Wolfgang (Blub) Bumiller" Date: Fri, 16 Nov 2012 20:02:27 +0100 Subject: [PATCH 09/59] Unary NOT in #if --- ftepp.c | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/ftepp.c b/ftepp.c index 6645166..a240643 100644 --- a/ftepp.c +++ b/ftepp.c @@ -198,10 +198,18 @@ static bool ftepp_define(ftepp_t *ftepp) static bool ftepp_if_expr(ftepp_t *ftepp, bool *out) { ppmacro *macro; + bool wasnot = false; if (!ftepp_skipspace(ftepp)) return false; + while (ftepp->token == '!') { + wasnot = true; + ftepp_next(ftepp); + if (!ftepp_skipspace(ftepp)) + return false; + } + switch (ftepp->token) { case TOKEN_IDENT: case TOKEN_TYPENAME: @@ -278,6 +286,8 @@ static bool ftepp_if_expr(ftepp_t *ftepp, bool *out) ftepp_error(ftepp, "junk in #if"); return false; } + if (wasnot) + *out = !*out; ftepp->lex->flags.noops = false; ftepp_next(ftepp); From 34646cfca7876cbf23c5789ca040efb8441e2d27 Mon Sep 17 00:00:00 2001 From: "Wolfgang (Blub) Bumiller" Date: Fri, 16 Nov 2012 20:04:30 +0100 Subject: [PATCH 10/59] Update #if description comment --- ftepp.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/ftepp.c b/ftepp.c index a240643..32312ba 100644 --- a/ftepp.c +++ b/ftepp.c @@ -185,12 +185,13 @@ static bool ftepp_define(ftepp_t *ftepp) * defined(FOO) => true if FOO was #defined regardless of parameters or contents * => True if the number is not 0 * ! => True if the factor yields false + * !! => ERROR on 2 or more unary nots * => becomes the macro's FIRST token regardless of parameters * && => True if both expressions are true * || => True if either expression is true * => False * => False (remember for macros the rule applies instead) - * Unary + and - are skipped + * Unary + and - are weird and wrong in fteqcc so we don't allow them * parenthesis in expressions are allowed * parameter lists on macros are errors * No mathematical calculations are executed From d4fe6ed82dff8773a66802c8045b18081274709e Mon Sep 17 00:00:00 2001 From: "Wolfgang (Blub) Bumiller" Date: Fri, 16 Nov 2012 20:15:04 +0100 Subject: [PATCH 11/59] fixing up dtors, parsing parameters of macros --- ftepp.c | 64 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 63 insertions(+), 1 deletion(-) diff --git a/ftepp.c b/ftepp.c index 32312ba..72e6a48 100644 --- a/ftepp.c +++ b/ftepp.c @@ -87,6 +87,21 @@ static void ftepp_error(ftepp_t *ftepp, const char *fmt, ...) va_end(ap); } +pptoken *pptoken_make(ftepp_t *ftepp) +{ + pptoken *token = (pptoken*)mem_a(sizeof(pptoken)); + token->token = ftepp->token; + token->value = util_strdup(ftepp_tokval(ftepp)); + memcpy(&token->constval, &ftepp->lex->tok.constval, sizeof(token->constval)); + return token; +} + +void pptoken_delete(pptoken *self) +{ + mem_d(self->value); + mem_d(self); +} + ppmacro *ppmacro_new(lex_ctx ctx, const char *name) { ppmacro *macro = (ppmacro*)mem_a(sizeof(ppmacro)); @@ -97,7 +112,12 @@ ppmacro *ppmacro_new(lex_ctx ctx, const char *name) void ppmacro_delete(ppmacro *self) { + size_t i; + for (i = 0; i < vec_size(self->params); ++i) + mem_d(self->params[i]); vec_free(self->params); + for (i = 0; i < vec_size(self->output); ++i) + pptoken_delete(self->output[i]); vec_free(self->output); mem_d(self->name); mem_d(self); @@ -115,8 +135,12 @@ ftepp_t* ftepp_init() void ftepp_delete(ftepp_t *self) { + size_t i; + 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); mem_d(self); } @@ -151,6 +175,35 @@ static bool ftepp_skipspace(ftepp_t *ftepp) /** * The huge macro parsing code... */ +static bool ftepp_define_params(ftepp_t *ftepp, ppmacro *macro) +{ + do { + ftepp_next(ftepp); + if (!ftepp_skipspace(ftepp)) + return false; + switch (ftepp->token) { + case TOKEN_IDENT: + case TOKEN_TYPENAME: + case TOKEN_KEYWORD: + 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; + } while (ftepp->token == ','); + if (ftepp->token != ')') { + ftepp_error(ftepp, "expected closing paren after macro parameter list"); + return false; + } + ftepp_next(ftepp); + /* skipspace happens in ftepp_define */ + return true; +} + static bool ftepp_define(ftepp_t *ftepp) { ppmacro *macro; @@ -170,8 +223,16 @@ static bool ftepp_define(ftepp_t *ftepp) } (void)ftepp_next(ftepp); + + if (ftepp->token == '(') { + macro->has_params = true; + if (!ftepp_define_params(ftepp, macro)) + return false; + } + if (!ftepp_skipspace(ftepp)) return false; + if (ftepp->token != TOKEN_EOL) { ftepp_error(ftepp, "stray tokens after macro"); return false; @@ -553,8 +614,9 @@ static bool ftepp_preprocess(ftepp_t *ftepp) } } while (!ftepp->errors && ftepp->token < TOKEN_EOF); + newline = ftepp->token == TOKEN_EOF; ftepp_delete(ftepp); - return (ftepp->token == TOKEN_EOF); + return newline; } bool ftepp_preprocess_file(const char *filename) From 053d8fb0e20044eb0a3756102d3ad5372d9b52e2 Mon Sep 17 00:00:00 2001 From: "Wolfgang (Blub) Bumiller" Date: Fri, 16 Nov 2012 20:29:20 +0100 Subject: [PATCH 12/59] mergelines flag for the lexer to handle a backslash-newline --- ftepp.c | 3 ++- lexer.c | 15 ++++++++++++++- lexer.h | 1 + 3 files changed, 17 insertions(+), 2 deletions(-) diff --git a/ftepp.c b/ftepp.c index 72e6a48..b29b96b 100644 --- a/ftepp.c +++ b/ftepp.c @@ -576,7 +576,8 @@ static bool ftepp_preprocess(ftepp_t *ftepp) bool newline = true; ftepp->lex->flags.preprocessing = true; - ftepp->lex->flags.noops = true; + ftepp->lex->flags.mergelines = true; + ftepp->lex->flags.noops = true; ftepp_next(ftepp); do diff --git a/lexer.c b/lexer.c index 96644c2..1d1c42f 100644 --- a/lexer.c +++ b/lexer.c @@ -675,7 +675,20 @@ int lex_do(lex_file *lex) return TOKEN_FATAL; #endif - ch = lex_skipwhite(lex); + while (true) { + ch = lex_skipwhite(lex); + if (!lex->flags.mergelines || ch != '\\') + break; + ch = lex_getch(lex); + if (ch != '\n') { + lex_ungetch(lex, ch); + ch = '\\'; + break; + } + /* we reached a linemerge */ + continue; + } + lex->sline = lex->line; lex->tok.ctx.line = lex->sline; lex->tok.ctx.file = lex->name; diff --git a/lexer.h b/lexer.h index 76d2b7a..b8e2e01 100644 --- a/lexer.h +++ b/lexer.h @@ -119,6 +119,7 @@ typedef struct { bool noops; bool nodigraphs; /* used when lexing string constants */ bool preprocessing; /* whitespace and EOLs become actual tokens */ + bool mergelines; /* backslash at the end of a line escapes the newline */ } flags; int framevalue; From 52d7a5d7bebd8a2eea8f1161800401662baf2f57 Mon Sep 17 00:00:00 2001 From: "Wolfgang (Blub) Bumiller" Date: Fri, 16 Nov 2012 20:32:03 +0100 Subject: [PATCH 13/59] Set the mergelines flag only for preprocessor commands --- ftepp.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/ftepp.c b/ftepp.c index b29b96b..50461e6 100644 --- a/ftepp.c +++ b/ftepp.c @@ -576,7 +576,7 @@ static bool ftepp_preprocess(ftepp_t *ftepp) bool newline = true; ftepp->lex->flags.preprocessing = true; - ftepp->lex->flags.mergelines = true; + ftepp->lex->flags.mergelines = false; ftepp->lex->flags.noops = true; ftepp_next(ftepp); @@ -595,6 +595,7 @@ static bool ftepp_preprocess(ftepp_t *ftepp) ftepp_next(ftepp); break; } + ftepp->lex->flags.mergelines = true; if (ftepp_next(ftepp) >= TOKEN_EOF) { ftepp_error(ftepp, "error in preprocessor directive"); ftepp->token = TOKEN_ERROR; @@ -602,6 +603,7 @@ static bool ftepp_preprocess(ftepp_t *ftepp) } if (!ftepp_hash(ftepp)) ftepp->token = TOKEN_ERROR; + ftepp->lex->flags.mergelines = false; break; case TOKEN_EOL: newline = true; From c48b9dbf61d9afbc480a2fb487d8181cb077792e Mon Sep 17 00:00:00 2001 From: "Wolfgang (Blub) Bumiller" Date: Fri, 16 Nov 2012 20:38:44 +0100 Subject: [PATCH 14/59] Parsing basic macro body, no special tokens yet --- ftepp.c | 28 ++++++++++++++++++++++++---- 1 file changed, 24 insertions(+), 4 deletions(-) diff --git a/ftepp.c b/ftepp.c index 50461e6..15702b0 100644 --- a/ftepp.c +++ b/ftepp.c @@ -91,7 +91,10 @@ pptoken *pptoken_make(ftepp_t *ftepp) { pptoken *token = (pptoken*)mem_a(sizeof(pptoken)); token->token = ftepp->token; - token->value = util_strdup(ftepp_tokval(ftepp)); + if (token->token == TOKEN_WHITE) + token->value = util_strdup(" "); + else + token->value = util_strdup(ftepp_tokval(ftepp)); memcpy(&token->constval, &ftepp->lex->tok.constval, sizeof(token->constval)); return token; } @@ -204,6 +207,24 @@ static bool ftepp_define_params(ftepp_t *ftepp, ppmacro *macro) return true; } +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); + if (!ftepp_skipspace(ftepp)) + return false; + } + if (ftepp->token != TOKEN_EOL) { + ftepp_error(ftepp, "unexpected junk after macro or unexpected end of file"); + return false; + } + return true; +} + static bool ftepp_define(ftepp_t *ftepp) { ppmacro *macro; @@ -233,10 +254,9 @@ static bool ftepp_define(ftepp_t *ftepp) if (!ftepp_skipspace(ftepp)) return false; - if (ftepp->token != TOKEN_EOL) { - ftepp_error(ftepp, "stray tokens after macro"); + if (!ftepp_define_body(ftepp, macro)) return false; - } + vec_push(ftepp->macros, macro); return true; } From 0bd0a430d6df2f0289c67122439678d43e1ccf05 Mon Sep 17 00:00:00 2001 From: "Wolfgang (Blub) Bumiller" Date: Fri, 16 Nov 2012 20:41:20 +0100 Subject: [PATCH 15/59] Also allow empty-parameter-list macros --- ftepp.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/ftepp.c b/ftepp.c index 15702b0..04e41bb 100644 --- a/ftepp.c +++ b/ftepp.c @@ -184,6 +184,8 @@ static bool ftepp_define_params(ftepp_t *ftepp, ppmacro *macro) ftepp_next(ftepp); if (!ftepp_skipspace(ftepp)) return false; + if (ftepp->token == ')') + break; switch (ftepp->token) { case TOKEN_IDENT: case TOKEN_TYPENAME: From 5f287fc47652957ec0f6990ba29bbcc959658109 Mon Sep 17 00:00:00 2001 From: "Wolfgang (Blub) Bumiller" Date: Fri, 16 Nov 2012 20:46:52 +0100 Subject: [PATCH 16/59] ftepp_macro_call/ftepp_skipallwhite --- ftepp.c | 43 ++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 42 insertions(+), 1 deletion(-) diff --git a/ftepp.c b/ftepp.c index 04e41bb..cd42217 100644 --- a/ftepp.c +++ b/ftepp.c @@ -175,6 +175,21 @@ static bool ftepp_skipspace(ftepp_t *ftepp) return true; } +/* this one skips EOLs as well */ +static bool ftepp_skipallwhite(ftepp_t *ftepp) +{ + if (ftepp->token != TOKEN_WHITE && ftepp->token != TOKEN_EOL) + return true; + do { + ftepp_next(ftepp); + } while (ftepp->token == TOKEN_WHITE || ftepp->token == TOKEN_EOL); + if (ftepp->token >= TOKEN_EOF) { + ftepp_error(ftepp, "unexpected end of preprocessor directive"); + return false; + } + return true; +} + /** * The huge macro parsing code... */ @@ -263,6 +278,19 @@ static bool ftepp_define(ftepp_t *ftepp) return true; } +static bool ftepp_macro_call(ftepp_t *ftepp, ppmacro *macro) +{ + ftepp_next(ftepp); + if (!ftepp_skipallwhite(ftepp)) + return false; + return true; +} + +/** + * When a macro is used we have to handle parameters as well + * as special-concatenation via ## or stringification via # + */ + /** * #if - the FTEQCC way: * defined(FOO) => true if FOO was #defined regardless of parameters or contents @@ -595,7 +623,8 @@ static void ftepp_out(ftepp_t *ftepp, const char *str, bool ignore_cond) static bool ftepp_preprocess(ftepp_t *ftepp) { - bool newline = true; + ppmacro *macro; + bool newline = true; ftepp->lex->flags.preprocessing = true; ftepp->lex->flags.mergelines = false; @@ -611,6 +640,18 @@ static bool ftepp_preprocess(ftepp_t *ftepp) newline = false; switch (ftepp->token) { + case TOKEN_KEYWORD: + case TOKEN_IDENT: + case TOKEN_TYPENAME: + macro = ftepp_macro_find(ftepp, ftepp_tokval(ftepp)); + if (!macro) { + ftepp_out(ftepp, ftepp_tokval(ftepp), false); + ftepp_next(ftepp); + break; + } + if (!ftepp_macro_call(ftepp, macro)) + ftepp->token = TOKEN_ERROR; + break; case '#': if (!ftepp->newline) { ftepp_out(ftepp, ftepp_tokval(ftepp), false); From 6c0b4a46fe1939a18ce851d63066f0f05e90a5d8 Mon Sep 17 00:00:00 2001 From: "Wolfgang (Blub) Bumiller" Date: Fri, 16 Nov 2012 20:49:37 +0100 Subject: [PATCH 17/59] moving ftepp_out further up, using it in ftepp_macro_call, generating output of parameter-less macros --- ftepp.c | 29 +++++++++++++++++++---------- 1 file changed, 19 insertions(+), 10 deletions(-) diff --git a/ftepp.c b/ftepp.c index cd42217..fcdaea7 100644 --- a/ftepp.c +++ b/ftepp.c @@ -147,6 +147,16 @@ void ftepp_delete(ftepp_t *self) 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) + { + printf("%s", str); + } +} + ppmacro* ftepp_macro_find(ftepp_t *ftepp, const char *name) { size_t i; @@ -280,7 +290,16 @@ static bool ftepp_define(ftepp_t *ftepp) static bool ftepp_macro_call(ftepp_t *ftepp, ppmacro *macro) { + size_t o; ftepp_next(ftepp); + + if (!macro->has_params) { + for (o = 0; o < vec_size(macro->output); ++o) { + ftepp_out(ftepp, macro->output[o]->value, false); + } + return true; + } + if (!ftepp_skipallwhite(ftepp)) return false; return true; @@ -611,16 +630,6 @@ static bool ftepp_hash(ftepp_t *ftepp) return true; } -static void ftepp_out(ftepp_t *ftepp, const char *str, bool ignore_cond) -{ - if (ignore_cond || - !vec_size(ftepp->conditions) || - vec_last(ftepp->conditions).on) - { - printf("%s", str); - } -} - static bool ftepp_preprocess(ftepp_t *ftepp) { ppmacro *macro; From a56c224f9cf8b68625e0ef779ab9071fc7f35b3f Mon Sep 17 00:00:00 2001 From: "Wolfgang (Blub) Bumiller" Date: Fri, 16 Nov 2012 20:50:28 +0100 Subject: [PATCH 18/59] macro_body parse function shouldn't skip whitespace inside --- ftepp.c | 3 --- 1 file changed, 3 deletions(-) diff --git a/ftepp.c b/ftepp.c index fcdaea7..7fe6b42 100644 --- a/ftepp.c +++ b/ftepp.c @@ -240,10 +240,7 @@ static bool ftepp_define_body(ftepp_t *ftepp, ppmacro *macro) while (ftepp->token != TOKEN_EOL && ftepp->token < TOKEN_EOF) { ptok = pptoken_make(ftepp); vec_push(macro->output, ptok); - ftepp_next(ftepp); - if (!ftepp_skipspace(ftepp)) - return false; } if (ftepp->token != TOKEN_EOL) { ftepp_error(ftepp, "unexpected junk after macro or unexpected end of file"); From d6888205186c1663e89119cc0443078550558d5e Mon Sep 17 00:00:00 2001 From: "Wolfgang (Blub) Bumiller" Date: Fri, 16 Nov 2012 21:20:31 +0100 Subject: [PATCH 19/59] Parsing parameters for a macro call --- ftepp.c | 115 +++++++++++++++++++++++++++++++++++++++++++++++++------- 1 file changed, 102 insertions(+), 13 deletions(-) diff --git a/ftepp.c b/ftepp.c index 7fe6b42..ec5c3f6 100644 --- a/ftepp.c +++ b/ftepp.c @@ -87,7 +87,7 @@ static void ftepp_error(ftepp_t *ftepp, const char *fmt, ...) va_end(ap); } -pptoken *pptoken_make(ftepp_t *ftepp) +static pptoken *pptoken_make(ftepp_t *ftepp) { pptoken *token = (pptoken*)mem_a(sizeof(pptoken)); token->token = ftepp->token; @@ -99,13 +99,13 @@ pptoken *pptoken_make(ftepp_t *ftepp) return token; } -void pptoken_delete(pptoken *self) +static void pptoken_delete(pptoken *self) { mem_d(self->value); mem_d(self); } -ppmacro *ppmacro_new(lex_ctx ctx, const char *name) +static ppmacro *ppmacro_new(lex_ctx ctx, const char *name) { ppmacro *macro = (ppmacro*)mem_a(sizeof(ppmacro)); memset(macro, 0, sizeof(*macro)); @@ -113,7 +113,7 @@ ppmacro *ppmacro_new(lex_ctx ctx, const char *name) return macro; } -void ppmacro_delete(ppmacro *self) +static void ppmacro_delete(ppmacro *self) { size_t i; for (i = 0; i < vec_size(self->params); ++i) @@ -126,7 +126,7 @@ void ppmacro_delete(ppmacro *self) mem_d(self); } -ftepp_t* ftepp_init() +static ftepp_t* ftepp_init() { ftepp_t *ftepp; @@ -136,7 +136,7 @@ ftepp_t* ftepp_init() return ftepp; } -void ftepp_delete(ftepp_t *self) +static void ftepp_delete(ftepp_t *self) { size_t i; for (i = 0; i < vec_size(self->macros); ++i) @@ -285,9 +285,86 @@ static bool ftepp_define(ftepp_t *ftepp) return true; } +/** + * When a macro is used we have to handle parameters as well + * as special-concatenation via ## or stringification via # + * + * Note: parenthesis can nest, so FOO((a),b) is valid, but only + * this kind of parens. Curly braces or [] don't count towards the + * paren-level. + */ +typedef struct { + pptoken **tokens; +} macroparam; + +static void macroparam_clean(macroparam *self) +{ + size_t i; + for (i = 0; i < vec_size(self->tokens); ++i) + pptoken_delete(self->tokens[i]); + vec_free(self->tokens); +} + +static bool ftepp_macro_call_params(ftepp_t *ftepp, macroparam **out_params) +{ + macroparam *params = NULL; + pptoken *ptok; + macroparam mp; + size_t parens = 0; + size_t i; + + while (true) { + mp.tokens = NULL; + while (parens || ftepp->token != ',') { + if (ftepp->token == '(') + ++parens; + else if (ftepp->token == ')') { + if (!parens) + break; + --parens; + } + ptok = pptoken_make(ftepp); + vec_push(mp.tokens, ptok); + if (ftepp_next(ftepp) >= TOKEN_EOF) { + ftepp_error(ftepp, "unexpected EOF in macro call"); + goto on_error; + } + } + vec_push(params, mp); + mp.tokens = NULL; + if (ftepp->token == ')') + break; + if (ftepp->token != ',') { + ftepp_error(ftepp, "expected closing paren or comma in macro call"); + goto on_error; + } + if (ftepp_next(ftepp) >= TOKEN_EOF) { + ftepp_error(ftepp, "unexpected EOF in macro call"); + goto on_error; + } + } + if (ftepp_next(ftepp) >= TOKEN_EOF) { + ftepp_error(ftepp, "unexpected EOF in macro call"); + goto on_error; + } + *out_params = params; + return true; + +on_error: + if (mp.tokens) + macroparam_clean(&mp); + for (i = 0; i < vec_size(params); ++i) + macroparam_clean(¶ms[i]); + vec_free(params); + return false; +} + static bool ftepp_macro_call(ftepp_t *ftepp, ppmacro *macro) { - size_t o; + size_t o; + macroparam *params = NULL; + bool retval = true; + ftepp_next(ftepp); if (!macro->has_params) { @@ -299,13 +376,25 @@ static bool ftepp_macro_call(ftepp_t *ftepp, ppmacro *macro) if (!ftepp_skipallwhite(ftepp)) return false; - return true; -} -/** - * When a macro is used we have to handle parameters as well - * as special-concatenation via ## or stringification via # - */ + if (ftepp->token != '(') { + ftepp_error(ftepp, "expected macro parameters in parenthesis"); + return false; + } + + ftepp_next(ftepp); + if (!ftepp_macro_call_params(ftepp, ¶ms)) + return false; + + ftepp_out(ftepp, "Parsed macro parameters", false); + goto cleanup; + +cleanup: + for (o = 0; o < vec_size(params); ++o) + macroparam_clean(¶ms[o]); + vec_free(params); + return retval; +} /** * #if - the FTEQCC way: From 7e660951c2086d51a154fd0fff367fb377038cf1 Mon Sep 17 00:00:00 2001 From: "Wolfgang (Blub) Bumiller" Date: Fri, 16 Nov 2012 21:27:15 +0100 Subject: [PATCH 20/59] Fix a bug in macro call parameter parsing --- ftepp.c | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/ftepp.c b/ftepp.c index ec5c3f6..fe3b783 100644 --- a/ftepp.c +++ b/ftepp.c @@ -313,7 +313,7 @@ static bool ftepp_macro_call_params(ftepp_t *ftepp, macroparam **out_params) size_t parens = 0; size_t i; - while (true) { + while (ftepp->token != ')') { mp.tokens = NULL; while (parens || ftepp->token != ',') { if (ftepp->token == '(') @@ -386,6 +386,15 @@ static bool ftepp_macro_call(ftepp_t *ftepp, ppmacro *macro) if (!ftepp_macro_call_params(ftepp, ¶ms)) return false; + if (vec_size(params) != vec_size(macro->params)) { + ftepp_error(ftepp, "macro %s expects %u paramteters, %u provided", macro->name, + (unsigned int)vec_size(macro->params), + (unsigned int)vec_size(params)); + retval = false; + goto cleanup; + } + + ftepp_out(ftepp, "Parsed macro parameters", false); goto cleanup; From 5b91c2af5ee78a1293177cc96922f24fdfbf8aa4 Mon Sep 17 00:00:00 2001 From: "Wolfgang (Blub) Bumiller" Date: Fri, 16 Nov 2012 21:37:34 +0100 Subject: [PATCH 21/59] Relax the preprocessor condition: # in the middle of a line also counts, but take care of builtin numbers since they use # as well --- ftepp.c | 22 ++++++++++++++++++---- 1 file changed, 18 insertions(+), 4 deletions(-) diff --git a/ftepp.c b/ftepp.c index fe3b783..1ac8a4c 100644 --- a/ftepp.c +++ b/ftepp.c @@ -359,6 +359,11 @@ on_error: return false; } +static bool ftepp_macro_expand(ftepp_t *ftepp, ppmacro *macro, macroparam *params) +{ + return true; +} + static bool ftepp_macro_call(ftepp_t *ftepp, ppmacro *macro) { size_t o; @@ -394,9 +399,8 @@ static bool ftepp_macro_call(ftepp_t *ftepp, ppmacro *macro) goto cleanup; } - - ftepp_out(ftepp, "Parsed macro parameters", false); - goto cleanup; + if (!ftepp_macro_expand(ftepp, macro, params)) + retval = false; cleanup: for (o = 0; o < vec_size(params); ++o) @@ -719,6 +723,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; @@ -739,9 +749,13 @@ static bool ftepp_preprocess(ftepp_t *ftepp) { if (ftepp->token >= TOKEN_EOF) break; - +#if 0 ftepp->newline = newline; newline = false; +#else + /* For the sake of FTE compatibility... FU, really */ + ftepp->newline = newline = true; +#endif switch (ftepp->token) { case TOKEN_KEYWORD: From b8a5c873601ea29e4d0afc04771e59c0d0430a1b Mon Sep 17 00:00:00 2001 From: "Wolfgang (Blub) Bumiller" Date: Fri, 16 Nov 2012 22:02:38 +0100 Subject: [PATCH 22/59] Correctly handle nested #ifs --- ftepp.c | 25 +++++++++++++++++++++---- 1 file changed, 21 insertions(+), 4 deletions(-) diff --git a/ftepp.c b/ftepp.c index 1ac8a4c..9cd2903 100644 --- a/ftepp.c +++ b/ftepp.c @@ -58,6 +58,7 @@ typedef struct { bool newline; unsigned int errors; + bool output_on; ppcondition *conditions; ppmacro **macros; } ftepp_t; @@ -133,6 +134,8 @@ static ftepp_t* ftepp_init() ftepp = (ftepp_t*)mem_a(sizeof(*ftepp)); memset(ftepp, 0, sizeof(*ftepp)); + ftepp->output_on = true; + return ftepp; } @@ -149,15 +152,21 @@ static void ftepp_delete(ftepp_t *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); } } -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) { @@ -643,6 +652,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), "ifndef")) { @@ -651,6 +661,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")) { @@ -661,6 +672,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")) { @@ -672,6 +684,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")) { @@ -682,6 +695,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")) { @@ -689,6 +703,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")) { @@ -698,6 +713,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")) { @@ -707,6 +723,7 @@ static bool ftepp_hash(ftepp_t *ftepp) } vec_pop(ftepp->conditions); ftepp_next(ftepp); + ftepp_update_output_condition(ftepp); break; } else { From 03f0e39f7f0389dce84406e489ec33debce8e555 Mon Sep 17 00:00:00 2001 From: "Wolfgang (Blub) Bumiller" Date: Fri, 16 Nov 2012 22:06:07 +0100 Subject: [PATCH 23/59] ftepp_out can now output to a string buffer --- ftepp.c | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/ftepp.c b/ftepp.c index 9cd2903..e1a8e30 100644 --- a/ftepp.c +++ b/ftepp.c @@ -61,6 +61,9 @@ typedef struct { bool output_on; ppcondition *conditions; ppmacro **macros; + + bool output_string; + char *output; } ftepp_t; #define ftepp_tokval(f) ((f)->lex->tok.value) @@ -154,7 +157,15 @@ static void ftepp_out(ftepp_t *ftepp, const char *str, bool ignore_cond) { if (ignore_cond || ftepp->output_on) { - printf("%s", str); + size_t len; + char *data; + if (!ftepp->output_string) { + printf("%s", str); + return; + } + len = strlen(str); + data = vec_add(ftepp->output, len); + memcpy(data, str, len); } } From 99dd2088803c5feaefc3804396d264e6838dcd05 Mon Sep 17 00:00:00 2001 From: "Wolfgang (Blub) Bumiller" Date: Fri, 16 Nov 2012 22:07:53 +0100 Subject: [PATCH 24/59] moving ftepp_delete out of ftepp_preprocess so that it can actually be called recursively --- ftepp.c | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/ftepp.c b/ftepp.c index e1a8e30..d4cf761 100644 --- a/ftepp.c +++ b/ftepp.c @@ -827,7 +827,6 @@ static bool ftepp_preprocess(ftepp_t *ftepp) } while (!ftepp->errors && ftepp->token < TOKEN_EOF); newline = ftepp->token == TOKEN_EOF; - ftepp_delete(ftepp); return newline; } @@ -839,7 +838,12 @@ bool ftepp_preprocess_file(const char *filename) con_out("failed to open file \"%s\"\n", filename); return false; } - return ftepp_preprocess(ftepp); + if (!ftepp_preprocess(ftepp)) { + ftepp_delete(ftepp); + return false; + } + ftepp_delete(ftepp); + return true; } bool ftepp_preprocess_string(const char *name, const char *str) @@ -850,5 +854,10 @@ bool ftepp_preprocess_string(const char *name, const char *str) con_out("failed to create lexer for string \"%s\"\n", name); return false; } - return ftepp_preprocess(ftepp); + if (!ftepp_preprocess(ftepp)) { + ftepp_delete(ftepp); + return false; + } + ftepp_delete(ftepp); + return true; } From 05a26333fc36638104c9af98404567993d450f60 Mon Sep 17 00:00:00 2001 From: "Wolfgang (Blub) Bumiller" Date: Fri, 16 Nov 2012 22:22:31 +0100 Subject: [PATCH 25/59] Expand macros into a string buffer --- ftepp.c | 64 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++- lexer.c | 1 + 2 files changed, 64 insertions(+), 1 deletion(-) diff --git a/ftepp.c b/ftepp.c index d4cf761..c03c3d2 100644 --- a/ftepp.c +++ b/ftepp.c @@ -333,8 +333,12 @@ static bool ftepp_macro_call_params(ftepp_t *ftepp, macroparam **out_params) size_t parens = 0; size_t i; + if (!ftepp_skipallwhite(ftepp)) + return false; while (ftepp->token != ')') { mp.tokens = NULL; + if (!ftepp_skipallwhite(ftepp)) + return false; while (parens || ftepp->token != ',') { if (ftepp->token == '(') ++parens; @@ -379,9 +383,67 @@ 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 bool ftepp_macro_expand(ftepp_t *ftepp, ppmacro *macro, macroparam *params) { - return true; + char *old_string = ftepp->output; + bool old_string_flag = ftepp->output_string; + bool retval = true; + + size_t o, pi, pv; + + /* really ... */ + if (!vec_size(macro->output)) + return true; + + ftepp->output = NULL; + ftepp->output_string = true; + for (o = 0; o < vec_size(macro->output); ++o) { + pptoken *out = macro->output[o]; + switch (out->token) { + case TOKEN_IDENT: + case TOKEN_TYPENAME: + case TOKEN_KEYWORD: + if (!macro_params_find(macro, out->value, &pi)) { + ftepp_out(ftepp, out->value, false); + break; + } else { + for (pv = 0; pv < vec_size(params[pi].tokens); ++pv) { + out = params[pi].tokens[pv]; + if (out->token == TOKEN_EOL) + ftepp_out(ftepp, "\n", false); + else + ftepp_out(ftepp, out->value, false); + } + } + break; + case TOKEN_EOL: + ftepp_out(ftepp, "\n", false); + break; + default: + ftepp_out(ftepp, out->value, false); + break; + } + } + vec_push(ftepp->output, 0); + printf("_________________\n%s\n=================\n", ftepp->output); + goto cleanup; + +cleanup: + ftepp->output = old_string; + ftepp->output_string = old_string_flag; + return retval; } static bool ftepp_macro_call(ftepp_t *ftepp, ppmacro *macro) diff --git a/lexer.c b/lexer.c index 1d1c42f..8558404 100644 --- a/lexer.c +++ b/lexer.c @@ -861,6 +861,7 @@ int lex_do(lex_file *lex) return (lex->tok.ttype = TOKEN_OPERATOR); case ')': case ';': + case ':': case '{': case '}': case ']': From 57ac55ad01f95d50b08aa24c6816712bbdfcde09 Mon Sep 17 00:00:00 2001 From: "Wolfgang (Blub) Bumiller" Date: Fri, 16 Nov 2012 22:31:51 +0100 Subject: [PATCH 26/59] recursively call the preprocessor on expanded macros --- ftepp.c | 32 ++++++++++++++++++++++++++------ 1 file changed, 26 insertions(+), 6 deletions(-) diff --git a/ftepp.c b/ftepp.c index c03c3d2..3e890e2 100644 --- a/ftepp.c +++ b/ftepp.c @@ -325,6 +325,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; @@ -367,10 +368,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; @@ -395,13 +398,16 @@ static bool macro_params_find(ppmacro *macro, const char *name, size_t *idx) return false; } +static bool ftepp_preprocess(ftepp_t *ftepp); static bool ftepp_macro_expand(ftepp_t *ftepp, ppmacro *macro, macroparam *params) { - char *old_string = ftepp->output; - bool old_string_flag = ftepp->output_string; + char *old_string = ftepp->output; + bool old_string_flag = ftepp->output_string; + lex_file *old_lexer = ftepp->lex; bool retval = true; - size_t o, pi, pv; + size_t o, pi, pv; + lex_file *inlex; /* really ... */ if (!vec_size(macro->output)) @@ -437,11 +443,24 @@ static bool ftepp_macro_expand(ftepp_t *ftepp, ppmacro *macro, macroparam *param } } vec_push(ftepp->output, 0); - printf("_________________\n%s\n=================\n", ftepp->output); - goto cleanup; + /* Now run the preprocessor recursively on this string buffer */ + inlex = lex_open_string(ftepp->output, vec_size(ftepp->output)-1, ftepp->lex->name); + if (!inlex) { + ftepp_error(ftepp, "internal error: failed to instantiate lexer"); + retval = false; + goto cleanup; + } + ftepp->output = old_string; + ftepp->output_string = old_string_flag; + ftepp->lex = inlex; + if (!ftepp_preprocess(ftepp)) { + retval = false; + goto cleanup; + } cleanup: - ftepp->output = old_string; + ftepp->lex = old_lexer; + ftepp->output = old_string; ftepp->output_string = old_string_flag; return retval; } @@ -483,6 +502,7 @@ static bool ftepp_macro_call(ftepp_t *ftepp, ppmacro *macro) if (!ftepp_macro_expand(ftepp, macro, params)) retval = false; + ftepp_next(ftepp); cleanup: for (o = 0; o < vec_size(params); ++o) From 0ad2b8b2c7a7fff3d32afe31de0d23400ed3a615 Mon Sep 17 00:00:00 2001 From: "Wolfgang (Blub) Bumiller" Date: Fri, 16 Nov 2012 22:38:58 +0100 Subject: [PATCH 27/59] also use recursive-expansion on macros without parameters --- ftepp.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/ftepp.c b/ftepp.c index 3e890e2..cdc0d1e 100644 --- a/ftepp.c +++ b/ftepp.c @@ -444,6 +444,7 @@ static bool ftepp_macro_expand(ftepp_t *ftepp, ppmacro *macro, macroparam *param } vec_push(ftepp->output, 0); /* Now run the preprocessor recursively on this string buffer */ + printf("__________\n%s\n=========\n", ftepp->output); inlex = lex_open_string(ftepp->output, vec_size(ftepp->output)-1, ftepp->lex->name); if (!inlex) { ftepp_error(ftepp, "internal error: failed to instantiate lexer"); @@ -471,14 +472,13 @@ static bool ftepp_macro_call(ftepp_t *ftepp, ppmacro *macro) 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; From d237ff173619bc73f10eb3d4266ecfe57b2ce2b5 Mon Sep 17 00:00:00 2001 From: "Wolfgang (Blub) Bumiller" Date: Fri, 16 Nov 2012 22:41:29 +0100 Subject: [PATCH 28/59] don't error on non-EOL EOF at the end of a macro line --- ftepp.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/ftepp.c b/ftepp.c index cdc0d1e..ba8829d 100644 --- a/ftepp.c +++ b/ftepp.c @@ -262,7 +262,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; } @@ -444,7 +445,9 @@ static bool ftepp_macro_expand(ftepp_t *ftepp, ppmacro *macro, macroparam *param } vec_push(ftepp->output, 0); /* Now run the preprocessor recursively on this string buffer */ + /* printf("__________\n%s\n=========\n", ftepp->output); + */ inlex = lex_open_string(ftepp->output, vec_size(ftepp->output)-1, ftepp->lex->name); if (!inlex) { ftepp_error(ftepp, "internal error: failed to instantiate lexer"); From 990450bfe02839007bf2364d918df5168e4b37a7 Mon Sep 17 00:00:00 2001 From: "Wolfgang (Blub) Bumiller" Date: Fri, 16 Nov 2012 22:54:59 +0100 Subject: [PATCH 29/59] ## concatenation, -Wpreprocessor warning about redefining macros, #undef --- ftepp.c | 83 +++++++++++++++++++++++++++++++++++++++++++++++++++++++- main.c | 1 + opts.def | 1 + 3 files changed, 84 insertions(+), 1 deletion(-) diff --git a/ftepp.c b/ftepp.c index ba8829d..1a2ff21 100644 --- a/ftepp.c +++ b/ftepp.c @@ -91,6 +91,25 @@ static void ftepp_error(ftepp_t *ftepp, const char *fmt, ...) va_end(ap); } +static bool GMQCC_WARN ftepp_warn(ftepp_t *ftepp, int warntype, const char *fmt, ...) +{ + va_list ap; + int lvl = LVL_WARNING; + + if (!OPTS_WARN(warntype)) + return false; + + if (opts_werror) { + lvl = LVL_ERROR; + ftepp->errors++; + } + + va_start(ap, fmt); + con_vprintmsg(lvl, ftepp->lex->tok.ctx.file, ftepp->lex->tok.ctx.line, "error", fmt, ap); + va_end(ap); + return opts_werror; +} + static pptoken *pptoken_make(ftepp_t *ftepp) { pptoken *token = (pptoken*)mem_a(sizeof(pptoken)); @@ -187,6 +206,17 @@ static ppmacro* ftepp_macro_find(ftepp_t *ftepp, const char *name) return NULL; } +static void ftepp_macro_delete(ftepp_t *ftepp, const char *name) +{ + size_t i; + for (i = 0; i < vec_size(ftepp->macros); ++i) { + if (!strcmp(name, ftepp->macros[i]->name)) { + vec_remove(ftepp->macros, i, 1); + return; + } + } +} + static inline int ftepp_next(ftepp_t *ftepp) { return (ftepp->token = lex_do(ftepp->lex)); @@ -281,6 +311,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) { + 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: @@ -435,6 +471,14 @@ static bool ftepp_macro_expand(ftepp_t *ftepp, ppmacro *macro, macroparam *param } } break; + case '#': + if (o + 1 < vec_size(macro->output) && macro->output[o+1]->token == '#') { + /* raw concatenation */ + ++o; + break; + } + ftepp_out(ftepp, "#", false); + break; case TOKEN_EOL: ftepp_out(ftepp, "\n", false); break; @@ -704,14 +748,48 @@ 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; + + switch (ftepp->token) { + case TOKEN_IDENT: + case TOKEN_TYPENAME: + case TOKEN_KEYWORD: + ftepp_macro_delete(ftepp, ftepp_tokval(ftepp)); + break; + default: + ftepp_error(ftepp, "expected macro name"); + return false; + } + + (void)ftepp_next(ftepp); + if (!ftepp_skipspace(ftepp)) + return false; + /* relaxing this condition + if (ftepp->token != TOKEN_EOL && ftepp->token != TOKEN_EOF) { + ftepp_error(ftepp, "stray tokens after #ifdef"); + return false; + } + */ + return true; +} + /* Basic structure handlers */ static bool ftepp_else_allowed(ftepp_t *ftepp) { @@ -743,6 +821,9 @@ 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; diff --git a/main.c b/main.c index ba4cba0..1c11b27 100644 --- a/main.c +++ b/main.c @@ -435,6 +435,7 @@ int main(int argc, char **argv) { options_set(opts_warn, WARN_EFFECTLESS_STATEMENT, true); options_set(opts_warn, WARN_END_SYS_FIELDS, true); options_set(opts_warn, WARN_ASSIGN_FUNCTION_TYPES, true); + options_set(opts_warn, WARN_PREPROCESSOR, true); options_set(opts_flags, ADJUST_VECTOR_FIELDS, true); diff --git a/opts.def b/opts.def index e628f43..1c362d3 100644 --- a/opts.def +++ b/opts.def @@ -52,6 +52,7 @@ GMQCC_DEFINE_FLAG(EFFECTLESS_STATEMENT) GMQCC_DEFINE_FLAG(END_SYS_FIELDS) GMQCC_DEFINE_FLAG(ASSIGN_FUNCTION_TYPES) + GMQCC_DEFINE_FLAG(PREPROCESSOR) #endif /* some cleanup so we don't have to */ From 515cafe8bd17a64f38c6bba73d234e79a215b775 Mon Sep 17 00:00:00 2001 From: "Wolfgang (Blub) Bumiller" Date: Fri, 16 Nov 2012 23:13:53 +0100 Subject: [PATCH 30/59] Lexer should keep newlines in merged lines, so will the preprocessor, but therefore the lexer will replace comments with actual spaces so we don't get borken output... also don't error about redifining a macro when inside a non-outputting #if branch --- ftepp.c | 31 ++++++++++++++++++++----------- lexer.c | 17 +++++++++++++++-- 2 files changed, 35 insertions(+), 13 deletions(-) diff --git a/ftepp.c b/ftepp.c index 1a2ff21..59ec545 100644 --- a/ftepp.c +++ b/ftepp.c @@ -114,10 +114,13 @@ 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; } @@ -312,7 +315,7 @@ static bool ftepp_define(ftepp_t *ftepp) case TOKEN_TYPENAME: case TOKEN_KEYWORD: macro = ftepp_macro_find(ftepp, ftepp_tokval(ftepp)); - if (macro) { + 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)); @@ -338,7 +341,11 @@ static bool ftepp_define(ftepp_t *ftepp) if (!ftepp_define_body(ftepp, macro)) return false; - vec_push(ftepp->macros, macro); + if (ftepp->output_on) + vec_push(ftepp->macros, macro); + else { + ppmacro_delete(macro); + } return true; } @@ -767,15 +774,17 @@ static bool ftepp_undef(ftepp_t *ftepp) if (!ftepp_skipspace(ftepp)) return false; - 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; + 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); diff --git a/lexer.c b/lexer.c index 8558404..de09bfa 100644 --- a/lexer.c +++ b/lexer.c @@ -412,13 +412,17 @@ static int lex_skipwhite(lex_file *lex) if (lex->flags.preprocessing) { haswhite = true; + /* lex_tokench(lex, '/'); lex_tokench(lex, '/'); + */ + lex_tokench(lex, ' '); + lex_tokench(lex, ' '); } while (ch != EOF && ch != '\n') { if (lex->flags.preprocessing) - lex_tokench(lex, ch); + lex_tokench(lex, ' '); /* ch); */ ch = lex_getch(lex); } if (lex->flags.preprocessing) { @@ -433,8 +437,12 @@ static int lex_skipwhite(lex_file *lex) /* multiline comment */ if (lex->flags.preprocessing) { haswhite = true; + /* lex_tokench(lex, '/'); lex_tokench(lex, '*'); + */ + lex_tokench(lex, ' '); + lex_tokench(lex, ' '); } while (ch != EOF) @@ -444,14 +452,18 @@ static int lex_skipwhite(lex_file *lex) ch = lex_getch(lex); if (ch == '/') { if (lex->flags.preprocessing) { + /* lex_tokench(lex, '*'); lex_tokench(lex, '/'); + */ + lex_tokench(lex, ' '); + lex_tokench(lex, ' '); } break; } } if (lex->flags.preprocessing) { - lex_tokench(lex, ch); + lex_tokench(lex, ' '); /* ch); */ } } ch = ' '; /* cause TRUE in the isspace check */ @@ -686,6 +698,7 @@ int lex_do(lex_file *lex) break; } /* we reached a linemerge */ + lex_tokench(lex, '\n'); continue; } From 2b65ea599fffe30ff8f9bc0dc8a166f822cc8cf6 Mon Sep 17 00:00:00 2001 From: "Wolfgang (Blub) Bumiller" Date: Sun, 18 Nov 2012 11:43:46 +0100 Subject: [PATCH 31/59] Some printf/con_out/con_err conversions, guarded some outputs with not-opts_pp_only so the -E switch can print to stdout normally --- ir.c | 3 ++- main.c | 30 ++++++++++++++++++------------ parser.c | 4 ++-- 3 files changed, 22 insertions(+), 15 deletions(-) diff --git a/ir.c b/ir.c index 1a99150..f652f6a 100644 --- a/ir.c +++ b/ir.c @@ -2972,7 +2972,8 @@ bool ir_builder_generate(ir_builder *self, const char *filename) stmt.o3.u1 = 0; vec_push(code_statements, stmt); - printf("writing '%s'...\n", filename); + if (!opts_pp_only) + con_out("writing '%s'...\n", filename); return code_write(filename); } diff --git a/main.c b/main.c index 1c11b27..2db65f9 100644 --- a/main.c +++ b/main.c @@ -473,15 +473,19 @@ int main(int argc, char **argv) { util_debug("COM", "starting ...\n"); if (vec_size(items)) { - con_out("Mode: manual\n"); - con_out("There are %lu items to compile:\n", (unsigned long)vec_size(items)); + if (!opts_pp_only) { + con_out("Mode: manual\n"); + con_out("There are %lu items to compile:\n", (unsigned long)vec_size(items)); + } for (itr = 0; itr < vec_size(items); ++itr) { - con_out(" item: %s (%s)\n", - items[itr].filename, - ( (items[itr].type == TYPE_QC ? "qc" : - (items[itr].type == TYPE_ASM ? "asm" : - (items[itr].type == TYPE_SRC ? "progs.src" : - ("unknown")))))); + if (!opts_pp_only) { + con_out(" item: %s (%s)\n", + items[itr].filename, + ( (items[itr].type == TYPE_QC ? "qc" : + (items[itr].type == TYPE_ASM ? "asm" : + (items[itr].type == TYPE_SRC ? "progs.src" : + ("unknown")))))); + } if (opts_pp_only) { if (!ftepp_preprocess_file(items[itr].filename)) { @@ -505,17 +509,18 @@ int main(int argc, char **argv) { char *line; size_t linelen = 0; - con_out("Mode: progs.src\n"); + if (!opts_pp_only) + con_out("Mode: progs.src\n"); src = util_fopen("progs.src", "rb"); if (!src) { - con_out("failed to open `progs.src` for reading\n"); + con_err("failed to open `progs.src` for reading\n"); retval = 1; goto cleanup; } line = NULL; if (!progs_nextline(&line, &linelen, src) || !line[0]) { - con_out("illformatted progs.src file: expected output filename in first line\n"); + con_err("illformatted progs.src file: expected output filename in first line\n"); retval = 1; goto srcdone; } @@ -528,7 +533,8 @@ int main(int argc, char **argv) { while (progs_nextline(&line, &linelen, src)) { if (!line[0] || (line[0] == '/' && line[1] == '/')) continue; - con_out(" src: %s\n", line); + if (!opts_pp_only) + con_out(" src: %s\n", line); if (!parser_compile_file(line)) { retval = 1; goto srcdone; diff --git a/parser.c b/parser.c index 805c28c..a292c90 100644 --- a/parser.c +++ b/parser.c @@ -3431,7 +3431,7 @@ bool parser_compile_file(const char *filename) { parser->lex = lex_open(filename); if (!parser->lex) { - con_out("failed to open file \"%s\"\n", filename); + con_err("failed to open file \"%s\"\n", filename); return false; } return parser_compile(); @@ -3441,7 +3441,7 @@ bool parser_compile_string(const char *name, const char *str) { parser->lex = lex_open_string(str, strlen(str), name); if (!parser->lex) { - con_out("failed to create lexer for string \"%s\"\n", name); + con_err("failed to create lexer for string \"%s\"\n", name); return false; } return parser_compile(); From 366557bbab6605e819743b183372bb15569dfd7c Mon Sep 17 00:00:00 2001 From: "Wolfgang (Blub) Bumiller" Date: Sun, 18 Nov 2012 11:54:11 +0100 Subject: [PATCH 32/59] Since like with parsing, the preprocessor state has to be preserved across files for macros, we do that now, and with that introduce a new warning: -Wmultifile-if in case an #if spans over several command-line-provided source files (or progs.src file provided for that matter) --- ftepp.c | 40 +++++++++++++++++++++++++++++++++------- gmqcc.h | 2 ++ main.c | 18 ++++++++++++++---- opts.def | 1 + 4 files changed, 50 insertions(+), 11 deletions(-) diff --git a/ftepp.c b/ftepp.c index 59ec545..865cd4e 100644 --- a/ftepp.c +++ b/ftepp.c @@ -152,7 +152,7 @@ static void ppmacro_delete(ppmacro *self) mem_d(self); } -static ftepp_t* ftepp_init() +static ftepp_t* ftepp_new() { ftepp_t *ftepp; @@ -509,6 +509,7 @@ static bool ftepp_macro_expand(ftepp_t *ftepp, ppmacro *macro, macroparam *param ftepp->output_string = old_string_flag; ftepp->lex = inlex; if (!ftepp_preprocess(ftepp)) { + lex_close(ftepp->lex); retval = false; goto cleanup; } @@ -1005,9 +1006,25 @@ static bool ftepp_preprocess(ftepp_t *ftepp) return newline; } +/* 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; + 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; + } + return retval; +} + bool ftepp_preprocess_file(const char *filename) { - ftepp_t *ftepp = ftepp_init(); ftepp->lex = lex_open(filename); if (!ftepp->lex) { con_out("failed to open file \"%s\"\n", filename); @@ -1017,13 +1034,12 @@ bool ftepp_preprocess_file(const char *filename) ftepp_delete(ftepp); return false; } - ftepp_delete(ftepp); - return true; + return ftepp_preprocess_done(); } bool ftepp_preprocess_string(const char *name, const char *str) { - ftepp_t *ftepp = ftepp_init(); + ftepp_t *ftepp = ftepp_new(); ftepp->lex = lex_open_string(str, strlen(str), name); if (!ftepp->lex) { con_out("failed to create lexer for string \"%s\"\n", name); @@ -1033,6 +1049,16 @@ bool ftepp_preprocess_string(const char *name, const char *str) ftepp_delete(ftepp); return false; } - ftepp_delete(ftepp); - return true; + return ftepp_preprocess_done(); +} + +bool ftepp_init() +{ + ftepp = ftepp_new(); + return !!ftepp; +} + +void ftepp_finish() +{ + ftepp_delete(ftepp); } diff --git a/gmqcc.h b/gmqcc.h index 4f92eaf..d99a778 100644 --- a/gmqcc.h +++ b/gmqcc.h @@ -755,8 +755,10 @@ void parser_cleanup (); /*===================================================================*/ /*====================== ftepp.c commandline ========================*/ /*===================================================================*/ +bool ftepp_init (); bool ftepp_preprocess_file (const char *filename); bool ftepp_preprocess_string(const char *name, const char *str); +void ftepp_finish (); /*===================================================================*/ /*======================= main.c commandline ========================*/ diff --git a/main.c b/main.c index 2db65f9..d1ac7f1 100644 --- a/main.c +++ b/main.c @@ -436,6 +436,7 @@ int main(int argc, char **argv) { options_set(opts_warn, WARN_END_SYS_FIELDS, true); options_set(opts_warn, WARN_ASSIGN_FUNCTION_TYPES, true); options_set(opts_warn, WARN_PREPROCESSOR, true); + options_set(opts_warn, WARN_MULTIFILE_IF, true); options_set(opts_flags, ADJUST_VECTOR_FIELDS, true); @@ -464,10 +465,19 @@ int main(int argc, char **argv) { con_out("standard = %i\n", opts_standard); } - if (!parser_init()) { - con_out("failed to initialize parser\n"); - retval = 1; - goto cleanup; + if (!opts_pp_only) { + if (!parser_init()) { + con_err("failed to initialize parser\n"); + retval = 1; + goto cleanup; + } + } + if (opts_pp_only || opts_standard == COMPILER_FTEQCC) { + if (!ftepp_init()) { + con_err("failed to initialize parser\n"); + retval = 1; + goto cleanup; + } } util_debug("COM", "starting ...\n"); diff --git a/opts.def b/opts.def index 1c362d3..b958989 100644 --- a/opts.def +++ b/opts.def @@ -53,6 +53,7 @@ GMQCC_DEFINE_FLAG(END_SYS_FIELDS) GMQCC_DEFINE_FLAG(ASSIGN_FUNCTION_TYPES) GMQCC_DEFINE_FLAG(PREPROCESSOR) + GMQCC_DEFINE_FLAG(MULTIFILE_IF) #endif /* some cleanup so we don't have to */ From d1373af7fc9b4657b1fb663bf6ea03c73e9ab8c7 Mon Sep 17 00:00:00 2001 From: "Wolfgang (Blub) Bumiller" Date: Sun, 18 Nov 2012 12:51:38 +0100 Subject: [PATCH 33/59] Make -E print to stdout by default and honor -o, changed handling of source list a bit --- ftepp.c | 34 ++++++++++------- gmqcc.h | 2 +- main.c | 113 +++++++++++++++++++++++++++++++++----------------------- 3 files changed, 88 insertions(+), 61 deletions(-) diff --git a/ftepp.c b/ftepp.c index 865cd4e..4318633 100644 --- a/ftepp.c +++ b/ftepp.c @@ -62,8 +62,9 @@ typedef struct { ppcondition *conditions; ppmacro **macros; - bool output_string; + bool to_string; char *output; + FILE *output_file; } ftepp_t; #define ftepp_tokval(f) ((f)->lex->tok.value) @@ -171,7 +172,10 @@ static void ftepp_delete(ftepp_t *self) ppmacro_delete(self->macros[i]); vec_free(self->macros); vec_free(self->conditions); - lex_close(self->lex); + if (self->lex) + lex_close(self->lex); + if (self->output_file) + fclose(self->output_file); mem_d(self); } @@ -181,8 +185,8 @@ static void ftepp_out(ftepp_t *ftepp, const char *str, bool ignore_cond) { size_t len; char *data; - if (!ftepp->output_string) { - printf("%s", str); + if (!ftepp->to_string) { + fprintf((ftepp->output_file ? ftepp->output_file : stdout), "%s", str); return; } len = strlen(str); @@ -446,7 +450,7 @@ static bool ftepp_preprocess(ftepp_t *ftepp); static bool ftepp_macro_expand(ftepp_t *ftepp, ppmacro *macro, macroparam *params) { char *old_string = ftepp->output; - bool old_string_flag = ftepp->output_string; + bool old_string_flag = ftepp->to_string; lex_file *old_lexer = ftepp->lex; bool retval = true; @@ -457,8 +461,8 @@ static bool ftepp_macro_expand(ftepp_t *ftepp, ppmacro *macro, macroparam *param if (!vec_size(macro->output)) return true; - ftepp->output = NULL; - ftepp->output_string = true; + ftepp->output = NULL; + ftepp->to_string = true; for (o = 0; o < vec_size(macro->output); ++o) { pptoken *out = macro->output[o]; switch (out->token) { @@ -505,8 +509,8 @@ static bool ftepp_macro_expand(ftepp_t *ftepp, ppmacro *macro, macroparam *param retval = false; goto cleanup; } - ftepp->output = old_string; - ftepp->output_string = old_string_flag; + ftepp->output = old_string; + ftepp->to_string = old_string_flag; ftepp->lex = inlex; if (!ftepp_preprocess(ftepp)) { lex_close(ftepp->lex); @@ -515,9 +519,9 @@ static bool ftepp_macro_expand(ftepp_t *ftepp, ppmacro *macro, macroparam *param } cleanup: - ftepp->lex = old_lexer; - ftepp->output = old_string; - ftepp->output_string = old_string_flag; + ftepp->lex = old_lexer; + ftepp->output = old_string; + ftepp->to_string = old_string_flag; return retval; } @@ -1052,13 +1056,17 @@ bool ftepp_preprocess_string(const char *name, const char *str) return ftepp_preprocess_done(); } -bool ftepp_init() +bool ftepp_init(FILE *out) { ftepp = ftepp_new(); + ftepp->output_file = out; return !!ftepp; } void ftepp_finish() { + if (!ftepp) + return; ftepp_delete(ftepp); + ftepp = NULL; } diff --git a/gmqcc.h b/gmqcc.h index d99a778..c421970 100644 --- a/gmqcc.h +++ b/gmqcc.h @@ -755,7 +755,7 @@ void parser_cleanup (); /*===================================================================*/ /*====================== ftepp.c commandline ========================*/ /*===================================================================*/ -bool ftepp_init (); +bool ftepp_init (FILE *out); bool ftepp_preprocess_file (const char *filename); bool ftepp_preprocess_string(const char *name, const char *str); void ftepp_finish (); diff --git a/main.c b/main.c index d1ac7f1..1301e75 100644 --- a/main.c +++ b/main.c @@ -415,6 +415,7 @@ int main(int argc, char **argv) { size_t itr; int retval = 0; bool opts_output_free = false; + bool progs_src = false; app_name = argv[0]; con_init(); @@ -473,7 +474,16 @@ int main(int argc, char **argv) { } } if (opts_pp_only || opts_standard == COMPILER_FTEQCC) { - if (!ftepp_init()) { + FILE *out = NULL; + if (opts_output_wasset) { + out = util_fopen(opts_output, "wb"); + if (!out) { + con_err("failed to open `%s` for writing\n", opts_output); + retval = 1; + goto cleanup; + } + } + if (!ftepp_init(out)) { con_err("failed to initialize parser\n"); retval = 1; goto cleanup; @@ -482,9 +492,52 @@ int main(int argc, char **argv) { util_debug("COM", "starting ...\n"); + if (!vec_size(items)) { + FILE *src; + char *line; + size_t linelen = 0; + + progs_src = true; + + src = util_fopen("progs.src", "rb"); + if (!src) { + con_err("failed to open `progs.src` for reading\n"); + retval = 1; + goto cleanup; + } + + line = NULL; + if (!progs_nextline(&line, &linelen, src) || !line[0]) { + con_err("illformatted progs.src file: expected output filename in first line\n"); + retval = 1; + goto srcdone; + } + + if (!opts_output_wasset) { + opts_output = util_strdup(line); + opts_output_free = true; + } + + while (progs_nextline(&line, &linelen, src)) { + argitem item; + if (!line[0] || (line[0] == '/' && line[1] == '/')) + continue; + item.filename = util_strdup(line); + item.type = TYPE_QC; + vec_push(items, item); + } + +srcdone: + fclose(src); + mem_d(line); + } + + if (retval) + goto cleanup; + if (vec_size(items)) { if (!opts_pp_only) { - con_out("Mode: manual\n"); + con_out("Mode: %s\n", (progs_src ? "progs.src" : "manual")); con_out("There are %lu items to compile:\n", (unsigned long)vec_size(items)); } for (itr = 0; itr < vec_size(items); ++itr) { @@ -507,61 +560,27 @@ int main(int argc, char **argv) { retval = 1; goto cleanup; } - } - if (!parser_finish(opts_output)) { - retval = 1; - goto cleanup; - } - - } else { - FILE *src; - char *line; - size_t linelen = 0; - - if (!opts_pp_only) - con_out("Mode: progs.src\n"); - src = util_fopen("progs.src", "rb"); - if (!src) { - con_err("failed to open `progs.src` for reading\n"); - retval = 1; - goto cleanup; - } - - line = NULL; - if (!progs_nextline(&line, &linelen, src) || !line[0]) { - con_err("illformatted progs.src file: expected output filename in first line\n"); - retval = 1; - goto srcdone; - } - - if (!opts_output_wasset) { - opts_output = util_strdup(line); - opts_output_free = true; - } - - while (progs_nextline(&line, &linelen, src)) { - if (!line[0] || (line[0] == '/' && line[1] == '/')) - continue; - if (!opts_pp_only) - con_out(" src: %s\n", line); - if (!parser_compile_file(line)) { - retval = 1; - goto srcdone; + if (progs_src) { + mem_d(items[itr].filename); + items[itr].filename = NULL; } } - parser_finish(opts_output); - -srcdone: - fclose(src); - mem_d(line); + ftepp_finish(); + if (!opts_pp_only) { + if (!parser_finish(opts_output)) { + retval = 1; + goto cleanup; + } + } } /* stuff */ cleanup: util_debug("COM", "cleaning ...\n"); + ftepp_finish(); con_close(); vec_free(items); From f47a20aa536ff345363415962f46bc4be5f60617 Mon Sep 17 00:00:00 2001 From: "Wolfgang (Blub) Bumiller" Date: Sun, 18 Nov 2012 13:00:18 +0100 Subject: [PATCH 34/59] Removing some code duplication --- parser.c | 43 +++++++++++++------------------------------ 1 file changed, 13 insertions(+), 30 deletions(-) diff --git a/parser.c b/parser.c index 805c28c..ade4cc6 100644 --- a/parser.c +++ b/parser.c @@ -2396,6 +2396,7 @@ static bool parser_create_array_accessor(parser_t *parser, ast_value *array, con { ast_function *func = NULL; ast_value *fval = NULL; + ast_block *body = NULL; fval = ast_value_new(ast_ctx(array), funcname, TYPE_FUNCTION); if (!fval) { @@ -2410,6 +2411,15 @@ static bool parser_create_array_accessor(parser_t *parser, ast_value *array, con return false; } + body = ast_block_new(ast_ctx(array)); + if (!body) { + parseerror(parser, "failed to create block for array accessor"); + ast_delete(fval); + ast_delete(func); + return false; + } + + vec_push(func->blocks, body); *out = fval; return true; @@ -2418,7 +2428,6 @@ static bool parser_create_array_accessor(parser_t *parser, ast_value *array, con static bool parser_create_array_setter(parser_t *parser, ast_value *array, const char *funcname) { ast_expression *root = NULL; - ast_block *body = NULL; ast_value *index = NULL; ast_value *value = NULL; ast_function *func; @@ -2434,12 +2443,6 @@ static bool parser_create_array_setter(parser_t *parser, ast_value *array, const func = fval->constval.vfunc; fval->expression.next = (ast_expression*)ast_value_new(ast_ctx(array), "", TYPE_VOID); - body = ast_block_new(ast_ctx(array)); - if (!body) { - parseerror(parser, "failed to create block for array accessor"); - goto cleanup; - } - index = ast_value_new(ast_ctx(array), "index", TYPE_FLOAT); value = ast_value_copy((ast_value*)array->expression.next); @@ -2457,12 +2460,10 @@ static bool parser_create_array_setter(parser_t *parser, ast_value *array, const goto cleanup; } - vec_push(body->exprs, root); - vec_push(func->blocks, body); + vec_push(func->blocks[0]->exprs, root); array->setter = fval; return true; cleanup: - if (body) ast_delete(body); if (index) ast_delete(index); if (value) ast_delete(value); if (root) ast_delete(root); @@ -2474,7 +2475,6 @@ cleanup: static bool parser_create_array_field_setter(parser_t *parser, ast_value *array, const char *funcname) { ast_expression *root = NULL; - ast_block *body = NULL; ast_value *entity = NULL; ast_value *index = NULL; ast_value *value = NULL; @@ -2491,12 +2491,6 @@ static bool parser_create_array_field_setter(parser_t *parser, ast_value *array, func = fval->constval.vfunc; fval->expression.next = (ast_expression*)ast_value_new(ast_ctx(array), "", TYPE_VOID); - body = ast_block_new(ast_ctx(array)); - if (!body) { - parseerror(parser, "failed to create block for array accessor"); - goto cleanup; - } - entity = ast_value_new(ast_ctx(array), "entity", TYPE_ENTITY); index = ast_value_new(ast_ctx(array), "index", TYPE_FLOAT); value = ast_value_copy((ast_value*)array->expression.next); @@ -2515,12 +2509,10 @@ static bool parser_create_array_field_setter(parser_t *parser, ast_value *array, goto cleanup; } - vec_push(body->exprs, root); - vec_push(func->blocks, body); + vec_push(func->blocks[0]->exprs, root); array->setter = fval; return true; cleanup: - if (body) ast_delete(body); if (entity) ast_delete(entity); if (index) ast_delete(index); if (value) ast_delete(value); @@ -2533,7 +2525,6 @@ cleanup: static bool parser_create_array_getter(parser_t *parser, ast_value *array, const ast_expression *elemtype, const char *funcname) { ast_expression *root = NULL; - ast_block *body = NULL; ast_value *index = NULL; ast_value *fval; ast_function *func; @@ -2551,12 +2542,6 @@ static bool parser_create_array_getter(parser_t *parser, ast_value *array, const func = fval->constval.vfunc; fval->expression.next = ast_type_copy(ast_ctx(array), elemtype); - body = ast_block_new(ast_ctx(array)); - if (!body) { - parseerror(parser, "failed to create block for array accessor"); - goto cleanup; - } - index = ast_value_new(ast_ctx(array), "index", TYPE_FLOAT); if (!index) { @@ -2571,12 +2556,10 @@ static bool parser_create_array_getter(parser_t *parser, ast_value *array, const goto cleanup; } - vec_push(body->exprs, root); - vec_push(func->blocks, body); + vec_push(func->blocks[0]->exprs, root); array->getter = fval; return true; cleanup: - if (body) ast_delete(body); if (index) ast_delete(index); if (root) ast_delete(root); ast_delete(func); From cec58b53e42bc8f0f966210f28e32d4c07b63d0c Mon Sep 17 00:00:00 2001 From: "Wolfgang (Blub) Bumiller" Date: Sun, 18 Nov 2012 13:06:16 +0100 Subject: [PATCH 35/59] Collect accessor functions in the parser so they can later be deleted, fixed that leak --- parser.c | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/parser.c b/parser.c index ade4cc6..5738869 100644 --- a/parser.c +++ b/parser.c @@ -20,6 +20,9 @@ typedef struct { ast_value **imm_string; ast_value **imm_vector; + /* must be deleted first, they reference immediates and values */ + ast_value **accessors; + ast_value *imm_float_zero; ast_value *imm_vector_zero; @@ -2422,6 +2425,8 @@ static bool parser_create_array_accessor(parser_t *parser, ast_value *array, con vec_push(func->blocks, body); *out = fval; + vec_push(parser->accessors, fval); + return true; } @@ -3433,6 +3438,11 @@ bool parser_compile_string(const char *name, const char *str) void parser_cleanup() { size_t i; + for (i = 0; i < vec_size(parser->accessors); ++i) { + ast_delete(parser->accessors[i]->constval.vfunc); + parser->accessors[i]->constval.vfunc = NULL; + ast_delete(parser->accessors[i]); + } for (i = 0; i < vec_size(parser->functions); ++i) { ast_delete(parser->functions[i]); } @@ -3453,6 +3463,7 @@ void parser_cleanup() ast_delete(parser->globals[i].var); mem_d(parser->globals[i].name); } + vec_free(parser->accessors); vec_free(parser->functions); vec_free(parser->imm_vector); vec_free(parser->imm_string); From 0330b082a2c88454fbf7426bc37fe1a748201d82 Mon Sep 17 00:00:00 2001 From: "Wolfgang (Blub) Bumiller" Date: Sun, 18 Nov 2012 13:33:53 +0100 Subject: [PATCH 36/59] The ftepp now always outputs into a string buffer, with -E the buffer will be dumped to stdout or the via -o specified file, without -E it'll be passed to the parser to be compiled --- ftepp.c | 45 +++++++++++++++++++++------------------------ gmqcc.h | 6 +++++- main.c | 54 ++++++++++++++++++++++++++++++++++++++++-------------- opts.def | 1 + parser.c | 10 ++++++++++ 5 files changed, 77 insertions(+), 39 deletions(-) diff --git a/ftepp.c b/ftepp.c index 4318633..96c62e7 100644 --- a/ftepp.c +++ b/ftepp.c @@ -62,9 +62,7 @@ typedef struct { ppcondition *conditions; ppmacro **macros; - bool to_string; - char *output; - FILE *output_file; + char *output_string; } ftepp_t; #define ftepp_tokval(f) ((f)->lex->tok.value) @@ -174,8 +172,6 @@ static void ftepp_delete(ftepp_t *self) vec_free(self->conditions); if (self->lex) lex_close(self->lex); - if (self->output_file) - fclose(self->output_file); mem_d(self); } @@ -185,12 +181,8 @@ static void ftepp_out(ftepp_t *ftepp, const char *str, bool ignore_cond) { size_t len; char *data; - if (!ftepp->to_string) { - fprintf((ftepp->output_file ? ftepp->output_file : stdout), "%s", str); - return; - } len = strlen(str); - data = vec_add(ftepp->output, len); + data = vec_add(ftepp->output_string, len); memcpy(data, str, len); } } @@ -449,8 +441,7 @@ static bool macro_params_find(ppmacro *macro, const char *name, size_t *idx) static bool ftepp_preprocess(ftepp_t *ftepp); static bool ftepp_macro_expand(ftepp_t *ftepp, ppmacro *macro, macroparam *params) { - char *old_string = ftepp->output; - bool old_string_flag = ftepp->to_string; + char *old_string = ftepp->output_string; lex_file *old_lexer = ftepp->lex; bool retval = true; @@ -461,8 +452,7 @@ static bool ftepp_macro_expand(ftepp_t *ftepp, ppmacro *macro, macroparam *param if (!vec_size(macro->output)) return true; - ftepp->output = NULL; - ftepp->to_string = true; + ftepp->output_string = NULL; for (o = 0; o < vec_size(macro->output); ++o) { pptoken *out = macro->output[o]; switch (out->token) { @@ -498,19 +488,18 @@ static bool ftepp_macro_expand(ftepp_t *ftepp, ppmacro *macro, macroparam *param break; } } - vec_push(ftepp->output, 0); + vec_push(ftepp->output_string, 0); /* Now run the preprocessor recursively on this string buffer */ /* - printf("__________\n%s\n=========\n", ftepp->output); + printf("__________\n%s\n=========\n", ftepp->output_string); */ - inlex = lex_open_string(ftepp->output, vec_size(ftepp->output)-1, ftepp->lex->name); + 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 = old_string; - ftepp->to_string = old_string_flag; + ftepp->output_string = old_string; ftepp->lex = inlex; if (!ftepp_preprocess(ftepp)) { lex_close(ftepp->lex); @@ -519,9 +508,8 @@ static bool ftepp_macro_expand(ftepp_t *ftepp, ppmacro *macro, macroparam *param } cleanup: - ftepp->lex = old_lexer; - ftepp->output = old_string; - ftepp->to_string = old_string_flag; + ftepp->lex = old_lexer; + ftepp->output_string = old_string; return retval; } @@ -1056,13 +1044,22 @@ bool ftepp_preprocess_string(const char *name, const char *str) return ftepp_preprocess_done(); } -bool ftepp_init(FILE *out) +bool ftepp_init() { ftepp = ftepp_new(); - ftepp->output_file = out; return !!ftepp; } +const char *ftepp_get() +{ + return ftepp->output_string; +} + +void ftepp_flush() +{ + vec_free(ftepp->output_string); +} + void ftepp_finish() { if (!ftepp) diff --git a/gmqcc.h b/gmqcc.h index c421970..1ea2cef 100644 --- a/gmqcc.h +++ b/gmqcc.h @@ -751,14 +751,18 @@ bool parser_compile_file (const char *filename); bool parser_compile_string(const char *name, const char *str); bool parser_finish (const char *output); void parser_cleanup (); +/* There's really no need to strlen() preprocessed files */ +bool parser_compile_string_len(const char *name, const char *str, size_t len); /*===================================================================*/ /*====================== ftepp.c commandline ========================*/ /*===================================================================*/ -bool ftepp_init (FILE *out); +bool ftepp_init (); bool ftepp_preprocess_file (const char *filename); bool ftepp_preprocess_string(const char *name, const char *str); void ftepp_finish (); +const char *ftepp_get (); +void ftepp_flush (); /*===================================================================*/ /*======================= main.c commandline ========================*/ diff --git a/main.c b/main.c index 1301e75..3f00888 100644 --- a/main.c +++ b/main.c @@ -202,6 +202,7 @@ static bool options_parse(int argc, char **argv) { options_set(opts_flags, ADJUST_VECTOR_FIELDS, false); opts_standard = COMPILER_QCC; } else if (!strcmp(argarg, "fte") || !strcmp(argarg, "fteqcc")) { + options_set(opts_flags, FTEPP, true); options_set(opts_flags, ADJUST_VECTOR_FIELDS, false); opts_standard = COMPILER_FTEQCC; } else if (!strcmp(argarg, "qccx")) { @@ -416,6 +417,7 @@ int main(int argc, char **argv) { int retval = 0; bool opts_output_free = false; bool progs_src = false; + FILE *outfile = NULL; app_name = argv[0]; con_init(); @@ -440,6 +442,7 @@ int main(int argc, char **argv) { options_set(opts_warn, WARN_MULTIFILE_IF, true); options_set(opts_flags, ADJUST_VECTOR_FIELDS, true); + options_set(opts_flags, FTEPP, false); if (!options_parse(argc, argv)) { return usage(); @@ -466,6 +469,19 @@ int main(int argc, char **argv) { con_out("standard = %i\n", opts_standard); } + if (opts_pp_only) { + if (opts_output_wasset) { + outfile = util_fopen(opts_output, "wb"); + if (!outfile) { + con_err("failed to open `%s` for writing\n", opts_output); + retval = 1; + goto cleanup; + } + } + else + outfile = stdout; + } + if (!opts_pp_only) { if (!parser_init()) { con_err("failed to initialize parser\n"); @@ -473,17 +489,8 @@ int main(int argc, char **argv) { goto cleanup; } } - if (opts_pp_only || opts_standard == COMPILER_FTEQCC) { - FILE *out = NULL; - if (opts_output_wasset) { - out = util_fopen(opts_output, "wb"); - if (!out) { - con_err("failed to open `%s` for writing\n", opts_output); - retval = 1; - goto cleanup; - } - } - if (!ftepp_init(out)) { + if (opts_pp_only || OPTS_FLAG(FTEPP)) { + if (!ftepp_init()) { con_err("failed to initialize parser\n"); retval = 1; goto cleanup; @@ -555,10 +562,29 @@ srcdone: retval = 1; goto cleanup; } + fprintf(outfile, "%s", ftepp_get()); + ftepp_flush(); } - else if (!parser_compile_file(items[itr].filename)) { - retval = 1; - goto cleanup; + else { + if (OPTS_FLAG(FTEPP)) { + const char *data; + if (!ftepp_preprocess_file(items[itr].filename)) { + retval = 1; + goto cleanup; + } + data = ftepp_get(); + if (!parser_compile_string_len(items[itr].filename, data, vec_size(data)-1)) { + retval = 1; + goto cleanup; + } + ftepp_flush(); + } + else { + if (!parser_compile_file(items[itr].filename)) { + retval = 1; + goto cleanup; + } + } } if (progs_src) { diff --git a/opts.def b/opts.def index b958989..9b64833 100644 --- a/opts.def +++ b/opts.def @@ -31,6 +31,7 @@ GMQCC_DEFINE_FLAG(DARKPLACES_STRING_TABLE_BUG) GMQCC_DEFINE_FLAG(OMIT_NULL_BYTES) GMQCC_DEFINE_FLAG(ADJUST_VECTOR_FIELDS) + GMQCC_DEFINE_FLAG(FTEPP) #endif /* warning flags */ diff --git a/parser.c b/parser.c index 38af9a9..c4cdf2f 100644 --- a/parser.c +++ b/parser.c @@ -3425,6 +3425,16 @@ bool parser_compile_file(const char *filename) return parser_compile(); } +bool parser_compile_string_len(const char *name, const char *str, size_t len) +{ + parser->lex = lex_open_string(str, len, name); + if (!parser->lex) { + con_err("failed to create lexer for string \"%s\"\n", name); + return false; + } + return parser_compile(); +} + bool parser_compile_string(const char *name, const char *str) { parser->lex = lex_open_string(str, strlen(str), name); From a014480987f809e38227d4cd986f9adad758c2a2 Mon Sep 17 00:00:00 2001 From: "Wolfgang (Blub) Bumiller" Date: Sun, 18 Nov 2012 14:26:40 +0100 Subject: [PATCH 37/59] Add quotes to the known control sequences... darn. Add stringification via # --- ftepp.c | 63 +++++++++++++++++++++++++++++++++++++++++++++++++++++---- lexer.c | 14 ++++++++++++- 2 files changed, 72 insertions(+), 5 deletions(-) diff --git a/ftepp.c b/ftepp.c index 96c62e7..fb89682 100644 --- a/ftepp.c +++ b/ftepp.c @@ -438,6 +438,47 @@ static bool macro_params_find(ppmacro *macro, const char *name, size_t *idx) 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) { + 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 bool ftepp_preprocess(ftepp_t *ftepp); static bool ftepp_macro_expand(ftepp_t *ftepp, ppmacro *macro, macroparam *params) { @@ -448,6 +489,8 @@ static bool ftepp_macro_expand(ftepp_t *ftepp, ppmacro *macro, macroparam *param size_t o, pi, pv; lex_file *inlex; + int nextok; + /* really ... */ if (!vec_size(macro->output)) return true; @@ -473,10 +516,22 @@ static bool ftepp_macro_expand(ftepp_t *ftepp, ppmacro *macro, macroparam *param } break; case '#': - if (o + 1 < vec_size(macro->output) && macro->output[o+1]->token == '#') { - /* raw concatenation */ - ++o; - break; + 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; diff --git a/lexer.c b/lexer.c index 34da7d8..bdea722 100644 --- a/lexer.c +++ b/lexer.c @@ -573,7 +573,17 @@ static int GMQCC_WARN lex_finish_string(lex_file *lex, int quote) if (ch == quote) return TOKEN_STRINGCONST; - if (!lex->flags.preprocessing && ch == '\\') { + if (lex->flags.preprocessing && ch == '\\') { + lex_tokench(lex, ch); + ch = lex_getch(lex); + if (ch == EOF) { + lexerror(lex, "unexpected end of file"); + lex_ungetch(lex, EOF); /* next token to be TOKEN_EOF */ + return (lex->tok.ttype = TOKEN_ERROR); + } + lex_tokench(lex, ch); + } + else if (ch == '\\') { ch = lex_getch(lex); if (ch == EOF) { lexerror(lex, "unexpected end of file"); @@ -583,6 +593,8 @@ static int GMQCC_WARN lex_finish_string(lex_file *lex, int quote) switch (ch) { case '\\': break; + case '\'': break; + case '"': break; case 'a': ch = '\a'; break; case 'b': ch = '\b'; break; case 'r': ch = '\r'; break; From edbe8f54cb92d547ee056fc48871ca0f09f04dfc Mon Sep 17 00:00:00 2001 From: "Wolfgang (Blub) Bumiller" Date: Sun, 18 Nov 2012 14:27:47 +0100 Subject: [PATCH 38/59] don't call parser_cleanup with -E --- main.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/main.c b/main.c index 3f00888..6a77d63 100644 --- a/main.c +++ b/main.c @@ -610,7 +610,8 @@ cleanup: con_close(); vec_free(items); - parser_cleanup(); + if (!opts_pp_only) + parser_cleanup(); if (opts_output_free) mem_d((char*)opts_output); From 441a224435bcf7d76c66ba2d8055aed55efeb79e Mon Sep 17 00:00:00 2001 From: "Wolfgang (Blub) Bumiller" Date: Sun, 18 Nov 2012 14:29:15 +0100 Subject: [PATCH 39/59] Comment about why stringify is so short --- ftepp.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/ftepp.c b/ftepp.c index fb89682..ad5e7c7 100644 --- a/ftepp.c +++ b/ftepp.c @@ -447,6 +447,10 @@ static void ftepp_stringify_token(ftepp_t *ftepp, pptoken *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; From 4c2e5d7ebf200b32711b6b6a1d4fd94287fc2394 Mon Sep 17 00:00:00 2001 From: "Wolfgang (Blub) Bumiller" Date: Sun, 18 Nov 2012 14:39:24 +0100 Subject: [PATCH 40/59] update old_string after a recursive preprocess call so we don't reset to a free'd vector; add recursion header/footer pragmas --- ftepp.c | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/ftepp.c b/ftepp.c index ad5e7c7..f0c34e4 100644 --- a/ftepp.c +++ b/ftepp.c @@ -483,6 +483,16 @@ static void ftepp_stringify(ftepp_t *ftepp, macroparam *param) 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) { @@ -560,11 +570,14 @@ static bool ftepp_macro_expand(ftepp_t *ftepp, ppmacro *macro, macroparam *param } ftepp->output_string = old_string; 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; From 79619fbf50c473672f268b27b8c667f0a2e83271 Mon Sep 17 00:00:00 2001 From: "Wolfgang (Blub) Bumiller" Date: Sun, 18 Nov 2012 15:14:42 +0100 Subject: [PATCH 41/59] Experimental support for push/pop(line) pragmas --- lexer.c | 108 ++++++++++++++++++++++++++++++++++++++++++++++++++++++-- lexer.h | 2 ++ 2 files changed, 107 insertions(+), 3 deletions(-) diff --git a/lexer.c b/lexer.c index bdea722..c28ad88 100644 --- a/lexer.c +++ b/lexer.c @@ -289,13 +289,13 @@ static int lex_getch(lex_file *lex) if (lex->peekpos) { lex->peekpos--; - if (lex->peek[lex->peekpos] == '\n') + if (!lex->push_line && lex->peek[lex->peekpos] == '\n') lex->line++; return lex->peek[lex->peekpos]; } ch = lex_fgetc(lex); - if (ch == '\n') + if (!lex->push_line && ch == '\n') lex->line++; else if (ch == '?') return lex_try_trigraph(lex, ch); @@ -307,7 +307,7 @@ static int lex_getch(lex_file *lex) static void lex_ungetch(lex_file *lex, int ch) { lex->peek[lex->peekpos++] = ch; - if (ch == '\n') + if (!lex->push_line && ch == '\n') lex->line--; } @@ -347,6 +347,102 @@ static void lex_endtoken(lex_file *lex) vec_shrinkby(lex->tok.value, 1); } +static bool lex_try_pragma(lex_file *lex) +{ + int ch; + char *pragma = NULL; + char *command = NULL; + char *param = NULL; + + if (lex->flags.preprocessing) + return false; + + ch = lex_getch(lex); + if (ch != '#') { + lex_ungetch(lex, ch); + return false; + } + + for (ch = lex_getch(lex); vec_size(pragma) < 8 && ch >= 'a' && ch <= 'z'; ch = lex_getch(lex)) + vec_push(pragma, ch); + vec_push(pragma, 0); + + if (ch != ' ' || strcmp(pragma, "pragma")) { + lex_ungetch(lex, ch); + goto unroll; + } + + for (ch = lex_getch(lex); vec_size(command) < 32 && ch >= 'a' && ch <= 'z'; ch = lex_getch(lex)) + vec_push(command, ch); + vec_push(command, 0); + + if (ch != '(') { + lex_ungetch(lex, ch); + goto unroll; + } + + for (ch = lex_getch(lex); vec_size(param) < 32 && ch != ')' && ch != '\n'; ch = lex_getch(lex)) + vec_push(param, ch); + vec_push(param, 0); + + if (ch != ')') { + lex_ungetch(lex, ch); + goto unroll; + } + + if (!strcmp(command, "push")) { + if (!strcmp(param, "line")) { + lex->push_line++; + lex->line--; + } + else + goto unroll; + } + else if (!strcmp(command, "pop")) { + if (!strcmp(param, "line")) { + if (lex->push_line) + lex->push_line--; + lex->line--; + } + else + goto unroll; + } + else + goto unroll; + + while (ch != '\n') + ch = lex_getch(lex); + return true; + +unroll: + if (command) { + vec_pop(command); + while (vec_size(command)) { + lex_ungetch(lex, vec_last(command)); + vec_pop(command); + } + vec_free(command); + } + if (command) { + vec_pop(command); + while (vec_size(command)) { + lex_ungetch(lex, vec_last(command)); + vec_pop(command); + } + vec_free(command); + } + if (pragma) { + vec_pop(pragma); + while (vec_size(pragma)) { + lex_ungetch(lex, vec_last(pragma)); + vec_pop(pragma); + } + vec_free(pragma); + } + lex_ungetch(lex, '#'); + return false; +} + /* Skip whitespace and comments and return the first * non-white character. * As this makes use of the above getch() ungetch() functions, @@ -388,6 +484,12 @@ static int lex_skipwhite(lex_file *lex) { ch = lex_getch(lex); while (ch != EOF && isspace(ch)) { + if (ch == '\n') { + if (lex_try_pragma(lex)) { + ch = lex_getch(lex); + continue; + } + } if (lex->flags.preprocessing) { if (ch == '\n') { /* end-of-line */ diff --git a/lexer.h b/lexer.h index b8e2e01..561148c 100644 --- a/lexer.h +++ b/lexer.h @@ -125,6 +125,8 @@ typedef struct { int framevalue; frame_macro *frames; char *modelname; + + size_t push_line; } lex_file; lex_file* lex_open (const char *file); From 9898ab5316d50a817a06c9c56d82e78f383437f8 Mon Sep 17 00:00:00 2001 From: "Wolfgang (Blub) Bumiller" Date: Sun, 18 Nov 2012 15:23:00 +0100 Subject: [PATCH 42/59] fixup line counting in #pragmas --- lexer.c | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/lexer.c b/lexer.c index c28ad88..40756c0 100644 --- a/lexer.c +++ b/lexer.c @@ -353,10 +353,13 @@ static bool lex_try_pragma(lex_file *lex) char *pragma = NULL; char *command = NULL; char *param = NULL; + size_t line; if (lex->flags.preprocessing) return false; + line = lex->line; + ch = lex_getch(lex); if (ch != '#') { lex_ungetch(lex, ch); @@ -393,7 +396,7 @@ static bool lex_try_pragma(lex_file *lex) if (!strcmp(command, "push")) { if (!strcmp(param, "line")) { lex->push_line++; - lex->line--; + --line; } else goto unroll; @@ -402,7 +405,6 @@ static bool lex_try_pragma(lex_file *lex) if (!strcmp(param, "line")) { if (lex->push_line) lex->push_line--; - lex->line--; } else goto unroll; @@ -412,6 +414,7 @@ static bool lex_try_pragma(lex_file *lex) while (ch != '\n') ch = lex_getch(lex); + lex->line = line; return true; unroll: @@ -440,6 +443,8 @@ unroll: vec_free(pragma); } lex_ungetch(lex, '#'); + + lex->line = line; return false; } From d3861cea9f305fe6a514b66986e7e8f098651410 Mon Sep 17 00:00:00 2001 From: "Wolfgang (Blub) Bumiller" Date: Sun, 18 Nov 2012 15:30:24 +0100 Subject: [PATCH 43/59] Make pragmas pass through the preprocessor so the user can write them manually --- ftepp.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/ftepp.c b/ftepp.c index f0c34e4..295139e 100644 --- a/ftepp.c +++ b/ftepp.c @@ -977,6 +977,10 @@ static bool ftepp_hash(ftepp_t *ftepp) ftepp_update_output_condition(ftepp); break; } + else if (!strcmp(ftepp_tokval(ftepp), "pragma")) { + ftepp_out(ftepp, "#", false); + break; + } else { ftepp_error(ftepp, "unrecognized preprocessor directive: `%s`", ftepp_tokval(ftepp)); return false; From e66f2bcb330de3d11444000d191fd097ad111682 Mon Sep 17 00:00:00 2001 From: "Wolfgang (Blub) Bumiller" Date: Sun, 18 Nov 2012 15:30:40 +0100 Subject: [PATCH 44/59] Better linecounting for pragmas; support for prgama file(filename) --- lexer.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/lexer.c b/lexer.c index 40756c0..ecda7f7 100644 --- a/lexer.c +++ b/lexer.c @@ -405,16 +405,21 @@ static bool lex_try_pragma(lex_file *lex) if (!strcmp(param, "line")) { if (lex->push_line) lex->push_line--; + --line; } else goto unroll; } + else if (!strcmp(command, "file")) { + lex->name = util_strdup(param); + vec_push(lex_filenames, lex->name); + } else goto unroll; + lex->line = line; while (ch != '\n') ch = lex_getch(lex); - lex->line = line; return true; unroll: From a5dbfacf2ff71fc748f0201ee001581e817afc0a Mon Sep 17 00:00:00 2001 From: "Wolfgang (Blub) Bumiller" Date: Sun, 18 Nov 2012 15:32:03 +0100 Subject: [PATCH 45/59] pragma line(lineno) - sets the linenumber for the line after the pragma --- lexer.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/lexer.c b/lexer.c index ecda7f7..872df8c 100644 --- a/lexer.c +++ b/lexer.c @@ -414,6 +414,9 @@ static bool lex_try_pragma(lex_file *lex) lex->name = util_strdup(param); vec_push(lex_filenames, lex->name); } + else if (!strcmp(command, "line")) { + line = strtol(param, NULL, 0)-1; + } else goto unroll; From 43a72b2a6304126a67fe23a8756490b040748a5f Mon Sep 17 00:00:00 2001 From: "Wolfgang (Blub) Bumiller" Date: Sun, 18 Nov 2012 16:17:19 +0100 Subject: [PATCH 46/59] Don't skip the \n after parsing a pragma in the lexer otherwise 2 pragmas in the row wouldn't work --- lexer.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/lexer.c b/lexer.c index 872df8c..82f7527 100644 --- a/lexer.c +++ b/lexer.c @@ -498,10 +498,8 @@ static int lex_skipwhite(lex_file *lex) ch = lex_getch(lex); while (ch != EOF && isspace(ch)) { if (ch == '\n') { - if (lex_try_pragma(lex)) { - ch = lex_getch(lex); + if (lex_try_pragma(lex)) continue; - } } if (lex->flags.preprocessing) { if (ch == '\n') { From e4998e0798c8e66fa0a50ea591fed8a2d03645b5 Mon Sep 17 00:00:00 2001 From: "Wolfgang (Blub) Bumiller" Date: Sun, 18 Nov 2012 16:22:07 +0100 Subject: [PATCH 47/59] Added #include --- ftepp.c | 137 +++++++++++++++++++++++++++++++++++++++++++++++++++++++- lexer.c | 2 +- 2 files changed, 137 insertions(+), 2 deletions(-) diff --git a/ftepp.c b/ftepp.c index 295139e..1d73430 100644 --- a/ftepp.c +++ b/ftepp.c @@ -63,6 +63,8 @@ typedef struct { ppmacro **macros; char *output_string; + + char *itemname; } ftepp_t; #define ftepp_tokval(f) ((f)->lex->tok.value) @@ -166,6 +168,8 @@ static ftepp_t* ftepp_new() static void ftepp_delete(ftepp_t *self) { size_t i; + if (self->itemname) + mem_d(self->itemname); for (i = 0; i < vec_size(self->macros); ++i) ppmacro_delete(self->macros[i]); vec_free(self->macros); @@ -864,6 +868,129 @@ static bool ftepp_undef(ftepp_t *ftepp) 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(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, '/'); + } + else { + len = strlen(ftepp->itemname); + memcpy(vec_add(filename, len), ftepp->itemname, len); + if (vec_last(filename) != '/') + vec_push(filename, '/'); + } + } + len = strlen(file); + memcpy(vec_add(filename, len), file, len); + vec_push(filename, 0); + return filename; +} + +/** + * 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; + + (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)); + inlex = lex_open(filename); + if (!inlex) { + ftepp_error(ftepp, "failed to open include file `%s`", filename); + vec_free(filename); + return false; + } + vec_free(filename); + ftepp->lex = inlex; + if (!ftepp_preprocess(ftepp)) { + lex_close(ftepp->lex); + ftepp->lex = old_lexer; + return false; + } + 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) { @@ -977,6 +1104,9 @@ static bool ftepp_hash(ftepp_t *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; @@ -1088,12 +1218,17 @@ static bool ftepp_preprocess_done() if (ftepp_warn(ftepp, WARN_MULTIFILE_IF, "#if spanning multiple files, is this intended?")) retval = false; } + if (ftepp->itemname) { + mem_d(ftepp->itemname); + ftepp->itemname = NULL; + } return retval; } bool ftepp_preprocess_file(const char *filename) { ftepp->lex = lex_open(filename); + ftepp->itemname = util_strdup(filename); if (!ftepp->lex) { con_out("failed to open file \"%s\"\n", filename); return false; @@ -1107,8 +1242,8 @@ bool ftepp_preprocess_file(const char *filename) bool ftepp_preprocess_string(const char *name, const char *str) { - ftepp_t *ftepp = ftepp_new(); 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; diff --git a/lexer.c b/lexer.c index 82f7527..25cf4e1 100644 --- a/lexer.c +++ b/lexer.c @@ -421,7 +421,7 @@ static bool lex_try_pragma(lex_file *lex) goto unroll; lex->line = line; - while (ch != '\n') + while (ch != '\n' && ch != EOF) ch = lex_getch(lex); return true; From 2ab7d8d5c36008cf75146f4f239f9511a6e61f85 Mon Sep 17 00:00:00 2001 From: "Wolfgang (Blub) Bumiller" Date: Sun, 18 Nov 2012 17:06:26 +0100 Subject: [PATCH 48/59] Adding myself to main.c's copyright notice --- main.c | 1 + 1 file changed, 1 insertion(+) diff --git a/main.c b/main.c index 6a77d63..990acf2 100644 --- a/main.c +++ b/main.c @@ -1,6 +1,7 @@ /* * Copyright (C) 2012 * Dale Weiler + * Wolfgang Bumiller * * 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 From 2652353f507345d485aef3b4e5fba5b2267d9a60 Mon Sep 17 00:00:00 2001 From: "Wolfgang (Blub) Bumiller" Date: Sun, 18 Nov 2012 18:54:10 +0100 Subject: [PATCH 49/59] Checking in an INSTALL file --- INSTALL | 39 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 39 insertions(+) create mode 100644 INSTALL diff --git a/INSTALL b/INSTALL new file mode 100644 index 0000000..9283835 --- /dev/null +++ b/INSTALL @@ -0,0 +1,39 @@ + Installing gmqcc + +1. Prerequisites + - A C-Compiler such as gcc or clang + - GNU Make. This document will assume GNU-Make to be executed via + `make'. On BSD systems you probably have to use `gmake' instead. + +2. Compilation + Run the GNU make program `make' or `gmake'. + + make + + If no error appears, the following binary files will have been + created: + - gmqcc + - qcvm + +3. Installation + The `install' target will install the 2 binaries to /usr/local/bin + by default. + The Makefile honors the following variables: + + - DESTDIR: The installation directory root. + - PREFIX: The installation prefix, default: /usr/local + - BINDIR: Directory for binary executables, + deafult: $PREFIX/bin + + To install to /usr/local run: + + make install + + To install to /usr run: + + make PREFIX=/usr install + + To install to a package-staging directory such as $pkgdir when + writing an ArchLinux PKGBUILD file: + + make DESTDIR=$pkgdir install From ccb9cf534790c0b1db0f9dcf9718ba344f1cb187 Mon Sep 17 00:00:00 2001 From: "Wolfgang (Blub) Bumiller" Date: Sun, 18 Nov 2012 19:14:06 +0100 Subject: [PATCH 50/59] Importing LICENSE file --- LICENSE | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) create mode 100644 LICENSE diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..029afc5 --- /dev/null +++ b/LICENSE @@ -0,0 +1,19 @@ +Copyright (C) 2012 Dale Weiler, Wolfgang Bumiller + +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 +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +of the Software, and to permit persons to whom the Software is furnished to do +so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. From 41235108c49a03e5577a104d6e44a2f6b5356f6f Mon Sep 17 00:00:00 2001 From: "Wolfgang (Blub) Bumiller" Date: Sun, 18 Nov 2012 19:15:20 +0100 Subject: [PATCH 51/59] Importing a tiny README file --- README | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 README diff --git a/README b/README new file mode 100644 index 0000000..2999a47 --- /dev/null +++ b/README @@ -0,0 +1,5 @@ + The gmqcc Quake C Compiler + +For licensing, see the LICENSE file. + +For installation notes, see the INSTALL file. From e8d1e5dbc0fdbc87d5ce3a0285a0e3df4a2a4728 Mon Sep 17 00:00:00 2001 From: "Wolfgang (Blub) Bumiller" Date: Sun, 18 Nov 2012 19:19:38 +0100 Subject: [PATCH 52/59] Make the >8 parameter message a warning --- parser.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/parser.c b/parser.c index c4cdf2f..600b088 100644 --- a/parser.c +++ b/parser.c @@ -2637,8 +2637,8 @@ static ast_value *parse_parameter_list(parser_t *parser, ast_value *var) } /* sanity check */ - if (vec_size(params) > 8) - parseerror(parser, "more than 8 parameters are currently not supported"); + if (vec_size(params) > 8 && opts_standard == COMPILER_QCC) + parsewarning(parser, WARN_EXTENSIONS, "more than 8 parameters are not supported by this standard"); /* parse-out */ if (!parser_next(parser)) { From f023004a677b75d939ab18eda239f0474ae2176e Mon Sep 17 00:00:00 2001 From: "Wolfgang (Blub) Bumiller" Date: Sun, 18 Nov 2012 20:06:28 +0100 Subject: [PATCH 53/59] Make functions copy their extparams --- ast.c | 1 + ir.c | 67 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ ir.h | 5 +++++ parser.c | 2 +- 4 files changed, 74 insertions(+), 1 deletion(-) diff --git a/ast.c b/ast.c index 9e59ca9..3fc7353 100644 --- a/ast.c +++ b/ast.c @@ -1247,6 +1247,7 @@ bool ast_function_codegen(ast_function *self, ir_builder *ir) /* fill the parameter list */ ec = &self->vtype->expression; + irf->max_parameters = vec_size(ec->params); for (i = 0; i < vec_size(ec->params); ++i) { vec_push(irf->params, ec->params[i]->expression.vtype); diff --git a/ir.c b/ir.c index f652f6a..1fd94a0 100644 --- a/ir.c +++ b/ir.c @@ -259,6 +259,7 @@ ir_builder* ir_builder_new(const char *modulename) self->functions = NULL; self->globals = NULL; self->fields = NULL; + self->extparams = NULL; self->filenames = NULL; self->filestrings = NULL; @@ -280,6 +281,10 @@ void ir_builder_delete(ir_builder* self) ir_function_delete_quick(self->functions[i]); } vec_free(self->functions); + for (i = 0; i != vec_size(self->extparams); ++i) { + ir_value_delete(self->extparams[i]); + } + vec_free(self->extparams); for (i = 0; i != vec_size(self->globals); ++i) { ir_value_delete(self->globals[i]); } @@ -427,6 +432,8 @@ ir_function* ir_function_new(ir_builder* owner, int outtype) self->values = NULL; self->locals = NULL; + self->max_parameters = 0; + self->code_function_def = -1; self->allocated_locals = 0; @@ -2698,6 +2705,62 @@ static bool gen_global_function(ir_builder *ir, ir_value *global) return true; } +static void ir_gen_extparam(ir_builder *ir) +{ + prog_section_def def; + ir_value *global; + char name[128]; + + snprintf(name, sizeof(name), "EXTPARM%i", (int)(vec_size(ir->extparams)+8)); + global = ir_value_var(name, store_global, TYPE_VECTOR); + + def.name = code_genstring(name); + def.type = TYPE_VECTOR; + def.offset = vec_size(code_globals); + + vec_push(code_defs, def); + ir_value_code_setaddr(global, def.offset); + vec_push(code_globals, 0); + vec_push(code_globals, 0); + vec_push(code_globals, 0); + + vec_push(ir->extparams, global); +} + +static bool gen_function_extparam_copy(ir_function *self) +{ + size_t i, ext; + + ir_builder *ir = self->owner; + ir_value *ep; + prog_section_statement stmt; + + if (!self->max_parameters) + return true; + + stmt.opcode = INSTR_STORE_F; + stmt.o3.s1 = 0; + for (i = 8; i < self->max_parameters; ++i) { + ext = i - 8; + if (ext >= vec_size(ir->extparams)) + ir_gen_extparam(ir); + + ep = ir->extparams[ext]; + + stmt.opcode = type_store_instr[self->locals[i]->vtype]; + if (self->locals[i]->vtype == TYPE_FIELD && + self->locals[i]->fieldtype == TYPE_VECTOR) + { + stmt.opcode = INSTR_STORE_V; + } + stmt.o1.u1 = ir_value_code_addr(ep); + stmt.o2.u1 = ir_value_code_addr(self->locals[i]); + vec_push(code_statements, stmt); + } + + return true; +} + static bool gen_global_function_code(ir_builder *ir, ir_value *global) { prog_section_function *fundef; @@ -2721,6 +2784,10 @@ static bool gen_global_function_code(ir_builder *ir, ir_value *global) fundef = &code_functions[irfun->code_function_def]; fundef->entry = vec_size(code_statements); + if (!gen_function_extparam_copy(irfun)) { + irerror(irfun->context, "Failed to generate extparam-copy code for function %s", irfun->name); + return false; + } if (!gen_function_code(irfun)) { irerror(irfun->context, "Failed to generate code for function %s", irfun->name); return false; diff --git a/ir.h b/ir.h index 9b5171d..00e2ddc 100644 --- a/ir.h +++ b/ir.h @@ -240,6 +240,9 @@ typedef struct ir_function_s /* locally defined variables */ ir_value **locals; + /* how many of the locals are parameters */ + size_t max_parameters; + size_t allocated_locals; ir_block* first; @@ -290,6 +293,8 @@ typedef struct ir_builder_s ir_value **globals; ir_value **fields; + ir_value **extparams; + const char **filenames; qcint *filestrings; /* we cache the #IMMEDIATE string here */ diff --git a/parser.c b/parser.c index 600b088..25c9c74 100644 --- a/parser.c +++ b/parser.c @@ -2638,7 +2638,7 @@ static ast_value *parse_parameter_list(parser_t *parser, ast_value *var) /* sanity check */ if (vec_size(params) > 8 && opts_standard == COMPILER_QCC) - parsewarning(parser, WARN_EXTENSIONS, "more than 8 parameters are not supported by this standard"); + (void)!parsewarning(parser, WARN_EXTENSIONS, "more than 8 parameters are not supported by this standard"); /* parse-out */ if (!parser_next(parser)) { From 45cd90c777245b3e7db9fb72816266987c909f07 Mon Sep 17 00:00:00 2001 From: "Wolfgang (Blub) Bumiller" Date: Sun, 18 Nov 2012 20:11:14 +0100 Subject: [PATCH 54/59] Add a # prefix to EXTPARM%i --- ir.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ir.c b/ir.c index 1fd94a0..46f5229 100644 --- a/ir.c +++ b/ir.c @@ -2711,7 +2711,7 @@ static void ir_gen_extparam(ir_builder *ir) ir_value *global; char name[128]; - snprintf(name, sizeof(name), "EXTPARM%i", (int)(vec_size(ir->extparams)+8)); + snprintf(name, sizeof(name), "#EXTPARM%i", (int)(vec_size(ir->extparams)+8)); global = ir_value_var(name, store_global, TYPE_VECTOR); def.name = code_genstring(name); From 149481b80c831f886b590452293af6cee729822b Mon Sep 17 00:00:00 2001 From: "Wolfgang (Blub) Bumiller" Date: Sun, 18 Nov 2012 20:17:59 +0100 Subject: [PATCH 55/59] CALL to handle extparams, renamed extparams again --- ir.c | 35 ++++++++++++++++++++++++++++++++--- 1 file changed, 32 insertions(+), 3 deletions(-) diff --git a/ir.c b/ir.c index 46f5229..bf27f38 100644 --- a/ir.c +++ b/ir.c @@ -2391,6 +2391,7 @@ static bool gen_global_pointer(ir_value *global) return true; } +static void ir_gen_extparam(ir_builder *ir); static bool gen_blocks_recursive(ir_function *func, ir_block *block) { prog_section_statement stmt; @@ -2507,10 +2508,13 @@ tailcall: * generation already. This would even include later * reuse.... probably... :) */ - size_t p; + size_t p, first; ir_value *retvalue; - for (p = 0; p < vec_size(instr->params); ++p) + first = vec_size(instr->params); + if (first > 8) + first = 8; + for (p = 0; p < first; ++p) { ir_value *param = instr->params[p]; @@ -2525,6 +2529,31 @@ tailcall: stmt.o2.u1 = OFS_PARM0 + 3 * p; vec_push(code_statements, stmt); } + /* No whandle extparams */ + first = vec_size(instr->params); + for (; p < first; ++p) + { + ir_builder *ir = func->owner; + ir_value *param = instr->params[p]; + ir_value *target; + + if (p-8 >= vec_size(ir->extparams)) + ir_gen_extparam(ir); + + target = ir->extparams[p-8]; + + stmt.opcode = INSTR_STORE_F; + stmt.o3.u1 = 0; + + if (param->vtype == TYPE_FIELD) + stmt.opcode = field_store_instr[param->fieldtype]; + else + stmt.opcode = type_store_instr[param->vtype]; + stmt.o1.u1 = ir_value_code_addr(param); + stmt.o2.u1 = ir_value_code_addr(target); + vec_push(code_statements, stmt); + } + stmt.opcode = INSTR_CALL0 + vec_size(instr->params); if (stmt.opcode > INSTR_CALL8) stmt.opcode = INSTR_CALL8; @@ -2711,7 +2740,7 @@ static void ir_gen_extparam(ir_builder *ir) ir_value *global; char name[128]; - snprintf(name, sizeof(name), "#EXTPARM%i", (int)(vec_size(ir->extparams)+8)); + snprintf(name, sizeof(name), "EXTPARM#%i", (int)(vec_size(ir->extparams)+8)); global = ir_value_var(name, store_global, TYPE_VECTOR); def.name = code_genstring(name); From ed6189e65531819b21c12651e2eb8118bf3e0f17 Mon Sep 17 00:00:00 2001 From: "Wolfgang (Blub) Bumiller" Date: Sun, 18 Nov 2012 20:32:06 +0100 Subject: [PATCH 56/59] do not set a function-defs nargs to > 8 --- ir.c | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/ir.c b/ir.c index bf27f38..025a96a 100644 --- a/ir.c +++ b/ir.c @@ -2391,7 +2391,6 @@ static bool gen_global_pointer(ir_value *global) return true; } -static void ir_gen_extparam(ir_builder *ir); static bool gen_blocks_recursive(ir_function *func, ir_block *block) { prog_section_statement stmt; @@ -2537,8 +2536,10 @@ tailcall: ir_value *param = instr->params[p]; ir_value *target; - if (p-8 >= vec_size(ir->extparams)) - ir_gen_extparam(ir); + if (p-8 >= vec_size(ir->extparams)) { + irerror(instr->context, "Not enough extparam-globals have been created"); + return false; + } target = ir->extparams[p-8]; @@ -2688,6 +2689,8 @@ static bool gen_global_function(ir_builder *ir, ir_value *global) fun.file = ir_builder_filestring(ir, global->context.file); fun.profile = 0; /* always 0 */ fun.nargs = vec_size(irfun->params); + if (fun.nargs > 8) + fun.nargs = 8; for (i = 0;i < 8; ++i) { if (i >= fun.nargs) From 12fca5ef8ff45e7408b2d9633a6954fb76673499 Mon Sep 17 00:00:00 2001 From: "Wolfgang (Blub) Bumiller" Date: Sun, 18 Nov 2012 20:32:22 +0100 Subject: [PATCH 57/59] Make the disasm a bit more verbose as to where globals are --- exec.c | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/exec.c b/exec.c index ee61c1a..f50eac1 100644 --- a/exec.c +++ b/exec.c @@ -334,16 +334,15 @@ static void trace_print_global(qc_program *prog, unsigned int glob, int vtype) def = prog_getdef(prog, glob); value = (qcany*)(&prog->globals[glob]); + len = printf("[@%u] ", glob); if (def) { const char *name = prog_getstring(prog, def->name); if (name[0] == '#') - len = printf("$"); + len += printf("$"); else - len = printf("%s ", name); + len += printf("%s ", name); vtype = def->type & DEF_TYPEMASK; } - else - len = printf("[@%u] ", glob); switch (vtype) { case TYPE_VOID: From f1735d91b8a3b31fce6330bbe4c3dc43b55c3cd3 Mon Sep 17 00:00:00 2001 From: "Wolfgang (Blub) Bumiller" Date: Sun, 18 Nov 2012 20:36:02 +0100 Subject: [PATCH 58/59] remove a redundant ir_function member --- ast.c | 1 - ir.c | 9 ++++----- ir.h | 3 --- 3 files changed, 4 insertions(+), 9 deletions(-) diff --git a/ast.c b/ast.c index 3fc7353..9e59ca9 100644 --- a/ast.c +++ b/ast.c @@ -1247,7 +1247,6 @@ bool ast_function_codegen(ast_function *self, ir_builder *ir) /* fill the parameter list */ ec = &self->vtype->expression; - irf->max_parameters = vec_size(ec->params); for (i = 0; i < vec_size(ec->params); ++i) { vec_push(irf->params, ec->params[i]->expression.vtype); diff --git a/ir.c b/ir.c index 025a96a..9907b35 100644 --- a/ir.c +++ b/ir.c @@ -432,8 +432,6 @@ ir_function* ir_function_new(ir_builder* owner, int outtype) self->values = NULL; self->locals = NULL; - self->max_parameters = 0; - self->code_function_def = -1; self->allocated_locals = 0; @@ -2761,18 +2759,19 @@ static void ir_gen_extparam(ir_builder *ir) static bool gen_function_extparam_copy(ir_function *self) { - size_t i, ext; + size_t i, ext, numparams; ir_builder *ir = self->owner; ir_value *ep; prog_section_statement stmt; - if (!self->max_parameters) + numparams = vec_size(self->params); + if (!numparams) return true; stmt.opcode = INSTR_STORE_F; stmt.o3.s1 = 0; - for (i = 8; i < self->max_parameters; ++i) { + for (i = 8; i < numparams; ++i) { ext = i - 8; if (ext >= vec_size(ir->extparams)) ir_gen_extparam(ir); diff --git a/ir.h b/ir.h index 00e2ddc..06efa5b 100644 --- a/ir.h +++ b/ir.h @@ -240,9 +240,6 @@ typedef struct ir_function_s /* locally defined variables */ ir_value **locals; - /* how many of the locals are parameters */ - size_t max_parameters; - size_t allocated_locals; ir_block* first; From 19e82883ea29ce43ff45f830bc2c8cfbc8f49007 Mon Sep 17 00:00:00 2001 From: "Wolfgang (Blub) Bumiller" Date: Sun, 18 Nov 2012 20:56:11 +0100 Subject: [PATCH 59/59] Fixing some indentation --- lexer.c | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/lexer.c b/lexer.c index 25cf4e1..a71e50f 100644 --- a/lexer.c +++ b/lexer.c @@ -10,32 +10,32 @@ char* *lex_filenames; void lexerror(lex_file *lex, const char *fmt, ...) { - va_list ap; + va_list ap; - va_start(ap, fmt); - if (lex) + va_start(ap, fmt); + if (lex) con_vprintmsg(LVL_ERROR, lex->name, lex->sline, "parse error", fmt, ap); else con_vprintmsg(LVL_ERROR, "", 0, "parse error", fmt, ap); - va_end(ap); + va_end(ap); } bool lexwarn(lex_file *lex, int warntype, const char *fmt, ...) { - va_list ap; - int lvl = LVL_WARNING; + va_list ap; + int lvl = LVL_WARNING; if (!OPTS_WARN(warntype)) return false; if (opts_werror) - lvl = LVL_ERROR; + lvl = LVL_ERROR; - va_start(ap, fmt); + va_start(ap, fmt); con_vprintmsg(lvl, lex->name, lex->sline, "warning", fmt, ap); - va_end(ap); + va_end(ap); - return opts_werror; + return opts_werror; }