]> git.xonotic.org Git - xonotic/gmqcc.git/blobdiff - parser.c
while/for/do now all use process_condition, ast_loop got 2 new parameters for more...
[xonotic/gmqcc.git] / parser.c
index e8110bb6da6fe03bcd2477dace6d1be658410ddf..5129962c768c216f8a1e16d15e78b4a8e4026d8c 100644 (file)
--- a/parser.c
+++ b/parser.c
@@ -468,6 +468,32 @@ static bool rotate_entfield_array_index_nodes(ast_expression **out)
     return true;
 }
 
+static bool immediate_is_true(lex_ctx ctx, ast_value *v)
+{
+    switch (v->expression.vtype) {
+        case TYPE_FLOAT:
+            return !!v->constval.vfloat;
+        case TYPE_INTEGER:
+            return !!v->constval.vint;
+        case TYPE_VECTOR:
+            if (OPTS_FLAG(CORRECT_LOGIC))
+                return v->constval.vvec.x &&
+                       v->constval.vvec.y &&
+                       v->constval.vvec.z;
+            else
+                return !!(v->constval.vvec.x);
+        case TYPE_STRING:
+            if (!v->constval.vstring)
+                return false;
+            if (v->constval.vstring && OPTS_FLAG(TRUE_EMPTY_STRINGS))
+                return true;
+            return !!v->constval.vstring[0];
+        default:
+            compile_error(ctx, "internal error: immediate_is_true on invalid type");
+            return !!v->constval.vfunc;
+    }
+}
+
 static bool parser_sy_apply_operator(parser_t *parser, shunt *sy)
 {
     const oper_info *op;
@@ -526,7 +552,8 @@ static bool parser_sy_apply_operator(parser_t *parser, shunt *sy)
              (exprs[0]->expression.vtype != exprs[1]->expression.vtype || \
               exprs[0]->expression.vtype != T)
 #define CanConstFold1(A) \
-             (ast_istype((A), ast_value) && ((ast_value*)(A))->hasvalue && (((ast_value*)(A))->cvq == CV_CONST))
+             (ast_istype((A), ast_value) && ((ast_value*)(A))->hasvalue && (((ast_value*)(A))->cvq == CV_CONST) &&\
+              (A)->expression.vtype != TYPE_FUNCTION)
 #define CanConstFold(A, B) \
              (CanConstFold1(A) && CanConstFold1(B))
 #define ConstV(i) (asvalue[(i)]->constval.vvec)
@@ -648,10 +675,17 @@ static bool parser_sy_apply_operator(parser_t *parser, shunt *sy)
                         out = (ast_expression*)ast_unary_new(ctx, INSTR_NOT_V, exprs[0]);
                     break;
                 case TYPE_STRING:
-                    if (CanConstFold1(exprs[0]))
-                        out = (ast_expression*)parser_const_float(parser, !ConstS(0) || !*ConstS(0));
-                    else
-                        out = (ast_expression*)ast_unary_new(ctx, INSTR_NOT_S, exprs[0]);
+                    if (CanConstFold1(exprs[0])) {
+                        if (OPTS_FLAG(TRUE_EMPTY_STRINGS))
+                            out = (ast_expression*)parser_const_float(parser, !ConstS(0));
+                        else
+                            out = (ast_expression*)parser_const_float(parser, !ConstS(0) || !*ConstS(0));
+                    } else {
+                        if (OPTS_FLAG(TRUE_EMPTY_STRINGS))
+                            out = (ast_expression*)ast_unary_new(ctx, INSTR_NOT_F, exprs[0]);
+                        else
+                            out = (ast_expression*)ast_unary_new(ctx, INSTR_NOT_S, exprs[0]);
+                    }
                     break;
                 /* we don't constant-fold NOT for these types */
                 case TYPE_ENTITY:
@@ -839,8 +873,16 @@ static bool parser_sy_apply_operator(parser_t *parser, shunt *sy)
 #endif
             if (CanConstFold(exprs[0], exprs[1]))
             {
-                out = (ast_expression*)parser_const_float(parser,
-                    (generated_op == INSTR_OR ? (ConstF(0) || ConstF(1)) : (ConstF(0) && ConstF(1))));
+                if (OPTS_FLAG(PERL_LOGIC)) {
+                    if (immediate_is_true(ctx, asvalue[0]))
+                        out = exprs[1];
+                }
+                else
+                    out = (ast_expression*)parser_const_float(parser,
+                          ( (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);
             }
             else
             {
@@ -1852,10 +1894,61 @@ static void parser_addlocal(parser_t *parser, const char *name, ast_expression *
     util_htset(vec_last(parser->variables), name, (void*)e);
 }
 
+static ast_expression* process_condition(parser_t *parser, ast_expression *cond, bool *_ifnot)
+{
+    bool       ifnot = false;
+    ast_unary *unary;
+    ast_expression *prev;
+
+    if (OPTS_FLAG(FALSE_EMPTY_STRINGS) && cond->expression.vtype == TYPE_STRING) {
+        prev = cond;
+        cond = (ast_expression*)ast_unary_new(ast_ctx(cond), INSTR_NOT_S, cond);
+        if (!cond) {
+            ast_unref(prev);
+            parseerror(parser, "internal error: failed to process condition");
+            return NULL;
+        }
+        ifnot = !ifnot;
+    }
+    else if (OPTS_FLAG(CORRECT_LOGIC)) {
+        /* everything must use a NOT_ */
+        unary = (ast_unary*)cond;
+        if (!ast_istype(cond, ast_unary) || unary->op < INSTR_NOT_F || unary->op > INSTR_NOT_FNC)
+        {
+            /* use the right NOT_ */
+            prev = cond;
+            cond = (ast_expression*)ast_unary_new(ast_ctx(cond), type_not_instr[cond->expression.vtype], cond);
+            if (!cond) {
+                ast_unref(prev);
+                parseerror(parser, "internal error: failed to process condition");
+                return NULL;
+            }
+            ifnot = !ifnot;
+        }
+    }
+
+    unary = (ast_unary*)cond;
+    while (ast_istype(cond, ast_unary) && unary->op == INSTR_NOT_F)
+        /*&& unary->operand->expression.vtype != TYPE_STRING) */
+    {
+        cond = unary->operand;
+        unary->operand = NULL;
+        ast_delete(unary);
+        ifnot = !ifnot;
+        unary = (ast_unary*)cond;
+    }
+
+    if (!cond)
+        parseerror(parser, "internal error: failed to process condition");
+
+    if (ifnot) *_ifnot = !*_ifnot;
+    return cond;
+}
+
 static bool parse_if(parser_t *parser, ast_block *block, ast_expression **out)
 {
     ast_ifthen *ifthen;
-    ast_expression *cond, *ontrue, *onfalse = NULL;
+    ast_expression *cond, *ontrue = NULL, *onfalse = NULL;
     bool ifnot = false;
 
     lex_ctx ctx = parser_ctx(parser);
@@ -1919,6 +2012,13 @@ static bool parse_if(parser_t *parser, ast_block *block, ast_expression **out)
         }
     }
 
+    cond = process_condition(parser, cond, &ifnot);
+    if (!cond) {
+        if (ontrue)  ast_delete(ontrue);
+        if (onfalse) ast_delete(onfalse);
+        return false;
+    }
+
     if (ifnot)
         ifthen = ast_ifthen_new(ctx, cond, onfalse, ontrue);
     else
@@ -1932,6 +2032,8 @@ static bool parse_while(parser_t *parser, ast_block *block, ast_expression **out
     ast_loop *aloop;
     ast_expression *cond, *ontrue;
 
+    bool ifnot = false;
+
     lex_ctx ctx = parser_ctx(parser);
 
     (void)block; /* not touching */
@@ -1967,7 +2069,12 @@ static bool parse_while(parser_t *parser, ast_block *block, ast_expression **out
         return false;
     }
 
-    aloop = ast_loop_new(ctx, NULL, cond, NULL, NULL, ontrue);
+    cond = process_condition(parser, cond, &ifnot);
+    if (!cond) {
+        ast_delete(ontrue);
+        return false;
+    }
+    aloop = ast_loop_new(ctx, NULL, cond, ifnot, NULL, false, NULL, ontrue);
     *out = (ast_expression*)aloop;
     return true;
 }
@@ -1977,6 +2084,8 @@ static bool parse_dowhile(parser_t *parser, ast_block *block, ast_expression **o
     ast_loop *aloop;
     ast_expression *cond, *ontrue;
 
+    bool ifnot = false;
+
     lex_ctx ctx = parser_ctx(parser);
 
     (void)block; /* not touching */
@@ -2036,7 +2145,12 @@ static bool parse_dowhile(parser_t *parser, ast_block *block, ast_expression **o
         return false;
     }
 
-    aloop = ast_loop_new(ctx, NULL, NULL, cond, NULL, ontrue);
+    cond = process_condition(parser, cond, &ifnot);
+    if (!cond) {
+        ast_delete(ontrue);
+        return false;
+    }
+    aloop = ast_loop_new(ctx, NULL, NULL, false, cond, ifnot, NULL, ontrue);
     *out = (ast_expression*)aloop;
     return true;
 }
@@ -2046,7 +2160,9 @@ static bool parse_for(parser_t *parser, ast_block *block, ast_expression **out)
     ast_loop       *aloop;
     ast_expression *initexpr, *cond, *increment, *ontrue;
     ast_value      *typevar;
-    bool   retval = true;
+
+    bool retval = true;
+    bool ifnot  = false;
 
     lex_ctx ctx = parser_ctx(parser);
 
@@ -2139,7 +2255,10 @@ static bool parse_for(parser_t *parser, ast_block *block, ast_expression **out)
     if (!parse_statement_or_block(parser, &ontrue))
         goto onerr;
 
-    aloop = ast_loop_new(ctx, initexpr, cond, NULL, increment, ontrue);
+    cond = process_condition(parser, cond, &ifnot);
+    if (!cond)
+        goto onerr;
+    aloop = ast_loop_new(ctx, initexpr, cond, ifnot, NULL, false, increment, ontrue);
     *out = (ast_expression*)aloop;
 
     if (!parser_leaveblock(parser))