]> de.git.xonotic.org Git - xonotic/gmqcc.git/blobdiff - parser.c
added --nocolor and fixed bug
[xonotic/gmqcc.git] / parser.c
index ab1abeb77ef4861e2eae0e537a11c119bd76e3c1..59d4a33e772a4668b4ee753d151be8b2f527eea5 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
@@ -48,7 +51,7 @@ MEM_VEC_FUNCTIONS(parser_t, varentry_t, locals)
 MEM_VEC_FUNCTIONS(parser_t, ast_function*, functions)
 
 static bool GMQCC_WARN parser_pop_local(parser_t *parser);
-static bool parse_variable(parser_t *parser, ast_block *localblock);
+static bool parse_variable(parser_t *parser, ast_block *localblock, bool nofields);
 static ast_block* parse_block(parser_t *parser, bool warnreturn);
 static bool parse_block_into(parser_t *parser, ast_block *block, bool warnreturn);
 static ast_expression* parse_statement_or_block(parser_t *parser);
@@ -62,7 +65,7 @@ static void parseerror(parser_t *parser, const char *fmt, ...)
        parser->errors++;
 
        va_start(ap, fmt);
-    vprintmsg(LVL_ERROR, parser->lex->tok.ctx.file, parser->lex->tok.ctx.line, "parse error", fmt, ap);
+    con_vprintmsg(LVL_ERROR, parser->lex->tok.ctx.file, parser->lex->tok.ctx.line, "parse error", fmt, ap);
        va_end(ap);
 }
 
@@ -81,7 +84,7 @@ static bool GMQCC_WARN parsewarning(parser_t *parser, int warntype, const char *
        }
 
        va_start(ap, fmt);
-    vprintmsg(lvl, parser->lex->tok.ctx.file, parser->lex->tok.ctx.line, "warning", fmt, ap);
+    con_vprintmsg(lvl, parser->lex->tok.ctx.file, parser->lex->tok.ctx.line, "warning", fmt, ap);
        va_end(ap);
 
        return opts_werror;
@@ -99,7 +102,7 @@ static bool GMQCC_WARN genwarning(lex_ctx ctx, int warntype, const char *fmt, ..
            lvl = LVL_ERROR;
 
        va_start(ap, fmt);
-    vprintmsg(lvl, ctx.file, ctx.line, "warning", fmt, ap);
+    con_vprintmsg(lvl, ctx.file, ctx.line, "warning", fmt, ap);
        va_end(ap);
 
        return opts_werror;
@@ -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;
@@ -397,7 +445,7 @@ static bool parser_sy_pop(parser_t *parser, shunt *sy)
     op = &operators[sy->ops[sy->ops_count-1].etype - 1];
     ctx = sy->ops[sy->ops_count-1].ctx;
 
-    DEBUGSHUNTDO(printf("apply %s\n", op->op));
+    DEBUGSHUNTDO(con_out("apply %s\n", op->op));
 
     if (sy->out_count < op->operands) {
         parseerror(parser, "internal error: not enough operands: %i (operator %s (%i))", sy->out_count,
@@ -453,6 +501,32 @@ 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\n"
+                                        " -> this is an extension from -std=gmqcc");
+                }
+            }
+            break;
+
         case opid1(','):
             if (blocks[0]) {
                 if (!ast_block_exprs_add(blocks[0], exprs[1]))
@@ -701,7 +775,7 @@ static bool parser_sy_pop(parser_t *parser, shunt *sy)
                 return false;
             }
             if (opts_standard == COMPILER_GMQCC)
-                printf("TODO: early out logic\n");
+                con_out("TODO: early out logic\n");
             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))));
@@ -747,10 +821,15 @@ static bool parser_sy_pop(parser_t *parser, shunt *sy)
         case opid1('='):
             if (ast_istype(exprs[0], ast_entfield)) {
                 ast_expression *field = ((ast_entfield*)exprs[0])->field;
-                assignop = type_storep_instr[exprs[0]->expression.vtype];
+                if (OPTS_FLAG(ADJUST_VECTOR_FIELDS) &&
+                    exprs[0]->expression.vtype == TYPE_FIELD &&
+                    exprs[0]->expression.next->expression.vtype == TYPE_VECTOR)
+                {
+                    assignop = type_storep_instr[TYPE_VECTOR];
+                }
+                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 &&
@@ -769,10 +848,22 @@ static bool parser_sy_pop(parser_t *parser, shunt *sy)
             }
             else
             {
-                assignop = type_store_instr[exprs[0]->expression.vtype];
-                if (!ast_compare_type(exprs[0], exprs[1])) {
-                    char ty1[1024];
-                    char ty2[1024];
+                if (OPTS_FLAG(ADJUST_VECTOR_FIELDS) &&
+                    exprs[0]->expression.vtype == TYPE_FIELD &&
+                    exprs[0]->expression.next->expression.vtype == TYPE_VECTOR)
+                {
+                    assignop = type_store_instr[TYPE_VECTOR];
+                }
+                else {
+                    assignop = type_store_instr[exprs[0]->expression.vtype];
+                }
+
+                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 &&
@@ -831,7 +922,7 @@ static bool parser_sy_pop(parser_t *parser, shunt *sy)
         return false;
     }
 
-    DEBUGSHUNTDO(printf("applied %s\n", op->op));
+    DEBUGSHUNTDO(con_out("applied %s\n", op->op));
     sy->out[sy->out_count++] = syexp(ctx, out);
     return true;
 }
@@ -963,15 +1054,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;
     }
@@ -1058,7 +1159,7 @@ static ast_expression* parse_expression_leave(parser_t *parser, bool stopatcomma
                 parseerror(parser, "out of memory");
                 goto onerr;
             }
-            DEBUGSHUNTDO(printf("push %s\n", parser_tokval(parser)));
+            DEBUGSHUNTDO(con_out("push %s\n", parser_tokval(parser)));
         }
         else if (parser->tok == TOKEN_FLOATCONST) {
             ast_value *val;
@@ -1074,7 +1175,7 @@ static ast_expression* parse_expression_leave(parser_t *parser, bool stopatcomma
                 parseerror(parser, "out of memory");
                 goto onerr;
             }
-            DEBUGSHUNTDO(printf("push %g\n", parser_token(parser)->constval.f));
+            DEBUGSHUNTDO(con_out("push %g\n", parser_token(parser)->constval.f));
         }
         else if (parser->tok == TOKEN_INTCONST) {
             ast_value *val;
@@ -1090,7 +1191,7 @@ static ast_expression* parse_expression_leave(parser_t *parser, bool stopatcomma
                 parseerror(parser, "out of memory");
                 goto onerr;
             }
-            DEBUGSHUNTDO(printf("push %i\n", parser_token(parser)->constval.i));
+            DEBUGSHUNTDO(con_out("push %i\n", parser_token(parser)->constval.i));
         }
         else if (parser->tok == TOKEN_STRINGCONST) {
             ast_value *val;
@@ -1106,7 +1207,7 @@ static ast_expression* parse_expression_leave(parser_t *parser, bool stopatcomma
                 parseerror(parser, "out of memory");
                 goto onerr;
             }
-            DEBUGSHUNTDO(printf("push string\n"));
+            DEBUGSHUNTDO(con_out("push string\n"));
         }
         else if (parser->tok == TOKEN_VECTORCONST) {
             ast_value *val;
@@ -1122,7 +1223,7 @@ static ast_expression* parse_expression_leave(parser_t *parser, bool stopatcomma
                 parseerror(parser, "out of memory");
                 goto onerr;
             }
-            DEBUGSHUNTDO(printf("push '%g %g %g'\n",
+            DEBUGSHUNTDO(con_out("push '%g %g %g'\n",
                                 parser_token(parser)->constval.v.x,
                                 parser_token(parser)->constval.v.y,
                                 parser_token(parser)->constval.v.z));
@@ -1131,9 +1232,13 @@ 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"));
+                DEBUGSHUNTDO(con_out("do[op] )\n"));
                 --parens;
                 if (parens < 0)
                     break;
@@ -1142,7 +1247,7 @@ static ast_expression* parse_expression_leave(parser_t *parser, bool stopatcomma
                 if (!parser_close_paren(parser, &sy, false))
                     goto onerr;
             } else {
-                DEBUGSHUNTDO(printf("do[nop] )\n"));
+                DEBUGSHUNTDO(con_out("do[nop] )\n"));
                 --parens;
                 if (parens < 0)
                     break;
@@ -1152,6 +1257,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");
@@ -1223,24 +1338,36 @@ static ast_expression* parse_expression_leave(parser_t *parser, bool stopatcomma
 
             if (op->id == opid1('(')) {
                 if (wantop) {
-                    DEBUGSHUNTDO(printf("push [op] (\n"));
+                    DEBUGSHUNTDO(con_out("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"));
+                    DEBUGSHUNTDO(con_out("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));
+                DEBUGSHUNTDO(con_out("push operator %s\n", op->op));
                 if (!shunt_ops_add(&sy, syop(parser_ctx(parser), op)))
                     goto onerr;
                 wantop = false;
@@ -1249,7 +1376,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;
         }
     }
@@ -1267,7 +1394,7 @@ static ast_expression* parse_expression_leave(parser_t *parser, bool stopatcomma
         expr = sy.out[0].out;
     MEM_VECTOR_CLEAR(&sy, out);
     MEM_VECTOR_CLEAR(&sy, ops);
-    DEBUGSHUNTDO(printf("shunt done\n"));
+    DEBUGSHUNTDO(con_out("shunt done\n"));
     return expr;
 
 onerr:
@@ -1293,11 +1420,23 @@ static bool parse_if(parser_t *parser, ast_block *block, ast_expression **out)
 {
     ast_ifthen *ifthen;
     ast_expression *cond, *ontrue, *onfalse = NULL;
+    bool ifnot = false;
 
     lex_ctx ctx = parser_ctx(parser);
 
-    /* skip the 'if' and check for opening paren */
-    if (!parser_next(parser) || parser->tok != '(') {
+    /* skip the 'if', parse an optional 'not' and check for an opening paren */
+    if (!parser_next(parser)) {
+        parseerror(parser, "expected condition or 'not'");
+        return false;
+    }
+    if (parser->tok == TOKEN_KEYWORD && !strcmp(parser_tokval(parser), "not")) {
+        ifnot = true;
+        if (!parser_next(parser)) {
+            parseerror(parser, "expected condition in parenthesis");
+            return false;
+        }
+    }
+    if (parser->tok != '(') {
         parseerror(parser, "expected 'if' condition in parenthesis");
         return false;
     }
@@ -1344,7 +1483,10 @@ static bool parse_if(parser_t *parser, ast_block *block, ast_expression **out)
         }
     }
 
-    ifthen = ast_ifthen_new(ctx, cond, ontrue, onfalse);
+    if (ifnot)
+        ifthen = ast_ifthen_new(ctx, cond, onfalse, ontrue);
+    else
+        ifthen = ast_ifthen_new(ctx, cond, ontrue, onfalse);
     *out = (ast_expression*)ifthen;
     return true;
 }
@@ -1498,7 +1640,7 @@ static bool parse_for(parser_t *parser, ast_block *block, ast_expression **out)
 
         parseerror(parser, "TODO: assignment of new variables to be non-const");
         goto onerr;
-        if (!parse_variable(parser, block))
+        if (!parse_variable(parser, block, true))
             goto onerr;
     }
     else if (parser->tok != ';')
@@ -1583,7 +1725,7 @@ onerr:
 
 static bool parse_statement(parser_t *parser, ast_block *block, ast_expression **out)
 {
-    if (parser->tok == TOKEN_TYPENAME)
+    if (parser->tok == TOKEN_TYPENAME || parser->tok == '.')
     {
         /* local variable */
         if (!block) {
@@ -1594,7 +1736,7 @@ static bool parse_statement(parser_t *parser, ast_block *block, ast_expression *
             if (parsewarning(parser, WARN_EXTENSIONS, "missing 'local' keyword when declaring a local variable"))
                 return false;
         }
-        if (!parse_variable(parser, block))
+        if (!parse_variable(parser, block, false))
             return false;
         *out = NULL;
         return true;
@@ -1611,7 +1753,7 @@ static bool parse_statement(parser_t *parser, ast_block *block, ast_expression *
                 parseerror(parser, "expected variable declaration");
                 return false;
             }
-            if (!parse_variable(parser, block))
+            if (!parse_variable(parser, block, true))
                 return false;
             *out = NULL;
             return true;
@@ -2153,6 +2295,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;
@@ -2212,8 +2746,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;
+            }
         }
     }
 
@@ -2245,6 +2787,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
@@ -2265,7 +2861,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);
 
@@ -2297,29 +2894,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 */
@@ -2327,17 +2920,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;
         }
     }
@@ -2356,7 +2971,7 @@ static ast_value *parse_typename(parser_t *parser, ast_value **storebase)
     return var;
 }
 
-static bool parse_variable(parser_t *parser, ast_block *localblock)
+static bool parse_variable(parser_t *parser, ast_block *localblock, bool nofields)
 {
     ast_value *var;
     ast_value *proto;
@@ -2369,6 +2984,7 @@ static bool parse_variable(parser_t *parser, ast_block *localblock)
     bool      isparam   = false;
     bool      isvector  = false;
     bool      cleanvar  = true;
+    bool      wasarray  = false;
 
     varentry_t varent, ve[3];
 
@@ -2385,9 +3001,35 @@ static bool parse_variable(parser_t *parser, ast_block *localblock)
 
     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");
             var = parse_parameter_list(parser, var);
             if (!var) {
                 retval = false;
@@ -2421,7 +3063,7 @@ static bool parse_variable(parser_t *parser, ast_block *localblock)
                 }
             }
 
-            if (var->expression.vtype == TYPE_FIELD)
+            if (!nofields && var->expression.vtype == TYPE_FIELD)
             {
                 /* deal with field declarations */
                 old = parser_find_field(parser, var->name);
@@ -2548,7 +3190,7 @@ static bool parse_variable(parser_t *parser, ast_block *localblock)
 
             if (!localblock) {
                 /* deal with global variables, fields, functions */
-                if (var->expression.vtype == TYPE_FIELD) {
+                if (!nofields && var->expression.vtype == TYPE_FIELD) {
                     if (!(retval = parser_t_fields_add(parser, varent)))
                         goto cleanup;
                     if (isvector) {
@@ -2604,6 +3246,46 @@ static bool parse_variable(parser_t *parser, ast_block *localblock)
             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 == ';') {
@@ -2618,7 +3300,7 @@ skipvar:
         if (parser->tok == ',')
             goto another;
 
-        if (!var || (!localblock && basetype->expression.vtype == TYPE_FIELD)) {
+        if (!var || (!localblock && !nofields && basetype->expression.vtype == TYPE_FIELD)) {
             parseerror(parser, "missing comma or semicolon while parsing variables");
             break;
         }
@@ -2643,6 +3325,9 @@ skipvar:
                 break;
             }
         }
+        else if (opts_standard == COMPILER_QCC) {
+            parseerror(parser, "expected '=' before function body in this standard");
+        }
 
         if (parser->tok == '#') {
             ast_function *func;
@@ -2708,17 +3393,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);
             }
         }
 
@@ -2780,11 +3491,18 @@ static bool parser_global_statement(parser_t *parser)
 {
     if (parser->tok == TOKEN_TYPENAME || parser->tok == '.')
     {
-        return parse_variable(parser, NULL);
+        return parse_variable(parser, NULL, false);
     }
     else if (parser->tok == TOKEN_KEYWORD)
     {
         /* handle 'var' and 'const' */
+        if (!strcmp(parser_tokval(parser), "var")) {
+            if (!parser_next(parser)) {
+                parseerror(parser, "expected variable declaration after 'var'");
+                return false;
+            }
+            return parse_variable(parser, NULL, true);
+        }
         return false;
     }
     else if (parser->tok == '$')
@@ -2806,22 +3524,29 @@ 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));
-    return true;
-}
 
-bool parser_compile(const char *filename)
-{
-    parser->lex = lex_open(filename);
-    if (!parser->lex) {
-        printf("failed to open file \"%s\"\n", filename);
+    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;
+}
 
+bool parser_compile()
+{
     /* initial lexer/parser state */
     parser->lex->flags.noops = true;
 
@@ -2852,6 +3577,26 @@ bool parser_compile(const char *filename)
     return !parser->errors;
 }
 
+bool parser_compile_file(const char *filename)
+{
+    parser->lex = lex_open(filename);
+    if (!parser->lex) {
+        con_out("failed to open file \"%s\"\n", filename);
+        return false;
+    }
+    return parser_compile();
+}
+
+bool parser_compile_string(const char *name, const char *str)
+{
+    parser->lex = lex_open_string(str, strlen(str), name);
+    if (!parser->lex) {
+        con_out("failed to create lexer for string \"%s\"\n", name);
+        return false;
+    }
+    return parser_compile();
+}
+
 void parser_cleanup()
 {
     size_t i;
@@ -2968,7 +3713,7 @@ bool parser_finish(const char *output)
     {
         ir = ir_builder_new("gmqcc_out");
         if (!ir) {
-            printf("failed to allocate builder\n");
+            con_out("failed to allocate builder\n");
             return false;
         }
 
@@ -2980,8 +3725,8 @@ bool parser_finish(const char *output)
             field = (ast_value*)parser->fields[i].var;
             isconst = field->isconst;
             field->isconst = false;
-            if (!ast_global_codegen((ast_value*)field, ir)) {
-                printf("failed to generate field %s\n", field->name);
+            if (!ast_global_codegen((ast_value*)field, ir, true)) {
+                con_out("failed to generate field %s\n", field->name);
                 ir_builder_delete(ir);
                 return false;
             }
@@ -3011,41 +3756,96 @@ bool parser_finish(const char *output)
                                                    "unused global: `%s`", asvalue->name);
                 }
             }
-            if (!ast_global_codegen(asvalue, ir)) {
-                printf("failed to generate global %s\n", parser->globals[i].name);
+            if (!ast_global_codegen(asvalue, ir, false)) {
+                con_out("failed to generate global %s\n", parser->globals[i].name);
                 ir_builder_delete(ir);
                 return false;
             }
         }
         for (i = 0; i < parser->imm_float_count; ++i) {
-            if (!ast_global_codegen(parser->imm_float[i], ir)) {
-                printf("failed to generate global %s\n", parser->imm_float[i]->name);
+            if (!ast_global_codegen(parser->imm_float[i], ir, false)) {
+                con_out("failed to generate global %s\n", parser->imm_float[i]->name);
                 ir_builder_delete(ir);
                 return false;
             }
         }
         for (i = 0; i < parser->imm_string_count; ++i) {
-            if (!ast_global_codegen(parser->imm_string[i], ir)) {
-                printf("failed to generate global %s\n", parser->imm_string[i]->name);
+            if (!ast_global_codegen(parser->imm_string[i], ir, false)) {
+                con_out("failed to generate global %s\n", parser->imm_string[i]->name);
                 ir_builder_delete(ir);
                 return false;
             }
         }
         for (i = 0; i < parser->imm_vector_count; ++i) {
-            if (!ast_global_codegen(parser->imm_vector[i], ir)) {
-                printf("failed to generate global %s\n", parser->imm_vector[i]->name);
+            if (!ast_global_codegen(parser->imm_vector[i], ir, false)) {
+                con_out("failed to generate global %s\n", parser->imm_vector[i]->name);
                 ir_builder_delete(ir);
                 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);
+                con_out("failed to generate function %s\n", parser->functions[i]->name);
                 ir_builder_delete(ir);
                 return false;
             }
             if (!ir_function_finalize(parser->functions[i]->ir_func)) {
-                printf("failed to finalize function %s\n", parser->functions[i]->name);
+                con_out("failed to finalize function %s\n", parser->functions[i]->name);
                 ir_builder_delete(ir);
                 return false;
             }
@@ -3053,12 +3853,12 @@ bool parser_finish(const char *output)
 
         if (retval) {
             if (opts_dump)
-                ir_builder_dump(ir, printf);
+                ir_builder_dump(ir, con_out);
 
             generate_checksum(parser);
 
             if (!ir_builder_generate(ir, output)) {
-                printf("*** failed to generate output file\n");
+                con_out("*** failed to generate output file\n");
                 ir_builder_delete(ir);
                 return false;
             }
@@ -3068,6 +3868,6 @@ bool parser_finish(const char *output)
         return retval;
     }
 
-    printf("*** there were compile errors\n");
+    con_out("*** there were compile errors\n");
     return false;
 }