]> de.git.xonotic.org Git - xonotic/gmqcc.git/blobdiff - parser.c
Removed -fenhanced-diagnostics - it's now --correct, which makes sense since it doesn...
[xonotic/gmqcc.git] / parser.c
index 5fa0d899f201d9948e2f971eb3126a8c33841582..3ed0f08eeeaaa61ff9cdc089f405c9307a059bbe 100644 (file)
--- a/parser.c
+++ b/parser.c
@@ -100,9 +100,12 @@ typedef struct {
 
     /* pragma flags */
     bool noref;
+
+    /* collected information */
+    size_t     max_param_count;
 } parser_t;
 
-static const ast_expression *intrinsic_debug_typestring = (ast_expression*)0x10;
+static ast_expression * const intrinsic_debug_typestring = (ast_expression*)0x1;
 
 static void parser_enterblock(parser_t *parser);
 static bool parser_leaveblock(parser_t *parser);
@@ -116,6 +119,9 @@ 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, bool truthvalue, bool with_labels);
 static ast_expression* parse_expression(parser_t *parser, bool stopatcomma, bool with_labels);
+static ast_value* parser_create_array_setter_proto(parser_t *parser, ast_value *array, const char *funcname);
+static ast_value* parser_create_array_getter_proto(parser_t *parser, ast_value *array, const ast_expression *elemtype, const char *funcname);
+static ast_value *parse_typename(parser_t *parser, ast_value **storebase, ast_value *cached_typedef);
 
 static void parseerror(parser_t *parser, const char *fmt, ...)
 {
@@ -207,12 +213,18 @@ static ast_value* parser_const_float(parser_t *parser, double d)
 {
     size_t i;
     ast_value *out;
+    lex_ctx ctx;
     for (i = 0; i < vec_size(parser->imm_float); ++i) {
         const double compare = parser->imm_float[i]->constval.vfloat;
         if (memcmp((const void*)&compare, (const void *)&d, sizeof(double)) == 0)
             return parser->imm_float[i];
     }
-    out = ast_value_new(parser_ctx(parser), "#IMMEDIATE", TYPE_FLOAT);
+    if (parser->lex)
+        ctx = parser_ctx(parser);
+    else {
+        memset(&ctx, 0, sizeof(ctx));
+    }
+    out = ast_value_new(ctx, "#IMMEDIATE", TYPE_FLOAT);
     out->cvq      = CV_CONST;
     out->hasvalue = true;
     out->constval.vfloat = d;
@@ -1342,6 +1354,7 @@ static bool parser_close_call(parser_t *parser, shunt *sy)
 {
     /* was a function call */
     ast_expression *fun;
+    ast_value      *funval = NULL;
     ast_call       *call;
 
     size_t          fid;
@@ -1403,12 +1416,28 @@ static bool parser_close_call(parser_t *parser, shunt *sy)
             params->exprs = NULL;
             ast_delete(params);
         }
+        if (parser->max_param_count < paramcount)
+            parser->max_param_count = paramcount;
         (void)!ast_call_check_types(call);
     } else {
         parseerror(parser, "invalid function call");
         return false;
     }
 
+    if (ast_istype(fun, ast_value)) {
+        funval = (ast_value*)fun;
+        if ((fun->expression.flags & AST_FLAG_VARIADIC) &&
+            !(/*funval->cvq == CV_CONST && */ funval->hasvalue && funval->constval.vfunc->builtin))
+        {
+            size_t va_count;
+            if (paramcount < vec_size(fun->expression.params))
+                va_count = 0;
+            else
+                va_count = paramcount - vec_size(fun->expression.params);
+            call->va_count = (ast_expression*)parser_const_float(parser, (double)va_count);
+        }
+    }
+
     /* overwrite fid, the function, with a call */
     sy->out[fid] = syexp(call->expression.node.context, (ast_expression*)call);
 
@@ -1527,6 +1556,102 @@ static void parser_reclassify_token(parser_t *parser)
     }
 }
 
+static ast_expression* parse_vararg_do(parser_t *parser)
+{
+    ast_expression *idx, *out;
+    ast_value      *typevar;
+    ast_value      *funtype = parser->function->vtype;
+
+    lex_ctx ctx = parser_ctx(parser);
+
+    if (!parser_next(parser) || parser->tok != '(') {
+        parseerror(parser, "expected parameter index and type in parenthesis");
+        return NULL;
+    }
+    if (!parser_next(parser)) {
+        parseerror(parser, "error parsing parameter index");
+        return NULL;
+    }
+
+    idx = parse_expression_leave(parser, true, false, false);
+    if (!idx)
+        return NULL;
+
+    if (parser->tok != ',') {
+        ast_unref(idx);
+        parseerror(parser, "expected comma after parameter index");
+        return NULL;
+    }
+
+    if (!parser_next(parser) || (parser->tok != TOKEN_IDENT && parser->tok != TOKEN_TYPENAME)) {
+        ast_unref(idx);
+        parseerror(parser, "expected typename for vararg");
+        return NULL;
+    }
+
+    typevar = parse_typename(parser, NULL, NULL);
+    if (!typevar) {
+        ast_unref(idx);
+        return NULL;
+    }
+
+    if (parser->tok != ')') {
+        ast_unref(idx);
+        ast_delete(typevar);
+        parseerror(parser, "expected closing paren");
+        return NULL;
+    }
+
+#if 0
+    if (!parser_next(parser)) {
+        ast_unref(idx);
+        ast_delete(typevar);
+        parseerror(parser, "parse error after vararg");
+        return NULL;
+    }
+#endif
+
+    if (!parser->function->varargs) {
+        ast_unref(idx);
+        ast_delete(typevar);
+        parseerror(parser, "function has no variable argument list");
+        return NULL;
+    }
+
+    if (funtype->expression.varparam &&
+        !ast_compare_type((ast_expression*)typevar, (ast_expression*)funtype->expression.varparam))
+    {
+        char ty1[1024];
+        char ty2[1024];
+        ast_type_to_string((ast_expression*)typevar, ty1, sizeof(ty1));
+        ast_type_to_string((ast_expression*)funtype->expression.varparam, ty2, sizeof(ty2));
+        compile_error(ast_ctx(typevar),
+                      "function was declared to take varargs of type `%s`, requested type is: %s",
+                      ty2, ty1);
+    }
+
+    out = (ast_expression*)ast_array_index_new(ctx, (ast_expression*)(parser->function->varargs), idx);
+    ast_type_adopt(out, typevar);
+    ast_delete(typevar);
+    return out;
+}
+
+static ast_expression* parse_vararg(parser_t *parser)
+{
+    bool             old_noops = parser->lex->flags.noops;
+    enum parser_pot *old_pot   = parser->pot;
+
+    ast_expression *out;
+
+    parser->pot = NULL;
+    parser->lex->flags.noops = true;
+    out = parse_vararg_do(parser);
+
+    parser->pot              = old_pot;
+    parser->lex->flags.noops = old_noops;
+    return out;
+}
+
 static ast_expression* parse_expression_leave(parser_t *parser, bool stopatcomma, bool truthvalue, bool with_labels)
 {
     ast_expression *expr = NULL;
@@ -1585,6 +1710,24 @@ static ast_expression* parse_expression_leave(parser_t *parser, bool stopatcomma
                 goto onerr;
             }
         }
+        else if (parser->tok == TOKEN_DOTS)
+        {
+            ast_expression *va;
+            if (!OPTS_FLAG(VARIADIC_ARGS)) {
+                parseerror(parser, "cannot access varargs (try -fvariadic-args)");
+                goto onerr;
+            }
+            if (wantop) {
+                parseerror(parser, "expected operator or end of statement");
+                goto onerr;
+            }
+            wantop = true;
+            va = parse_vararg(parser);
+            if (!va)
+                goto onerr;
+            vec_push(sy.out, syexp(parser_ctx(parser), va));
+            DEBUGSHUNTDO(con_out("push `...`\n"));
+        }
         else if (parser->tok == TOKEN_IDENT)
         {
             const char     *ctoken = parser_tokval(parser);
@@ -1652,7 +1795,7 @@ static ast_expression* parse_expression_leave(parser_t *parser, bool stopatcomma
                      * We should also consider adding correction tables for
                      * other things as well.
                      */
-                    if (OPTS_FLAG(ENHANCED_DIAGNOSTICS)) {
+                    if (opts.correction) {
                         correction_t corr;
                         correct_init(&corr);
 
@@ -2750,7 +2893,7 @@ static bool parse_qualifiers(parser_t *parser, bool with_local, int *cvq, bool *
             else if (!strcmp(parser_tokval(parser), "deprecated") && !(flags & AST_FLAG_DEPRECATED)) {
                 flags   |= AST_FLAG_DEPRECATED;
                 *message = NULL;
-                
+
                 if (!parser_next(parser)) {
                     parseerror(parser, "parse error in attribute");
                     goto argerr;
@@ -2803,7 +2946,7 @@ static bool parse_qualifiers(parser_t *parser, bool with_local, int *cvq, bool *
                 }
             }
         }
-        else if (!strcmp(parser_tokval(parser), "static"))
+        else if (with_local && !strcmp(parser_tokval(parser), "static"))
             had_static = true;
         else if (!strcmp(parser_tokval(parser), "const"))
             had_const = true;
@@ -3637,7 +3780,7 @@ static bool parse_function_body(parser_t *parser, ast_value *var)
         return false;
     }
 
-    if (var->expression.flags & AST_FLAG_VARIADIC) {
+    if (!OPTS_FLAG(VARIADIC_ARGS) && var->expression.flags & AST_FLAG_VARIADIC) {
         if (parsewarning(parser, WARN_VARIADIC_FUNCTION,
                          "variadic function with implementation will not be able to access additional parameters"))
         {
@@ -3867,6 +4010,33 @@ static bool parse_function_body(parser_t *parser, ast_value *var)
     }
     vec_push(parser->functions, func);
 
+    if (var->argcounter) {
+        ast_value *argc = ast_value_new(ast_ctx(var), var->argcounter, TYPE_FLOAT);
+        parser_addlocal(parser, argc->name, (ast_expression*)argc);
+        func->argc = argc;
+    }
+
+    if (OPTS_FLAG(VARIADIC_ARGS) && var->expression.flags & AST_FLAG_VARIADIC) {
+        char name[1024];
+        ast_value *varargs = ast_value_new(ast_ctx(var), "reserved:va_args", TYPE_ARRAY);
+        varargs->expression.flags |= AST_FLAG_IS_VARARG;
+        varargs->expression.next = (ast_expression*)ast_value_new(ast_ctx(var), NULL, TYPE_VECTOR);
+        varargs->expression.count = 0;
+        snprintf(name, sizeof(name), "%s##va##SET", var->name);
+        if (!parser_create_array_setter_proto(parser, varargs, name)) {
+            ast_delete(varargs);
+            ast_block_delete(block);
+            goto enderr;
+        }
+        snprintf(name, sizeof(name), "%s##va##GET", var->name);
+        if (!parser_create_array_getter_proto(parser, varargs, varargs->expression.next, name)) {
+            ast_delete(varargs);
+            ast_block_delete(block);
+            goto enderr;
+        }
+        func->varargs = varargs;
+    }
+
     parser->function = func;
     if (!parse_block_into(parser, block)) {
         ast_block_delete(block);
@@ -4025,6 +4195,9 @@ static ast_expression *array_field_setter_node(
         if (!subscript)
             return NULL;
 
+        subscript->expression.next = ast_type_copy(ast_ctx(subscript), (ast_expression*)subscript);
+        subscript->expression.vtype = TYPE_FIELD;
+
         entfield = ast_entfield_new_force(ctx,
                                           (ast_expression*)entity,
                                           (ast_expression*)subscript,
@@ -4137,9 +4310,8 @@ static bool parser_create_array_accessor(parser_t *parser, ast_value *array, con
     return true;
 }
 
-static bool parser_create_array_setter(parser_t *parser, ast_value *array, const char *funcname)
+static ast_value* parser_create_array_setter_proto(parser_t *parser, ast_value *array, const char *funcname)
 {
-    ast_expression *root = NULL;
     ast_value      *index = NULL;
     ast_value      *value = NULL;
     ast_function   *func;
@@ -4147,11 +4319,11 @@ static bool parser_create_array_setter(parser_t *parser, ast_value *array, const
 
     if (!ast_istype(array->expression.next, ast_value)) {
         parseerror(parser, "internal error: array accessor needs to build an ast_value with a copy of the element type");
-        return false;
+        return NULL;
     }
 
     if (!parser_create_array_accessor(parser, array, funcname, &fval))
-        return false;
+        return NULL;
     func = fval->constval.vfunc;
     fval->expression.next = (ast_expression*)ast_value_new(ast_ctx(array), "<void>", TYPE_VOID);
 
@@ -4166,21 +4338,39 @@ static bool parser_create_array_setter(parser_t *parser, ast_value *array, const
     vec_push(fval->expression.params, index);
     vec_push(fval->expression.params, value);
 
-    root = array_setter_node(parser, array, index, value, 0, array->expression.count);
-    if (!root) {
-        parseerror(parser, "failed to build accessor search tree");
-        goto cleanup;
-    }
-
     array->setter = fval;
-    return ast_block_add_expr(func->blocks[0], root);
+    return fval;
 cleanup:
     if (index) ast_delete(index);
     if (value) ast_delete(value);
-    if (root)  ast_delete(root);
     ast_delete(func);
     ast_delete(fval);
-    return false;
+    return NULL;
+}
+
+static bool parser_create_array_setter_impl(parser_t *parser, ast_value *array)
+{
+    ast_expression *root = NULL;
+    root = array_setter_node(parser, array,
+                             array->setter->expression.params[0],
+                             array->setter->expression.params[1],
+                             0, array->expression.count);
+    if (!root) {
+        parseerror(parser, "failed to build accessor search tree");
+        return false;
+    }
+    if (!ast_block_add_expr(array->setter->constval.vfunc->blocks[0], root)) {
+        ast_delete(root);
+        return false;
+    }
+    return true;
+}
+
+static bool parser_create_array_setter(parser_t *parser, ast_value *array, const char *funcname)
+{
+    if (!parser_create_array_setter_proto(parser, array, funcname))
+        return false;
+    return parser_create_array_setter_impl(parser, array);
 }
 
 static bool parser_create_array_field_setter(parser_t *parser, ast_value *array, const char *funcname)
@@ -4232,9 +4422,8 @@ cleanup:
     return false;
 }
 
-static bool parser_create_array_getter(parser_t *parser, ast_value *array, const ast_expression *elemtype, const char *funcname)
+static ast_value* parser_create_array_getter_proto(parser_t *parser, ast_value *array, const ast_expression *elemtype, const char *funcname)
 {
-    ast_expression *root = NULL;
     ast_value      *index = NULL;
     ast_value      *fval;
     ast_function   *func;
@@ -4244,11 +4433,11 @@ static bool parser_create_array_getter(parser_t *parser, ast_value *array, const
      */
     if (!ast_istype(array->expression.next, ast_value)) {
         parseerror(parser, "internal error: array accessor needs to build an ast_value with a copy of the element type");
-        return false;
+        return NULL;
     }
 
     if (!parser_create_array_accessor(parser, array, funcname, &fval))
-        return false;
+        return NULL;
     func = fval->constval.vfunc;
     fval->expression.next = ast_type_copy(ast_ctx(array), elemtype);
 
@@ -4260,20 +4449,36 @@ static bool parser_create_array_getter(parser_t *parser, ast_value *array, const
     }
     vec_push(fval->expression.params, index);
 
-    root = array_getter_node(parser, array, index, 0, array->expression.count);
-    if (!root) {
-        parseerror(parser, "failed to build accessor search tree");
-        goto cleanup;
-    }
-
     array->getter = fval;
-    return ast_block_add_expr(func->blocks[0], root);
+    return fval;
 cleanup:
     if (index) ast_delete(index);
-    if (root)  ast_delete(root);
     ast_delete(func);
     ast_delete(fval);
-    return false;
+    return NULL;
+}
+
+static bool parser_create_array_getter_impl(parser_t *parser, ast_value *array)
+{
+    ast_expression *root = NULL;
+
+    root = array_getter_node(parser, array, array->getter->expression.params[0], 0, array->expression.count);
+    if (!root) {
+        parseerror(parser, "failed to build accessor search tree");
+        return false;
+    }
+    if (!ast_block_add_expr(array->getter->constval.vfunc->blocks[0], root)) {
+        ast_delete(root);
+        return false;
+    }
+    return true;
+}
+
+static bool parser_create_array_getter(parser_t *parser, ast_value *array, const ast_expression *elemtype, const char *funcname)
+{
+    if (!parser_create_array_getter_proto(parser, array, elemtype, funcname))
+        return false;
+    return parser_create_array_getter_impl(parser, array);
 }
 
 static ast_value *parse_typename(parser_t *parser, ast_value **storebase, ast_value *cached_typedef);
@@ -4286,6 +4491,8 @@ static ast_value *parse_parameter_list(parser_t *parser, ast_value *var)
     ast_value  *fval;
     bool        first = true;
     bool        variadic = false;
+    ast_value  *varparam = NULL;
+    char       *argcounter = NULL;
 
     ctx = parser_ctx(parser);
 
@@ -4315,14 +4522,17 @@ static ast_value *parse_parameter_list(parser_t *parser, ast_value *var)
         if (parser->tok == TOKEN_DOTS) {
             /* '...' indicates a varargs function */
             variadic = true;
-            if (!parser_next(parser)) {
-                parseerror(parser, "expected parameter");
-                return NULL;
-            }
-            if (parser->tok != ')') {
+            if (!parser_next(parser) || (parser->tok != ')' && parser->tok != TOKEN_IDENT)) {
                 parseerror(parser, "`...` must be the last parameter of a variadic function declaration");
                 goto on_error;
             }
+            if (parser->tok == TOKEN_IDENT) {
+                argcounter = util_strdup(parser_tokval(parser));
+                if (!parser_next(parser) || parser->tok != ')') {
+                    parseerror(parser, "`...` must be the last parameter of a variadic function declaration");
+                    goto on_error;
+                }
+            }
         }
         else
         {
@@ -4337,6 +4547,23 @@ static ast_value *parse_parameter_list(parser_t *parser, ast_value *var)
                 parseerror(parser, "type not supported as part of a parameter list: %s", tname);
                 goto on_error;
             }
+            /* type-restricted varargs */
+            if (parser->tok == TOKEN_DOTS) {
+                variadic = true;
+                varparam = vec_last(params);
+                vec_pop(params);
+                if (!parser_next(parser) || (parser->tok != ')' && parser->tok != TOKEN_IDENT)) {
+                    parseerror(parser, "`...` must be the last parameter of a variadic function declaration");
+                    goto on_error;
+                }
+                if (parser->tok == TOKEN_IDENT) {
+                    argcounter = util_strdup(parser_tokval(parser));
+                    if (!parser_next(parser) || parser->tok != ')') {
+                        parseerror(parser, "`...` must be the last parameter of a variadic function declaration");
+                        goto on_error;
+                    }
+                }
+            }
         }
     }
 
@@ -4360,12 +4587,16 @@ static ast_value *parse_parameter_list(parser_t *parser, ast_value *var)
         fval->expression.flags |= AST_FLAG_VARIADIC;
     var = fval;
 
-    var->expression.params = params;
+    var->expression.params   = params;
+    var->expression.varparam = (ast_expression*)varparam;
+    var->argcounter          = argcounter;
     params = NULL;
 
     return var;
 
 on_error:
+    if (argcounter)
+        mem_d(argcounter);
     ast_delete(var);
     for (i = 0; i < vec_size(params); ++i)
         ast_delete(params[i]);
@@ -4787,7 +5018,7 @@ static bool parse_variable(parser_t *parser, ast_block *localblock, bool nofield
                     goto cleanup;
                     */
                 }
-                if (opts.standard == COMPILER_QCC &&
+                if ((opts.standard == COMPILER_QCC || opts.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");
@@ -5643,13 +5874,31 @@ bool parser_finish(const char *output)
             return false;
         }
     }
-    if (parser->reserved_version &&
-        !ast_global_codegen(parser->reserved_version, ir, false))
-    {
-        con_out("failed to generate reserved::version");
-        ir_builder_delete(ir);
-        return false;
+    /* Build function vararg accessor ast tree now before generating
+     * immediates, because the accessors may add new immediates
+     */
+    for (i = 0; i < vec_size(parser->functions); ++i) {
+        ast_function *f = parser->functions[i];
+        if (f->varargs) {
+            if (parser->max_param_count > vec_size(f->vtype->expression.params)) {
+                f->varargs->expression.count = parser->max_param_count - vec_size(f->vtype->expression.params);
+                if (!parser_create_array_setter_impl(parser, f->varargs)) {
+                    con_out("failed to generate vararg setter for %s\n", f->name);
+                    ir_builder_delete(ir);
+                    return false;
+                }
+                if (!parser_create_array_getter_impl(parser, f->varargs)) {
+                    con_out("failed to generate vararg getter for %s\n", f->name);
+                    ir_builder_delete(ir);
+                    return false;
+                }
+            } else {
+                ast_delete(f->varargs);
+                f->varargs = NULL;
+            }
+        }
     }
+    /* Now we can generate immediates */
     for (i = 0; i < vec_size(parser->imm_float); ++i) {
         if (!ast_global_codegen(parser->imm_float[i], ir, false)) {
             con_out("failed to generate global %s\n", parser->imm_float[i]->name);
@@ -5705,9 +5954,17 @@ bool parser_finish(const char *output)
             return false;
         }
     }
+    if (parser->reserved_version &&
+        !ast_global_codegen(parser->reserved_version, ir, false))
+    {
+        con_out("failed to generate reserved::version");
+        ir_builder_delete(ir);
+        return false;
+    }
     for (i = 0; i < vec_size(parser->functions); ++i) {
-        if (!ast_function_codegen(parser->functions[i], ir)) {
-            con_out("failed to generate function %s\n", parser->functions[i]->name);
+        ast_function *f = parser->functions[i];
+        if (!ast_function_codegen(f, ir)) {
+            con_out("failed to generate function %s\n", f->name);
             ir_builder_delete(ir);
             return false;
         }