]> git.xonotic.org Git - xonotic/gmqcc.git/blobdiff - parser.c
Implemented [[accumulate]] attribute. This will hopefully be used by Xonotic to repla...
[xonotic/gmqcc.git] / parser.c
index e0f46a4d2f092fcefddbacc874b764d531f39705..628f89d691aedefaae09b3ea10fae8cdb7edd502 100644 (file)
--- a/parser.c
+++ b/parser.c
@@ -23,6 +23,7 @@
  */
 #include <string.h>
 #include <math.h>
+
 #include "parser.h"
 
 #define PARSER_HT_LOCALS  2
@@ -371,6 +372,7 @@ static bool parser_sy_apply_operator(parser_t *parser, shunt *sy)
 #define NotSameType(T) \
              (exprs[0]->vtype != exprs[1]->vtype || \
               exprs[0]->vtype != T)
+
     switch (op->id)
     {
         default:
@@ -465,24 +467,16 @@ static bool parser_sy_apply_operator(parser_t *parser, shunt *sy)
             out = exprs[0];
             break;
         case opid2('-','P'):
-            if (!(out = fold_op(parser->fold, op, exprs))) {
-                switch (exprs[0]->vtype) {
-                    case TYPE_FLOAT:
-                        out = (ast_expression*)ast_binary_new(ctx, INSTR_SUB_F,
-                                                                  (ast_expression*)parser->fold->imm_float[0],
-                                                                  exprs[0]);
-                        break;
-                    case TYPE_VECTOR:
-                        out = (ast_expression*)ast_binary_new(ctx, INSTR_SUB_V,
-                                                                  (ast_expression*)parser->fold->imm_vector[0],
-                                                                  exprs[0]);
-                        break;
-                    default:
-                    compile_error(ctx, "invalid types used in expression: cannot negate type %s",
+            if ((out = fold_op(parser->fold, op, exprs)))
+                break;
+
+            if (exprs[0]->vtype != TYPE_FLOAT &&
+                exprs[0]->vtype != TYPE_VECTOR) {
+                    compile_error(ctx, "invalid types used in unary expression: cannot negate type %s",
                                   type_name[exprs[0]->vtype]);
-                    return false;
-                }
+                return false;
             }
+            out = (ast_expression*)ast_unary_new(ctx, (VINSTR_NEG_F-TYPE_FLOAT) + exprs[0]->vtype, exprs[0]);
             break;
 
         case opid2('!','P'):
@@ -978,6 +972,7 @@ static bool parser_sy_apply_operator(parser_t *parser, shunt *sy)
             out = (ast_expression*)ast_binary_new(ctx, subop,
                                                   out,
                                                   (ast_expression*)parser->fold->imm_float[1]);
+
             break;
         case opid2('+','='):
         case opid2('-','='):
@@ -1232,7 +1227,7 @@ static bool parser_close_call(parser_t *parser, shunt *sy)
          * sy->out should I be doing here?
          */
         sy->out[fid] = syexp(foldval->node.context, foldval);
-        vec_shrinkby(sy->out, 1);
+        vec_shrinkby(sy->out, paramcount);
         vec_free(exprs);
 
         return true;
@@ -2806,6 +2801,14 @@ static bool parse_qualifiers(parser_t *parser, bool with_local, int *cvq, bool *
                     return false;
                 }
             }
+            else if (!strcmp(parser_tokval(parser), "accumulate")) {
+                flags |= AST_FLAG_ACCUMULATE;
+                if (!parser_next(parser) || parser->tok != TOKEN_ATTRIBUTE_CLOSE) {
+                    parseerror(parser, "`accumulate` attribute has no parameters, expected `]]`");
+                    *cvq = CV_WRONG;
+                    return false;
+                }
+            }
             else if (!strcmp(parser_tokval(parser), "alias") && !(flags & AST_FLAG_ALIAS)) {
                 flags   |= AST_FLAG_ALIAS;
                 *message = NULL;
@@ -3299,7 +3302,13 @@ static bool parse_pragma_do(parser_t *parser)
     else
     {
         (void)!parsewarning(parser, WARN_UNKNOWN_PRAGMAS, "ignoring #pragma %s", parser_tokval(parser));
-        return false;
+
+        /* skip to eol */
+        while (!parse_eol(parser)) {
+            parser_next(parser);
+        }
+
+        return true;
     }
 
     return true;
@@ -3337,7 +3346,7 @@ static bool parse_statement(parser_t *parser, ast_block *block, ast_expression *
     if (parser->tok == TOKEN_IDENT)
         typevar = parser_find_typedef(parser, parser_tokval(parser), 0);
 
-    if (typevar || parser->tok == TOKEN_TYPENAME || parser->tok == '.')
+    if (typevar || parser->tok == TOKEN_TYPENAME || parser->tok == '.' || parser->tok == TOKEN_DOTS)
     {
         /* local variable */
         if (!block) {
@@ -3975,19 +3984,50 @@ static bool parse_function_body(parser_t *parser, ast_value *var)
         }
     }
 
-    if (var->hasvalue) {
+    if (var->hasvalue && !(var->expression.flags & AST_FLAG_ACCUMULATE)) {
         parseerror(parser, "function `%s` declared with multiple bodies", var->name);
         ast_block_delete(block);
         goto enderr;
     }
 
-    func = ast_function_new(ast_ctx(var), var->name, var);
+    /* accumulation? */
+    if (var->hasvalue) {
+        ast_value    *accum    = NULL;
+        ast_function *previous = NULL;
+        char          acname[1024];
+
+        /* generate a new name increasing the accumulation count*/
+        util_snprintf(acname, sizeof(acname), "$ACCUMULATE_%s_%d", var->name, var->constval.vfunc->accumulation++);
+        accum = ast_value_new(parser_ctx(parser), acname, ((ast_expression*)var)->vtype);
+        if (!accum)
+            return false;
+
+        ast_type_adopt(accum, var);
+        func = ast_function_new(ast_ctx(var), NULL, accum);
+        if (!func)
+            return false;
+
+        parser_addglobal(parser, acname, (ast_expression*)accum);
+        vec_push(parser->functions, func);
+
+        /* update the previous calls accumulate pointer for the codegen */
+        previous = var->constval.vfunc;
+        while (previous->accumulate)
+            previous = previous->accumulate;
+
+        if (ast_istype(previous, ast_function))
+            previous->accumulate = func;
+
+    } else {
+        func = ast_function_new(ast_ctx(var), var->name, var);
+        vec_push(parser->functions, func);
+    }
+
     if (!func) {
         parseerror(parser, "failed to allocate function for `%s`", var->name);
         ast_block_delete(block);
         goto enderr;
     }
-    vec_push(parser->functions, func);
 
     parser_enterblock(parser);
 
@@ -4711,8 +4751,10 @@ static ast_value *parse_typename(parser_t *parser, ast_value **storebase, ast_va
     ctx = parser_ctx(parser);
 
     /* types may start with a dot */
-    if (parser->tok == '.') {
+    if (parser->tok == '.' || parser->tok == TOKEN_DOTS) {
         isfield = true;
+        if (parser->tok == TOKEN_DOTS)
+            morefields += 2;
         /* if we parsed a dot we need a typename now */
         if (!parser_next(parser)) {
             parseerror(parser, "expected typename for field definition");
@@ -4722,8 +4764,13 @@ static ast_value *parse_typename(parser_t *parser, ast_value **storebase, ast_va
         /* Further dots are handled seperately because they won't be part of the
          * basetype
          */
-        while (parser->tok == '.') {
-            ++morefields;
+        while (true) {
+            if (parser->tok == '.')
+                ++morefields;
+            else if (parser->tok == TOKEN_DOTS)
+                morefields += 3;
+            else
+                break;
             if (!parser_next(parser)) {
                 parseerror(parser, "expected typename for field definition");
                 return NULL;
@@ -5080,6 +5127,13 @@ static bool parse_variable(parser_t *parser, ast_block *localblock, bool nofield
             var->expression.flags & AST_FLAG_ALIAS)
             var->desc = vstring;
 
+        if (parser_find_global(parser, var->name) && var->expression.flags & AST_FLAG_ALIAS) {
+            parseerror(parser, "function aliases cannot be forward declared");
+            retval = false;
+            goto cleanup;
+        }
+
+
         /* Part 1:
          * check for validity: (end_sys_..., multiple-definitions, prototypes, ...)
          * Also: if there was a prototype, `var` will be deleted and set to `proto` which
@@ -5302,7 +5356,7 @@ static bool parse_variable(parser_t *parser, ast_block *localblock, bool nofield
                             return false;
                         }
 
-                        if (var->expression.vtype != find->vtype) {
+                        if (!ast_compare_type((ast_expression*)var, find)) {
                             char ty1[1024];
                             char ty2[1024];
 
@@ -5769,7 +5823,7 @@ static bool parser_global_statement(parser_t *parser)
     if (parser->tok == TOKEN_IDENT)
         istype = parser_find_typedef(parser, parser_tokval(parser), 0);
 
-    if (istype || parser->tok == TOKEN_TYPENAME || parser->tok == '.')
+    if (istype || parser->tok == TOKEN_TYPENAME || parser->tok == '.' || parser->tok == TOKEN_DOTS)
     {
         return parse_variable(parser, NULL, false, CV_NONE, istype, false, false, 0, NULL);
     }
@@ -5908,7 +5962,7 @@ parser_t *parser_create()
         }
     }
     if (!parser->assign_op) {
-        printf("internal error: initializing parser: failed to find assign operator\n");
+        con_err("internal error: initializing parser: failed to find assign operator\n");
         mem_d(parser);
         return NULL;
     }