]> de.git.xonotic.org Git - xonotic/gmqcc.git/blobdiff - parser.c
Implemented >< (vector cross product operator). Currently support for constants only.
[xonotic/gmqcc.git] / parser.c
index 9ed80431364fc6b04692362effdad2bfd265dfcf..3c29399d203f1ff5fc55caafda7b3eee11984e18 100644 (file)
--- a/parser.c
+++ b/parser.c
@@ -282,6 +282,21 @@ static bool rotate_entfield_array_index_nodes(ast_expression **out)
     return true;
 }
 
+static bool check_write_to(lex_ctx_t ctx, ast_expression *expr)
+{
+    if (ast_istype(expr, ast_value)) {
+        ast_value *val = (ast_value*)expr;
+        if (val->cvq == CV_CONST) {
+            if (val->name[0] == '#')
+                compile_error(ctx, "invalid assignment to a literal constant");
+            else
+                compile_error(ctx, "assignment to constant `%s`", val->name);
+            return false;
+        }
+    }
+    return true;
+}
+
 static bool parser_sy_apply_operator(parser_t *parser, shunt *sy)
 {
     const oper_info *op;
@@ -289,7 +304,6 @@ static bool parser_sy_apply_operator(parser_t *parser, shunt *sy)
     ast_expression *out = NULL;
     ast_expression *exprs[3];
     ast_block      *blocks[3];
-    ast_value      *asvalue[3];
     ast_binstore   *asbinstore;
     size_t i, assignop, addop, subop;
     qcint_t  generated_op = 0;
@@ -311,8 +325,10 @@ static bool parser_sy_apply_operator(parser_t *parser, shunt *sy)
     ctx = vec_last(sy->ops).ctx;
 
     if (vec_size(sy->out) < op->operands) {
-        compile_error(ctx, "internal error: not enough operands: %i (operator %s (%i))", vec_size(sy->out),
-                      op->op, (int)op->id);
+        if (op->flags & OP_PREFIX)
+            compile_error(ctx, "expected expression after unary operator `%s`", op->op, (int)op->id);
+        else /* this should have errored previously already */
+            compile_error(ctx, "expected expression after operator `%s`", op->op, (int)op->id);
         return false;
     }
 
@@ -326,7 +342,6 @@ static bool parser_sy_apply_operator(parser_t *parser, shunt *sy)
     for (i = 0; i < op->operands; ++i) {
         exprs[i]  = sy->out[vec_size(sy->out)+i].out;
         blocks[i] = sy->out[vec_size(sy->out)+i].block;
-        asvalue[i] = (ast_value*)exprs[i];
 
         if (exprs[i]->vtype == TYPE_NOEXPR &&
             !(i != 0 && op->id == opid2('?',':')) &&
@@ -747,6 +762,22 @@ static bool parser_sy_apply_operator(parser_t *parser, shunt *sy)
             }
             break;
 
+        case opid2('>', '<'):
+            if (NotSameType(TYPE_VECTOR)) {
+                ast_type_to_string(exprs[0], ty1, sizeof(ty1));
+                ast_type_to_string(exprs[1], ty2, sizeof(ty2));
+                compile_error(ctx, "invalid types used in cross product: %s and %s",
+                    ty1, ty2);
+                return false;
+            }
+
+            if (!(out = fold_op(parser->fold, op, exprs))) {
+                compile_error(ctx, "cross product for non-constant vectors unimplemented");
+                return false;
+            }
+
+            break;
+
         case opid3('<','=','>'): /* -1, 0, or 1 */
             if (NotSameType(TYPE_FLOAT)) {
                 ast_type_to_string(exprs[0], ty1, sizeof(ty1));
@@ -879,9 +910,7 @@ static bool parser_sy_apply_operator(parser_t *parser, shunt *sy)
                         compile_error(ctx, "invalid types in assignment: cannot assign %s to %s", ty2, ty1);
                 }
             }
-            if (ast_istype(exprs[0], ast_value) && asvalue[0]->cvq == CV_CONST) {
-                compile_error(ctx, "assignment to constant `%s`", asvalue[0]->name);
-            }
+            (void)check_write_to(ctx, exprs[0]);
             out = (ast_expression*)ast_store_new(ctx, assignop, exprs[0], exprs[1]);
             break;
         case opid3('+','+','P'):
@@ -896,9 +925,7 @@ static bool parser_sy_apply_operator(parser_t *parser, shunt *sy)
                 addop = INSTR_ADD_F;
             else
                 addop = INSTR_SUB_F;
-            if (ast_istype(exprs[0], ast_value) && asvalue[0]->cvq == CV_CONST) {
-                compile_error(ast_ctx(exprs[0]), "assignment to constant `%s`", asvalue[0]->name);
-            }
+            (void)check_write_to(ast_ctx(exprs[0]), exprs[0]);
             if (ast_istype(exprs[0], ast_entfield)) {
                 out = (ast_expression*)ast_binstore_new(ctx, INSTR_STOREP_F, addop,
                                                         exprs[0],
@@ -924,9 +951,7 @@ static bool parser_sy_apply_operator(parser_t *parser, shunt *sy)
                 addop = INSTR_SUB_F;
                 subop = INSTR_ADD_F;
             }
-            if (ast_istype(exprs[0], ast_value) && asvalue[0]->cvq == CV_CONST) {
-                compile_error(ast_ctx(exprs[0]), "assignment to constant `%s`", asvalue[0]->name);
-            }
+            (void)check_write_to(ast_ctx(exprs[0]), exprs[0]);
             if (ast_istype(exprs[0], ast_entfield)) {
                 out = (ast_expression*)ast_binstore_new(ctx, INSTR_STOREP_F, addop,
                                                         exprs[0],
@@ -953,9 +978,7 @@ static bool parser_sy_apply_operator(parser_t *parser, shunt *sy)
                               ty1, ty2);
                 return false;
             }
-            if (ast_istype(exprs[0], ast_value) && asvalue[0]->cvq == CV_CONST) {
-                compile_error(ctx, "assignment to constant `%s`", asvalue[0]->name);
-            }
+            (void)check_write_to(ctx, exprs[0]);
             if (ast_istype(exprs[0], ast_entfield))
                 assignop = type_storep_instr[exprs[0]->vtype];
             else
@@ -990,9 +1013,7 @@ static bool parser_sy_apply_operator(parser_t *parser, shunt *sy)
                               ty1, ty2);
                 return false;
             }
-            if (ast_istype(exprs[0], ast_value) && asvalue[0]->cvq == CV_CONST) {
-                compile_error(ctx, "assignment to constant `%s`", asvalue[0]->name);
-            }
+            (void)check_write_to(ctx, exprs[0]);
             if (ast_istype(exprs[0], ast_entfield))
                 assignop = type_storep_instr[exprs[0]->vtype];
             else
@@ -1036,9 +1057,7 @@ static bool parser_sy_apply_operator(parser_t *parser, shunt *sy)
                               ty1, ty2);
                 return false;
             }
-            if (ast_istype(exprs[0], ast_value) && asvalue[0]->cvq == CV_CONST) {
-                compile_error(ctx, "assignment to constant `%s`", asvalue[0]->name);
-            }
+            (void)check_write_to(ctx, exprs[0]);
             if (ast_istype(exprs[0], ast_entfield))
                 assignop = type_storep_instr[exprs[0]->vtype];
             else
@@ -1074,9 +1093,7 @@ static bool parser_sy_apply_operator(parser_t *parser, shunt *sy)
                 out = (ast_expression*)ast_binary_new(ctx, VINSTR_BITAND_V, exprs[0], exprs[1]);
             if (!out)
                 return false;
-            if (ast_istype(exprs[0], ast_value) && asvalue[0]->cvq == CV_CONST) {
-                compile_error(ctx, "assignment to constant `%s`", asvalue[0]->name);
-            }
+            (void)check_write_to(ctx, exprs[0]);
             if (exprs[0]->vtype == TYPE_FLOAT)
                 asbinstore = ast_binstore_new(ctx, assignop, INSTR_SUB_F, exprs[0], out);
             else
@@ -1119,6 +1136,7 @@ static bool parser_close_call(parser_t *parser, shunt *sy)
 
     size_t          fid;
     size_t          paramcount, i;
+    bool            fold = true;
 
     fid = vec_last(sy->ops).off;
     vec_shrinkby(sy->ops, 1);
@@ -1161,14 +1179,59 @@ static bool parser_close_call(parser_t *parser, shunt *sy)
         vec_shrinkby(sy->out, 1);
         return true;
     }
+
+    /*
+     * Now we need to determine if the function that is being called is
+     * an intrinsic so we can evaluate if the arguments to it are constant
+     * and than fruitfully fold them.
+     */
+#define fold_can_1(X)  \
+    (ast_istype(((ast_expression*)(X)), ast_value) && (X)->hasvalue && ((X)->cvq == CV_CONST) && \
+                ((ast_expression*)(X))->vtype != TYPE_FUNCTION)
+
+    if (fid + 1 < vec_size(sy->out))
+        ++paramcount;
+
+    for (i = 0; i < paramcount; ++i) {
+        if (!fold_can_1((ast_value*)sy->out[fid + 1 + i].out)) {
+            fold = false;
+            break;
+        }
+    }
+
+    /*
+     * All is well which ends well, if we make it into here we can ignore the
+     * intrinsic call and just evaluate it i.e constant fold it.
+     */
+    if (fold && ast_istype(fun, ast_value) && ((ast_value*)fun)->intrinsic) {
+        ast_expression **exprs  = NULL;
+        ast_expression *foldval = NULL;
+
+        for (i = 0; i < paramcount; i++)
+            vec_push(exprs, sy->out[fid+1 + i].out);
+
+        if (!(foldval = intrin_fold(parser->intrin, (ast_value*)fun, exprs))) {
+            vec_free(exprs);
+            goto fold_leave;
+        }
+
+        /*
+         * Blub: what sorts of unreffing and resizing of
+         * sy->out should I be doing here?
+         */
+        sy->out[fid] = syexp(foldval->node.context, foldval);
+        vec_shrinkby(sy->out, 1);
+        vec_free(exprs);
+
+        return true;
+    }
+
+    fold_leave:
     call = ast_call_new(sy->ops[vec_size(sy->ops)].ctx, fun);
 
     if (!call)
         return false;
 
-    if (fid+1 < vec_size(sy->out))
-        ++paramcount;
-
     if (fid+1 + paramcount != vec_size(sy->out)) {
         parseerror(parser, "internal error: parameter count mismatch: (%lu+1+%lu), %lu",
                    (unsigned long)fid, (unsigned long)paramcount, (unsigned long)vec_size(sy->out));
@@ -1583,7 +1646,7 @@ static ast_expression* parse_expression_leave(parser_t *parser, bool stopatcomma
     /* only warn once about an assignment in a truth value because the current code
      * would trigger twice on: if(a = b && ...), once for the if-truth-value, once for the && part
      */
-    bool warn_truthvalue = true;
+    bool warn_parenthesis = true;
 
     /* count the parens because an if starts with one, so the
      * end of a condition is an unmatched closing paren
@@ -1618,7 +1681,7 @@ static ast_expression* parse_expression_leave(parser_t *parser, bool stopatcomma
                 }
             }
             if (o == operator_count) {
-                compile_error(parser_ctx(parser), "unknown operator: %s", parser_tokval(parser));
+                compile_error(parser_ctx(parser), "unexpected operator: %s", parser_tokval(parser));
                 goto onerr;
             }
             /* found an operator */
@@ -1646,6 +1709,17 @@ static ast_expression* parse_expression_leave(parser_t *parser, bool stopatcomma
             if (vec_size(sy.ops) && !vec_last(sy.ops).isparen)
                 olast = &operators[vec_last(sy.ops).etype-1];
 
+            /* first only apply higher precedences, assoc_left+equal comes after we warn about precedence rules */
+            while (olast && op->prec < olast->prec)
+            {
+                if (!parser_sy_apply_operator(parser, &sy))
+                    goto onerr;
+                if (vec_size(sy.ops) && !vec_last(sy.ops).isparen)
+                    olast = &operators[vec_last(sy.ops).etype-1];
+                else
+                    olast = NULL;
+            }
+
 #define IsAssignOp(x) (\
                 (x) == opid1('=') || \
                 (x) == opid2('+','=') || \
@@ -1657,14 +1731,29 @@ static ast_expression* parse_expression_leave(parser_t *parser, bool stopatcomma
                 (x) == opid2('|','=') || \
                 (x) == opid3('&','~','=') \
                 )
-            if (warn_truthvalue) {
+            if (warn_parenthesis) {
                 if ( (olast && IsAssignOp(olast->id) && (op->id == opid2('&','&') || op->id == opid2('|','|'))) ||
                      (olast && IsAssignOp(op->id) && (olast->id == opid2('&','&') || olast->id == opid2('|','|'))) ||
                      (truthvalue && !vec_size(sy.paren) && IsAssignOp(op->id))
                    )
                 {
                     (void)!parsewarning(parser, WARN_PARENTHESIS, "suggesting parenthesis around assignment used as truth value");
-                    warn_truthvalue = false;
+                    warn_parenthesis = false;
+                }
+
+                if (olast && olast->id != op->id) {
+                    if ((op->id    == opid1('&') || op->id    == opid1('|') || op->id    == opid1('^')) &&
+                        (olast->id == opid1('&') || olast->id == opid1('|') || olast->id == opid1('^')))
+                    {
+                        (void)!parsewarning(parser, WARN_PARENTHESIS, "suggesting parenthesis around bitwise operations");
+                        warn_parenthesis = false;
+                    }
+                    else if ((op->id    == opid2('&','&') || op->id    == opid2('|','|')) &&
+                             (olast->id == opid2('&','&') || olast->id == opid2('|','|')))
+                    {
+                        (void)!parsewarning(parser, WARN_PARENTHESIS, "suggesting parenthesis around logical operations");
+                        warn_parenthesis = false;
+                    }
                 }
             }
 
@@ -1826,7 +1915,7 @@ static ast_expression* parse_expression_leave(parser_t *parser, bool stopatcomma
 
     parser->lex->flags.noops = true;
     if (vec_size(sy.out) != 1) {
-        parseerror(parser, "expression with not 1 but %lu output values...", (unsigned long) vec_size(sy.out));
+        parseerror(parser, "expression expected");
         expr = NULL;
     } else
         expr = sy.out[0].out;
@@ -2692,7 +2781,15 @@ static bool parse_qualifiers(parser_t *parser, bool with_local, int *cvq, bool *
             else if (!strcmp(parser_tokval(parser), "inline")) {
                 flags |= AST_FLAG_INLINE;
                 if (!parser_next(parser) || parser->tok != TOKEN_ATTRIBUTE_CLOSE) {
-                    parseerror(parser, "`noref` attribute has no parameters, expected `]]`");
+                    parseerror(parser, "`inline` attribute has no parameters, expected `]]`");
+                    *cvq = CV_WRONG;
+                    return false;
+                }
+            }
+            else if (!strcmp(parser_tokval(parser), "eraseable")) {
+                flags |= AST_FLAG_ERASEABLE;
+                if (!parser_next(parser) || parser->tok != TOKEN_ATTRIBUTE_CLOSE) {
+                    parseerror(parser, "`eraseable` attribute has no parameters, expected `]]`");
                     *cvq = CV_WRONG;
                     return false;
                 }
@@ -4577,6 +4674,11 @@ static ast_value *parse_arraysize(parser_t *parser, ast_value *var)
  * The base type makes up every bit of type information which comes *before* the
  * variable name.
  *
+ * NOTE: The value must either be named, have a NULL name, or a name starting
+ *       with '<'. In the first case, this will be the actual variable or type
+ *       name, in the other cases it is assumed that the name will appear
+ *       later, and an error is generated otherwise.
+ *
  * The following will be parsed in its entirety:
  *     void() foo()
  * The 'basetype' in this case is 'void()'
@@ -4737,6 +4839,13 @@ static bool parse_typedef(parser_t *parser)
     if (!typevar)
         return false;
 
+    /* while parsing types, the ast_value's get named '<something>' */
+    if (!typevar->name || typevar->name[0] == '<') {
+        parseerror(parser, "missing name in typedef");
+        ast_delete(typevar);
+        return false;
+    }
+
     if ( (old = parser_find_var(parser, typevar->name)) ) {
         parseerror(parser, "cannot define a type with the same name as a variable: %s\n"
                    " -> `%s` has been declared here: %s:%i",
@@ -4902,6 +5011,14 @@ static bool parse_variable(parser_t *parser, ast_block *localblock, bool nofield
         return false;
     }
 
+    /* while parsing types, the ast_value's get named '<something>' */
+    if (!var->name || var->name[0] == '<') {
+        parseerror(parser, "declaration does not declare anything");
+        if (basetype)
+            ast_delete(basetype);
+        return false;
+    }
+
     while (true) {
         proto = NULL;
         wasarray = false;
@@ -5112,8 +5229,12 @@ static bool parse_variable(parser_t *parser, ast_block *localblock, bool nofield
                 }
                 if (OPTS_OPTION_U32(OPTION_STANDARD) != COMPILER_GMQCC) {
                     ast_delete(var);
-                    var = NULL;
-                    goto skipvar;
+                    if (ast_istype(old, ast_value))
+                        var = (ast_value*)old;
+                    else {
+                        var = NULL;
+                        goto skipvar;
+                    }
                 }
             }
         }