]> git.xonotic.org Git - xonotic/gmqcc.git/blobdiff - parser.c
ast referencing
[xonotic/gmqcc.git] / parser.c
index cef598573dcf5f72cc0f7afe4cf01135633aa556..f589d63629361ddc6b48edd09d20299bb93227ef 100644 (file)
--- a/parser.c
+++ b/parser.c
@@ -51,7 +51,10 @@ typedef struct {
 
     ast_value *imm_float_zero;
     ast_value *imm_float_one;
+    ast_value *imm_float_neg_one;
+
     ast_value *imm_vector_zero;
+
     ast_value *nil;
     ast_value *reserved_version;
 
@@ -135,16 +138,6 @@ static bool GMQCC_WARN parsewarning(parser_t *parser, int warntype, const char *
     return r;
 }
 
-static bool GMQCC_WARN genwarning(lex_ctx ctx, int warntype, const char *fmt, ...)
-{
-    bool    r;
-    va_list ap;
-    va_start(ap, fmt);
-    r = vcompile_warning(ctx, warntype, fmt, ap);
-    va_end(ap);
-    return r;
-}
-
 /**********************************************************************
  * some maths used for constant folding
  */
@@ -232,6 +225,12 @@ static ast_value* parser_const_float_0(parser_t *parser)
     return parser->imm_float_zero;
 }
 
+static ast_value* parser_const_float_neg1(parser_t *parser) {
+    if (!parser->imm_float_neg_one)
+        parser->imm_float_neg_one = parser_const_float(parser, -1);
+    return parser->imm_float_neg_one;
+}
+
 static ast_value* parser_const_float_1(parser_t *parser)
 {
     if (!parser->imm_float_one)
@@ -450,7 +449,7 @@ static sy_elem syparen(lex_ctx ctx, size_t off) {
  */
 static bool rotate_entfield_array_index_nodes(ast_expression **out)
 {
-    ast_array_index *index;
+    ast_array_index *index, *oldindex;
     ast_entfield    *entfield;
 
     ast_value       *field;
@@ -474,12 +473,16 @@ static bool rotate_entfield_array_index_nodes(ast_expression **out)
     sub    = index->index;
     entity = entfield->entity;
 
-    ast_delete(index);
+    oldindex = index;
 
     index = ast_array_index_new(ctx, (ast_expression*)field, sub);
     entfield = ast_entfield_new(ctx, entity, (ast_expression*)index);
     *out = (ast_expression*)entfield;
 
+    oldindex->array = NULL;
+    oldindex->index = NULL;
+    ast_delete(oldindex);
+
     return true;
 }
 
@@ -640,7 +643,7 @@ static bool parser_sy_apply_operator(parser_t *parser, shunt *sy)
             {
 #if 0
                 /* This is not broken in fteqcc anymore */
-                if (opts.standard != COMPILER_GMQCC) {
+                if (OPTS_OPTION_U32(OPTION_STANDARD) != COMPILER_GMQCC) {
                     /* this error doesn't need to make us bail out */
                     (void)!parsewarning(parser, WARN_EXTENSIONS,
                                         "accessing array-field members of an entity without parenthesis\n"
@@ -668,8 +671,7 @@ static bool parser_sy_apply_operator(parser_t *parser, shunt *sy)
                     return false;
                 }
             }
-            if (!ast_block_set_type(blocks[0], exprs[1]))
-                return false;
+            ast_block_set_type(blocks[0], exprs[1]);
 
             vec_push(sy->out, syblock(ctx, blocks[0]));
             return true;
@@ -1065,6 +1067,49 @@ static bool parser_sy_apply_operator(parser_t *parser, shunt *sy)
                 out = (ast_expression*)ast_ternary_new(ctx, exprs[0], exprs[1], exprs[2]);
             break;
 
+        case opid3('<', '=', '>'): /* -1, 0, or 1 */
+            if (NotSameType(TYPE_FLOAT)) {
+                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 comparision: %s and %s",
+                    ty1, ty2);
+
+                return false;
+            }
+
+            if (CanConstFold(exprs[0], exprs[1])) {
+                if (ConstF(0) < ConstF(1))
+                    out = (ast_expression*)parser_const_float_neg1(parser);
+                else if (ConstF(0) == ConstF(1))
+                    out = (ast_expression*)parser_const_float_0(parser);
+                else if (ConstF(0) > ConstF(1))
+                    out = (ast_expression*)parser_const_float_1(parser);
+            } else {
+                ast_binary *eq = ast_binary_new(ctx, INSTR_EQ_F, exprs[0], exprs[1]);
+
+                eq->refs = false; /* references nothing */
+
+                    /* if (lt) { */
+                out = (ast_expression*)ast_ternary_new(ctx,
+                        (ast_expression*)ast_binary_new(ctx, INSTR_LT, exprs[0], exprs[1]),
+                        /* out = -1 */
+                        (ast_expression*)parser_const_float_neg1(parser),
+                    /* } else { */
+                        /* if (eq) { */
+                        (ast_expression*)ast_ternary_new(ctx, (ast_expression*)eq,
+                            /* out = 0 */
+                            (ast_expression*)parser_const_float_0(parser),
+                        /* } else { */
+                            /* out = 1 */
+                            (ast_expression*)parser_const_float_1(parser)
+                        /* } */
+                        )
+                    /* } */
+                    );
+
+            }
+            break;
+
         case opid1('>'):
             generated_op += 1; /* INSTR_GT */
         case opid1('<'):
@@ -1111,7 +1156,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 == VINSTR_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));
@@ -1138,7 +1183,7 @@ static bool parser_sy_apply_operator(parser_t *parser, shunt *sy)
                     assignop = type_store_instr[exprs[0]->expression.vtype];
                 }
 
-                if (assignop == AINSTR_END) {
+                if (assignop == VINSTR_END) {
                     ast_type_to_string(exprs[0], ty1, sizeof(ty1));
                     ast_type_to_string(exprs[1], ty2, sizeof(ty2));
                     compile_error(ctx, "invalid types in assignment: cannot assign %s to %s", ty2, ty1);
@@ -1356,6 +1401,19 @@ static bool parser_sy_apply_operator(parser_t *parser, shunt *sy)
             asbinstore->keep_dest = true;
             out = (ast_expression*)asbinstore;
             break;
+
+        case opid2('~', 'P'):
+            if (exprs[0]->expression.vtype != TYPE_FLOAT) {
+                ast_type_to_string(exprs[0], ty1, sizeof(ty1));
+                compile_error(ast_ctx(exprs[0]), "invalid type for bit not: %s", ty1);
+                return false;
+            }
+
+            if(CanConstFold1(exprs[0]))
+                out = (ast_expression*)parser_const_float(parser, ~(qcint)ConstF(0));
+            else
+                out = (ast_expression*)ast_binary_new(ctx, INSTR_SUB_F, (ast_expression*)parser_const_float_neg1(parser), exprs[0]);
+            break;
     }
 #undef NotSameType
 
@@ -1794,7 +1852,7 @@ static bool parse_sya_operand(parser_t *parser, shunt *sy, bool with_labels)
                  * We should also consider adding correction tables for
                  * other things as well.
                  */
-                if (opts.correction) {
+                if (OPTS_OPTION_BOOL(OPTION_CORRECTION)) {
                     correction_t corr;
                     correct_init(&corr);
 
@@ -1881,8 +1939,8 @@ static ast_expression* parse_expression_leave(parser_t *parser, bool stopatcomma
                 }
             }
             if (o == operator_count) {
-                /* no operator found... must be the end of the statement */
-                break;
+                compile_error(parser_ctx(parser), "unknown operator: %s", parser_tokval(parser));
+                goto onerr;
             }
             /* found an operator */
             op = &operators[o];
@@ -1991,9 +2049,13 @@ static ast_expression* parse_expression_leave(parser_t *parser, bool stopatcomma
             }
         }
         else if (parser->tok == ')') {
+            while (vec_size(sy.paren) && vec_last(sy.paren) == PAREN_TERNARY2) {
+                if (!parser_sy_apply_operator(parser, &sy))
+                    goto onerr;
+            }
+            if (!vec_size(sy.paren))
+                break;
             if (wantop) {
-                if (!vec_size(sy.paren))
-                    break;
                 if (vec_last(sy.paren) == PAREN_TERNARY1) {
                     parseerror(parser, "mismatched parentheses (closing paren in ternary expression?)");
                     goto onerr;
@@ -2002,8 +2064,6 @@ static ast_expression* parse_expression_leave(parser_t *parser, bool stopatcomma
                     goto onerr;
             } else {
                 /* must be a function call without parameters */
-                if (!vec_size(sy.paren))
-                    break;
                 if (vec_last(sy.paren) != PAREN_FUNC) {
                     parseerror(parser, "closing paren in invalid position");
                     goto onerr;
@@ -2022,14 +2082,19 @@ static ast_expression* parse_expression_leave(parser_t *parser, bool stopatcomma
             goto onerr;
         }
         else if (parser->tok == ']') {
+            while (vec_size(sy.paren) && vec_last(sy.paren) == PAREN_TERNARY2) {
+                if (!parser_sy_apply_operator(parser, &sy))
+                    goto onerr;
+            }
             if (!vec_size(sy.paren))
                 break;
-            if (!vec_size(sy.paren) || vec_last(sy.paren) != PAREN_INDEX) {
+            if (vec_last(sy.paren) != PAREN_INDEX) {
                 parseerror(parser, "mismatched parentheses, unexpected ']'");
                 goto onerr;
             }
             if (!parser_close_paren(parser, &sy))
                 goto onerr;
+            wantop = true;
         }
         else if (!wantop) {
             if (!parse_sya_operand(parser, &sy, with_labels))
@@ -2198,6 +2263,12 @@ static ast_expression* process_condition(parser_t *parser, ast_expression *cond,
     ast_unary *unary;
     ast_expression *prev;
 
+    if (cond->expression.vtype == TYPE_VOID || cond->expression.vtype >= TYPE_VARIANT) {
+        char ty[1024];
+        ast_type_to_string(cond, ty, sizeof(ty));
+        compile_error(ast_ctx(cond), "invalid type for if() condition: %s", ty);
+    }
+
     if (OPTS_FLAG(FALSE_EMPTY_STRINGS) && cond->expression.vtype == TYPE_STRING)
     {
         prev = cond;
@@ -2632,7 +2703,7 @@ static bool parse_for_go(parser_t *parser, ast_block *block, ast_expression **ou
 
     if (typevar || parser->tok == TOKEN_TYPENAME) {
 #if 0
-        if (opts.standard != COMPILER_GMQCC) {
+        if (OPTS_OPTION_U32(OPTION_STANDARD) != COMPILER_GMQCC) {
             if (parsewarning(parser, WARN_EXTENSIONS,
                              "current standard does not allow variable declarations in for-loop initializers"))
                 goto onerr;
@@ -2677,11 +2748,12 @@ static bool parse_for_go(parser_t *parser, ast_block *block, ast_expression **ou
 
     /* parse the incrementor */
     if (parser->tok != ')') {
+        lex_ctx condctx = parser_ctx(parser);
         increment = parse_expression_leave(parser, false, false, false);
         if (!increment)
             goto onerr;
         if (!ast_side_effects(increment)) {
-            if (genwarning(ast_ctx(increment), WARN_EFFECTLESS_STATEMENT, "statement has no effect"))
+            if (compile_warning(condctx, WARN_EFFECTLESS_STATEMENT, "statement has no effect"))
                 goto onerr;
         }
     }
@@ -3361,7 +3433,7 @@ static bool parse_statement(parser_t *parser, ast_block *block, ast_expression *
             parseerror(parser, "cannot declare a variable from here");
             return false;
         }
-        if (opts.standard == COMPILER_QCC) {
+        if (OPTS_OPTION_U32(OPTION_STANDARD) == COMPILER_QCC) {
             if (parsewarning(parser, WARN_EXTENSIONS, "missing 'local' keyword when declaring a local variable"))
                 return false;
         }
@@ -3428,7 +3500,7 @@ static bool parse_statement(parser_t *parser, ast_block *block, ast_expression *
         }
         else if (!strcmp(parser_tokval(parser), "for"))
         {
-            if (opts.standard == COMPILER_QCC) {
+            if (OPTS_OPTION_U32(OPTION_STANDARD) == COMPILER_QCC) {
                 if (parsewarning(parser, WARN_EXTENSIONS, "for loops are not recognized in the original Quake C standard, to enable try an alternate standard --std=?"))
                     return false;
             }
@@ -3527,12 +3599,13 @@ static bool parse_statement(parser_t *parser, ast_block *block, ast_expression *
     }
     else
     {
+        lex_ctx ctx = parser_ctx(parser);
         ast_expression *exp = parse_expression(parser, false, false);
         if (!exp)
             return false;
         *out = exp;
         if (!ast_side_effects(exp)) {
-            if (genwarning(ast_ctx(exp), WARN_EFFECTLESS_STATEMENT, "statement has no effect"))
+            if (compile_warning(ctx, WARN_EFFECTLESS_STATEMENT, "statement has no effect"))
                 return false;
         }
         return true;
@@ -3819,11 +3892,12 @@ static bool parse_function_body(parser_t *parser, ast_value *var)
             ast_expression *functype = fld_think->expression.next;
 
             thinkfunc = ast_value_new(parser_ctx(parser), parser_tokval(parser), functype->expression.vtype);
-            if (!thinkfunc || !ast_type_adopt(thinkfunc, functype)) {
+            if (!thinkfunc) { /* || !ast_type_adopt(thinkfunc, functype)*/
                 ast_unref(framenum);
                 parseerror(parser, "failed to create implicit prototype for `%s`", parser_tokval(parser));
                 return false;
             }
+            ast_type_adopt(thinkfunc, functype);
 
             if (!parser_next(parser)) {
                 ast_unref(framenum);
@@ -3949,6 +4023,12 @@ static bool parse_function_body(parser_t *parser, ast_value *var)
         }
     }
 
+    if (var->hasvalue) {
+        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);
     if (!func) {
         parseerror(parser, "failed to allocate function for `%s`", var->name);
@@ -4029,7 +4109,7 @@ static bool parse_function_body(parser_t *parser, ast_value *var)
 
     if (parser->tok == ';')
         return parser_next(parser);
-    else if (opts.standard == COMPILER_QCC)
+    else if (OPTS_OPTION_U32(OPTION_STANDARD) == COMPILER_QCC)
         parseerror(parser, "missing semicolon after function body (mandatory with -std=qcc)");
     return retval;
 
@@ -4545,7 +4625,7 @@ static ast_value *parse_parameter_list(parser_t *parser, ast_value *var)
         vec_free(params);
 
     /* sanity check */
-    if (vec_size(params) > 8 && opts.standard == COMPILER_QCC)
+    if (vec_size(params) > 8 && OPTS_OPTION_U32(OPTION_STANDARD) == COMPILER_QCC)
         (void)!parsewarning(parser, WARN_EXTENSIONS, "more than 8 parameters are not supported by this standard");
 
     /* parse-out */
@@ -4759,7 +4839,7 @@ static ast_value *parse_typename(parser_t *parser, ast_value **storebase, ast_va
     }
 
     /* now there may be function parens again */
-    if (parser->tok == '(' && opts.standard == COMPILER_QCC)
+    if (parser->tok == '(' && OPTS_OPTION_U32(OPTION_STANDARD) == COMPILER_QCC)
         parseerror(parser, "C-style function syntax is not allowed in -std=qcc");
     if (parser->tok == '(' && wasarray)
         parseerror(parser, "arrays as part of a return type is not supported");
@@ -4898,7 +4978,7 @@ static bool parse_variable(parser_t *parser, ast_block *localblock, bool nofield
 
         /* Part 0: finish the type */
         if (parser->tok == '(') {
-            if (opts.standard == COMPILER_QCC)
+            if (OPTS_OPTION_U32(OPTION_STANDARD) == COMPILER_QCC)
                 parseerror(parser, "C-style function syntax is not allowed in -std=qcc");
             var = parse_parameter_list(parser, var);
             if (!var) {
@@ -4921,7 +5001,7 @@ static bool parse_variable(parser_t *parser, ast_block *localblock, bool nofield
         }
         /* for functions returning functions */
         while (parser->tok == '(') {
-            if (opts.standard == COMPILER_QCC)
+            if (OPTS_OPTION_U32(OPTION_STANDARD) == COMPILER_QCC)
                 parseerror(parser, "C-style function syntax is not allowed in -std=qcc");
             var = parse_parameter_list(parser, var);
             if (!var) {
@@ -4991,7 +5071,7 @@ static bool parse_variable(parser_t *parser, ast_block *localblock, bool nofield
                     goto cleanup;
                     */
                 }
-                if ((opts.standard == COMPILER_QCC || opts.standard == COMPILER_FTEQCC) &&
+                if ((OPTS_OPTION_U32(OPTION_STANDARD) == COMPILER_QCC || OPTS_OPTION_U32(OPTION_STANDARD) == COMPILER_FTEQCC) &&
                     (old = parser_find_global(parser, var->name)))
                 {
                     parseerror(parser, "cannot declare a field and a global of the same name with -std=qcc");
@@ -5063,7 +5143,7 @@ static bool parse_variable(parser_t *parser, ast_block *localblock, bool nofield
                         ast_delete(var);
                         var = proto;
                     }
-                    if (opts.standard == COMPILER_QCC &&
+                    if (OPTS_OPTION_U32(OPTION_STANDARD) == COMPILER_QCC &&
                         (old = parser_find_field(parser, var->name)))
                     {
                         parseerror(parser, "cannot declare a field and a global of the same name with -std=qcc");
@@ -5094,7 +5174,7 @@ static bool parse_variable(parser_t *parser, ast_block *localblock, bool nofield
                     retval = false;
                     goto cleanup;
                 }
-                if (opts.standard != COMPILER_GMQCC) {
+                if (OPTS_OPTION_U32(OPTION_STANDARD) != COMPILER_GMQCC) {
                     ast_delete(var);
                     var = NULL;
                     goto skipvar;
@@ -5274,7 +5354,7 @@ skipvar:
             break;
         }
 
-        if (localblock && opts.standard == COMPILER_QCC) {
+        if (localblock && OPTS_OPTION_U32(OPTION_STANDARD) == COMPILER_QCC) {
             if (parsewarning(parser, WARN_LOCAL_CONSTANTS,
                              "initializing expression turns variable `%s` into a constant in this standard",
                              var->name) )
@@ -5294,7 +5374,7 @@ skipvar:
                 break;
             }
         }
-        else if (opts.standard == COMPILER_QCC) {
+        else if (OPTS_OPTION_U32(OPTION_STANDARD) == COMPILER_QCC) {
             parseerror(parser, "expected '=' before function body in this standard");
         }
 
@@ -5526,7 +5606,7 @@ static bool parser_global_statement(parser_t *parser)
     }
     else
     {
-        parseerror(parser, "unexpected token: %s", parser->lex->tok.value);
+        parseerror(parser, "unexpected token: `%s`", parser->lex->tok.value);
         return false;
     }
     return true;
@@ -5655,7 +5735,7 @@ bool parser_init()
     parser->const_vec[1] = ast_value_new(empty_ctx, "<vector.y>", TYPE_NOEXPR);
     parser->const_vec[2] = ast_value_new(empty_ctx, "<vector.z>", TYPE_NOEXPR);
 
-    if (opts.add_info) {
+    if (OPTS_OPTION_BOOL(OPTION_ADD_INFO)) {
         parser->reserved_version = ast_value_new(empty_ctx, "reserved:version", TYPE_STRING);
         parser->reserved_version->cvq = CV_CONST;
         parser->reserved_version->hasvalue = true;
@@ -5840,8 +5920,8 @@ bool parser_finish(const char *output)
             continue;
         asvalue = (ast_value*)(parser->globals[i]);
         if (!asvalue->uses && !asvalue->hasvalue && asvalue->expression.vtype != TYPE_FUNCTION) {
-            retval = retval && !genwarning(ast_ctx(asvalue), WARN_UNUSED_VARIABLE,
-                                           "unused global: `%s`", asvalue->name);
+            retval = retval && !compile_warning(ast_ctx(asvalue), WARN_UNUSED_VARIABLE,
+                                                "unused global: `%s`", asvalue->name);
         }
         if (!ast_global_codegen(asvalue, ir, false)) {
             con_out("failed to generate global %s\n", asvalue->name);
@@ -5944,7 +6024,7 @@ bool parser_finish(const char *output)
             return false;
         }
     }
-    if (opts.dump)
+    if (OPTS_OPTION_BOOL(OPTION_DUMP))
         ir_builder_dump(ir, con_out);
     for (i = 0; i < vec_size(parser->functions); ++i) {
         if (!ir_function_finalize(parser->functions[i]->ir_func)) {
@@ -5961,7 +6041,7 @@ bool parser_finish(const char *output)
     }
 
     if (retval) {
-        if (opts.dumpfin)
+        if (OPTS_OPTION_BOOL(OPTION_DUMPFIN))
             ir_builder_dump(ir, con_out);
 
         generate_checksum(parser);