]> git.xonotic.org Git - xonotic/gmqcc.git/blobdiff - parser.c
Another hardcoded -std=gmqcc option removed and added to the implied -std=gmqcc flags
[xonotic/gmqcc.git] / parser.c
index 1a9e40dd2f89691b828e43746f2171ea49d2eda9..ce1b212071b3910e2c9fb868e46f2b74f73c37ea 100644 (file)
--- a/parser.c
+++ b/parser.c
@@ -110,7 +110,7 @@ static ast_block* parse_block(parser_t *parser);
 static bool parse_block_into(parser_t *parser, ast_block *block);
 static bool parse_statement_or_block(parser_t *parser, ast_expression **out);
 static bool parse_statement(parser_t *parser, ast_block *block, ast_expression **out, bool allow_cases);
-static ast_expression* parse_expression_leave(parser_t *parser, bool stopatcomma);
+static ast_expression* parse_expression_leave(parser_t *parser, bool stopatcomma, bool truthvalue);
 static ast_expression* parse_expression(parser_t *parser, bool stopatcomma);
 
 static void parseerror(parser_t *parser, const char *fmt, ...)
@@ -939,7 +939,7 @@ static bool parser_sy_apply_operator(parser_t *parser, shunt *sy)
                           ( (generated_op == INSTR_OR)
                             ? (immediate_is_true(ctx, asvalue[0]) || immediate_is_true(ctx, asvalue[1]))
                             : (immediate_is_true(ctx, asvalue[0]) && immediate_is_true(ctx, asvalue[1])) )
-                          ? 0 : 1);
+                          ? 1 : 0);
             }
             else
             {
@@ -983,14 +983,14 @@ static bool parser_sy_apply_operator(parser_t *parser, shunt *sy)
                 return false;
             }
             vec_pop(parser->pot);
-            if (exprs[1]->expression.vtype != exprs[2]->expression.vtype) {
+            if (!ast_compare_type(exprs[1], exprs[2])) {
                 ast_type_to_string(exprs[1], ty1, sizeof(ty1));
                 ast_type_to_string(exprs[2], ty2, sizeof(ty2));
                 parseerror(parser, "operands of ternary expression must have the same type, got %s and %s", ty1, ty2);
                 return false;
             }
             if (CanConstFold1(exprs[0]))
-                out = (ConstF(0) ? exprs[1] : exprs[2]);
+                out = (immediate_is_true(ctx, asvalue[0]) ? exprs[1] : exprs[2]);
             else
                 out = (ast_expression*)ast_ternary_new(ctx, exprs[0], exprs[1], exprs[2]);
             break;
@@ -1041,8 +1041,7 @@ static bool parser_sy_apply_operator(parser_t *parser, shunt *sy)
                 }
                 else
                     assignop = type_storep_instr[exprs[0]->expression.vtype];
-                if (assignop == AINSTR_END ||
-                    !ast_compare_type(field->expression.next, exprs[1]))
+                if (assignop == AINSTR_END || !ast_compare_type(field->expression.next, exprs[1]))
                 {
                     ast_type_to_string(field->expression.next, ty1, sizeof(ty1));
                     ast_type_to_string(exprs[1], ty2, sizeof(ty2));
@@ -1074,8 +1073,7 @@ static bool parser_sy_apply_operator(parser_t *parser, shunt *sy)
                     ast_type_to_string(exprs[1], ty2, sizeof(ty2));
                     parseerror(parser, "invalid types in assignment: cannot assign %s to %s", ty2, ty1);
                 }
-                else if (exprs[1]->expression.vtype != TYPE_NIL &&
-                         !ast_compare_type(exprs[0], exprs[1]))
+                else if (!ast_compare_type(exprs[0], exprs[1]))
                 {
                     ast_type_to_string(exprs[0], ty1, sizeof(ty1));
                     ast_type_to_string(exprs[1], ty2, sizeof(ty2));
@@ -1405,35 +1403,18 @@ static bool parser_close_call(parser_t *parser, shunt *sy)
               vec_size(fun->expression.params) < paramcount))
         {
             const char *fewmany = (vec_size(fun->expression.params) > paramcount) ? "few" : "many";
-            if (opts.standard == COMPILER_GMQCC)
-            {
-                if (fval)
-                    parseerror(parser, "too %s parameters for call to %s: expected %i, got %i\n"
-                               " -> `%s` has been declared here: %s:%i",
-                               fewmany, fval->name, (int)vec_size(fun->expression.params), (int)paramcount,
-                               fval->name, ast_ctx(fun).file, (int)ast_ctx(fun).line);
-                else
-                    parseerror(parser, "too %s parameters for function call: expected %i, got %i\n"
-                               " -> it has been declared here: %s:%i",
-                               fewmany, (int)vec_size(fun->expression.params), (int)paramcount,
-                               ast_ctx(fun).file, (int)ast_ctx(fun).line);
-                return false;
-            }
+            if (fval)
+                return !parsewarning(parser, WARN_INVALID_PARAMETER_COUNT,
+                                     "too %s parameters for call to %s: expected %i, got %i\n"
+                                     " -> `%s` has been declared here: %s:%i",
+                                     fewmany, fval->name, (int)vec_size(fun->expression.params), (int)paramcount,
+                                     fval->name, ast_ctx(fun).file, (int)ast_ctx(fun).line);
             else
-            {
-                if (fval)
-                    return !parsewarning(parser, WARN_TOO_FEW_PARAMETERS,
-                                         "too %s parameters for call to %s: expected %i, got %i\n"
-                                         " -> `%s` has been declared here: %s:%i",
-                                         fewmany, fval->name, (int)vec_size(fun->expression.params), (int)paramcount,
-                                         fval->name, ast_ctx(fun).file, (int)ast_ctx(fun).line);
-                else
-                    return !parsewarning(parser, WARN_TOO_FEW_PARAMETERS,
-                                         "too %s parameters for function call: expected %i, got %i\n"
-                                         " -> it has been declared here: %s:%i",
-                                         fewmany, (int)vec_size(fun->expression.params), (int)paramcount,
-                                         ast_ctx(fun).file, (int)ast_ctx(fun).line);
-            }
+                return !parsewarning(parser, WARN_INVALID_PARAMETER_COUNT,
+                                     "too %s parameters for function call: expected %i, got %i\n"
+                                     " -> it has been declared here: %s:%i",
+                                     fewmany, (int)vec_size(fun->expression.params), (int)paramcount,
+                                     ast_ctx(fun).file, (int)ast_ctx(fun).line);
         }
     }
 
@@ -1501,12 +1482,16 @@ static void parser_reclassify_token(parser_t *parser)
     }
 }
 
-static ast_expression* parse_expression_leave(parser_t *parser, bool stopatcomma)
+static ast_expression* parse_expression_leave(parser_t *parser, bool stopatcomma, bool truthvalue)
 {
     ast_expression *expr = NULL;
     shunt sy;
     bool wantop = false;
     bool gotmemberof = false;
+    /* 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;
 
     /* count the parens because an if starts with one, so the
      * end of a condition is an unmatched closing paren
@@ -1783,6 +1768,28 @@ static ast_expression* parse_expression_leave(parser_t *parser, bool stopatcomma
             if (vec_size(sy.ops) && !vec_last(sy.ops).paren)
                 olast = &operators[vec_last(sy.ops).etype-1];
 
+#define IsAssignOp(x) (\
+                (x) == opid1('=') || \
+                (x) == opid2('+','=') || \
+                (x) == opid2('-','=') || \
+                (x) == opid2('*','=') || \
+                (x) == opid2('/','=') || \
+                (x) == opid2('%','=') || \
+                (x) == opid2('&','=') || \
+                (x) == opid2('|','=') || \
+                (x) == opid3('&','~','=') \
+                )
+            if (warn_truthvalue) {
+                if ( (olast && IsAssignOp(olast->id) && (op->id == opid2('&','&') || op->id == opid2('|','|'))) ||
+                     (olast && IsAssignOp(op->id) && (olast->id == opid2('&','&') || olast->id == opid2('|','|'))) ||
+                     (truthvalue && !vec_size(parser->pot) && IsAssignOp(op->id))
+                   )
+                {
+                    (void)!parsewarning(parser, WARN_PARENTHESIS, "suggesting parenthesis around assignment used as truth value");
+                    warn_truthvalue = false;
+                }
+            }
+
             while (olast && (
                     (op->prec < olast->prec) ||
                     (op->assoc == ASSOC_LEFT && op->prec <= olast->prec) ) )
@@ -1902,7 +1909,7 @@ onerr:
 
 static ast_expression* parse_expression(parser_t *parser, bool stopatcomma)
 {
-    ast_expression *e = parse_expression_leave(parser, stopatcomma);
+    ast_expression *e = parse_expression_leave(parser, stopatcomma, false);
     if (!e)
         return NULL;
     if (!parser_next(parser)) {
@@ -2052,7 +2059,7 @@ static bool parse_if(parser_t *parser, ast_block *block, ast_expression **out)
         return false;
     }
     /* parse the condition */
-    cond = parse_expression_leave(parser, false);
+    cond = parse_expression_leave(parser, false, true);
     if (!cond)
         return false;
     /* closing paren */
@@ -2173,7 +2180,7 @@ static bool parse_while_go(parser_t *parser, ast_block *block, ast_expression **
         return false;
     }
     /* parse the condition */
-    cond = parse_expression_leave(parser, false);
+    cond = parse_expression_leave(parser, false, true);
     if (!cond)
         return false;
     /* closing paren */
@@ -2288,7 +2295,7 @@ static bool parse_dowhile_go(parser_t *parser, ast_block *block, ast_expression
         return false;
     }
     /* parse the condition */
-    cond = parse_expression_leave(parser, false);
+    cond = parse_expression_leave(parser, false, true);
     if (!cond)
         return false;
     /* closing paren */
@@ -2405,17 +2412,19 @@ static bool parse_for_go(parser_t *parser, ast_block *block, ast_expression **ou
         typevar = parser_find_typedef(parser, parser_tokval(parser), 0);
 
     if (typevar || parser->tok == TOKEN_TYPENAME) {
+#if 0
         if (opts.standard != COMPILER_GMQCC) {
             if (parsewarning(parser, WARN_EXTENSIONS,
                              "current standard does not allow variable declarations in for-loop initializers"))
                 goto onerr;
         }
+#endif
         if (!parse_variable(parser, block, true, CV_VAR, typevar, false, false, 0, NULL))
             goto onerr;
     }
     else if (parser->tok != ';')
     {
-        initexpr = parse_expression_leave(parser, false);
+        initexpr = parse_expression_leave(parser, false, false);
         if (!initexpr)
             goto onerr;
     }
@@ -2432,7 +2441,7 @@ static bool parse_for_go(parser_t *parser, ast_block *block, ast_expression **ou
 
     /* parse the condition */
     if (parser->tok != ';') {
-        cond = parse_expression_leave(parser, false);
+        cond = parse_expression_leave(parser, false, true);
         if (!cond)
             goto onerr;
     }
@@ -2449,7 +2458,7 @@ static bool parse_for_go(parser_t *parser, ast_block *block, ast_expression **ou
 
     /* parse the incrementor */
     if (parser->tok != ')') {
-        increment = parse_expression_leave(parser, false);
+        increment = parse_expression_leave(parser, false, false);
         if (!increment)
             goto onerr;
         if (!ast_side_effects(increment)) {
@@ -2510,7 +2519,9 @@ static bool parse_return(parser_t *parser, ast_block *block, ast_expression **ou
         if (!exp)
             return false;
 
-        if (exp->expression.vtype != expected->expression.next->expression.vtype) {
+        if (exp->expression.vtype != TYPE_NIL &&
+            exp->expression.vtype != expected->expression.next->expression.vtype)
+        {
             parseerror(parser, "return with invalid expression");
         }
 
@@ -2523,10 +2534,7 @@ static bool parse_return(parser_t *parser, ast_block *block, ast_expression **ou
         if (!parser_next(parser))
             parseerror(parser, "parse error");
         if (expected->expression.next->expression.vtype != TYPE_VOID) {
-            if (opts.standard != COMPILER_GMQCC)
-                (void)!parsewarning(parser, WARN_MISSING_RETURN_VALUES, "return without value");
-            else
-                parseerror(parser, "return without value");
+            (void)!parsewarning(parser, WARN_MISSING_RETURN_VALUES, "return without value");
         }
         ret = ast_return_new(ctx, NULL);
     }
@@ -2793,7 +2801,7 @@ static bool parse_switch_go(parser_t *parser, ast_block *block, ast_expression *
         return false;
     }
     /* parse the operand */
-    operand = parse_expression_leave(parser, false);
+    operand = parse_expression_leave(parser, false, false);
     if (!operand)
         return false;
 
@@ -2857,7 +2865,7 @@ static bool parse_switch_go(parser_t *parser, ast_block *block, ast_expression *
                 parseerror(parser, "expected expression for case");
                 return false;
             }
-            swcase.value = parse_expression_leave(parser, false);
+            swcase.value = parse_expression_leave(parser, false, false);
             if (!swcase.value) {
                 ast_delete(switchnode);
                 parseerror(parser, "expected expression for case");
@@ -3390,7 +3398,7 @@ static bool parse_function_body(parser_t *parser, ast_value *var)
         if (!parser_next(parser))
             return false;
 
-        framenum = parse_expression_leave(parser, true);
+        framenum = parse_expression_leave(parser, true, false);
         if (!framenum) {
             parseerror(parser, "expected a framenumber constant in[frame,think] notation");
             return false;
@@ -3438,7 +3446,7 @@ static bool parse_function_body(parser_t *parser, ast_value *var)
             nextthink = (ast_expression*)thinkfunc;
 
         } else {
-            nextthink = parse_expression_leave(parser, true);
+            nextthink = parse_expression_leave(parser, true, false);
             if (!nextthink) {
                 ast_unref(framenum);
                 parseerror(parser, "expected a think-function in [frame,think] notation");
@@ -4104,7 +4112,7 @@ static ast_value *parse_arraysize(parser_t *parser, ast_value *var)
         return NULL;
     }
 
-    cexp = parse_expression_leave(parser, true);
+    cexp = parse_expression_leave(parser, true, false);
 
     if (!cexp || !ast_istype(cexp, ast_value)) {
         if (cexp)
@@ -4553,35 +4561,28 @@ static bool parse_variable(parser_t *parser, ast_block *localblock, bool nofield
                 {
                     /* other globals */
                     if (old) {
-                        if (opts.standard == COMPILER_GMQCC) {
-                            parseerror(parser, "global `%s` already declared here: %s:%i",
-                                       var->name, ast_ctx(old).file, ast_ctx(old).line);
+                        if (parsewarning(parser, WARN_DOUBLE_DECLARATION,
+                                         "global `%s` already declared here: %s:%i",
+                                         var->name, ast_ctx(old).file, ast_ctx(old).line))
+                        {
                             retval = false;
                             goto cleanup;
-                        } else {
-                            if (parsewarning(parser, WARN_DOUBLE_DECLARATION,
-                                             "global `%s` already declared here: %s:%i",
-                                             var->name, ast_ctx(old).file, ast_ctx(old).line))
-                            {
-                                retval = false;
-                                goto cleanup;
-                            }
-                            proto = (ast_value*)old;
-                            if (!ast_istype(old, ast_value)) {
-                                parseerror(parser, "internal error: not an ast_value");
-                                retval = false;
-                                proto = NULL;
-                                goto cleanup;
-                            }
-                            if (!parser_check_qualifiers(parser, var, proto)) {
-                                retval = false;
-                                proto = NULL;
-                                goto cleanup;
-                            }
-                            proto->expression.flags |= var->expression.flags;
-                            ast_delete(var);
-                            var = proto;
                         }
+                        proto = (ast_value*)old;
+                        if (!ast_istype(old, ast_value)) {
+                            parseerror(parser, "internal error: not an ast_value");
+                            retval = false;
+                            proto = NULL;
+                            goto cleanup;
+                        }
+                        if (!parser_check_qualifiers(parser, var, proto)) {
+                            retval = false;
+                            proto = NULL;
+                            goto cleanup;
+                        }
+                        proto->expression.flags |= var->expression.flags;
+                        ast_delete(var);
+                        var = proto;
                     }
                     if (opts.standard == COMPILER_QCC &&
                         (old = parser_find_field(parser, var->name)))
@@ -4877,7 +4878,7 @@ skipvar:
             ast_expression *cexp;
             ast_value      *cval;
 
-            cexp = parse_expression_leave(parser, true);
+            cexp = parse_expression_leave(parser, true, false);
             if (!cexp)
                 break;
 
@@ -4891,8 +4892,7 @@ skipvar:
                 }
                 else
                 {
-                    if (opts.standard != COMPILER_GMQCC &&
-                        !OPTS_FLAG(INITIALIZED_NONCONSTANTS) &&
+                    if (!OPTS_FLAG(INITIALIZED_NONCONSTANTS) &&
                         qualifier != CV_VAR)
                     {
                         var->cvq = CV_CONST;