]> git.xonotic.org Git - xonotic/gmqcc.git/blobdiff - ftepp.c
Rename
[xonotic/gmqcc.git] / ftepp.c
diff --git a/ftepp.c b/ftepp.c
index ecc36d8e1851f422aed1c31f0e2696ae71db548a..34eec593bc8754019c635a4ddf1accc604cb54cb 100644 (file)
--- a/ftepp.c
+++ b/ftepp.c
@@ -21,6 +21,7 @@
  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
  * SOFTWARE.
  */
+#include <time.h>
 #include "gmqcc.h"
 #include "lexer.h"
 
@@ -67,13 +68,9 @@ typedef struct {
 
     char        *itemname;
     char        *includename;
+    bool         in_macro;
 } 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.
@@ -81,6 +78,42 @@ typedef struct {
 static uint32_t ftepp_predef_countval = 0;
 static uint32_t ftepp_predef_randval  = 0;
 
+/* __DATE__ */
+char *ftepp_predef_date(lex_file *context) {
+    struct tm *itime;
+    time_t     rtime;
+    char      *value = mem_a(82);
+    /* 82 is enough for strftime but we also have " " in our string */
+
+    (void)context;
+
+    /* get time */
+    time (&rtime);
+    itime = localtime(&rtime);
+
+    strftime(value, 82, "\"%b %d %Y\"", itime);
+
+    return value;
+}
+
+/* __TIME__ */
+char *ftepp_predef_time(lex_file *context) {
+    struct tm *itime;
+    time_t     rtime;
+    char      *value = mem_a(82);
+    /* 82 is enough for strftime but we also have " " in our string */
+
+    (void)context;
+
+    /* get time */
+    time (&rtime);
+    itime = localtime(&rtime);
+
+    strftime(value, 82, "\"%X\"", itime);
+
+    return value;
+}
+
 /* __LINE__ */
 char *ftepp_predef_line(lex_file *context) {
     char   *value;
@@ -131,13 +164,15 @@ char *ftepp_predef_randomlast(lex_file *context) {
     return value;
 }
 
-static const predef_t ftepp_predefs[] = {
+const ftepp_predef_t ftepp_predefs[FTEPP_PREDEF_COUNT] = {
     { "__LINE__",         &ftepp_predef_line        },
     { "__FILE__",         &ftepp_predef_file        },
     { "__COUNTER__",      &ftepp_predef_counter     },
     { "__COUNTER_LAST__", &ftepp_predef_counterlast },
     { "__RANDOM__",       &ftepp_predef_random      },
     { "__RANDOM_LAST__",  &ftepp_predef_randomlast  },
+    { "__DATE__",         &ftepp_predef_date        },
+    { "__TIME__",         &ftepp_predef_time        }
 };
 
 #define ftepp_tokval(f) ((f)->lex->tok.value)
@@ -373,11 +408,55 @@ static bool ftepp_define_body(ftepp_t *ftepp, ppmacro *macro)
 {
     pptoken *ptok;
     while (ftepp->token != TOKEN_EOL && ftepp->token < TOKEN_EOF) {
-        if (macro->variadic && !strcmp(ftepp_tokval(ftepp), "__VA_ARGS__"))
-            ftepp->token = TOKEN_VA_ARGS;
-        ptok = pptoken_make(ftepp);
-        vec_push(macro->output, ptok);
-        ftepp_next(ftepp);
+        bool   subscript = false;
+        size_t index     = 0;
+        if (macro->variadic && !strcmp(ftepp_tokval(ftepp), "__VA_ARGS__")) {
+            subscript = !!(ftepp_next(ftepp) == '#');
+
+            if (subscript && ftepp_next(ftepp) != '#') {
+                ftepp_error(ftepp, "expected `##` in __VA_ARGS__ for subscripting");
+                return false;
+            } else if (subscript) {
+                if (ftepp_next(ftepp) == '[') {
+                    if (ftepp_next(ftepp) != TOKEN_INTCONST) {
+                        ftepp_error(ftepp, "expected index for __VA_ARGS__ subscript");
+                        return false;
+                    }
+
+                    index = atoi(ftepp_tokval(ftepp));
+
+                    if (ftepp_next(ftepp) != ']') {
+                        ftepp_error(ftepp, "expected `]` in __VA_ARGS__ subscript");
+                        return false;
+                    }
+
+                    /*
+                     * mark it as an array to be handled later as such and not
+                     * as traditional __VA_ARGS__
+                     */
+                    ftepp->token = TOKEN_VA_ARGS_ARRAY;
+                    ptok = pptoken_make(ftepp);
+                    ptok->constval.i = index;
+                    vec_push(macro->output, ptok);
+                    ftepp_next(ftepp);
+                } else {
+                    ftepp_error(ftepp, "expected `[` for subscripting of __VA_ARGS__");
+                    return false;
+                }
+            } else {
+                int old = ftepp->token;
+                ftepp->token = TOKEN_VA_ARGS;
+                ptok = pptoken_make(ftepp);
+                vec_push(macro->output, ptok);
+                ftepp->token = old;
+            }
+        }
+        else
+        {
+            ptok = pptoken_make(ftepp);
+            vec_push(macro->output, ptok);
+            ftepp_next(ftepp);
+        }
     }
     /* recursive expansion can cause EOFs here */
     if (ftepp->token != TOKEN_EOL && ftepp->token != TOKEN_EOF) {
@@ -402,7 +481,7 @@ static bool ftepp_define(ftepp_t *ftepp)
         case TOKEN_KEYWORD:
             macro = ftepp_macro_find(ftepp, ftepp_tokval(ftepp));
             if (macro && ftepp->output_on) {
-                if (ftepp_warn(ftepp, WARN_PREPROCESSOR, "redefining `%s`", ftepp_tokval(ftepp)))
+                if (ftepp_warn(ftepp, WARN_CPP, "redefining `%s`", ftepp_tokval(ftepp)))
                     return false;
                 ftepp_macro_delete(ftepp, ftepp_tokval(ftepp));
             }
@@ -600,17 +679,21 @@ static void ftepp_param_out(ftepp_t *ftepp, macroparam *param)
 }
 
 static bool ftepp_preprocess(ftepp_t *ftepp);
-static bool ftepp_macro_expand(ftepp_t *ftepp, ppmacro *macro, macroparam *params)
+static bool ftepp_macro_expand(ftepp_t *ftepp, ppmacro *macro, macroparam *params, bool resetline)
 {
     char     *old_string   = ftepp->output_string;
+    char     *inner_string;
     lex_file *old_lexer    = ftepp->lex;
     size_t    vararg_start = vec_size(macro->params);
     bool      retval       = true;
+    bool      has_newlines;
     size_t    varargs;
 
     size_t    o, pi;
     lex_file *inlex;
 
+    bool      old_inmacro;
+
     int nextok;
 
     if (vararg_start < vec_size(params))
@@ -629,10 +712,12 @@ static bool ftepp_macro_expand(ftepp_t *ftepp, ppmacro *macro, macroparam *param
             case TOKEN_VA_ARGS:
                 if (!macro->variadic) {
                     ftepp_error(ftepp, "internal preprocessor error: TOKEN_VA_ARGS in non-variadic macro");
+                    vec_free(old_string);
                     return false;
                 }
                 if (!varargs)
                     break;
+
                 pi = 0;
                 ftepp_param_out(ftepp, &params[pi + vararg_start]);
                 for (++pi; pi < varargs; ++pi) {
@@ -640,6 +725,17 @@ static bool ftepp_macro_expand(ftepp_t *ftepp, ppmacro *macro, macroparam *param
                     ftepp_param_out(ftepp, &params[pi + vararg_start]);
                 }
                 break;
+
+            case TOKEN_VA_ARGS_ARRAY:
+                if ((size_t)out->constval.i >= varargs) {
+                    ftepp_error(ftepp, "subscript of `[%u]` is out of bounds for `__VA_ARGS__`", out->constval.i);
+                    vec_free(old_string);
+                    return false;
+                }
+
+                ftepp_param_out(ftepp, &params[out->constval.i + vararg_start]);
+                break;
+
             case TOKEN_IDENT:
             case TOKEN_TYPENAME:
             case TOKEN_KEYWORD:
@@ -688,22 +784,47 @@ static bool ftepp_macro_expand(ftepp_t *ftepp, ppmacro *macro, macroparam *param
         retval = false;
         goto cleanup;
     }
-    ftepp->output_string = old_string;
-    inlex->line = ftepp->lex->line;
+
+    inlex->line  = ftepp->lex->line;
     inlex->sline = ftepp->lex->sline;
-    ftepp->lex = inlex;
-    ftepp_recursion_header(ftepp);
+    ftepp->lex   = inlex;
+
+    old_inmacro     = ftepp->in_macro;
+    ftepp->in_macro = true;
+    ftepp->output_string = NULL;
     if (!ftepp_preprocess(ftepp)) {
+        ftepp->in_macro = old_inmacro;
         vec_free(ftepp->lex->open_string);
-        old_string = ftepp->output_string;
+        vec_free(ftepp->output_string);
         lex_close(ftepp->lex);
         retval = false;
         goto cleanup;
     }
+    ftepp->in_macro = old_inmacro;
     vec_free(ftepp->lex->open_string);
-    ftepp_recursion_footer(ftepp);
-    old_string = ftepp->output_string;
+    lex_close(ftepp->lex);
+
+    inner_string = ftepp->output_string;
+    ftepp->output_string = old_string;
+
+    has_newlines = (strchr(inner_string, '\n') != NULL);
+
+    if (has_newlines && !old_inmacro)
+        ftepp_recursion_header(ftepp);
 
+    vec_append(ftepp->output_string, vec_size(inner_string), inner_string);
+    vec_free(inner_string);
+
+    if (has_newlines && !old_inmacro)
+        ftepp_recursion_footer(ftepp);
+
+    if (resetline && !ftepp->in_macro) {
+        char lineno[128];
+        sprintf(lineno, "\n#pragma line(%lu)\n", (unsigned long)(old_lexer->sline));
+        ftepp_out(ftepp, lineno, false);
+    }
+
+    old_string = ftepp->output_string;
 cleanup:
     ftepp->lex           = old_lexer;
     ftepp->output_string = old_string;
@@ -715,9 +836,10 @@ static bool ftepp_macro_call(ftepp_t *ftepp, ppmacro *macro)
     size_t     o;
     macroparam *params = NULL;
     bool        retval = true;
+    size_t      paramline;
 
     if (!macro->has_params) {
-        if (!ftepp_macro_expand(ftepp, macro, NULL))
+        if (!ftepp_macro_expand(ftepp, macro, NULL, false))
             return false;
         ftepp_next(ftepp);
         return true;
@@ -733,6 +855,7 @@ static bool ftepp_macro_call(ftepp_t *ftepp, ppmacro *macro)
     }
 
     ftepp_next(ftepp);
+    paramline = ftepp->lex->sline;
     if (!ftepp_macro_call_params(ftepp, &params))
         return false;
 
@@ -747,7 +870,7 @@ static bool ftepp_macro_call(ftepp_t *ftepp, ppmacro *macro)
         goto cleanup;
     }
 
-    if (!ftepp_macro_expand(ftepp, macro, params))
+    if (!ftepp_macro_expand(ftepp, macro, params, (paramline != ftepp->lex->sline)))
         retval = false;
     ftepp_next(ftepp);
 
@@ -888,7 +1011,7 @@ static bool ftepp_if_value(ftepp_t *ftepp, bool *out, double *value_out)
 
         default:
             ftepp_error(ftepp, "junk in #if: `%s` ...", ftepp_tokval(ftepp));
-            if (opts.debug)
+            if (OPTS_OPTION_BOOL(OPTION_DEBUG))
                 ftepp_error(ftepp, "internal: token %i\n", ftepp->token);
             return false;
     }
@@ -1146,55 +1269,6 @@ 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?
@@ -1390,14 +1464,6 @@ static bool ftepp_hash(ftepp_t *ftepp)
                 ftepp_out(ftepp, "#", false);
                 break;
             }
-            else if (!strcmp(ftepp_tokval(ftepp), "warning")) {
-                ftepp_directive_warning(ftepp);
-                break;
-            }
-            else if (!strcmp(ftepp_tokval(ftepp), "error")) {
-                ftepp_directive_error(ftepp);
-                break;
-            }
             else {
                 if (ftepp->output_on) {
                     ftepp_error(ftepp, "unrecognized preprocessor directive: `%s`", ftepp_tokval(ftepp));
@@ -1505,7 +1571,7 @@ static bool ftepp_preprocess(ftepp_t *ftepp)
                 break;
             case TOKEN_WHITE:
                 /* same as default but don't set newline=false */
-                ftepp_out(ftepp, ftepp_tokval(ftepp), false);
+                ftepp_out(ftepp, ftepp_tokval(ftepp), true);
                 ftepp_next(ftepp);
                 break;
             default:
@@ -1604,7 +1670,7 @@ bool ftepp_init()
 
     /* set the right macro based on the selected standard */
     ftepp_add_define(NULL, "GMQCC");
-    if (opts.standard == COMPILER_FTEQCC) {
+    if (OPTS_OPTION_U32(OPTION_STANDARD) == COMPILER_FTEQCC) {
         ftepp_add_define(NULL, "__STD_FTEQCC__");
         /* 1.00 */
         major[0] = '"';
@@ -1614,11 +1680,15 @@ bool ftepp_init()
         minor[0] = '"';
         minor[1] = '0';
         minor[2] = '"';
-    } else if (opts.standard == COMPILER_GMQCC) {
+    } else if (OPTS_OPTION_U32(OPTION_STANDARD) == COMPILER_GMQCC) {
         ftepp_add_define(NULL, "__STD_GMQCC__");
         sprintf(major, "\"%d\"", GMQCC_VERSION_MAJOR);
         sprintf(minor, "\"%d\"", GMQCC_VERSION_MINOR);
-    } else if (opts.standard == COMPILER_QCC) {
+    } else if (OPTS_OPTION_U32(OPTION_STANDARD) == COMPILER_QCCX) {
+        ftepp_add_define(NULL, "__STD_QCCX__");
+        sprintf(major, "\"%d\"", GMQCC_VERSION_MAJOR);
+        sprintf(minor, "\"%d\"", GMQCC_VERSION_MINOR);
+    } else if (OPTS_OPTION_U32(OPTION_STANDARD) == COMPILER_QCC) {
         ftepp_add_define(NULL, "__STD_QCC__");
         /* 1.0 */
         major[0] = '"';