]> git.xonotic.org Git - xonotic/gmqcc.git/blobdiff - parser.c
Pushing the -fexpressions-for-builtins stuff and the modff for catching fractional...
[xonotic/gmqcc.git] / parser.c
old mode 100755 (executable)
new mode 100644 (file)
index 836f6ca..14cf583
--- a/parser.c
+++ b/parser.c
@@ -103,6 +103,9 @@ typedef struct parser_s {
 
     /* collected information */
     size_t     max_param_count;
+
+    /* code generator */
+    code_t     *code;
 } parser_t;
 
 static ast_expression * const intrinsic_debug_typestring = (ast_expression*)0x1;
@@ -217,6 +220,7 @@ static ast_value* parser_const_float(parser_t *parser, double d)
     out = ast_value_new(ctx, "#IMMEDIATE", TYPE_FLOAT);
     out->cvq      = CV_CONST;
     out->hasvalue = true;
+    out->isimm    = true;
     out->constval.vfloat = d;
     vec_push(parser->imm_float, out);
     return out;
@@ -279,6 +283,7 @@ static ast_value* parser_const_string(parser_t *parser, const char *str, bool do
         out = ast_value_new(parser_ctx(parser), "#IMMEDIATE", TYPE_STRING);
     out->cvq      = CV_CONST;
     out->hasvalue = true;
+    out->isimm    = true;
     out->constval.vstring = parser_strdup(str);
     vec_push(parser->imm_string, out);
     util_htseth(parser->ht_imm_string, str, hash, out);
@@ -296,6 +301,7 @@ static ast_value* parser_const_vector(parser_t *parser, vector v)
     out = ast_value_new(parser_ctx(parser), "#IMMEDIATE", TYPE_VECTOR);
     out->cvq      = CV_CONST;
     out->hasvalue = true;
+    out->isimm    = true;
     out->constval.vvec = v;
     vec_push(parser->imm_vector, out);
     return out;
@@ -1782,6 +1788,9 @@ static ast_expression* parse_vararg(parser_t *parser)
     return out;
 }
 
+/* not to be exposed */
+extern bool ftepp_predef_exists(const char *name);
+
 static bool parse_sya_operand(parser_t *parser, shunt *sy, bool with_labels)
 {
     if (OPTS_FLAG(TRANSLATABLE_STRINGS) &&
@@ -1888,6 +1897,8 @@ static bool parse_sya_operand(parser_t *parser, shunt *sy, bool with_labels)
                 vec_push(parser->labels, lbl);
             }
         }
+        if (!var && !strcmp(parser_tokval(parser), "__FUNC__"))
+            var = (ast_expression*)parser_const_string(parser, parser->function->name, false);
         if (!var) {
             /* intrinsics */
             if (!strcmp(parser_tokval(parser), "__builtin_debug_typestring")) {
@@ -1899,8 +1910,6 @@ static bool parse_sya_operand(parser_t *parser, shunt *sy, bool with_labels)
              */
             else if (!strncmp(parser_tokval(parser), "__builtin_", 10)) {
                 var = intrin_func(parser, parser_tokval(parser) + 10 /* skip __builtin */);
-            } else {
-                var = intrin_func(parser, parser_tokval(parser));
             }
 
             if (!var) {
@@ -1912,13 +1921,9 @@ static bool parse_sya_operand(parser_t *parser, shunt *sy, bool with_labels)
                  * i've done this thousands of times already myself.  Lets check for
                  * it in the predef table.  And diagnose it better :)
                  */
-                if (!OPTS_FLAG(FTEPP_PREDEFS)) {
-                    for (i = 0; i < sizeof(ftepp_predefs)/sizeof(*ftepp_predefs); i++) {
-                        if (!strcmp(ftepp_predefs[i].name, parser_tokval(parser))) {
-                            parseerror(parser, "unexpected ident: %s (use -fftepp-predef to enable pre-defined macros)", parser_tokval(parser));
-                            return false;
-                        }
-                    }
+                if (!OPTS_FLAG(FTEPP_PREDEFS) && ftepp_predef_exists(parser_tokval(parser))) {
+                    parseerror(parser, "unexpected ident: %s (use -fftepp-predef to enable pre-defined macros)", parser_tokval(parser));
+                    return false;
                 }
 
                 /*
@@ -2182,8 +2187,27 @@ static ast_expression* parse_expression_leave(parser_t *parser, bool stopatcomma
             wantop = true;
         }
         else {
-            parseerror(parser, "expected operator or end of statement");
-            goto onerr;
+            /* in this case we might want to allow constant string concatenation */
+            bool concatenated = false;
+            if (parser->tok == TOKEN_STRINGCONST && vec_size(sy.out)) {
+                ast_expression *lexpr = vec_last(sy.out).out;
+                if (ast_istype(lexpr, ast_value)) {
+                    ast_value *last = (ast_value*)lexpr;
+                    if (last->isimm == true && last->cvq == CV_CONST &&
+                        last->hasvalue && last->expression.vtype == TYPE_STRING)
+                    {
+                        char *newstr = NULL;
+                        util_asprintf(&newstr, "%s%s", last->constval.vstring, parser_tokval(parser));
+                        vec_last(sy.out).out = (ast_expression*)parser_const_string(parser, newstr, false);
+                        mem_d(newstr);
+                        concatenated = true;
+                    }
+                }
+            }
+            if (!concatenated) {
+                parseerror(parser, "expected operator or end of statement");
+                goto onerr;
+            }
         }
 
         if (!parser_next(parser)) {
@@ -5615,7 +5639,11 @@ skipvar:
         }
 
         if (parser->tok == '#') {
-            ast_function *func = NULL;
+            ast_function *func   = NULL;
+            ast_value    *number = NULL;
+            float         fractional;
+            float         integral;
+            int           builtin_num;
 
             if (localblock) {
                 parseerror(parser, "cannot declare builtins within functions");
@@ -5629,12 +5657,40 @@ skipvar:
                 parseerror(parser, "expected builtin number");
                 break;
             }
-            if (parser->tok != TOKEN_INTCONST) {
-                parseerror(parser, "builtin number must be an integer constant");
-                break;
-            }
-            if (parser_token(parser)->constval.i < 0) {
-                parseerror(parser, "builtin number must be an integer greater than zero");
+
+            if (OPTS_FLAG(EXPRESSIONS_FOR_BUILTINS)) {
+                number = (ast_value*)parse_expression_leave(parser, true, false, false);
+                if (!number) {
+                    parseerror(parser, "builtin number expected");
+                    break;
+                }
+                if (!ast_istype(number, ast_value) || !number->hasvalue || number->cvq != CV_CONST)
+                {
+                    ast_unref(number);
+                    parseerror(parser, "builtin number must be a compile time constant");
+                    break;
+                }
+                if (number->expression.vtype == TYPE_INTEGER)
+                    builtin_num = number->constval.vint;
+                else if (number->expression.vtype == TYPE_FLOAT)
+                    builtin_num = number->constval.vfloat;
+                else {
+                    ast_unref(number);
+                    parseerror(parser, "builtin number must be an integer constant");
+                    break;
+                }
+                ast_unref(number);
+
+                fractional = modff(builtin_num, &integral);
+                if (builtin_num < 0 || fractional != 0) {
+                    parseerror(parser, "builtin number must be an integer greater than zero");
+                    break;
+                }
+
+                /* we only want the integral part anyways */
+                builtin_num = integral;
+            } else if (parser->tok != TOKEN_INTCONST) {
+                parseerror(parser, "builtin number must be a compile time constant");
                 break;
             }
 
@@ -5653,10 +5709,15 @@ skipvar:
                 }
                 vec_push(parser->functions, func);
 
-                func->builtin = -parser_token(parser)->constval.i-1;
+                func->builtin = -((OPTS_FLAG(EXPRESSIONS_FOR_BUILTINS))
+                                    ? builtin_num
+                                    : parser_token(parser)->constval.i) - 1;
             }
 
-            if (!parser_next(parser)) {
+            if (OPTS_FLAG(EXPRESSIONS_FOR_BUILTINS)
+                    ? (parser->tok != ',' && parser->tok != ';')
+                    : (!parser_next(parser)))
+            {
                 parseerror(parser, "expected comma or semicolon");
                 if (func)
                     ast_function_delete(func);
@@ -5921,7 +5982,7 @@ static void generate_checksum(parser_t *parser)
     }
     crc = progdefs_crc_both(crc, "} entvars_t;\n\n");
 
-    code_crc = crc;
+    parser->code->crc = crc;
 }
 
 parser_t *parser_create()
@@ -5936,6 +5997,12 @@ parser_t *parser_create()
 
     memset(parser, 0, sizeof(*parser));
 
+    if (!(parser->code = code_init())) {
+        mem_d(parser);
+        return NULL;
+    }
+    
+
     for (i = 0; i < operator_count; ++i) {
         if (operators[i].id == opid1('=')) {
             parser->assign_op = operators+i;
@@ -6291,7 +6358,7 @@ bool parser_finish(parser_t *parser, const char *output)
 
         generate_checksum(parser);
 
-        if (!ir_builder_generate(ir, output)) {
+        if (!ir_builder_generate(parser->code, ir, output)) {
             con_out("*** failed to generate output file\n");
             ir_builder_delete(ir);
             return false;