]> git.xonotic.org Git - xonotic/gmqcc.git/blobdiff - parser.c
Warn (via -Wextensions) when accessing a field-of-array's element without putting...
[xonotic/gmqcc.git] / parser.c
index 0bd3d7f481ae7f88f0e6778e7dfb96d4b97b54ae..d53f3f15f09c5ab67914e78c6109c2e75b16bc90 100644 (file)
--- a/parser.c
+++ b/parser.c
@@ -32,6 +32,9 @@ typedef struct {
 
     size_t errors;
 
+    /* we store the '=' operator info */
+    const oper_info *assign_op;
+
     /* TYPE_FIELD -> parser_find_fields is used instead of find_var
      * TODO: TYPE_VECTOR -> x, y and z are accepted in the gmqcc standard
      * anything else: type error
@@ -323,6 +326,10 @@ typedef struct
 MEM_VEC_FUNCTIONS(shunt, sy_elem, out)
 MEM_VEC_FUNCTIONS(shunt, sy_elem, ops)
 
+#define SY_PAREN_EXPR '('
+#define SY_PAREN_FUNC 'f'
+#define SY_PAREN_INDEX '['
+
 static sy_elem syexp(lex_ctx ctx, ast_expression *v) {
     sy_elem e;
     e.etype = 0;
@@ -373,6 +380,44 @@ static sy_elem syparen(lex_ctx ctx, int p, size_t off) {
 # define DEBUGSHUNTDO(x)
 #endif
 
+/* With regular precedence rules, ent.foo[n] is the same as (ent.foo)[n],
+ * so we need to rotate it to become ent.(foo[n]).
+ */
+static bool rotate_entfield_array_index_nodes(ast_expression **out)
+{
+    ast_array_index *index;
+    ast_entfield    *entfield;
+
+    ast_value       *field;
+    ast_expression  *sub;
+    ast_expression  *entity;
+
+    lex_ctx ctx = ast_ctx(*out);
+
+    if (!ast_istype(*out, ast_array_index))
+        return false;
+    index = (ast_array_index*)*out;
+
+    if (!ast_istype(index->array, ast_entfield))
+        return false;
+    entfield = (ast_entfield*)index->array;
+
+    if (!ast_istype(entfield->field, ast_value))
+        return false;
+    field = (ast_value*)entfield->field;
+
+    sub    = index->index;
+    entity = entfield->entity;
+
+    ast_delete(index);
+
+    index = ast_array_index_new(ctx, (ast_expression*)field, sub);
+    entfield = ast_entfield_new(ctx, entity, (ast_expression*)index);
+    *out = (ast_expression*)entfield;
+
+    return true;
+}
+
 static bool parser_sy_pop(parser_t *parser, shunt *sy)
 {
     const oper_info *op;
@@ -384,6 +429,9 @@ static bool parser_sy_pop(parser_t *parser, shunt *sy)
     size_t i, assignop;
     qcint  generated_op = 0;
 
+    char ty1[1024];
+    char ty2[1024];
+
     if (!sy->ops_count) {
         parseerror(parser, "internal error: missing operator");
         return false;
@@ -453,6 +501,31 @@ static bool parser_sy_pop(parser_t *parser, shunt *sy)
             }
             break;
 
+        case opid1('['):
+            if (exprs[0]->expression.vtype != TYPE_ARRAY &&
+                !(exprs[0]->expression.vtype == TYPE_FIELD &&
+                  exprs[0]->expression.next->expression.vtype == TYPE_ARRAY))
+            {
+                ast_type_to_string(exprs[0], ty1, sizeof(ty1));
+                parseerror(parser, "cannot index value of type %s", ty1);
+                return false;
+            }
+            if (exprs[1]->expression.vtype != TYPE_FLOAT) {
+                ast_type_to_string(exprs[0], ty1, sizeof(ty1));
+                parseerror(parser, "index must be of type float, not %s", ty1);
+                return false;
+            }
+            out = (ast_expression*)ast_array_index_new(ctx, exprs[0], exprs[1]);
+            if (rotate_entfield_array_index_nodes(&out))
+            {
+                if (opts_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");
+                }
+            }
+            break;
+
         case opid1(','):
             if (blocks[0]) {
                 if (!ast_block_exprs_add(blocks[0], exprs[1]))
@@ -756,8 +829,6 @@ static bool parser_sy_pop(parser_t *parser, shunt *sy)
                 else
                     assignop = type_storep_instr[exprs[0]->expression.vtype];
                 if (!ast_compare_type(field->expression.next, exprs[1])) {
-                    char ty1[1024];
-                    char ty2[1024];
                     ast_type_to_string(field->expression.next, ty1, sizeof(ty1));
                     ast_type_to_string(exprs[1], ty2, sizeof(ty2));
                     if (opts_standard == COMPILER_QCC &&
@@ -782,11 +853,16 @@ static bool parser_sy_pop(parser_t *parser, shunt *sy)
                 {
                     assignop = type_store_instr[TYPE_VECTOR];
                 }
-                else
+                else {
                     assignop = type_store_instr[exprs[0]->expression.vtype];
-                if (!ast_compare_type(exprs[0], exprs[1])) {
-                    char ty1[1024];
-                    char ty2[1024];
+                }
+
+                if (assignop == AINSTR_END) {
+                    ast_type_to_string(exprs[0], ty1, sizeof(ty1));
+                    ast_type_to_string(exprs[1], ty2, sizeof(ty2));
+                    parseerror(parser, "invalid types in assignment: cannot assign %s to %s", ty2, ty1);
+                }
+                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));
                     if (opts_standard == COMPILER_QCC &&
@@ -977,15 +1053,25 @@ static bool parser_close_paren(parser_t *parser, shunt *sy, bool functions_only)
     }
     */
     while (sy->ops_count) {
-        if (sy->ops[sy->ops_count-1].paren == 'f') {
+        if (sy->ops[sy->ops_count-1].paren == SY_PAREN_FUNC) {
             if (!parser_close_call(parser, sy))
                 return false;
             break;
         }
-        if (sy->ops[sy->ops_count-1].paren == 1) {
+        if (sy->ops[sy->ops_count-1].paren == SY_PAREN_EXPR) {
             sy->ops_count--;
             return !functions_only;
         }
+        if (sy->ops[sy->ops_count-1].paren == SY_PAREN_INDEX) {
+            if (functions_only)
+                return false;
+            /* pop off the parenthesis */
+            sy->ops_count--;
+            /* then apply the index operator */
+            if (!parser_sy_pop(parser, sy))
+                return false;
+            return true;
+        }
         if (!parser_sy_pop(parser, sy))
             return false;
     }
@@ -1145,6 +1231,10 @@ static ast_expression* parse_expression_leave(parser_t *parser, bool stopatcomma
             parseerror(parser, "internal error: '(' should be classified as operator");
             goto onerr;
         }
+        else if (parser->tok == '[') {
+            parseerror(parser, "internal error: '[' should be classified as operator");
+            goto onerr;
+        }
         else if (parser->tok == ')') {
             if (wantop) {
                 DEBUGSHUNTDO(printf("do[op] )\n"));
@@ -1166,6 +1256,16 @@ static ast_expression* parse_expression_leave(parser_t *parser, bool stopatcomma
             }
             wantop = true;
         }
+        else if (parser->tok == ']') {
+            if (!wantop)
+                parseerror(parser, "operand expected");
+            --parens;
+            if (parens < 0)
+                break;
+            if (!parser_close_paren(parser, &sy, false))
+                goto onerr;
+            wantop = true;
+        }
         else if (parser->tok != TOKEN_OPERATOR) {
             if (wantop) {
                 parseerror(parser, "expected operator or end of statement");
@@ -1240,19 +1340,31 @@ static ast_expression* parse_expression_leave(parser_t *parser, bool stopatcomma
                     DEBUGSHUNTDO(printf("push [op] (\n"));
                     ++parens;
                     /* we expected an operator, this is the function-call operator */
-                    if (!shunt_ops_add(&sy, syparen(parser_ctx(parser), 'f', sy.out_count-1))) {
+                    if (!shunt_ops_add(&sy, syparen(parser_ctx(parser), SY_PAREN_FUNC, sy.out_count-1))) {
                         parseerror(parser, "out of memory");
                         goto onerr;
                     }
                 } else {
                     ++parens;
-                    if (!shunt_ops_add(&sy, syparen(parser_ctx(parser), 1, 0))) {
+                    if (!shunt_ops_add(&sy, syparen(parser_ctx(parser), SY_PAREN_EXPR, 0))) {
                         parseerror(parser, "out of memory");
                         goto onerr;
                     }
                     DEBUGSHUNTDO(printf("push [nop] (\n"));
                 }
                 wantop = false;
+            } else if (op->id == opid1('[')) {
+                if (!wantop) {
+                    parseerror(parser, "unexpected array subscript");
+                    goto onerr;
+                }
+                ++parens;
+                /* push both the operator and the paren, this makes life easier */
+                if (!shunt_ops_add(&sy, syop(parser_ctx(parser), op)))
+                    goto onerr;
+                if (!shunt_ops_add(&sy, syparen(parser_ctx(parser), SY_PAREN_INDEX, 0)))
+                    goto onerr;
+                wantop = false;
             } else {
                 DEBUGSHUNTDO(printf("push operator %s\n", op->op));
                 if (!shunt_ops_add(&sy, syop(parser_ctx(parser), op)))
@@ -1263,7 +1375,7 @@ static ast_expression* parse_expression_leave(parser_t *parser, bool stopatcomma
         if (!parser_next(parser)) {
             goto onerr;
         }
-        if (parser->tok == ';' || parser->tok == ']') {
+        if (parser->tok == ';' || (!parens && parser->tok == ']')) {
             break;
         }
     }
@@ -2167,6 +2279,398 @@ enderr:
     return false;
 }
 
+static ast_expression *array_accessor_split(
+    parser_t  *parser,
+    ast_value *array,
+    ast_value *index,
+    size_t     middle,
+    ast_expression *left,
+    ast_expression *right
+    )
+{
+    ast_ifthen *ifthen;
+    ast_binary *cmp;
+
+    lex_ctx ctx = ast_ctx(array);
+
+    if (!left || !right) {
+        if (left)  ast_delete(left);
+        if (right) ast_delete(right);
+        return NULL;
+    }
+
+    cmp = ast_binary_new(ctx, INSTR_LT,
+                         (ast_expression*)index,
+                         (ast_expression*)parser_const_float(parser, middle));
+    if (!cmp) {
+        ast_delete(left);
+        ast_delete(right);
+        parseerror(parser, "internal error: failed to create comparison for array setter");
+        return NULL;
+    }
+
+    ifthen = ast_ifthen_new(ctx, (ast_expression*)cmp, left, right);
+    if (!ifthen) {
+        ast_delete(cmp); /* will delete left and right */
+        parseerror(parser, "internal error: failed to create conditional jump for array setter");
+        return NULL;
+    }
+
+    return (ast_expression*)ifthen;
+}
+
+static ast_expression *array_setter_node(parser_t *parser, ast_value *array, ast_value *index, ast_value *value, size_t from, size_t afterend)
+{
+    lex_ctx ctx = ast_ctx(array);
+
+    if (from+1 == afterend) {
+        // set this value
+        ast_block       *block;
+        ast_return      *ret;
+        ast_array_index *subscript;
+        int assignop = type_store_instr[value->expression.vtype];
+
+        if (value->expression.vtype == TYPE_FIELD && value->expression.next->expression.vtype == TYPE_VECTOR)
+            assignop = INSTR_STORE_V;
+
+        subscript = ast_array_index_new(ctx, (ast_expression*)array, (ast_expression*)parser_const_float(parser, from));
+        if (!subscript)
+            return NULL;
+
+        ast_store *st = ast_store_new(ctx, assignop, (ast_expression*)subscript, (ast_expression*)value);
+        if (!st) {
+            ast_delete(subscript);
+            return NULL;
+        }
+
+        block = ast_block_new(ctx);
+        if (!block) {
+            ast_delete(st);
+            return NULL;
+        }
+
+        if (!ast_block_exprs_add(block, (ast_expression*)st)) {
+            ast_delete(block);
+            return NULL;
+        }
+
+        ret = ast_return_new(ctx, NULL);
+        if (!ret) {
+            ast_delete(block);
+            return NULL;
+        }
+
+        if (!ast_block_exprs_add(block, (ast_expression*)ret)) {
+            ast_delete(block);
+            return NULL;
+        }
+
+        return (ast_expression*)block;
+    } else {
+        ast_expression *left, *right;
+        size_t diff = afterend - from;
+        size_t middle = from + diff/2;
+        left  = array_setter_node(parser, array, index, value, from, middle);
+        right = array_setter_node(parser, array, index, value, middle, afterend);
+        return array_accessor_split(parser, array, index, middle, left, right);
+    }
+}
+
+static ast_expression *array_field_setter_node(
+    parser_t  *parser,
+    ast_value *array,
+    ast_value *entity,
+    ast_value *index,
+    ast_value *value,
+    size_t     from,
+    size_t     afterend)
+{
+    lex_ctx ctx = ast_ctx(array);
+
+    if (from+1 == afterend) {
+        // set this value
+        ast_block       *block;
+        ast_return      *ret;
+        ast_entfield    *entfield;
+        ast_array_index *subscript;
+        int assignop = type_storep_instr[value->expression.vtype];
+
+        if (value->expression.vtype == TYPE_FIELD && value->expression.next->expression.vtype == TYPE_VECTOR)
+            assignop = INSTR_STOREP_V;
+
+        subscript = ast_array_index_new(ctx, (ast_expression*)array, (ast_expression*)parser_const_float(parser, from));
+        if (!subscript)
+            return NULL;
+
+        entfield = ast_entfield_new_force(ctx,
+                                          (ast_expression*)entity,
+                                          (ast_expression*)subscript,
+                                          (ast_expression*)subscript);
+        if (!entfield) {
+            ast_delete(subscript);
+            return NULL;
+        }
+
+        ast_store *st = ast_store_new(ctx, assignop, (ast_expression*)entfield, (ast_expression*)value);
+        if (!st) {
+            ast_delete(entfield);
+            return NULL;
+        }
+
+        block = ast_block_new(ctx);
+        if (!block) {
+            ast_delete(st);
+            return NULL;
+        }
+
+        if (!ast_block_exprs_add(block, (ast_expression*)st)) {
+            ast_delete(block);
+            return NULL;
+        }
+
+        ret = ast_return_new(ctx, NULL);
+        if (!ret) {
+            ast_delete(block);
+            return NULL;
+        }
+
+        if (!ast_block_exprs_add(block, (ast_expression*)ret)) {
+            ast_delete(block);
+            return NULL;
+        }
+
+        return (ast_expression*)block;
+    } else {
+        ast_expression *left, *right;
+        size_t diff = afterend - from;
+        size_t middle = from + diff/2;
+        left  = array_field_setter_node(parser, array, entity, index, value, from, middle);
+        right = array_field_setter_node(parser, array, entity, index, value, middle, afterend);
+        return array_accessor_split(parser, array, index, middle, left, right);
+    }
+}
+
+static ast_expression *array_getter_node(parser_t *parser, ast_value *array, ast_value *index, size_t from, size_t afterend)
+{
+    lex_ctx ctx = ast_ctx(array);
+
+    if (from+1 == afterend) {
+        ast_return      *ret;
+        ast_array_index *subscript;
+
+        subscript = ast_array_index_new(ctx, (ast_expression*)array, (ast_expression*)parser_const_float(parser, from));
+        if (!subscript)
+            return NULL;
+
+        ret = ast_return_new(ctx, (ast_expression*)subscript);
+        if (!ret) {
+            ast_delete(subscript);
+            return NULL;
+        }
+
+        return (ast_expression*)ret;
+    } else {
+        ast_expression *left, *right;
+        size_t diff = afterend - from;
+        size_t middle = from + diff/2;
+        left  = array_getter_node(parser, array, index, from, middle);
+        right = array_getter_node(parser, array, index, middle, afterend);
+        return array_accessor_split(parser, array, index, middle, left, right);
+    }
+}
+
+static bool parser_create_array_accessor(parser_t *parser, ast_value *array, const char *funcname, ast_value **out)
+{
+    ast_function   *func = NULL;
+    ast_value      *fval = NULL;
+
+    fval = ast_value_new(ast_ctx(array), funcname, TYPE_FUNCTION);
+    if (!fval) {
+        parseerror(parser, "failed to create accessor function value");
+        return false;
+    }
+
+    func = ast_function_new(ast_ctx(array), funcname, fval);
+    if (!func) {
+        ast_delete(fval);
+        parseerror(parser, "failed to create accessor function node");
+        return false;
+    }
+
+    *out = fval;
+
+    return true;
+}
+
+static bool parser_create_array_setter(parser_t *parser, ast_value *array, const char *funcname)
+{
+    ast_expression *root = NULL;
+    ast_block      *body = NULL;
+    ast_value      *index = NULL;
+    ast_value      *value = NULL;
+    ast_function   *func;
+    ast_value      *fval;
+
+    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;
+    }
+
+    if (!parser_create_array_accessor(parser, array, funcname, &fval))
+        return false;
+    func = fval->constval.vfunc;
+    fval->expression.next = (ast_expression*)ast_value_new(ast_ctx(array), "<void>", TYPE_VOID);
+
+    body = ast_block_new(ast_ctx(array));
+    if (!body) {
+        parseerror(parser, "failed to create block for array accessor");
+        goto cleanup;
+    }
+
+    index = ast_value_new(ast_ctx(array), "index", TYPE_FLOAT);
+    value = ast_value_copy((ast_value*)array->expression.next);
+
+    if (!index || !value) {
+        parseerror(parser, "failed to create locals for array accessor");
+        goto cleanup;
+    }
+    (void)!ast_value_set_name(value, "value"); /* not important */
+    (void)!ast_expression_common_params_add(&fval->expression, index);
+    (void)!ast_expression_common_params_add(&fval->expression, 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;
+    }
+
+    (void)!ast_block_exprs_add(body, root);
+    (void)!ast_function_blocks_add(func, body);
+    array->setter = fval;
+    return true;
+cleanup:
+    if (body)  ast_delete(body);
+    if (index) ast_delete(index);
+    if (value) ast_delete(value);
+    if (root)  ast_delete(root);
+    ast_delete(func);
+    ast_delete(fval);
+    return false;
+}
+
+static bool parser_create_array_field_setter(parser_t *parser, ast_value *array, const char *funcname)
+{
+    ast_expression *root = NULL;
+    ast_block      *body = NULL;
+    ast_value      *entity = NULL;
+    ast_value      *index = NULL;
+    ast_value      *value = NULL;
+    ast_function   *func;
+    ast_value      *fval;
+
+    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;
+    }
+
+    if (!parser_create_array_accessor(parser, array, funcname, &fval))
+        return false;
+    func = fval->constval.vfunc;
+    fval->expression.next = (ast_expression*)ast_value_new(ast_ctx(array), "<void>", TYPE_VOID);
+
+    body = ast_block_new(ast_ctx(array));
+    if (!body) {
+        parseerror(parser, "failed to create block for array accessor");
+        goto cleanup;
+    }
+
+    entity = ast_value_new(ast_ctx(array), "entity", TYPE_ENTITY);
+    index  = ast_value_new(ast_ctx(array), "index",  TYPE_FLOAT);
+    value  = ast_value_copy((ast_value*)array->expression.next);
+    if (!entity || !index || !value) {
+        parseerror(parser, "failed to create locals for array accessor");
+        goto cleanup;
+    }
+    (void)!ast_value_set_name(value, "value"); /* not important */
+    (void)!ast_expression_common_params_add(&fval->expression, entity);
+    (void)!ast_expression_common_params_add(&fval->expression, index);
+    (void)!ast_expression_common_params_add(&fval->expression, value);
+
+    root = array_field_setter_node(parser, array, entity, index, value, 0, array->expression.count);
+    if (!root) {
+        parseerror(parser, "failed to build accessor search tree");
+        goto cleanup;
+    }
+
+    (void)!ast_block_exprs_add(body, root);
+    (void)!ast_function_blocks_add(func, body);
+    array->setter = fval;
+    return true;
+cleanup:
+    if (body)   ast_delete(body);
+    if (entity) ast_delete(entity);
+    if (index)  ast_delete(index);
+    if (value)  ast_delete(value);
+    if (root)   ast_delete(root);
+    ast_delete(func);
+    ast_delete(fval);
+    return false;
+}
+
+static bool parser_create_array_getter(parser_t *parser, ast_value *array, const ast_expression *elemtype, const char *funcname)
+{
+    ast_expression *root = NULL;
+    ast_block      *body = NULL;
+    ast_value      *index = NULL;
+    ast_value      *fval;
+    ast_function   *func;
+
+    /* NOTE: checking array->expression.next rather than elemtype since
+     * for fields elemtype is a temporary fieldtype.
+     */
+    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;
+    }
+
+    if (!parser_create_array_accessor(parser, array, funcname, &fval))
+        return false;
+    func = fval->constval.vfunc;
+    fval->expression.next = ast_type_copy(ast_ctx(array), elemtype);
+
+    body = ast_block_new(ast_ctx(array));
+    if (!body) {
+        parseerror(parser, "failed to create block for array accessor");
+        goto cleanup;
+    }
+
+    index = ast_value_new(ast_ctx(array), "index", TYPE_FLOAT);
+
+    if (!index) {
+        parseerror(parser, "failed to create locals for array accessor");
+        goto cleanup;
+    }
+    (void)!ast_expression_common_params_add(&fval->expression, index);
+
+    root = array_getter_node(parser, array, index, 0, array->expression.count);
+    if (!root) {
+        parseerror(parser, "failed to build accessor search tree");
+        goto cleanup;
+    }
+
+    (void)!ast_block_exprs_add(body, root);
+    (void)!ast_function_blocks_add(func, body);
+    array->getter = fval;
+    return true;
+cleanup:
+    if (body)  ast_delete(body);
+    if (index) ast_delete(index);
+    if (root)  ast_delete(root);
+    ast_delete(func);
+    ast_delete(fval);
+    return false;
+}
+
 typedef struct {
     MEM_VECTOR_MAKE(ast_value*, p);
 } paramlist_t;
@@ -2226,8 +2730,16 @@ static ast_value *parse_parameter_list(parser_t *parser, ast_value *var)
             param = parse_typename(parser, NULL);
             if (!param)
                 goto on_error;
-            if (!paramlist_t_p_add(&params, param))
+            if (!paramlist_t_p_add(&params, param)) {
+                ast_delete(param);
                 goto on_error;
+            }
+            if (param->expression.vtype >= TYPE_VARIANT) {
+                char typename[1024];
+                ast_type_to_string((ast_expression*)param, typename, sizeof(typename));
+                parseerror(parser, "type not supported as part of a parameter list: %s", typename);
+                goto on_error;
+            }
         }
     }
 
@@ -2259,6 +2771,60 @@ on_error:
     return NULL;
 }
 
+static ast_value *parse_arraysize(parser_t *parser, ast_value *var)
+{
+    ast_expression *cexp;
+    ast_value      *cval, *tmp;
+    lex_ctx ctx;
+
+    ctx = parser_ctx(parser);
+
+    if (!parser_next(parser)) {
+        ast_delete(var);
+        parseerror(parser, "expected array-size");
+        return NULL;
+    }
+
+    cexp = parse_expression_leave(parser, true);
+
+    if (!cexp || !ast_istype(cexp, ast_value)) {
+        if (cexp)
+            ast_unref(cexp);
+        ast_delete(var);
+        parseerror(parser, "expected array-size as constant positive integer");
+        return NULL;
+    }
+    cval = (ast_value*)cexp;
+
+    tmp = ast_value_new(ctx, "<type[]>", TYPE_ARRAY);
+    tmp->expression.next = (ast_expression*)var;
+    var = tmp;
+
+    if (cval->expression.vtype == TYPE_INTEGER)
+        tmp->expression.count = cval->constval.vint;
+    else if (cval->expression.vtype == TYPE_FLOAT)
+        tmp->expression.count = cval->constval.vfloat;
+    else {
+        ast_unref(cexp);
+        ast_delete(var);
+        parseerror(parser, "array-size must be a positive integer constant");
+        return NULL;
+    }
+    ast_unref(cexp);
+
+    if (parser->tok != ']') {
+        ast_delete(var);
+        parseerror(parser, "expected ']' after array-size");
+        return NULL;
+    }
+    if (!parser_next(parser)) {
+        ast_delete(var);
+        parseerror(parser, "error after parsing array size");
+        return NULL;
+    }
+    return var;
+}
+
 /* Parse a complete typename.
  * for single-variables (ie. function parameters or typedefs) storebase should be NULL
  * but when parsing variables separated by comma
@@ -2279,7 +2845,8 @@ static ast_value *parse_typename(parser_t *parser, ast_value **storebase)
     lex_ctx    ctx;
 
     const char *name = NULL;
-    bool        isfield = false;
+    bool        isfield  = false;
+    bool        wasarray = false;
 
     ctx = parser_ctx(parser);
 
@@ -2311,29 +2878,25 @@ static ast_value *parse_typename(parser_t *parser, ast_value **storebase)
         return NULL;
     }
 
-    /* an opening paren now starts the parameter-list of a function */
+    /* an opening paren now starts the parameter-list of a function
+     * this is where original-QC has parameter lists.
+     * We allow a single parameter list here.
+     * Much like fteqcc we don't allow `float()() x`
+     */
     if (parser->tok == '(') {
         var = parse_parameter_list(parser, var);
         if (!var)
             return NULL;
     }
-    /* This is the point where we can turn it into a field */
-    if (isfield) {
-        /* turn it into a field if desired */
-        tmp = ast_value_new(ctx, "<type:f>", TYPE_FIELD);
-        tmp->expression.next = (ast_expression*)var;
-        var = tmp;
-    }
-
-    while (parser->tok == '(') {
-        var = parse_parameter_list(parser, var);
-        if (!var)
-            return NULL;
-    }
 
     /* store the base if requested */
     if (storebase) {
         *storebase = ast_value_copy(var);
+        if (isfield) {
+            tmp = ast_value_new(ctx, "<type:f>", TYPE_FIELD);
+            tmp->expression.next = (ast_expression*)*storebase;
+            *storebase = tmp;
+        }
     }
 
     /* there may be a name now */
@@ -2341,19 +2904,39 @@ static ast_value *parse_typename(parser_t *parser, ast_value **storebase)
         name = util_strdup(parser_tokval(parser));
         /* parse on */
         if (!parser_next(parser)) {
+            ast_delete(var);
             parseerror(parser, "error after variable or field declaration");
             return NULL;
         }
     }
 
+    /* now this may be an array */
+    if (parser->tok == '[') {
+        wasarray = true;
+        var = parse_arraysize(parser, var);
+        if (!var)
+            return NULL;
+    }
+
+    /* This is the point where we can turn it into a field */
+    if (isfield) {
+        /* turn it into a field if desired */
+        tmp = ast_value_new(ctx, "<type:f>", TYPE_FIELD);
+        tmp->expression.next = (ast_expression*)var;
+        var = tmp;
+    }
+
     /* now there may be function parens again */
     if (parser->tok == '(' && opts_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");
     while (parser->tok == '(') {
         var = parse_parameter_list(parser, var);
         if (!var) {
             if (name)
                 mem_d((void*)name);
+            ast_delete(var);
             return NULL;
         }
     }
@@ -2385,6 +2968,7 @@ static bool parse_variable(parser_t *parser, ast_block *localblock, bool nofield
     bool      isparam   = false;
     bool      isvector  = false;
     bool      cleanvar  = true;
+    bool      wasarray  = false;
 
     varentry_t varent, ve[3];
 
@@ -2401,8 +2985,32 @@ static bool parse_variable(parser_t *parser, ast_block *localblock, bool nofield
 
     while (true) {
         proto = NULL;
+        wasarray = false;
 
         /* Part 0: finish the type */
+        if (parser->tok == '(') {
+            if (opts_standard == COMPILER_QCC)
+                parseerror(parser, "C-style function syntax is not allowed in -std=qcc");
+            var = parse_parameter_list(parser, var);
+            if (!var) {
+                retval = false;
+                goto cleanup;
+            }
+        }
+        /* we only allow 1-dimensional arrays */
+        if (parser->tok == '[') {
+            wasarray = true;
+            var = parse_arraysize(parser, var);
+            if (!var) {
+                retval = false;
+                goto cleanup;
+            }
+        }
+        if (parser->tok == '(' && wasarray) {
+            parseerror(parser, "arrays as part of a return type is not supported");
+            /* we'll still parse the type completely for now */
+        }
+        /* for functions returning functions */
         while (parser->tok == '(') {
             if (opts_standard == COMPILER_QCC)
                 parseerror(parser, "C-style function syntax is not allowed in -std=qcc");
@@ -2622,6 +3230,46 @@ static bool parse_variable(parser_t *parser, ast_block *localblock, bool nofield
             ve[0].var  = ve[1].var  = ve[2].var  = NULL;
             cleanvar = false;
         }
+        /* Part 2.2
+         * deal with arrays
+         */
+        if (var->expression.vtype == TYPE_ARRAY) {
+            char name[1024];
+            snprintf(name, sizeof(name), "%s##SET", var->name);
+            if (!parser_create_array_setter(parser, var, name))
+                goto cleanup;
+            snprintf(name, sizeof(name), "%s##GET", var->name);
+            if (!parser_create_array_getter(parser, var, var->expression.next, name))
+                goto cleanup;
+        }
+        else if (!localblock && !nofields &&
+                 var->expression.vtype == TYPE_FIELD &&
+                 var->expression.next->expression.vtype == TYPE_ARRAY)
+        {
+            char name[1024];
+            ast_expression *telem;
+            ast_value      *tfield;
+            ast_value      *array = (ast_value*)var->expression.next;
+
+            if (!ast_istype(var->expression.next, ast_value)) {
+                parseerror(parser, "internal error: field element type must be an ast_value");
+                goto cleanup;
+            }
+
+            snprintf(name, sizeof(name), "%s##SETF", var->name);
+            if (!parser_create_array_field_setter(parser, array, name))
+                goto cleanup;
+
+            telem = ast_type_copy(ast_ctx(var), array->expression.next);
+            tfield = ast_value_new(ast_ctx(var), "<.type>", TYPE_FIELD);
+            tfield->expression.next = telem;
+            snprintf(name, sizeof(name), "%s##GETFP", var->name);
+            if (!parser_create_array_getter(parser, array, (ast_expression*)tfield, name)) {
+                ast_delete(tfield);
+                goto cleanup;
+            }
+            ast_delete(tfield);
+        }
 
 skipvar:
         if (parser->tok == ';') {
@@ -2729,17 +3377,43 @@ skipvar:
             if (!cexp)
                 break;
 
-            cval = (ast_value*)cexp;
-            if (!ast_istype(cval, ast_value) || !cval->isconst)
-                parseerror(parser, "cannot initialize a global constant variable with a non-constant expression");
-            else
-            {
-                var->isconst = true;
-                if (cval->expression.vtype == TYPE_STRING)
-                    var->constval.vstring = parser_strdup(cval->constval.vstring);
+            if (!localblock) {
+                cval = (ast_value*)cexp;
+                if (!ast_istype(cval, ast_value) || !cval->isconst)
+                    parseerror(parser, "cannot initialize a global constant variable with a non-constant expression");
                 else
-                    memcpy(&var->constval, &cval->constval, sizeof(var->constval));
-                ast_unref(cval);
+                {
+                    var->isconst = true;
+                    if (cval->expression.vtype == TYPE_STRING)
+                        var->constval.vstring = parser_strdup(cval->constval.vstring);
+                    else
+                        memcpy(&var->constval, &cval->constval, sizeof(var->constval));
+                    ast_unref(cval);
+                }
+            } else {
+                shunt sy;
+                MEM_VECTOR_INIT(&sy, out);
+                MEM_VECTOR_INIT(&sy, ops);
+                if (!shunt_out_add(&sy, syexp(ast_ctx(var), (ast_expression*)var)) ||
+                    !shunt_out_add(&sy, syexp(ast_ctx(cexp), (ast_expression*)cexp)) ||
+                    !shunt_ops_add(&sy, syop(ast_ctx(var), parser->assign_op)))
+                {
+                    parseerror(parser, "internal error: failed to prepare initializer");
+                    ast_unref(cexp);
+                }
+                else if (!parser_sy_pop(parser, &sy))
+                    ast_unref(cexp);
+                else {
+                    if (sy.out_count != 1 && sy.ops_count != 0)
+                        parseerror(parser, "internal error: leaked operands");
+                    else if (!ast_block_exprs_add(localblock, (ast_expression*)sy.out[0].out)) {
+                        parseerror(parser, "failed to create intializing expression");
+                        ast_unref(sy.out[0].out);
+                        ast_unref(cexp);
+                    }
+                }
+                MEM_VECTOR_CLEAR(&sy, out);
+                MEM_VECTOR_CLEAR(&sy, ops);
             }
         }
 
@@ -2834,11 +3508,24 @@ static parser_t *parser;
 
 bool parser_init()
 {
+    size_t i;
     parser = (parser_t*)mem_a(sizeof(parser_t));
     if (!parser)
         return false;
 
     memset(parser, 0, sizeof(*parser));
+
+    for (i = 0; i < operator_count; ++i) {
+        if (operators[i].id == opid1('=')) {
+            parser->assign_op = operators+i;
+            break;
+        }
+    }
+    if (!parser->assign_op) {
+        printf("internal error: initializing parser: failed to find assign operator\n");
+        mem_d(parser);
+        return false;
+    }
     return true;
 }
 
@@ -3066,6 +3753,61 @@ bool parser_finish(const char *output)
                 return false;
             }
         }
+        for (i = 0; i < parser->globals_count; ++i) {
+            ast_value *asvalue;
+            if (!ast_istype(parser->globals[i].var, ast_value))
+                continue;
+            asvalue = (ast_value*)(parser->globals[i].var);
+            if (asvalue->setter) {
+                if (!ast_global_codegen(asvalue->setter, ir, false) ||
+                    !ast_function_codegen(asvalue->setter->constval.vfunc, ir) ||
+                    !ir_function_finalize(asvalue->setter->constval.vfunc->ir_func))
+                {
+                    printf("failed to generate setter for %s\n", parser->globals[i].name);
+                    ir_builder_delete(ir);
+                    return false;
+                }
+            }
+            if (asvalue->getter) {
+                if (!ast_global_codegen(asvalue->getter, ir, false) ||
+                    !ast_function_codegen(asvalue->getter->constval.vfunc, ir) ||
+                    !ir_function_finalize(asvalue->getter->constval.vfunc->ir_func))
+                {
+                    printf("failed to generate getter for %s\n", parser->globals[i].name);
+                    ir_builder_delete(ir);
+                    return false;
+                }
+            }
+        }
+        for (i = 0; i < parser->fields_count; ++i) {
+            ast_value *asvalue;
+            asvalue = (ast_value*)(parser->fields[i].var->expression.next);
+
+            if (!ast_istype((ast_expression*)asvalue, ast_value))
+                continue;
+            if (asvalue->expression.vtype != TYPE_ARRAY)
+                continue;
+            if (asvalue->setter) {
+                if (!ast_global_codegen(asvalue->setter, ir, false) ||
+                    !ast_function_codegen(asvalue->setter->constval.vfunc, ir) ||
+                    !ir_function_finalize(asvalue->setter->constval.vfunc->ir_func))
+                {
+                    printf("failed to generate setter for %s\n", parser->fields[i].name);
+                    ir_builder_delete(ir);
+                    return false;
+                }
+            }
+            if (asvalue->getter) {
+                if (!ast_global_codegen(asvalue->getter, ir, false) ||
+                    !ast_function_codegen(asvalue->getter->constval.vfunc, ir) ||
+                    !ir_function_finalize(asvalue->getter->constval.vfunc->ir_func))
+                {
+                    printf("failed to generate getter for %s\n", parser->fields[i].name);
+                    ir_builder_delete(ir);
+                    return false;
+                }
+            }
+        }
         for (i = 0; i < parser->functions_count; ++i) {
             if (!ast_function_codegen(parser->functions[i], ir)) {
                 printf("failed to generate function %s\n", parser->functions[i]->name);