]> git.xonotic.org Git - xonotic/gmqcc.git/blobdiff - parser.c
fix unhelpful error message; fixes #123
[xonotic/gmqcc.git] / parser.c
index 8c585f52234fde0ae5298a7afb913e67e98ec129..9cbb8d16b017b1b7e45e7bc7104fb93604eb382d 100644 (file)
--- a/parser.c
+++ b/parser.c
@@ -311,8 +311,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;
     }
 
@@ -1187,13 +1189,13 @@ static bool parser_close_call(parser_t *parser, shunt *sy)
      * 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 *fold   = NULL;
+        ast_expression **exprs  = NULL;
+        ast_expression *foldval = NULL;
 
         for (i = 0; i < paramcount; i++)
             vec_push(exprs, sy->out[fid+1 + i].out);
 
-        if (!(fold = intrin_fold(parser->intrin, (ast_value*)fun, exprs))) {
+        if (!(foldval = intrin_fold(parser->intrin, (ast_value*)fun, exprs))) {
             vec_free(exprs);
             goto fold_leave;
         }
@@ -1202,7 +1204,7 @@ static bool parser_close_call(parser_t *parser, shunt *sy)
          * Blub: what sorts of unreffing and resizing of
          * sy->out should I be doing here?
          */
-        sy->out[fid] = syexp(fold->node.context, fold);
+        sy->out[fid] = syexp(foldval->node.context, foldval);
         vec_shrinkby(sy->out, 1);
         vec_free(exprs);
 
@@ -1629,7 +1631,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
@@ -1664,7 +1666,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 */
@@ -1692,6 +1694,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('+','=') || \
@@ -1703,14 +1716,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;
+                    }
                 }
             }
 
@@ -2738,7 +2766,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;
                 }