]> de.git.xonotic.org Git - xonotic/gmqcc.git/blobdiff - ftepp.c
when recursinve into macros copy the previous line number; fixes macros showing line...
[xonotic/gmqcc.git] / ftepp.c
diff --git a/ftepp.c b/ftepp.c
index 4cdfb68fbf6f04042722ee102be0097b4c90628a..21669a8aa6a492910c23b12dcb3d8482c4fb09a8 100644 (file)
--- a/ftepp.c
+++ b/ftepp.c
@@ -1,6 +1,7 @@
 /*
- * Copyright (C) 2012
+ * Copyright (C) 2012, 2013
  *     Wolfgang Bumiller
+ *     Dale Weiler 
  *
  * Permission is hereby granted, free of charge, to any person obtaining a copy of
  * this software and associated documentation files (the "Software"), to deal in
@@ -55,7 +56,6 @@ typedef struct {
 typedef struct {
     lex_file    *lex;
     int          token;
-    bool         newline;
     unsigned int errors;
 
     bool         output_on;
@@ -68,6 +68,77 @@ typedef struct {
     char        *includename;
 } ftepp_t;
 
+typedef struct {
+    const char  *name;
+    char      *(*func)(lex_file *);
+} predef_t;
+
+/*
+ * Implement the predef subsystem now.  We can do this safely with the
+ * help of lexer contexts.
+ */  
+static uint32_t ftepp_predef_countval = 0;
+static uint32_t ftepp_predef_randval  = 0;
+
+/* __LINE__ */
+char *ftepp_predef_line(lex_file *context) {
+    char   *value;
+    util_asprintf(&value, "%d", (int)context->line);
+    return value;
+}
+/* __FILE__ */
+char *ftepp_predef_file(lex_file *context) {
+    size_t  length = strlen(context->name) + 3; /* two quotes and a terminator */
+    char   *value  = (char*)mem_a(length);
+    memset (value, 0, length);
+    sprintf(value, "\"%s\"", context->name);
+
+    return value;
+}
+/* __COUNTER_LAST__ */
+char *ftepp_predef_counterlast(lex_file *context) {
+    char   *value;
+    util_asprintf(&value, "%u", ftepp_predef_countval);
+
+    (void)context;
+    return value;
+}
+/* __COUNTER__ */
+char *ftepp_predef_counter(lex_file *context) {
+    char   *value;
+    ftepp_predef_countval ++;
+    util_asprintf(&value, "%u", ftepp_predef_countval);
+    (void)context;
+
+    return value;
+}
+/* __RANDOM__ */
+char *ftepp_predef_random(lex_file *context) {
+    char  *value;
+    ftepp_predef_randval = (util_rand() % 0xFF) + 1;
+    util_asprintf(&value, "%u", ftepp_predef_randval);
+
+    (void)context;
+    return value;
+}
+/* __RANDOM_LAST__ */
+char *ftepp_predef_randomlast(lex_file *context) {
+    char   *value;
+    util_asprintf(&value, "%u", ftepp_predef_randval);
+
+    (void)context;
+    return value;
+}
+
+static const predef_t ftepp_predefs[] = {
+    { "__LINE__",         &ftepp_predef_line        },
+    { "__FILE__",         &ftepp_predef_file        },
+    { "__COUNTER__",      &ftepp_predef_counter     },
+    { "__COUNTER_LAST__", &ftepp_predef_counterlast },
+    { "__RANDOM__",       &ftepp_predef_random      },
+    { "__RANDOM_LAST__",  &ftepp_predef_randomlast  },
+};
+
 #define ftepp_tokval(f) ((f)->lex->tok.value)
 #define ftepp_ctx(f)    ((f)->lex->tok.ctx)
 
@@ -95,21 +166,13 @@ static void ftepp_error(ftepp_t *ftepp, const char *fmt, ...)
 
 static bool GMQCC_WARN ftepp_warn(ftepp_t *ftepp, int warntype, const char *fmt, ...)
 {
+    bool    r;
     va_list ap;
-    int lvl = LVL_WARNING;
-
-    if (!OPTS_WARN(warntype))
-        return false;
-
-    if (opts_werror) {
-           lvl = LVL_ERROR;
-        ftepp->errors++;
-    }
 
     va_start(ap, fmt);
-    con_cvprintmsg((void*)&ftepp->lex->tok.ctx, lvl, "error", fmt, ap);
+    r = vcompile_warning(ftepp->lex->tok.ctx, warntype, fmt, ap);
     va_end(ap);
-    return opts_werror;
+    return r;
 }
 
 static pptoken *pptoken_make(ftepp_t *ftepp)
@@ -136,7 +199,7 @@ static void pptoken_delete(pptoken *self)
 static ppmacro *ppmacro_new(lex_ctx ctx, const char *name)
 {
     ppmacro *macro = (ppmacro*)mem_a(sizeof(ppmacro));
-    
+
     (void)ctx;
     memset(macro, 0, sizeof(*macro));
     macro->name = util_strdup(name);
@@ -171,6 +234,7 @@ static ftepp_t* ftepp_new()
 static void ftepp_delete(ftepp_t *self)
 {
     size_t i;
+    ftepp_flush(self);
     if (self->itemname)
         mem_d(self->itemname);
     if (self->includename)
@@ -225,7 +289,7 @@ static void ftepp_macro_delete(ftepp_t *ftepp, const char *name)
     }
 }
 
-static inline int ftepp_next(ftepp_t *ftepp)
+static GMQCC_INLINE int ftepp_next(ftepp_t *ftepp)
 {
     return (ftepp->token = lex_do(ftepp->lex));
 }
@@ -311,6 +375,8 @@ static bool ftepp_define_body(ftepp_t *ftepp, ppmacro *macro)
 static bool ftepp_define(ftepp_t *ftepp)
 {
     ppmacro *macro;
+    size_t l = ftepp_ctx(ftepp).line;
+
     (void)ftepp_next(ftepp);
     if (!ftepp_skipspace(ftepp))
         return false;
@@ -351,6 +417,9 @@ static bool ftepp_define(ftepp_t *ftepp)
     else {
         ppmacro_delete(macro);
     }
+
+    for (; l < ftepp_ctx(ftepp).line; ++l)
+        ftepp_out(ftepp, "\n", true);
     return true;
 }
 
@@ -578,6 +647,8 @@ static bool ftepp_macro_expand(ftepp_t *ftepp, ppmacro *macro, macroparam *param
         goto cleanup;
     }
     ftepp->output_string = old_string;
+    inlex->line = ftepp->lex->line;
+    inlex->sline = ftepp->lex->sline;
     ftepp->lex = inlex;
     ftepp_recursion_header(ftepp);
     if (!ftepp_preprocess(ftepp)) {
@@ -989,20 +1060,14 @@ static char *ftepp_include_find_path(const char *file, const char *pathfile)
         memcpy(vec_add(filename, len), pathfile, len);
         vec_push(filename, '/');
     }
-    else {
-        len = strlen(pathfile);
-        memcpy(vec_add(filename, len), pathfile, len);
-        if (vec_last(filename) != '/')
-            vec_push(filename, '/');
-    }
 
     len = strlen(file);
     memcpy(vec_add(filename, len+1), file, len);
     vec_last(filename) = 0;
 
-    fp = util_fopen(filename, "rb");
+    fp = file_open(filename, "rb");
     if (fp) {
-        fclose(fp);
+        file_close(fp);
         return filename;
     }
     vec_free(filename);
@@ -1019,6 +1084,55 @@ static char *ftepp_include_find(ftepp_t *ftepp, const char *file)
     return filename;
 }
 
+static bool ftepp_directive_warning(ftepp_t *ftepp) {
+    char *message = NULL;
+
+    if (!ftepp_skipspace(ftepp))
+        return false;
+
+    /* handle the odd non string constant case so it works like C */
+    if (ftepp->token != TOKEN_STRINGCONST) {
+        bool  store   = false;
+        vec_upload(message, "#warning", 8);
+        ftepp_next(ftepp);
+        while (ftepp->token != TOKEN_EOL) {
+            vec_upload(message, ftepp_tokval(ftepp), strlen(ftepp_tokval(ftepp)));
+            ftepp_next(ftepp);
+        }
+        vec_push(message, '\0');
+        store = ftepp_warn(ftepp, WARN_CPP, message);
+        vec_free(message);
+        return store;
+    }
+
+    unescape  (ftepp_tokval(ftepp), ftepp_tokval(ftepp));
+    return ftepp_warn(ftepp, WARN_CPP, "#warning %s", ftepp_tokval(ftepp));
+}
+
+static void ftepp_directive_error(ftepp_t *ftepp) {
+    char *message = NULL;
+
+    if (!ftepp_skipspace(ftepp))
+        return;
+
+    /* handle the odd non string constant case so it works like C */
+    if (ftepp->token != TOKEN_STRINGCONST) {
+        vec_upload(message, "#error", 6);
+        ftepp_next(ftepp);
+        while (ftepp->token != TOKEN_EOL) {
+            vec_upload(message, ftepp_tokval(ftepp), strlen(ftepp_tokval(ftepp)));
+            ftepp_next(ftepp);
+        }
+        vec_push(message, '\0');
+        ftepp_error(ftepp, message);
+        vec_free(message);
+        return;
+    }
+
+    unescape  (ftepp_tokval(ftepp), ftepp_tokval(ftepp));
+    ftepp_error(ftepp, "#error %s", ftepp_tokval(ftepp));
+}
+
 /**
  * Include a file.
  * FIXME: do we need/want a -I option?
@@ -1057,7 +1171,7 @@ static bool ftepp_include(ftepp_t *ftepp)
     }
     inlex = lex_open(filename);
     if (!inlex) {
-        ftepp_error(ftepp, "failed to open include file `%s`", filename);
+        ftepp_error(ftepp, "open failed on include file `%s`", filename);
         vec_free(filename);
         return false;
     }
@@ -1214,9 +1328,22 @@ static bool ftepp_hash(ftepp_t *ftepp)
                 ftepp_out(ftepp, "#", false);
                 break;
             }
+            else if (!strcmp(ftepp_tokval(ftepp), "warning")) {
+                ftepp_directive_warning(ftepp);
+                break;
+            }
+            else if (!strcmp(ftepp_tokval(ftepp), "error")) {
+                ftepp_directive_error(ftepp);
+                break;
+            }
             else {
-                ftepp_error(ftepp, "unrecognized preprocessor directive: `%s`", ftepp_tokval(ftepp));
-                return false;
+                if (ftepp->output_on) {
+                    ftepp_error(ftepp, "unrecognized preprocessor directive: `%s`", ftepp_tokval(ftepp));
+                    return false;
+                } else {
+                    ftepp_next(ftepp);
+                    break;
+                }
             }
             /* break; never reached */
         default:
@@ -1245,6 +1372,10 @@ static bool ftepp_preprocess(ftepp_t *ftepp)
     ppmacro *macro;
     bool     newline = true;
 
+    /* predef stuff */
+    char    *expand  = NULL;
+    size_t   i;
+
     ftepp->lex->flags.preprocessing = true;
     ftepp->lex->flags.mergelines    = false;
     ftepp->lex->flags.noops         = true;
@@ -1255,18 +1386,32 @@ static bool ftepp_preprocess(ftepp_t *ftepp)
         if (ftepp->token >= TOKEN_EOF)
             break;
 #if 0
-        ftepp->newline = newline;
-        newline = false;
-#else
-        /* For the sake of FTE compatibility... FU, really */
-        ftepp->newline = newline = true;
+        newline = true;
 #endif
 
         switch (ftepp->token) {
             case TOKEN_KEYWORD:
             case TOKEN_IDENT:
             case TOKEN_TYPENAME:
-                macro = ftepp_macro_find(ftepp, ftepp_tokval(ftepp));
+                /* is it a predef? */
+                if (OPTS_FLAG(FTEPP_PREDEFS)) {
+                    for (i = 0; i < sizeof(ftepp_predefs) / sizeof (*ftepp_predefs); i++) {
+                        if (!strcmp(ftepp_predefs[i].name, ftepp_tokval(ftepp))) {
+                            expand = ftepp_predefs[i].func(ftepp->lex);
+                            ftepp_out(ftepp, expand, false);
+                            ftepp_next(ftepp); /* skip */
+
+                            mem_d(expand); /* free memory */
+                            break;
+                        }
+                    }
+                }
+
+                if (ftepp->output_on)
+                    macro = ftepp_macro_find(ftepp, ftepp_tokval(ftepp));
+                else
+                    macro = NULL;
+
                 if (!macro) {
                     ftepp_out(ftepp, ftepp_tokval(ftepp), false);
                     ftepp_next(ftepp);
@@ -1276,7 +1421,7 @@ static bool ftepp_preprocess(ftepp_t *ftepp)
                     ftepp->token = TOKEN_ERROR;
                 break;
             case '#':
-                if (!ftepp->newline) {
+                if (!newline) {
                     ftepp_out(ftepp, ftepp_tokval(ftepp), false);
                     ftepp_next(ftepp);
                     break;
@@ -1296,7 +1441,13 @@ static bool ftepp_preprocess(ftepp_t *ftepp)
                 ftepp_out(ftepp, "\n", true);
                 ftepp_next(ftepp);
                 break;
+            case TOKEN_WHITE:
+                /* same as default but don't set newline=false */
+                ftepp_out(ftepp, ftepp_tokval(ftepp), false);
+                ftepp_next(ftepp);
+                break;
             default:
+                newline = false;
                 ftepp_out(ftepp, ftepp_tokval(ftepp), false);
                 ftepp_next(ftepp);
                 break;
@@ -1307,8 +1458,7 @@ static bool ftepp_preprocess(ftepp_t *ftepp)
     vec_push(ftepp->output_string, 0);
     vec_shrinkby(ftepp->output_string, 1);
 
-    newline = ftepp->token == TOKEN_EOF;
-    return newline;
+    return (ftepp->token == TOKEN_EOF);
 }
 
 /* Like in parser.c - files keep the previous state so we have one global
@@ -1358,12 +1508,69 @@ bool ftepp_preprocess_string(const char *name, const char *str)
     return ftepp_preprocess_done();
 }
 
+
+void ftepp_add_macro(const char *name, const char *value) {
+    char *create = NULL;
+
+    /* use saner path for empty macros */
+    if (!value) {
+        ftepp_add_define("__builtin__", name);
+        return;
+    }
+
+    vec_upload(create, "#define ", 8);
+    vec_upload(create, name,  strlen(name));
+    vec_push  (create, ' ');
+    vec_upload(create, value, strlen(value));
+    vec_push  (create, 0);
+
+    ftepp_preprocess_string("__builtin__", create);
+    vec_free  (create);
+}
+
 bool ftepp_init()
 {
+    char minor[32];
+    char major[32];
+
     ftepp = ftepp_new();
     if (!ftepp)
         return false;
+
+    memset(minor, 0, sizeof(minor));
+    memset(major, 0, sizeof(major));
+
+    /* set the right macro based on the selected standard */
     ftepp_add_define(NULL, "GMQCC");
+    if (opts.standard == COMPILER_FTEQCC) {
+        ftepp_add_define(NULL, "__STD_FTEQCC__");
+        /* 1.00 */
+        major[0] = '"';
+        major[1] = '1';
+        major[2] = '"';
+
+        minor[0] = '"';
+        minor[1] = '0';
+        minor[2] = '"';
+    } else if (opts.standard == COMPILER_GMQCC) {
+        ftepp_add_define(NULL, "__STD_GMQCC__");
+        sprintf(major, "\"%d\"", GMQCC_VERSION_MAJOR);
+        sprintf(minor, "\"%d\"", GMQCC_VERSION_MINOR);
+    } else if (opts.standard == COMPILER_QCC) {
+        ftepp_add_define(NULL, "__STD_QCC__");
+        /* 1.0 */
+        major[0] = '"';
+        major[1] = '1';
+        major[2] = '"';
+
+        minor[0] = '"';
+        minor[1] = '0';
+        minor[2] = '"';
+    }
+
+    ftepp_add_macro("__STD_VERSION_MINOR__", minor);
+    ftepp_add_macro("__STD_VERSION_MAJOR__", major);
+
     return true;
 }