]> git.xonotic.org Git - xonotic/gmqcc.git/commitdiff
Merging in new-syntax
authorWolfgang (Blub) Bumiller <blub@speed.at>
Wed, 14 Nov 2012 20:39:16 +0000 (21:39 +0100)
committerWolfgang (Blub) Bumiller <blub@speed.at>
Wed, 14 Nov 2012 20:39:16 +0000 (21:39 +0100)
1  2 
Makefile
exec.c
gmqcc.h
lexer.c
lexer.h
main.c
parser.c

diff --combined Makefile
index c800ecaf6200af9d98b8002999e290736522e713,a44b6ea6322af4c92148a32a813ed2610154f0c9..ce0bc8697702db02caf715af8c13360dfa3dd5d1
+++ b/Makefile
@@@ -1,5 -1,5 +1,5 @@@
  CC     ?= clang
- CFLAGS += -Wall -I. -fomit-frame-pointer -fno-stack-protector -O3
+ CFLAGS += -Wall -I. -fomit-frame-pointer -fno-stack-protector
  #turn on tons of warnings if clang is present
  ifeq ($(CC), clang)
        CFLAGS +=                  \
@@@ -24,12 -24,11 +24,12 @@@ OBJ     = 
            code.o      \
            ast.o       \
            ir.o        \
 -          error.o
 +          error.o     \
 +          con.o
  OBJ_A = test/ast-test.o
  OBJ_I = test/ir-test.o
  OBJ_C = main.o lexer.o parser.o
--OBJ_X = exec-standalone.o util.o
++OBJ_X = exec-standalone.o util.o con.o
  
  #default is compiler only
  default: gmqcc
diff --combined exec.c
index ee0fb1b38d149936bac8809779bc6c041b9ebf72,71b8eba2c1486f5ffe40f5f465362826333822b1..872475befa00ac994caef9d899db9fb6172db755
--- 1/exec.c
--- 2/exec.c
+++ b/exec.c
@@@ -50,6 -50,8 +50,8 @@@ MEM_VEC_FUN_RESIZE(qc_program,  size_t
  
  MEM_VEC_FUNCTIONS(qc_program,   prog_builtin, builtins)
  
+ MEM_VEC_FUNCTIONS(qc_program,   const char*, function_stack)
  static void loaderror(const char *fmt, ...)
  {
      int     err = errno;
@@@ -375,7 -377,7 +377,7 @@@ static void trace_print_global(qc_progr
              len = printf("$");
          else
              len = printf("%s ", name);
-         vtype = def->type;
+         vtype = def->type & DEF_TYPEMASK;
      }
      else
          len = printf("[@%u] ", glob);
@@@ -417,6 -419,12 +419,12 @@@ static void prog_print_statement(qc_pro
          printf("<illegal instruction %d>\n", st->opcode);
          return;
      }
+     if ((prog->xflags & VMXF_TRACE) && prog->function_stack_count) {
+         size_t i;
+         for (i = 0; i < prog->function_stack_count; ++i)
+             printf("->");
+         printf("%s:", prog->function_stack[prog->function_stack_count-1]);
+     }
      printf(" <> %-12s", asm_instr[st->opcode].m);
      if (st->opcode >= INSTR_IF &&
          st->opcode <= INSTR_IFNOT)
      else if (st->opcode >= INSTR_CALL0 &&
               st->opcode <= INSTR_CALL8)
      {
+         trace_print_global(prog, st->o1.u1, TYPE_FUNCTION);
          printf("\n");
      }
      else if (st->opcode == INSTR_GOTO)
@@@ -513,6 -522,10 +522,10 @@@ static qcint prog_enterfunction(qc_prog
      st.stmt     = prog->statement;
      st.function = func;
  
+     if (prog->xflags & VMXF_TRACE) {
+         (void)!qc_program_function_stack_add(prog, prog_getstring(prog, func->name));
+     }
  #ifdef QCVM_BACKUP_STRATEGY_CALLER_VARS
      if (prog->stack_count)
      {
@@@ -565,6 -578,11 +578,11 @@@ static qcint prog_leavefunction(qc_prog
  
      qc_exec_stack st = prog->stack[prog->stack_count-1];
  
+     if (prog->xflags & VMXF_TRACE) {
+         if (prog->function_stack_count)
+             prog->function_stack_count--;
+     }
  #ifdef QCVM_BACKUP_STRATEGY_CALLER_VARS
      if (prog->stack_count > 1) {
          prev  = prog->stack[prog->stack_count-2].function;
@@@ -1022,9 -1040,9 +1040,9 @@@ void prog_disasm_function(qc_program *p
  #endif
  
  while (1) {
 -      prog_section_function  *newf;
 -      qcany          *ed;
 -      qcany          *ptr;
 +    prog_section_function  *newf;
 +    qcany          *ed;
 +    qcany          *ptr;
  
      ++st;
  
              qcvmerror(prog, "Illegal instruction in %s\n", prog->filename);
              goto cleanup;
  
 -              case INSTR_DONE:
 -              case INSTR_RETURN:
 -                      /* TODO: add instruction count to function profile count */
 -                      GLOBAL(OFS_RETURN)->ivector[0] = OPA->ivector[0];
 -                      GLOBAL(OFS_RETURN)->ivector[1] = OPA->ivector[1];
 -                      GLOBAL(OFS_RETURN)->ivector[2] = OPA->ivector[2];
 +        case INSTR_DONE:
 +        case INSTR_RETURN:
 +            /* TODO: add instruction count to function profile count */
 +            GLOBAL(OFS_RETURN)->ivector[0] = OPA->ivector[0];
 +            GLOBAL(OFS_RETURN)->ivector[1] = OPA->ivector[1];
 +            GLOBAL(OFS_RETURN)->ivector[2] = OPA->ivector[2];
  
              st = prog->code + prog_leavefunction(prog);
              if (!prog->stack_count)
  
              break;
  
 -              case INSTR_MUL_F:
 -                      OPC->_float = OPA->_float * OPB->_float;
 -                      break;
 -              case INSTR_MUL_V:
 -                      OPC->_float = OPA->vector[0]*OPB->vector[0] +
 -                                    OPA->vector[1]*OPB->vector[1] +
 -                                    OPA->vector[2]*OPB->vector[2];
 -                      break;
 -              case INSTR_MUL_FV:
 -                      OPC->vector[0] = OPA->_float * OPB->vector[0];
 -                      OPC->vector[1] = OPA->_float * OPB->vector[1];
 -                      OPC->vector[2] = OPA->_float * OPB->vector[2];
 -                      break;
 -              case INSTR_MUL_VF:
 -                      OPC->vector[0] = OPB->_float * OPA->vector[0];
 -                      OPC->vector[1] = OPB->_float * OPA->vector[1];
 -                      OPC->vector[2] = OPB->_float * OPA->vector[2];
 -                      break;
 -              case INSTR_DIV_F:
 -                      if (OPB->_float != 0.0f)
 -                              OPC->_float = OPA->_float / OPB->_float;
 -                      else
 -                              OPC->_float = 0;
 -                      break;
 -
 -              case INSTR_ADD_F:
 -                      OPC->_float = OPA->_float + OPB->_float;
 -                      break;
 -              case INSTR_ADD_V:
 -                      OPC->vector[0] = OPA->vector[0] + OPB->vector[0];
 -                      OPC->vector[1] = OPA->vector[1] + OPB->vector[1];
 -                      OPC->vector[2] = OPA->vector[2] + OPB->vector[2];
 -                      break;
 -              case INSTR_SUB_F:
 -                      OPC->_float = OPA->_float - OPB->_float;
 -                      break;
 -              case INSTR_SUB_V:
 -                      OPC->vector[0] = OPA->vector[0] - OPB->vector[0];
 -                      OPC->vector[1] = OPA->vector[1] - OPB->vector[1];
 -                      OPC->vector[2] = OPA->vector[2] - OPB->vector[2];
 -                      break;
 -
 -              case INSTR_EQ_F:
 -                      OPC->_float = (OPA->_float == OPB->_float);
 -                      break;
 -              case INSTR_EQ_V:
 -                      OPC->_float = ((OPA->vector[0] == OPB->vector[0]) &&
 -                                         (OPA->vector[1] == OPB->vector[1]) &&
 -                                         (OPA->vector[2] == OPB->vector[2]) );
 -                      break;
 -              case INSTR_EQ_S:
 -                      OPC->_float = !strcmp(prog_getstring(prog, OPA->string),
 -                                            prog_getstring(prog, OPB->string));
 -                      break;
 -              case INSTR_EQ_E:
 -                      OPC->_float = (OPA->_int == OPB->_int);
 -                      break;
 -              case INSTR_EQ_FNC:
 -                      OPC->_float = (OPA->function == OPB->function);
 -                      break;
 -              case INSTR_NE_F:
 -                      OPC->_float = (OPA->_float != OPB->_float);
 -                      break;
 -              case INSTR_NE_V:
 -                      OPC->_float = ((OPA->vector[0] != OPB->vector[0]) ||
 -                                     (OPA->vector[1] != OPB->vector[1]) ||
 -                                     (OPA->vector[2] != OPB->vector[2]) );
 -                      break;
 -              case INSTR_NE_S:
 -                      OPC->_float = !!strcmp(prog_getstring(prog, OPA->string),
 -                                             prog_getstring(prog, OPB->string));
 -                      break;
 -              case INSTR_NE_E:
 -                      OPC->_float = (OPA->_int != OPB->_int);
 -                      break;
 -              case INSTR_NE_FNC:
 -                      OPC->_float = (OPA->function != OPB->function);
 -                      break;
 -
 -              case INSTR_LE:
 -                      OPC->_float = (OPA->_float <= OPB->_float);
 -                      break;
 -              case INSTR_GE:
 -                      OPC->_float = (OPA->_float >= OPB->_float);
 -                      break;
 -              case INSTR_LT:
 -                      OPC->_float = (OPA->_float < OPB->_float);
 -                      break;
 -              case INSTR_GT:
 -                      OPC->_float = (OPA->_float > OPB->_float);
 -                      break;
 -
 -              case INSTR_LOAD_F:
 -              case INSTR_LOAD_S:
 -              case INSTR_LOAD_FLD:
 -              case INSTR_LOAD_ENT:
 -              case INSTR_LOAD_FNC:
 -                      if (OPA->edict < 0 || OPA->edict >= prog->entities) {
 -                          qcvmerror(prog, "progs `%s` attempted to read an out of bounds entity", prog->filename);
 -                              goto cleanup;
 -                      }
 -                      if ((unsigned int)(OPB->_int) >= (unsigned int)(prog->entityfields)) {
 -                              qcvmerror(prog, "prog `%s` attempted to read an invalid field from entity (%i)",
 -                                        prog->filename,
 -                                        OPB->_int);
 -                              goto cleanup;
 -                      }
 -                      ed = prog_getedict(prog, OPA->edict);
 -                      OPC->_int = ((qcany*)( ((qcint*)ed) + OPB->_int ))->_int;
 -                      break;
 -              case INSTR_LOAD_V:
 -                      if (OPA->edict < 0 || OPA->edict >= prog->entities) {
 -                          qcvmerror(prog, "progs `%s` attempted to read an out of bounds entity", prog->filename);
 -                              goto cleanup;
 -                      }
 -                      if (OPB->_int < 0 || OPB->_int + 3 > prog->entityfields)
 -                      {
 -                              qcvmerror(prog, "prog `%s` attempted to read an invalid field from entity (%i)",
 -                                        prog->filename,
 -                                        OPB->_int + 2);
 -                              goto cleanup;
 -                      }
 -                      ed = prog_getedict(prog, OPA->edict);
 -                      OPC->ivector[0] = ((qcany*)( ((qcint*)ed) + OPB->_int ))->ivector[0];
 -                      OPC->ivector[1] = ((qcany*)( ((qcint*)ed) + OPB->_int ))->ivector[1];
 -                      OPC->ivector[2] = ((qcany*)( ((qcint*)ed) + OPB->_int ))->ivector[2];
 -                      break;
 -
 -              case INSTR_ADDRESS:
 -                      if (OPA->edict < 0 || OPA->edict >= prog->entities) {
 -                              qcvmerror(prog, "prog `%s` attempted to address an out of bounds entity %i", prog->filename, OPA->edict);
 -                              goto cleanup;
 -                      }
 -                      if ((unsigned int)(OPB->_int) >= (unsigned int)(prog->entityfields))
 -                      {
 -                              qcvmerror(prog, "prog `%s` attempted to read an invalid field from entity (%i)",
 -                                        prog->filename,
 -                                        OPB->_int);
 -                              goto cleanup;
 -                      }
 -
 -                      ed = prog_getedict(prog, OPA->edict);
 -                      OPC->_int = ((qcint*)ed) - prog->entitydata + OPB->_int;
 -                      break;
 -
 -              case INSTR_STORE_F:
 -              case INSTR_STORE_S:
 -              case INSTR_STORE_ENT:
 -              case INSTR_STORE_FLD:
 -              case INSTR_STORE_FNC:
 -                      OPB->_int = OPA->_int;
 -                      break;
 -              case INSTR_STORE_V:
 -                      OPB->ivector[0] = OPA->ivector[0];
 -                      OPB->ivector[1] = OPA->ivector[1];
 -                      OPB->ivector[2] = OPA->ivector[2];
 -                      break;
 -
 -              case INSTR_STOREP_F:
 -              case INSTR_STOREP_S:
 -              case INSTR_STOREP_ENT:
 -              case INSTR_STOREP_FLD:
 -              case INSTR_STOREP_FNC:
 -                      if (OPB->_int < 0 || OPB->_int >= prog->entitydata_count) {
 -                              qcvmerror(prog, "`%s` attempted to write to an out of bounds edict (%i)", prog->filename, OPB->_int);
 -                              goto cleanup;
 -                      }
 -                      if (OPB->_int < prog->entityfields && !prog->allowworldwrites)
 -                              qcvmerror(prog, "`%s` tried to assign to world.%s (field %i)\n",
 -                                        prog->filename,
 -                                        prog_getstring(prog, prog_entfield(prog, OPB->_int)->name),
 -                                        OPB->_int);
 -                      ptr = (qcany*)(prog->entitydata + OPB->_int);
 -                      ptr->_int = OPA->_int;
 -                      break;
 -              case INSTR_STOREP_V:
 -                      if (OPB->_int < 0 || OPB->_int + 2 >= prog->entitydata_count) {
 -                              qcvmerror(prog, "`%s` attempted to write to an out of bounds edict (%i)", prog->filename, OPB->_int);
 -                              goto cleanup;
 -                      }
 -                      if (OPB->_int < prog->entityfields && !prog->allowworldwrites)
 -                              qcvmerror(prog, "`%s` tried to assign to world.%s (field %i)\n",
 -                                        prog->filename,
 -                                        prog_getstring(prog, prog_entfield(prog, OPB->_int)->name),
 -                                        OPB->_int);
 -                      ptr = (qcany*)(prog->entitydata + OPB->_int);
 -                      ptr->ivector[0] = OPA->ivector[0];
 -                      ptr->ivector[1] = OPA->ivector[1];
 -                      ptr->ivector[2] = OPA->ivector[2];
 -                      break;
 -
 -              case INSTR_NOT_F:
 -                      OPC->_float = !FLOAT_IS_TRUE_FOR_INT(OPA->_int);
 -                      break;
 -              case INSTR_NOT_V:
 -                      OPC->_float = !OPA->vector[0] &&
 -                                    !OPA->vector[1] &&
 -                                    !OPA->vector[2];
 -                      break;
 -              case INSTR_NOT_S:
 -                      OPC->_float = !OPA->string ||
 -                                    !*prog_getstring(prog, OPA->string);
 -                      break;
 -              case INSTR_NOT_ENT:
 -                      OPC->_float = (OPA->edict == 0);
 -                      break;
 -              case INSTR_NOT_FNC:
 -                      OPC->_float = !OPA->function;
 -                      break;
 -
 -              case INSTR_IF:
 -                  /* this is consistent with darkplaces' behaviour */
 -                      if(FLOAT_IS_TRUE_FOR_INT(OPA->_int))
 -                      {
 -                              st += st->o2.s1 - 1;    /* offset the s++ */
 -                              if (++jumpcount >= maxjumps)
 -                                      qcvmerror(prog, "`%s` hit the runaway loop counter limit of %li jumps", prog->filename, jumpcount);
 -                      }
 -                      break;
 -              case INSTR_IFNOT:
 -                      if(!FLOAT_IS_TRUE_FOR_INT(OPA->_int))
 -                      {
 -                              st += st->o2.s1 - 1;    /* offset the s++ */
 -                              if (++jumpcount >= maxjumps)
 -                                      qcvmerror(prog, "`%s` hit the runaway loop counter limit of %li jumps", prog->filename, jumpcount);
 -                      }
 -                      break;
 -
 -              case INSTR_CALL0:
 -              case INSTR_CALL1:
 -              case INSTR_CALL2:
 -              case INSTR_CALL3:
 -              case INSTR_CALL4:
 -              case INSTR_CALL5:
 -              case INSTR_CALL6:
 -              case INSTR_CALL7:
 -              case INSTR_CALL8:
 -                      prog->argc = st->opcode - INSTR_CALL0;
 -                      if (!OPA->function)
 -                              qcvmerror(prog, "NULL function in `%s`", prog->filename);
 -
 -                      if(!OPA->function || OPA->function >= (unsigned int)prog->functions_count)
 -                      {
 -                              qcvmerror(prog, "CALL outside the program in `%s` (%i)", prog->filename, (int)OPA->function);
 -                              goto cleanup;
 -                      }
 -
 -                      newf = &prog->functions[OPA->function];
 -                      newf->profile++;
 -
 -                      prog->statement = (st - prog->code) + 1;
 -
 -                      if (newf->entry < 0)
 -                      {
 -                              /* negative statements are built in functions */
 -                              int builtinnumber = -newf->entry;
 -                              if (builtinnumber < prog->builtins_count && prog->builtins[builtinnumber])
 -                                      prog->builtins[builtinnumber](prog);
 -                              else
 -                                      qcvmerror(prog, "No such builtin #%i in %s! Try updating your gmqcc sources",
 -                                                builtinnumber, prog->filename);
 -                      }
 -                      else
 -                              st = prog->code + prog_enterfunction(prog, newf) - 1; /* offset st++ */
 -                      if (prog->vmerror)
 -                              goto cleanup;
 -                      break;
 -
 -              case INSTR_STATE:
 -                  qcvmerror(prog, "`%s` tried to execute a STATE operation", prog->filename);
 -                      break;
 -
 -              case INSTR_GOTO:
 -                      st += st->o1.s1 - 1;    /* offset the s++ */
 -                      if (++jumpcount == 10000000)
 -                                      qcvmerror(prog, "`%s` hit the runaway loop counter limit of %li jumps", prog->filename, jumpcount);
 -                      break;
 -
 -              case INSTR_AND:
 -                      OPC->_float = FLOAT_IS_TRUE_FOR_INT(OPA->_int) &&
 -                                    FLOAT_IS_TRUE_FOR_INT(OPB->_int);
 -                      break;
 -              case INSTR_OR:
 -                      OPC->_float = FLOAT_IS_TRUE_FOR_INT(OPA->_int) ||
 -                                    FLOAT_IS_TRUE_FOR_INT(OPB->_int);
 -                      break;
 -
 -              case INSTR_BITAND:
 -                      OPC->_float = ((int)OPA->_float) & ((int)OPB->_float);
 -                      break;
 -              case INSTR_BITOR:
 -                      OPC->_float = ((int)OPA->_float) | ((int)OPB->_float);
 -                      break;
 +        case INSTR_MUL_F:
 +            OPC->_float = OPA->_float * OPB->_float;
 +            break;
 +        case INSTR_MUL_V:
 +            OPC->_float = OPA->vector[0]*OPB->vector[0] +
 +                          OPA->vector[1]*OPB->vector[1] +
 +                          OPA->vector[2]*OPB->vector[2];
 +            break;
 +        case INSTR_MUL_FV:
 +            OPC->vector[0] = OPA->_float * OPB->vector[0];
 +            OPC->vector[1] = OPA->_float * OPB->vector[1];
 +            OPC->vector[2] = OPA->_float * OPB->vector[2];
 +            break;
 +        case INSTR_MUL_VF:
 +            OPC->vector[0] = OPB->_float * OPA->vector[0];
 +            OPC->vector[1] = OPB->_float * OPA->vector[1];
 +            OPC->vector[2] = OPB->_float * OPA->vector[2];
 +            break;
 +        case INSTR_DIV_F:
 +            if (OPB->_float != 0.0f)
 +                OPC->_float = OPA->_float / OPB->_float;
 +            else
 +                OPC->_float = 0;
 +            break;
 +
 +        case INSTR_ADD_F:
 +            OPC->_float = OPA->_float + OPB->_float;
 +            break;
 +        case INSTR_ADD_V:
 +            OPC->vector[0] = OPA->vector[0] + OPB->vector[0];
 +            OPC->vector[1] = OPA->vector[1] + OPB->vector[1];
 +            OPC->vector[2] = OPA->vector[2] + OPB->vector[2];
 +            break;
 +        case INSTR_SUB_F:
 +            OPC->_float = OPA->_float - OPB->_float;
 +            break;
 +        case INSTR_SUB_V:
 +            OPC->vector[0] = OPA->vector[0] - OPB->vector[0];
 +            OPC->vector[1] = OPA->vector[1] - OPB->vector[1];
 +            OPC->vector[2] = OPA->vector[2] - OPB->vector[2];
 +            break;
 +
 +        case INSTR_EQ_F:
 +            OPC->_float = (OPA->_float == OPB->_float);
 +            break;
 +        case INSTR_EQ_V:
 +            OPC->_float = ((OPA->vector[0] == OPB->vector[0]) &&
 +                           (OPA->vector[1] == OPB->vector[1]) &&
 +                           (OPA->vector[2] == OPB->vector[2]) );
 +            break;
 +        case INSTR_EQ_S:
 +            OPC->_float = !strcmp(prog_getstring(prog, OPA->string),
 +                                  prog_getstring(prog, OPB->string));
 +            break;
 +        case INSTR_EQ_E:
 +            OPC->_float = (OPA->_int == OPB->_int);
 +            break;
 +        case INSTR_EQ_FNC:
 +            OPC->_float = (OPA->function == OPB->function);
 +            break;
 +        case INSTR_NE_F:
 +            OPC->_float = (OPA->_float != OPB->_float);
 +            break;
 +        case INSTR_NE_V:
 +            OPC->_float = ((OPA->vector[0] != OPB->vector[0]) ||
 +                           (OPA->vector[1] != OPB->vector[1]) ||
 +                           (OPA->vector[2] != OPB->vector[2]) );
 +            break;
 +        case INSTR_NE_S:
 +            OPC->_float = !!strcmp(prog_getstring(prog, OPA->string),
 +                                   prog_getstring(prog, OPB->string));
 +            break;
 +        case INSTR_NE_E:
 +            OPC->_float = (OPA->_int != OPB->_int);
 +            break;
 +        case INSTR_NE_FNC:
 +            OPC->_float = (OPA->function != OPB->function);
 +            break;
 +
 +        case INSTR_LE:
 +            OPC->_float = (OPA->_float <= OPB->_float);
 +            break;
 +        case INSTR_GE:
 +            OPC->_float = (OPA->_float >= OPB->_float);
 +            break;
 +        case INSTR_LT:
 +            OPC->_float = (OPA->_float < OPB->_float);
 +            break;
 +        case INSTR_GT:
 +            OPC->_float = (OPA->_float > OPB->_float);
 +            break;
 +
 +        case INSTR_LOAD_F:
 +        case INSTR_LOAD_S:
 +        case INSTR_LOAD_FLD:
 +        case INSTR_LOAD_ENT:
 +        case INSTR_LOAD_FNC:
 +            if (OPA->edict < 0 || OPA->edict >= prog->entities) {
 +                qcvmerror(prog, "progs `%s` attempted to read an out of bounds entity", prog->filename);
 +                goto cleanup;
 +            }
 +            if ((unsigned int)(OPB->_int) >= (unsigned int)(prog->entityfields)) {
 +                qcvmerror(prog, "prog `%s` attempted to read an invalid field from entity (%i)",
 +                          prog->filename,
 +                          OPB->_int);
 +                goto cleanup;
 +            }
 +            ed = prog_getedict(prog, OPA->edict);
 +            OPC->_int = ((qcany*)( ((qcint*)ed) + OPB->_int ))->_int;
 +            break;
 +        case INSTR_LOAD_V:
 +            if (OPA->edict < 0 || OPA->edict >= prog->entities) {
 +                qcvmerror(prog, "progs `%s` attempted to read an out of bounds entity", prog->filename);
 +                goto cleanup;
 +            }
 +            if (OPB->_int < 0 || OPB->_int + 3 > prog->entityfields)
 +            {
 +                qcvmerror(prog, "prog `%s` attempted to read an invalid field from entity (%i)",
 +                          prog->filename,
 +                          OPB->_int + 2);
 +                goto cleanup;
 +            }
 +            ed = prog_getedict(prog, OPA->edict);
 +            OPC->ivector[0] = ((qcany*)( ((qcint*)ed) + OPB->_int ))->ivector[0];
 +            OPC->ivector[1] = ((qcany*)( ((qcint*)ed) + OPB->_int ))->ivector[1];
 +            OPC->ivector[2] = ((qcany*)( ((qcint*)ed) + OPB->_int ))->ivector[2];
 +            break;
 +
 +        case INSTR_ADDRESS:
 +            if (OPA->edict < 0 || OPA->edict >= prog->entities) {
 +                qcvmerror(prog, "prog `%s` attempted to address an out of bounds entity %i", prog->filename, OPA->edict);
 +                goto cleanup;
 +            }
 +            if ((unsigned int)(OPB->_int) >= (unsigned int)(prog->entityfields))
 +            {
 +                qcvmerror(prog, "prog `%s` attempted to read an invalid field from entity (%i)",
 +                          prog->filename,
 +                          OPB->_int);
 +                goto cleanup;
 +            }
 +
 +            ed = prog_getedict(prog, OPA->edict);
-             OPC->_int = ((qcint*)ed) - prog->entitydata;
-             OPC->_int += OPB->_int;
++            OPC->_int = ((qcint*)ed) - prog->entitydata + OPB->_int;
 +            break;
 +
 +        case INSTR_STORE_F:
 +        case INSTR_STORE_S:
 +        case INSTR_STORE_ENT:
 +        case INSTR_STORE_FLD:
 +        case INSTR_STORE_FNC:
 +            OPB->_int = OPA->_int;
 +            break;
 +        case INSTR_STORE_V:
 +            OPB->ivector[0] = OPA->ivector[0];
 +            OPB->ivector[1] = OPA->ivector[1];
 +            OPB->ivector[2] = OPA->ivector[2];
 +            break;
 +
 +        case INSTR_STOREP_F:
 +        case INSTR_STOREP_S:
 +        case INSTR_STOREP_ENT:
 +        case INSTR_STOREP_FLD:
 +        case INSTR_STOREP_FNC:
 +            if (OPB->_int < 0 || OPB->_int >= prog->entitydata_count) {
 +                qcvmerror(prog, "`%s` attempted to write to an out of bounds edict (%i)", prog->filename, OPB->_int);
 +                goto cleanup;
 +            }
 +            if (OPB->_int < prog->entityfields && !prog->allowworldwrites)
 +                qcvmerror(prog, "`%s` tried to assign to world.%s (field %i)\n",
 +                          prog->filename,
 +                          prog_getstring(prog, prog_entfield(prog, OPB->_int)->name),
 +                          OPB->_int);
 +            ptr = (qcany*)(prog->entitydata + OPB->_int);
 +            ptr->_int = OPA->_int;
 +            break;
 +        case INSTR_STOREP_V:
 +            if (OPB->_int < 0 || OPB->_int + 2 >= prog->entitydata_count) {
 +                qcvmerror(prog, "`%s` attempted to write to an out of bounds edict (%i)", prog->filename, OPB->_int);
 +                goto cleanup;
 +            }
 +            if (OPB->_int < prog->entityfields && !prog->allowworldwrites)
 +                qcvmerror(prog, "`%s` tried to assign to world.%s (field %i)\n",
 +                          prog->filename,
 +                          prog_getstring(prog, prog_entfield(prog, OPB->_int)->name),
 +                          OPB->_int);
 +            ptr = (qcany*)(prog->entitydata + OPB->_int);
 +            ptr->ivector[0] = OPA->ivector[0];
 +            ptr->ivector[1] = OPA->ivector[1];
 +            ptr->ivector[2] = OPA->ivector[2];
 +            break;
 +
 +        case INSTR_NOT_F:
 +            OPC->_float = !FLOAT_IS_TRUE_FOR_INT(OPA->_int);
 +            break;
 +        case INSTR_NOT_V:
 +            OPC->_float = !OPA->vector[0] &&
 +                          !OPA->vector[1] &&
 +                          !OPA->vector[2];
 +            break;
 +        case INSTR_NOT_S:
 +            OPC->_float = !OPA->string ||
 +                          !*prog_getstring(prog, OPA->string);
 +            break;
 +        case INSTR_NOT_ENT:
 +            OPC->_float = (OPA->edict == 0);
 +            break;
 +        case INSTR_NOT_FNC:
 +            OPC->_float = !OPA->function;
 +            break;
 +
 +        case INSTR_IF:
 +            /* this is consistent with darkplaces' behaviour */
 +            if(FLOAT_IS_TRUE_FOR_INT(OPA->_int))
 +            {
 +                st += st->o2.s1 - 1;    /* offset the s++ */
 +                if (++jumpcount >= maxjumps)
 +                    qcvmerror(prog, "`%s` hit the runaway loop counter limit of %li jumps", prog->filename, jumpcount);
 +            }
 +            break;
 +        case INSTR_IFNOT:
 +            if(!FLOAT_IS_TRUE_FOR_INT(OPA->_int))
 +            {
 +                st += st->o2.s1 - 1;    /* offset the s++ */
 +                if (++jumpcount >= maxjumps)
 +                    qcvmerror(prog, "`%s` hit the runaway loop counter limit of %li jumps", prog->filename, jumpcount);
 +            }
 +            break;
 +
 +        case INSTR_CALL0:
 +        case INSTR_CALL1:
 +        case INSTR_CALL2:
 +        case INSTR_CALL3:
 +        case INSTR_CALL4:
 +        case INSTR_CALL5:
 +        case INSTR_CALL6:
 +        case INSTR_CALL7:
 +        case INSTR_CALL8:
 +            prog->argc = st->opcode - INSTR_CALL0;
 +            if (!OPA->function)
 +                qcvmerror(prog, "NULL function in `%s`", prog->filename);
 +
 +            if(!OPA->function || OPA->function >= (unsigned int)prog->functions_count)
 +            {
 +                qcvmerror(prog, "CALL outside the program in `%s`", prog->filename);
 +                goto cleanup;
 +            }
 +
 +            newf = &prog->functions[OPA->function];
 +            newf->profile++;
 +
 +            prog->statement = (st - prog->code) + 1;
 +
 +            if (newf->entry < 0)
 +            {
 +                /* negative statements are built in functions */
 +                int builtinnumber = -newf->entry;
 +                if (builtinnumber < prog->builtins_count && prog->builtins[builtinnumber])
 +                    prog->builtins[builtinnumber](prog);
 +                else
 +                    qcvmerror(prog, "No such builtin #%i in %s! Try updating your gmqcc sources",
 +                              builtinnumber, prog->filename);
 +            }
 +            else
 +                st = prog->code + prog_enterfunction(prog, newf) - 1; /* offset st++ */
 +            if (prog->vmerror)
 +                goto cleanup;
 +            break;
 +
 +        case INSTR_STATE:
 +            qcvmerror(prog, "`%s` tried to execute a STATE operation", prog->filename);
 +            break;
 +
 +        case INSTR_GOTO:
 +            st += st->o1.s1 - 1;    /* offset the s++ */
 +            if (++jumpcount == 10000000)
 +                qcvmerror(prog, "`%s` hit the runaway loop counter limit of %li jumps", prog->filename, jumpcount);
 +            break;
 +
 +        case INSTR_AND:
 +            OPC->_float = FLOAT_IS_TRUE_FOR_INT(OPA->_int) &&
 +                          FLOAT_IS_TRUE_FOR_INT(OPB->_int);
 +            break;
 +        case INSTR_OR:
 +            OPC->_float = FLOAT_IS_TRUE_FOR_INT(OPA->_int) ||
 +                          FLOAT_IS_TRUE_FOR_INT(OPB->_int);
 +            break;
 +
 +        case INSTR_BITAND:
 +            OPC->_float = ((int)OPA->_float) & ((int)OPB->_float);
 +            break;
 +        case INSTR_BITOR:
 +            OPC->_float = ((int)OPA->_float) | ((int)OPB->_float);
 +            break;
      }
  }
  
diff --combined gmqcc.h
index c8d1649a3682e5b1c20d7c066922609a5eec831b,ef1841f713f73948bad62a805dfbb80618a4831e..9770929f8caaa104e04255dd5246bd656f0112c4
+++ b/gmqcc.h
@@@ -290,6 -290,9 +290,9 @@@ enum 
      TYPE_POINTER  ,
      TYPE_INTEGER  ,
      TYPE_VARIANT  ,
+     TYPE_STRUCT   ,
+     TYPE_UNION    ,
+     TYPE_ARRAY    ,
  
      TYPE_COUNT
  };
@@@ -298,6 -301,7 +301,7 @@@ extern const char *type_name[TYPE_COUNT
  
  extern size_t type_sizeof[TYPE_COUNT];
  extern uint16_t type_store_instr[TYPE_COUNT];
+ extern uint16_t field_store_instr[TYPE_COUNT];
  /* could use type_store_instr + INSTR_STOREP_F - INSTR_STORE_F
   * but this breaks when TYPE_INTEGER is added, since with the enhanced
   * instruction set, the old ones are left untouched, thus the _I instructions
@@@ -528,19 -532,6 +532,19 @@@ uint32_t code_genstring   (const char *
  uint32_t code_cachedstring(const char *string);
  qcint    code_alloc_field (size_t qcsize);
  
 +/*===================================================================*/
 +/*============================ con.c ================================*/
 +/*===================================================================*/
 +void con_close();
 +void con_color(int state);
 +void con_init ();
 +void con_reset();
 +int  con_change(const char *out, const char *err);
 +int  con_verr  (const char *fmt, va_list va);
 +int  con_vout  (const char *fmt, va_list va);
 +int  con_err   (const char *fmt, ...);
 +int  con_out   (const char *fmt, ...);
 +
  /*===================================================================*/
  /*========================= assembler.c =============================*/
  /*===================================================================*/
@@@ -618,6 -609,10 +622,6 @@@ static const struct 
  
      { "END"       , 0, 3 } /* virtual assembler instruction */
  };
 -
 -void asm_init (const char *, FILE **);
 -void asm_close(FILE *);
 -void asm_parse(FILE *);
  /*===================================================================*/
  /*============================= ast.c ===============================*/
  /*===================================================================*/
@@@ -881,6 -876,8 +885,8 @@@ typedef struct qc_program_s 
      MEM_VECTOR_MAKE(qcint,                  entitydata);
      MEM_VECTOR_MAKE(bool,                   entitypool);
  
+     MEM_VECTOR_MAKE(const char*,            function_stack);
      uint16_t crc16;
  
      size_t tempstring_start;
@@@ -948,11 -945,10 +954,11 @@@ void cprintmsg (lex_ctx ctx, int lvl, c
  /*===================== parser.c commandline ========================*/
  /*===================================================================*/
  
 -bool parser_init   ();
 -bool parser_compile(const char *filename);
 -bool parser_finish (const char *output);
 -void parser_cleanup();
 +bool parser_init          ();
 +bool parser_compile_file  (const char *filename);
 +bool parser_compile_string(const char *name, const char *str);
 +bool parser_finish        (const char *output);
 +void parser_cleanup       ();
  
  /*===================================================================*/
  /*======================= main.c commandline ========================*/
@@@ -1023,6 -1019,7 +1029,7 @@@ extern bool        opts_werror
  extern bool        opts_forcecrc;
  extern uint16_t    opts_forced_crc;
  extern bool        opts_pp_only;
+ extern size_t      opts_max_array_size;
  
  /*===================================================================*/
  #define OPTS_FLAG(i) (!! (opts_flags[(i)/32] & (1<< ((i)%32))))
diff --combined lexer.c
index 8485192e5ea730b274b738217c6f7f0f762fdfdd,a555895e854929404c586e86ab4a41b2e180207d..e190da0779ca3349d69ecae7a0eae9f5a2eacbca
+++ b/lexer.c
@@@ -162,34 -162,6 +162,34 @@@ lex_file* lex_open(const char *file
      return lex;
  }
  
 +lex_file* lex_open_string(const char *str, size_t len, const char *name)
 +{
 +    lex_file *lex;
 +
 +    lex = (lex_file*)mem_a(sizeof(*lex));
 +    if (!lex) {
 +        lexerror(NULL, "out of memory\n");
 +        return NULL;
 +    }
 +
 +    memset(lex, 0, sizeof(*lex));
 +
 +    lex->file = NULL;
 +    lex->open_string        = str;
 +    lex->open_string_length = len;
 +    lex->open_string_pos    = 0;
 +
 +    lex->name = util_strdup(name ? name : "<string-source>");
 +    lex->line = 1; /* we start counting at 1 */
 +
 +    lex->peekpos = 0;
 +    lex->eof = false;
 +
 +    lex_filenames_add(lex->name);
 +
 +    return lex;
 +}
 +
  void lex_cleanup(void)
  {
      size_t i;
@@@ -220,18 -192,6 +220,18 @@@ void lex_close(lex_file *lex
      mem_d(lex);
  }
  
 +static int lex_fgetc(lex_file *lex)
 +{
 +    if (lex->file)
 +        return fgetc(lex->file);
 +    if (lex->open_string) {
 +        if (lex->open_string_pos >= lex->open_string_length)
 +            return EOF;
 +        return lex->open_string[lex->open_string_pos++];
 +    }
 +    return EOF;
 +}
 +
  /* Get or put-back data
   * The following to functions do NOT understand what kind of data they
   * are working on.
@@@ -241,13 -201,13 +241,13 @@@ static void lex_ungetch(lex_file *lex, 
  static int lex_try_trigraph(lex_file *lex, int old)
  {
      int c2, c3;
 -    c2 = fgetc(lex->file);
 +    c2 = lex_fgetc(lex);
      if (c2 != '?') {
          lex_ungetch(lex, c2);
          return old;
      }
  
 -    c3 = fgetc(lex->file);
 +    c3 = lex_fgetc(lex);
      switch (c3) {
          case '=': return '#';
          case '/': return '\\';
  static int lex_try_digraph(lex_file *lex, int ch)
  {
      int c2;
 -    c2 = fgetc(lex->file);
 +    c2 = lex_fgetc(lex);
      if      (ch == '<' && c2 == ':')
          return '[';
      else if (ch == ':' && c2 == '>')
@@@ -294,7 -254,7 +294,7 @@@ static int lex_getch(lex_file *lex
          return lex->peek[lex->peekpos];
      }
  
 -    ch = fgetc(lex->file);
 +    ch = lex_fgetc(lex);
      if (ch == '\n')
          lex->line++;
      else if (ch == '?')
@@@ -892,6 -852,7 +892,7 @@@ int lex_do(lex_file *lex
      /* single-character tokens */
      switch (ch)
      {
+         case '[':
          case '(':
              if (!lex_tokench(lex, ch) ||
                  !lex_endtoken(lex))
          case ';':
          case '{':
          case '}':
-         case '[':
          case ']':
  
          case '#':
                  !strcmp(v, "struct") ||
                  !strcmp(v, "union")  ||
                  !strcmp(v, "break")  ||
-                 !strcmp(v, "continue"))
+                 !strcmp(v, "continue") ||
+                 !strcmp(v, "var"))
              {
                  lex->tok.ttype = TOKEN_KEYWORD;
              }
diff --combined lexer.h
index 0fbe61fc2e0cc45dfde13987a7ca0edc718a918d,8a93d233baf08060ddd1644590fc721136f19dba..a3b9833f3cab1727d66bbe85ffdb4e198e318fd5
+++ b/lexer.h
@@@ -100,10 -100,6 +100,10 @@@ typedef struct 
  
  typedef struct {
        FILE   *file;
 +      const char *open_string;
 +      size_t      open_string_length;
 +      size_t      open_string_pos;
 +
        char   *name;
        size_t  line;
        size_t  sline; /* line at the start of a token */
  MEM_VECTOR_PROTO(lex_file, char, token);
  
  lex_file* lex_open (const char *file);
 +lex_file* lex_open_string(const char *str, size_t len, const char *name);
  void      lex_close(lex_file   *lex);
  int       lex_do   (lex_file   *lex);
  void      lex_cleanup(void);
@@@ -167,6 -162,7 +167,7 @@@ static const oper_info c_operators[] = 
  
      { ".",   2, opid1('.'),         ASSOC_LEFT,  15, 0 },
      { "(",   0, opid1('('),         ASSOC_LEFT,  15, 0 }, /* function call */
+     { "[",   2, opid1('['),         ASSOC_LEFT,  15, 0 }, /* array subscript */
  
      { "!",   1, opid2('!', 'P'),    ASSOC_RIGHT, 14, OP_PREFIX },
      { "~",   1, opid2('~', 'P'),    ASSOC_RIGHT, 14, OP_PREFIX },
@@@ -227,6 -223,7 +228,7 @@@ static const oper_info qcc_operators[] 
  
      { ".",   2, opid1('.'),         ASSOC_LEFT,  15, 0 },
      { "(",   0, opid1('('),         ASSOC_LEFT,  15, 0 }, /* function call */
+     { "[",   2, opid1('['),         ASSOC_LEFT,  15, 0 }, /* array subscript */
  
      { "!",   1, opid2('!', 'P'),    ASSOC_RIGHT, 14, OP_PREFIX },
      { "+",   1, opid2('+','P'),     ASSOC_RIGHT, 14, OP_PREFIX },
diff --combined main.c
index d8a097c446c5942c57b9b919479f937b8d36a3d0,773b08ed711e815c89da14e1c64235f649b49a2f..0b9d726744a4169220e92916c96899835ad3716f
--- 1/main.c
--- 2/main.c
+++ b/main.c
@@@ -35,6 -35,7 +35,7 @@@ bool        opts_dump     = false
  bool        opts_werror   = false;
  bool        opts_forcecrc = false;
  bool        opts_pp_only  = false;
+ size_t      opts_max_array_size = 1024;
  
  uint16_t    opts_forced_crc;
  
@@@ -54,28 -55,28 +55,28 @@@ VECTOR_MAKE(argitem, items)
  static const char *app_name;
  
  static int usage() {
 -    printf("usage: %s [options] [files...]", app_name);
 -    printf("options:\n"
 +    con_out("usage: %s [options] [files...]", app_name);
 +    con_out("options:\n"
             "  -h, --help             show this help message\n"
             "  -debug                 turns on compiler debug messages\n"
             "  -memchk                turns on compiler memory leak check\n");
 -    printf("  -o, --output=file      output file, defaults to progs.dat\n"
 +    con_out("  -o, --output=file      output file, defaults to progs.dat\n"
             "  -a filename            add an asm file to be assembled\n"
             "  -s filename            add a progs.src file to be used\n");
 -    printf("  -E                     stop after preprocessing\n");
 -    printf("  -f<flag>               enable a flag\n"
 +    con_out("  -E                     stop after preprocessing\n");
 +    con_out("  -f<flag>               enable a flag\n"
             "  -fno-<flag>            disable a flag\n"
             "  -std standard          select one of the following standards\n"
             "       -std=qcc          original QuakeC\n"
             "       -std=fteqcc       fteqcc QuakeC\n"
             "       -std=gmqcc        this compiler (default)\n");
 -    printf("  -W<warning>            enable a warning\n"
 +    con_out("  -W<warning>            enable a warning\n"
             "  -Wno-<warning>         disable a warning\n"
             "  -Wall                  enable all warnings\n"
             "  -Werror                treat warnings as errors\n");
 -    printf("  -force-crc=num         force a specific checksum into the header\n");
 -    printf("\n");
 -    printf("flags:\n"
 +    con_out("  -force-crc=num         force a specific checksum into the header\n");
 +    con_out("\n");
 +    con_out("flags:\n"
             "  -fadjust-vector-fields\n"
             "            when assigning a vector field, its _y and _z fields also get assigned\n"
             );
@@@ -161,6 -162,22 +162,22 @@@ static bool options_long_gcc(const cha
      return options_long_witharg_all(optname, argc_, argv_, out, 1, false);
  }
  
+ static void options_set(uint32_t *flags, size_t idx, bool on)
+ {
+     longbit lb = LONGBIT(idx);
+ #if 0
+     if (on)
+         flags[lb.idx] |= (1<<(lb.bit));
+     else
+         flags[lb.idx] &= ~(1<<(lb.bit));
+ #else
+     if (on)
+         flags[0] |= (1<<(lb));
+     else
+         flags[0] &= ~(1<<(lb));
+ #endif
+ }
  static bool options_parse(int argc, char **argv) {
      bool argend = false;
      size_t itr;
          if (argv[0][0] == '-') {
      /* All gcc-type long options */
              if (options_long_gcc("std", &argc, &argv, &argarg)) {
-                 if      (!strcmp(argarg, "gmqcc") || !strcmp(argarg, "default"))
+                 if      (!strcmp(argarg, "gmqcc") || !strcmp(argarg, "default")) {
+                     options_set(opts_flags, ADJUST_VECTOR_FIELDS, true);
                      opts_standard = COMPILER_GMQCC;
-                 else if (!strcmp(argarg, "qcc"))
+                 } else if (!strcmp(argarg, "qcc")) {
+                     options_set(opts_flags, ADJUST_VECTOR_FIELDS, false);
                      opts_standard = COMPILER_QCC;
-                 else if (!strcmp(argarg, "fte") || !strcmp(argarg, "fteqcc"))
+                 } else if (!strcmp(argarg, "fte") || !strcmp(argarg, "fteqcc")) {
+                     options_set(opts_flags, ADJUST_VECTOR_FIELDS, false);
                      opts_standard = COMPILER_FTEQCC;
-                 else if (!strcmp(argarg, "qccx"))
+                 } else if (!strcmp(argarg, "qccx")) {
+                     options_set(opts_flags, ADJUST_VECTOR_FIELDS, false);
                      opts_standard = COMPILER_QCCX;
-                 else {
+                 else {
 -                    printf("Unknown standard: %s\n", argarg);
 +                    con_out("Unknown standard: %s\n", argarg);
                      return false;
                  }
                  continue;
                  case 'f':
                      util_strtocmd(argv[0]+2, argv[0]+2, strlen(argv[0]+2)+1);
                      if (!strcmp(argv[0]+2, "HELP")) {
 -                        printf("Possible flags:\n");
 +                        con_out("Possible flags:\n");
                          for (itr = 0; itr < COUNT_FLAGS; ++itr) {
                              util_strtononcmd(opts_flag_list[itr].name, buffer, sizeof(buffer));
 -                            printf(" -f%s\n", buffer);
 +                            con_out(" -f%s\n", buffer);
                          }
                          exit(0);
                      }
                      else if (!strncmp(argv[0]+2, "NO_", 3)) {
                          if (!options_setflag(argv[0]+5, false)) {
 -                            printf("unknown flag: %s\n", argv[0]+2);
 +                            con_out("unknown flag: %s\n", argv[0]+2);
                              return false;
                          }
                      }
                      else if (!options_setflag(argv[0]+2, true)) {
 -                        printf("unknown flag: %s\n", argv[0]+2);
 +                        con_out("unknown flag: %s\n", argv[0]+2);
                          return false;
                      }
                      break;
                  case 'W':
                      util_strtocmd(argv[0]+2, argv[0]+2, strlen(argv[0]+2)+1);
                      if (!strcmp(argv[0]+2, "HELP")) {
 -                        printf("Possible warnings:\n");
 +                        con_out("Possible warnings:\n");
                          for (itr = 0; itr < COUNT_WARNINGS; ++itr) {
                              util_strtononcmd(opts_warn_list[itr].name, buffer, sizeof(buffer));
 -                            printf(" -W%s\n", buffer);
 +                            con_out(" -W%s\n", buffer);
                          }
                          exit(0);
                      }
                      }
                      if (!strncmp(argv[0]+2, "NO_", 3)) {
                          if (!options_setwarn(argv[0]+5, false)) {
 -                            printf("unknown warning: %s\n", argv[0]+2);
 +                            con_out("unknown warning: %s\n", argv[0]+2);
                              return false;
                          }
                      }
                      else if (!options_setwarn(argv[0]+2, true)) {
 -                        printf("unknown warning: %s\n", argv[0]+2);
 +                        con_out("unknown warning: %s\n", argv[0]+2);
                          return false;
                      }
                      break;
  
                  case 'O':
                      if (!options_witharg(&argc, &argv, &argarg)) {
 -                        printf("option -O requires a numerical argument\n");
 +                        con_out("option -O requires a numerical argument\n");
                          return false;
                      }
                      opts_O = atoi(argarg);
  
                  case 'o':
                      if (!options_witharg(&argc, &argv, &argarg)) {
 -                        printf("option -o requires an argument: the output file name\n");
 +                        con_out("option -o requires an argument: the output file name\n");
                          return false;
                      }
                      opts_output = argarg;
                  case 's':
                      item.type = argv[0][1] == 'a' ? TYPE_ASM : TYPE_SRC;
                      if (!options_witharg(&argc, &argv, &argarg)) {
 -                        printf("option -a requires a filename %s\n",
 +                        con_out("option -a requires a filename %s\n",
                                  (argv[0][1] == 'a' ? "containing QC-asm" : "containing a progs.src formatted list"));
                          return false;
                      }
                              opts_output = argarg;
                              opts_output_wasset = true;
                          } else {
 -                            printf("Unknown parameter: %s\n", argv[0]);
 +                            con_out("Unknown parameter: %s\n", argv[0]);
                              return false;
                          }
                      }
                      break;
  
                  default:
 -                    printf("Unknown parameter: %s\n", argv[0]);
 +                    con_out("Unknown parameter: %s\n", argv[0]);
                      return false;
              }
          }
      return true;
  }
  
- static void options_set(uint32_t *flags, size_t idx, bool on)
- {
-     longbit lb = LONGBIT(idx);
- #if 0
-     if (on)
-         flags[lb.idx] |= (1<<(lb.bit));
-     else
-         flags[lb.idx] &= ~(1<<(lb.bit));
- #else
-     if (on)
-         flags[0] |= (1<<(lb));
-     else
-         flags[0] &= ~(1<<(lb));
- #endif
- }
  /* returns the line number, or -1 on error */
  static bool progs_nextline(char **out, size_t *alen,FILE *src)
  {
@@@ -398,7 -403,6 +403,7 @@@ int main(int argc, char **argv) 
      bool opts_output_free = false;
  
      app_name = argv[0];
 +    con_init();
  
      /* default options / warn flags */
      options_set(opts_warn, WARN_UNKNOWN_CONTROL_SEQUENCE, true);
      options_set(opts_warn, WARN_END_SYS_FIELDS, true);
      options_set(opts_warn, WARN_ASSIGN_FUNCTION_TYPES, true);
  
+     options_set(opts_flags, ADJUST_VECTOR_FIELDS, true);
      if (!options_parse(argc, argv)) {
          return usage();
      }
  
      if (opts_dump) {
          for (itr = 0; itr < COUNT_FLAGS; ++itr) {
 -            printf("Flag %s = %i\n", opts_flag_list[itr].name, OPTS_FLAG(itr));
 +            con_out("Flag %s = %i\n", opts_flag_list[itr].name, OPTS_FLAG(itr));
          }
          for (itr = 0; itr < COUNT_WARNINGS; ++itr) {
 -            printf("Warning %s = %i\n", opts_warn_list[itr].name, OPTS_WARN(itr));
 +            con_out("Warning %s = %i\n", opts_warn_list[itr].name, OPTS_WARN(itr));
          }
 -        printf("output = %s\n", opts_output);
 -        printf("optimization level = %i\n", (int)opts_O);
 -        printf("standard = %i\n", opts_standard);
 +        con_out("output = %s\n", opts_output);
 +        con_out("optimization level = %i\n", (int)opts_O);
 +        con_out("standard = %i\n", opts_standard);
      }
  
      if (!parser_init()) {
 -        printf("failed to initialize parser\n");
 +        con_out("failed to initialize parser\n");
          retval = 1;
          goto cleanup;
      }
      util_debug("COM", "starting ...\n");
  
      if (items_elements) {
 -        printf("Mode: manual\n");
 -        printf("There are %lu items to compile:\n", (unsigned long)items_elements);
 +        con_out("Mode: manual\n");
 +        con_out("There are %lu items to compile:\n", (unsigned long)items_elements);
          for (itr = 0; itr < items_elements; ++itr) {
 -            printf("  item: %s (%s)\n",
 +            con_out("  item: %s (%s)\n",
                     items_data[itr].filename,
                     ( (items_data[itr].type == TYPE_QC ? "qc" :
                       (items_data[itr].type == TYPE_ASM ? "asm" :
                       (items_data[itr].type == TYPE_SRC ? "progs.src" :
                       ("unknown"))))));
  
 -            if (!parser_compile(items_data[itr].filename)) {
 +        if (!parser_compile_file(items_data[itr].filename))
 +        {
                  retval = 1;
                  goto cleanup;
              }
          char *line;
          size_t linelen = 0;
  
 -        printf("Mode: progs.src\n");
 +        con_out("Mode: progs.src\n");
          src = util_fopen("progs.src", "rb");
          if (!src) {
 -            printf("failed to open `progs.src` for reading\n");
 +            con_out("failed to open `progs.src` for reading\n");
              retval = 1;
              goto cleanup;
          }
  
          line = NULL;
          if (!progs_nextline(&line, &linelen, src) || !line[0]) {
 -            printf("illformatted progs.src file: expected output filename in first line\n");
 +            con_out("illformatted progs.src file: expected output filename in first line\n");
              retval = 1;
              goto srcdone;
          }
          while (progs_nextline(&line, &linelen, src)) {
              if (!line[0] || (line[0] == '/' && line[1] == '/'))
                  continue;
 -            printf("  src: %s\n", line);
 -            if (!parser_compile(line)) {
 +            con_out("  src: %s\n", line);
 +            if (!parser_compile_file(line)) {
                  retval = 1;
                  goto srcdone;
              }
diff --combined parser.c
index ce3c778a977a17418177af5545b6f337bf0170ec,0f354a7b1ce6c1605e971464bc3074dd43c664c1..534f007db4d2ba65d0071a8408088ec8e5346f2a
+++ b/parser.c
@@@ -32,6 -32,9 +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 +51,7 @@@ MEM_VEC_FUNCTIONS(parser_t, varentry_t
  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);
@@@ -306,131 -309,6 +309,6 @@@ static ast_expression* parser_find_var(
      return v;
  }
  
- typedef struct {
-     MEM_VECTOR_MAKE(ast_value*, p);
- } paramlist_t;
- MEM_VEC_FUNCTIONS(paramlist_t, ast_value*, p)
- static ast_value *parse_type(parser_t *parser, int basetype, bool *isfunc)
- {
-     paramlist_t params;
-     ast_value *var;
-     lex_ctx   ctx = parser_ctx(parser);
-     int vtype = basetype;
-     int temptype;
-     size_t i;
-     bool variadic = false;
-     MEM_VECTOR_INIT(&params, p);
-     *isfunc = false;
-     if (parser->tok == '(') {
-         *isfunc = true;
-         while (true) {
-             ast_value *param;
-             ast_value *fld;
-             bool isfield = false;
-             bool isfuncparam = false;
-             if (!parser_next(parser))
-                 goto on_error;
-             if (parser->tok == ')')
-                 break;
-             if (parser->tok == '.') {
-                 isfield = true;
-                 if (!parser_next(parser)) {
-                     parseerror(parser, "expected field parameter type");
-                     goto on_error;
-                 }
-             }
-             if (parser->tok == TOKEN_DOTS) {
-                 /* variadic args */
-                 variadic = true;
-                 if (!parser_next(parser))
-                     goto on_error;
-                 if (parser->tok != ')') {
-                     parseerror(parser, "`...` must be the last parameter of a variadic function declaration");
-                     goto on_error;
-                 }
-                 if (opts_standard == COMPILER_QCC) {
-                     if (parsewarning(parser, WARN_EXTENSIONS, "variadic functions are not available in this standard"))
-                         goto on_error;
-                 }
-                 break;
-             }
-             temptype = parser_token(parser)->constval.t;
-             if (!parser_next(parser))
-                 goto on_error;
-             param = parse_type(parser, temptype, &isfuncparam);
-             if (!param)
-                 goto on_error;
-             if (parser->tok == TOKEN_IDENT) {
-                 /* named parameter */
-                 if (!ast_value_set_name(param, parser_tokval(parser)))
-                     goto on_error;
-                 if (!parser_next(parser))
-                     goto on_error;
-             }
-             /* This comes before the isfield part! */
-             if (isfuncparam) {
-                 ast_value *fval = ast_value_new(ast_ctx(param), param->name, TYPE_FUNCTION);
-                 if (!fval) {
-                     ast_delete(param);
-                     goto on_error;
-                 }
-                 fval->expression.next = (ast_expression*)param;
-                 MEM_VECTOR_MOVE(&param->expression, params, &fval->expression, params);
-                 fval->expression.variadic = param->expression.variadic;
-                 param = fval;
-             }
-             if (isfield) {
-                 fld = ast_value_new(ctx, param->name, TYPE_FIELD);
-                 fld->expression.next = (ast_expression*)param;
-                 param = fld;
-             }
-             if (!paramlist_t_p_add(&params, param)) {
-                 parseerror(parser, "Out of memory while parsing typename");
-                 goto on_error;
-             }
-             if (parser->tok == ',')
-                 continue;
-             if (parser->tok == ')')
-                 break;
-             parseerror(parser, "Unexpected token");
-             goto on_error;
-         }
-         if (!parser_next(parser))
-             goto on_error;
-     }
-     if (params.p_count > 8)
-         parseerror(parser, "more than 8 parameters are currently not supported");
-     var = ast_value_new(ctx, "<unnamed>", vtype);
-     if (!var)
-         goto on_error;
-     var->expression.variadic = variadic;
-     MEM_VECTOR_MOVE(&params, p, &var->expression, params);
-     return var;
- on_error:
-     for (i = 0; i < params.p_count; ++i)
-         ast_value_delete(params.p[i]);
-     MEM_VECTOR_CLEAR(&params, p);
-     return NULL;
- }
  typedef struct
  {
      size_t etype; /* 0 = expression, others are operators */
@@@ -448,6 -326,10 +326,10 @@@ typedef struc
  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;
@@@ -498,6 -380,44 +380,44 @@@ static sy_elem syparen(lex_ctx ctx, in
  # 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;
      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;
      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,
              }
              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]))
                  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))));
          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 &&
              }
              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 &&
          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;
  }
@@@ -1088,15 -1054,25 +1054,25 @@@ static bool parser_close_paren(parser_
      }
      */
      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;
      }
@@@ -1183,7 -1159,7 +1159,7 @@@ static ast_expression* parse_expression
                  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;
                  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;
                  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;
                  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;
                  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));
              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;
                  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;
              }
              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");
  
              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;
          if (!parser_next(parser)) {
              goto onerr;
          }
-         if (parser->tok == ';' || parser->tok == ']') {
+         if (parser->tok == ';' || (!parens && parser->tok == ']')) {
              break;
          }
      }
          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:
@@@ -1638,7 -1640,7 +1640,7 @@@ static bool parse_for(parser_t *parser
  
          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 != ';')
@@@ -1723,7 -1725,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) {
              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;
                  parseerror(parser, "expected variable declaration");
                  return false;
              }
-             if (!parse_variable(parser, block))
+             if (!parse_variable(parser, block, true))
                  return false;
              *out = NULL;
              return true;
      return false;
  }
  
- static bool parse_variable(parser_t *parser, ast_block *localblock)
+ static ast_expression *array_accessor_split(
+     parser_t  *parser,
+     ast_value *array,
+     ast_value *index,
+     size_t     middle,
+     ast_expression *left,
+     ast_expression *right
+     )
  {
-     bool            isfunc = false;
-     lex_ctx         ctx;
+     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;
+     }
  
-     ast_value      *var = NULL;
-     ast_value      *fld = NULL;
-     bool cleanvar = false;
+     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;
+     }
  
-     varentry_t      varent;
-     varentry_t      ve[3];
+     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;
+     }
  
-     ast_expression *olddecl;
+     return (ast_expression*)ifthen;
+ }
  
-     ast_value      *typevar;
+ 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);
  
-     bool hadproto;
-     bool isparam;
+     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];
  
-     bool retval = true;
-     bool isfield = false;
+         if (value->expression.vtype == TYPE_FIELD && value->expression.next->expression.vtype == TYPE_VECTOR)
+             assignop = INSTR_STORE_V;
  
-     /* go */
+         subscript = ast_array_index_new(ctx, (ast_expression*)array, (ast_expression*)parser_const_float(parser, from));
+         if (!subscript)
+             return NULL;
  
-     int basetype;
+         ast_store *st = ast_store_new(ctx, assignop, (ast_expression*)subscript, (ast_expression*)value);
+         if (!st) {
+             ast_delete(subscript);
+             return NULL;
+         }
  
-     if (parser->tok == '.') {
-         isfield = true;
-         if (!parser_next(parser)) {
-             parseerror(parser, "expected typename for field definition");
-             return false;
+         block = ast_block_new(ctx);
+         if (!block) {
+             ast_delete(st);
+             return NULL;
          }
-     }
  
-     basetype = parser_token(parser)->constval.t;
+         if (!ast_block_exprs_add(block, (ast_expression*)st)) {
+             ast_delete(block);
+             return NULL;
+         }
  
-     if (!parser_next(parser)) {
-         parseerror(parser, "expected variable definition");
-         return false;
+         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);
      }
+ }
  
-     typevar = parse_type(parser, basetype, &isfunc);
-     if (!typevar)
-         return false;
+ 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);
  
-     while (true)
-     {
-         hadproto    = false;
-         olddecl     = NULL;
-         isparam     = false;
-         varent.name = NULL;
+     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];
  
-         ve[0].name = ve[1].name = ve[2].name = NULL;
-         ve[0].var  = ve[1].var  = ve[2].var  = NULL;
+         if (value->expression.vtype == TYPE_FIELD && value->expression.next->expression.vtype == TYPE_VECTOR)
+             assignop = INSTR_STOREP_V;
  
-         ctx = parser_ctx(parser);
-         var = ast_value_copy(typevar);
-         cleanvar = true;
+         subscript = ast_array_index_new(ctx, (ast_expression*)array, (ast_expression*)parser_const_float(parser, from));
+         if (!subscript)
+             return NULL;
  
-         if (!var) {
-             parseerror(parser, "failed to create variable");
-             retval = false;
-             goto cleanup;
+         entfield = ast_entfield_new_force(ctx,
+                                           (ast_expression*)entity,
+                                           (ast_expression*)subscript,
+                                           (ast_expression*)subscript);
+         if (!entfield) {
+             ast_delete(subscript);
+             return NULL;
          }
  
-         if (parser->tok != TOKEN_IDENT) {
-             parseerror(parser, "expected variable name");
-             retval = false;
-             goto cleanup;
+         ast_store *st = ast_store_new(ctx, assignop, (ast_expression*)entfield, (ast_expression*)value);
+         if (!st) {
+             ast_delete(entfield);
+             return NULL;
          }
  
-         if (!localblock) {
-             bool was_end = false;
-             if      (!strcmp(parser_tokval(parser), "end_sys_globals")) {
-                 parser->crc_globals = parser->globals_count;
-                 was_end = true;
-             }
-             else if (!strcmp(parser_tokval(parser), "end_sys_fields")) {
-                 parser->crc_fields = parser->fields_count;
-                 was_end = true;
-             }
-             if (isfield && was_end) {
-                 if (parsewarning(parser, WARN_END_SYS_FIELDS,
-                                  "global '%s' hint should not be a field",
-                                  parser_tokval(parser)))
-                 {
-                     retval = false;
-                     goto cleanup;
-                 }
-             }
+         block = ast_block_new(ctx);
+         if (!block) {
+             ast_delete(st);
+             return NULL;
          }
  
-         if (!ast_value_set_name(var, parser_tokval(parser))) {
-             parseerror(parser, "failed to set variable name\n");
-             retval = false;
-             goto cleanup;
+         if (!ast_block_exprs_add(block, (ast_expression*)st)) {
+             ast_delete(block);
+             return NULL;
          }
  
-         if (isfunc) {
-             /* a function was defined */
-             ast_value *fval;
-             ast_value *proto = NULL;
-             bool dummy;
+         ret = ast_return_new(ctx, NULL);
+         if (!ret) {
+             ast_delete(block);
+             return NULL;
+         }
  
-             if (!localblock)
-                 olddecl = parser_find_global(parser, parser_tokval(parser));
-             else
-                 olddecl = parser_find_local(parser, parser_tokval(parser), parser->blocklocal, &dummy);
-             if (olddecl) {
-                 /* we had a prototype */
-                 if (!ast_istype(olddecl, ast_value)) {
-                     /* vector v;
-                      * void() v_x = {}
-                      */
-                     parseerror(parser, "cannot declare a function with the same name as a vector's member: %s",
-                                parser_tokval(parser));
-                     retval = false;
-                     goto cleanup;
-                 }
+         if (!ast_block_exprs_add(block, (ast_expression*)ret)) {
+             ast_delete(block);
+             return NULL;
+         }
  
-                 proto = (ast_value*)olddecl;
-             }
+         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);
+     }
+ }
  
-             /* turn var into a value of TYPE_FUNCTION, with the old var
-              * as return type
-              */
-             fval = ast_value_new(ctx, var->name, TYPE_FUNCTION);
-             if (!fval) {
-                 retval = false;
-                 goto cleanup;
-             }
+ 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);
  
-             fval->expression.next = (ast_expression*)var;
-             MEM_VECTOR_MOVE(&var->expression, params, &fval->expression, params);
-             fval->expression.variadic = var->expression.variadic;
-             var = NULL;
-             /* we compare the type late here, but it's easier than
-              * messing with the parameter-vector etc. earlier
-              */
-             if (proto) {
-                 size_t param;
-                 if (!ast_compare_type((ast_expression*)proto, (ast_expression*)fval)) {
-                     parseerror(parser, "conflicting types for `%s`, previous declaration was here: %s:%i",
-                                proto->name,
-                                ast_ctx(proto).file, ast_ctx(proto).line);
-                     ast_value_delete(fval);
-                     retval = false;
-                     goto cleanup;
-                 }
-                 /* copy over the parameter names */
-                 for (param = 0; param < fval->expression.params_count; ++param)
-                     ast_value_set_name(proto->expression.params[param], fval->expression.params[param]->name);
-                 /* copy the new context */
-                 ast_ctx(proto) = ast_ctx(fval);
+     if (from+1 == afterend) {
+         ast_return      *ret;
+         ast_array_index *subscript;
  
-                 /* now ditch the rest of the new data */
-                 ast_value_delete(fval);
-                 fval = proto;
-                 hadproto = true;
-             }
+         subscript = ast_array_index_new(ctx, (ast_expression*)array, (ast_expression*)parser_const_float(parser, from));
+         if (!subscript)
+             return NULL;
  
-             var = fval;
+         ret = ast_return_new(ctx, (ast_expression*)subscript);
+         if (!ret) {
+             ast_delete(subscript);
+             return NULL;
          }
  
-         if (isfield) {
-             ast_value *tmp;
-             fld = ast_value_new(ctx, var->name, TYPE_FIELD);
-             fld->expression.next = (ast_expression*)var;
-             tmp = var;
-             var = fld;
-             fld = tmp;
-         }
-         else
-             fld = var;
+         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);
+     }
+ }
  
-         if (!isfunc) {
-             if (!localblock)
-             {
-                 olddecl = parser_find_global(parser, var->name);
-                 if (olddecl) {
-                     if (!isfield) {
-                         parseerror(parser, "global `%s` already declared here: %s:%i",
-                                    var->name, ast_ctx(olddecl).file, (int)ast_ctx(olddecl).line);
-                         retval = false;
-                         goto cleanup;
-                     }
-                     else if (opts_standard == COMPILER_QCC) {
-                         parseerror(parser, "cannot declare a field and a global of the same name with -std=qcc");
-                         parseerror(parser, "global `%s` already declared here: %s:%i",
-                                    var->name, ast_ctx(olddecl).file, (int)ast_ctx(olddecl).line);
-                         retval = false;
-                         goto cleanup;
-                     }
-                 }
-                 olddecl = parser_find_field(parser, var->name);
-                 if (olddecl && opts_standard == COMPILER_QCC) {
-                     if (!isfield) {
-                         parseerror(parser, "cannot declare a field and a global of the same name with -std=qcc");
-                         parseerror(parser, "field `%s` already declared here: %s:%i",
-                                    var->name, ast_ctx(olddecl).file, (int)ast_ctx(olddecl).line);
-                         retval = false;
-                         goto cleanup;
-                     }
-                     else
-                     {
-                         if (parsewarning(parser, WARN_FIELD_REDECLARED, "field `%s` already declared here: %s:%i",
-                                          var->name, ast_ctx(olddecl).file, (int)ast_ctx(olddecl).line))
-                         {
-                             retval = false;
-                             goto cleanup;
-                         }
-                         if (!ast_compare_type(olddecl, (ast_expression*)var)) {
-                             parseerror(parser, "field %s has previously been declared with a different type here: %s:%i",
-                                        var->name, ast_ctx(olddecl).file, (int)ast_ctx(olddecl).line);
-                             retval = false;
-                             goto cleanup;
-                         }
-                         ast_delete(var);
-                         var = NULL;
-                         goto nextvar;
-                     }
-                 }
-                 else if (olddecl) {
-                     parseerror(parser, "field `%s` already declared here: %s:%i",
-                                var->name, ast_ctx(olddecl).file, (int)ast_ctx(olddecl).line);
-                     retval = false;
-                     goto cleanup;
-                 }
-             }
-             else /* if it's a local: */
-             {
-                 olddecl = parser_find_local(parser, var->name, parser->blocklocal, &isparam);
-                 if (opts_standard == COMPILER_GMQCC)
-                 {
-                     if (olddecl)
-                     {
-                         if (!isparam) {
-                             parseerror(parser, "local `%s` already declared here: %s:%i",
-                                        var->name, ast_ctx(olddecl).file, (int)ast_ctx(olddecl).line);
-                             retval = false;
-                             goto cleanup;
-                         }
-                     }
+ 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;
  
-                     if( (!isparam && olddecl) ||
-                         (olddecl = parser_find_local(parser, var->name, 0, &isparam))
-                       )
-                     {
-                         if (parsewarning(parser, WARN_LOCAL_SHADOWS,
-                                          "local `%s` is shadowing a parameter", var->name))
-                         {
-                             parseerror(parser, "local `%s` already declared here: %s:%i",
-                                        var->name, ast_ctx(olddecl).file, (int)ast_ctx(olddecl).line);
-                             retval = false;
-                             goto cleanup;
-                         }
-                     }
-                 }
-                 else
-                 {
-                     if (olddecl)
-                     {
-                         if (isparam &&
-                             parsewarning(parser, WARN_LOCAL_SHADOWS,
-                                          "a parameter is shadowing local `%s`", var->name))
-                         {
-                             ast_value_delete(var);
-                             var = NULL;
-                             retval = false;
-                             goto cleanup;
-                         }
-                         else if (!isparam)
-                         {
-                             parseerror(parser, "local `%s` already declared here: %s:%i",
-                                        var->name, ast_ctx(olddecl).file, (int)ast_ctx(olddecl).line);
-                             ast_value_delete(var);
-                             var = NULL;
-                             retval = false;
-                             goto cleanup;
-                         }
-                         ast_value_delete(var);
-                         var = NULL;
-                         goto nextvar;
-                     }
-                 }
+     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;
+ MEM_VEC_FUNCTIONS(paramlist_t, ast_value*, p)
+ static ast_value *parse_typename(parser_t *parser, ast_value **storebase);
+ static ast_value *parse_parameter_list(parser_t *parser, ast_value *var)
+ {
+     lex_ctx     ctx;
+     size_t      i;
+     paramlist_t params;
+     ast_value  *param;
+     ast_value  *fval;
+     bool        first = true;
+     bool        variadic = false;
+     ctx = parser_ctx(parser);
+     /* for the sake of less code we parse-in in this function */
+     if (!parser_next(parser)) {
+         parseerror(parser, "expected parameter list");
+         return NULL;
+     }
+     MEM_VECTOR_INIT(&params, p);
+     /* parse variables until we hit a closing paren */
+     while (parser->tok != ')') {
+         if (!first) {
+             /* there must be commas between them */
+             if (parser->tok != ',') {
+                 parseerror(parser, "expected comma or end of parameter list");
+                 goto on_error;
+             }
+             if (!parser_next(parser)) {
+                 parseerror(parser, "expected parameter");
+                 goto on_error;
+             }
+         }
+         first = false;
+         if (parser->tok == TOKEN_DOTS) {
+             /* '...' indicates a varargs function */
+             variadic = true;
+             if (!parser_next(parser)) {
+                 parseerror(parser, "expected parameter");
+                 return NULL;
+             }
+             if (parser->tok != ')') {
+                 parseerror(parser, "`...` must be the last parameter of a variadic function declaration");
+                 goto on_error;
+             }
+         }
+         else
+         {
+             /* for anything else just parse a typename */
+             param = parse_typename(parser, NULL);
+             if (!param)
+                 goto on_error;
+             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;
              }
          }
+     }
  
+     /* sanity check */
+     if (params.p_count > 8)
+         parseerror(parser, "more than 8 parameters are currently not supported");
  
-         if (!hadproto) {
-             varent.name = util_strdup(var->name);
-             varent.var = (ast_expression*)var;
+     /* parse-out */
+     if (!parser_next(parser)) {
+         parseerror(parser, "parse error after typename");
+         goto on_error;
+     }
  
-             if (!localblock) {
-                 if (!isfield) {
-                     if (!(retval = parser_t_globals_add(parser, varent)))
-                         goto cleanup;
+     /* now turn 'var' into a function type */
+     fval = ast_value_new(ctx, "<type()>", TYPE_FUNCTION);
+     fval->expression.next     = (ast_expression*)var;
+     fval->expression.variadic = variadic;
+     var = fval;
+     MEM_VECTOR_MOVE(&params, p, &var->expression, params);
+     return var;
+ on_error:
+     ast_delete(var);
+     for (i = 0; i < params.p_count; ++i)
+         ast_delete(params.p[i]);
+     MEM_VECTOR_CLEAR(&params, p);
+     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
+  * 'storebase' should point to where the base-type should be kept.
+  * The base type makes up every bit of type information which comes *before* the
+  * variable name.
+  *
+  * The following will be parsed in its entirety:
+  *     void() foo()
+  * The 'basetype' in this case is 'void()'
+  * and if there's a comma after it, say:
+  *     void() foo(), bar
+  * then the type-information 'void()' can be stored in 'storebase'
+  */
+ static ast_value *parse_typename(parser_t *parser, ast_value **storebase)
+ {
+     ast_value *var, *tmp;
+     lex_ctx    ctx;
+     const char *name = NULL;
+     bool        isfield  = false;
+     bool        wasarray = false;
+     ctx = parser_ctx(parser);
+     /* types may start with a dot */
+     if (parser->tok == '.') {
+         isfield = true;
+         /* if we parsed a dot we need a typename now */
+         if (!parser_next(parser)) {
+             parseerror(parser, "expected typename for field definition");
+             return NULL;
+         }
+         if (parser->tok != TOKEN_TYPENAME) {
+             parseerror(parser, "expected typename");
+             return NULL;
+         }
+     }
+     /* generate the basic type value */
+     var = ast_value_new(ctx, "<type>", parser_token(parser)->constval.t);
+     /* do not yet turn into a field - remember:
+      * .void() foo; is a field too
+      * .void()() foo; is a function
+      */
+     /* parse on */
+     if (!parser_next(parser)) {
+         ast_delete(var);
+         parseerror(parser, "parse error after typename");
+         return NULL;
+     }
+     /* 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;
+     }
+     /* 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 */
+     if (parser->tok == TOKEN_IDENT) {
+         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;
+         }
+     }
+     /* finally name it */
+     if (name) {
+         if (!ast_value_set_name(var, name)) {
+             ast_delete(var);
+             parseerror(parser, "internal error: failed to set name");
+             return NULL;
+         }
+         /* free the name, ast_value_set_name duplicates */
+         mem_d((void*)name);
+     }
+     return var;
+ }
+ static bool parse_variable(parser_t *parser, ast_block *localblock, bool nofields)
+ {
+     ast_value *var;
+     ast_value *proto;
+     ast_expression *old;
+     bool       was_end;
+     size_t     i;
+     ast_value *basetype = NULL;
+     bool      retval    = true;
+     bool      isparam   = false;
+     bool      isvector  = false;
+     bool      cleanvar  = true;
+     bool      wasarray  = false;
+     varentry_t varent, ve[3];
+     /* get the first complete variable */
+     var = parse_typename(parser, &basetype);
+     if (!var) {
+         if (basetype)
+             ast_delete(basetype);
+         return false;
+     }
+     memset(&varent, 0, sizeof(varent));
+     memset(&ve, 0, sizeof(ve));
+     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;
+                 goto cleanup;
+             }
+         }
+         /* Part 1:
+          * check for validity: (end_sys_..., multiple-definitions, prototypes, ...)
+          * Also: if there was a prototype, `var` will be deleted and set to `proto` which
+          * is then filled with the previous definition and the parameter-names replaced.
+          */
+         if (!localblock) {
+             /* Deal with end_sys_ vars */
+             was_end = false;
+             if (!strcmp(var->name, "end_sys_globals")) {
+                 parser->crc_globals = parser->globals_count;
+                 was_end = true;
+             }
+             else if (!strcmp(var->name, "end_sys_fields")) {
+                 parser->crc_fields = parser->fields_count;
+                 was_end = true;
+             }
+             if (was_end && var->expression.vtype == TYPE_FIELD) {
+                 if (parsewarning(parser, WARN_END_SYS_FIELDS,
+                                  "global '%s' hint should not be a field",
+                                  parser_tokval(parser)))
+                 {
+                     retval = false;
+                     goto cleanup;
                  }
-                 else {
-                     if (!(retval = parser_t_fields_add(parser, varent)))
+             }
+             if (!nofields && var->expression.vtype == TYPE_FIELD)
+             {
+                 /* deal with field declarations */
+                 old = parser_find_field(parser, var->name);
+                 if (old) {
+                     if (parsewarning(parser, WARN_FIELD_REDECLARED, "field `%s` already declared here: %s:%i",
+                                      var->name, ast_ctx(old).file, (int)ast_ctx(old).line))
+                     {
+                         retval = false;
                          goto cleanup;
+                     }
+                     ast_delete(var);
+                     var = NULL;
+                     goto skipvar;
+                     /*
+                     parseerror(parser, "field `%s` already declared here: %s:%i",
+                                var->name, ast_ctx(old).file, ast_ctx(old).line);
+                     retval = false;
+                     goto cleanup;
+                     */
                  }
-             } else {
-                 if (!(retval = parser_t_locals_add(parser, varent)))
+                 if (opts_standard == COMPILER_QCC &&
+                     (old = parser_find_global(parser, var->name)))
+                 {
+                     parseerror(parser, "cannot declare a field and a global of the same name with -std=qcc");
+                     parseerror(parser, "field `%s` already declared here: %s:%i",
+                                var->name, ast_ctx(old).file, ast_ctx(old).line);
+                     retval = false;
                      goto cleanup;
-                 if (!(retval = ast_block_locals_add(localblock, var))) {
-                     parser->locals_count--;
+                 }
+             }
+             else
+             {
+                 /* deal with other globals */
+                 old = parser_find_global(parser, var->name);
+                 if (old && var->expression.vtype == TYPE_FUNCTION && old->expression.vtype == TYPE_FUNCTION)
+                 {
+                     /* This is a function which had a prototype */
+                     if (!ast_istype(old, ast_value)) {
+                         parseerror(parser, "internal error: prototype is not an ast_value");
+                         retval = false;
+                         goto cleanup;
+                     }
+                     proto = (ast_value*)old;
+                     if (!ast_compare_type((ast_expression*)proto, (ast_expression*)var)) {
+                         parseerror(parser, "conflicting types for `%s`, previous declaration was here: %s:%i",
+                                    proto->name,
+                                    ast_ctx(proto).file, ast_ctx(proto).line);
+                         retval = false;
+                         goto cleanup;
+                     }
+                     /* we need the new parameter-names */
+                     for (i = 0; i < proto->expression.params_count; ++i)
+                         ast_value_set_name(proto->expression.params[i], var->expression.params[i]->name);
+                     ast_delete(var);
+                     var = proto;
+                 }
+                 else
+                 {
+                     /* other globals */
+                     if (old) {
+                         parseerror(parser, "global `%s` already declared here: %s:%i",
+                                    var->name, ast_ctx(old).file, ast_ctx(old).line);
+                         retval = false;
+                         goto cleanup;
+                     }
+                     if (opts_standard == COMPILER_QCC &&
+                         (old = parser_find_field(parser, var->name)))
+                     {
+                         parseerror(parser, "cannot declare a field and a global of the same name with -std=qcc");
+                         parseerror(parser, "global `%s` already declared here: %s:%i",
+                                    var->name, ast_ctx(old).file, ast_ctx(old).line);
+                         retval = false;
+                         goto cleanup;
+                     }
+                 }
+             }
+         }
+         else /* it's not a global */
+         {
+             old = parser_find_local(parser, var->name, parser->blocklocal, &isparam);
+             if (old && !isparam) {
+                 parseerror(parser, "local `%s` already declared here: %s:%i",
+                            var->name, ast_ctx(old).file, (int)ast_ctx(old).line);
+                 retval = false;
+                 goto cleanup;
+             }
+             old = parser_find_local(parser, var->name, 0, &isparam);
+             if (old && isparam) {
+                 if (parsewarning(parser, WARN_LOCAL_SHADOWS,
+                                  "local `%s` is shadowing a parameter", var->name))
+                 {
+                     parseerror(parser, "local `%s` already declared here: %s:%i",
+                                var->name, ast_ctx(old).file, (int)ast_ctx(old).line);
+                     retval = false;
                      goto cleanup;
                  }
+                 if (opts_standard != COMPILER_GMQCC) {
+                     ast_delete(var);
+                     var = NULL;
+                     goto skipvar;
+                 }
              }
+         }
  
-             if (fld->expression.vtype == TYPE_VECTOR)
-             {
-                 size_t e;
+         /* Part 2:
+          * Create the global/local, and deal with vector types.
+          */
+         if (!proto) {
+             if (var->expression.vtype == TYPE_VECTOR)
+                 isvector = true;
+             else if (var->expression.vtype == TYPE_FIELD &&
+                      var->expression.next->expression.vtype == TYPE_VECTOR)
+                 isvector = true;
+             if (isvector) {
                  if (!create_vector_members(parser, var, ve)) {
                      retval = false;
                      goto cleanup;
                  }
+             }
  
-                 if (!localblock) {
-                     for (e = 0; e < 3; ++e) {
-                         if (!isfield) {
-                             if (!(retval = parser_t_globals_add(parser, ve[e])))
+             varent.name = util_strdup(var->name);
+             varent.var  = (ast_expression*)var;
+             if (!localblock) {
+                 /* deal with global variables, fields, functions */
+                 if (!nofields && var->expression.vtype == TYPE_FIELD) {
+                     if (!(retval = parser_t_fields_add(parser, varent)))
+                         goto cleanup;
+                     if (isvector) {
+                         for (i = 0; i < 3; ++i) {
+                             if (!(retval = parser_t_fields_add(parser, ve[i])))
                                  break;
                          }
-                         else {
-                             if (!(retval = parser_t_fields_add(parser, ve[e])))
-                                 break;
+                         if (!retval) {
+                             parser->fields_count -= i+1;
+                             goto cleanup;
                          }
                      }
-                     if (!retval) {
-                         parser->globals_count -= e+1;
+                 }
+                 else {
+                     if (!(retval = parser_t_globals_add(parser, varent)))
                          goto cleanup;
+                     if (isvector) {
+                         for (i = 0; i < 3; ++i) {
+                             if (!(retval = parser_t_globals_add(parser, ve[i])))
+                                 break;
+                         }
+                         if (!retval) {
+                             parser->globals_count -= i+1;
+                             goto cleanup;
+                         }
                      }
-                 } else {
-                     for (e = 0; e < 3; ++e) {
-                         if (!(retval = parser_t_locals_add(parser, ve[e])))
+                 }
+             } else {
+                 if (!(retval = parser_t_locals_add(parser, varent)))
+                     goto cleanup;
+                 if (!(retval = ast_block_locals_add(localblock, var))) {
+                     parser->locals_count--;
+                     goto cleanup;
+                 }
+                 if (isvector) {
+                     for (i = 0; i < 3; ++i) {
+                         if (!(retval = parser_t_locals_add(parser, ve[i])))
                              break;
-                         if (!(retval = ast_block_collect(localblock, ve[e].var)))
+                         if (!(retval = ast_block_collect(localblock, ve[i].var)))
                              break;
-                         ve[e].var = NULL; /* from here it's being collected in the block */
+                         ve[i].var = NULL; /* from here it's being collected in the block */
                      }
                      if (!retval) {
-                         parser->locals_count -= e+1;
+                         parser->locals_count -= i+1;
                          localblock->locals_count--;
                          goto cleanup;
                      }
                  }
-                 ve[0].name = ve[1].name = ve[2].name = NULL;
-                 ve[0].var  = ve[1].var  = ve[2].var  = NULL;
              }
-             cleanvar = false;
              varent.name = NULL;
+             ve[0].name = ve[1].name = ve[2].name = NULL;
+             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;
  
- nextvar:
-         if (!(retval = parser_next(parser)))
-             goto cleanup;
+             if (!ast_istype(var->expression.next, ast_value)) {
+                 parseerror(parser, "internal error: field element type must be an ast_value");
+                 goto cleanup;
+             }
  
-         if (parser->tok == ';') {
-             ast_value_delete(typevar);
-             return parser_next(parser);
-         }
+             snprintf(name, sizeof(name), "%s##SETF", var->name);
+             if (!parser_create_array_field_setter(parser, array, name))
+                 goto cleanup;
  
-         if (parser->tok == ',') {
-             /* another var */
-             if (!(retval = parser_next(parser)))
+             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;
-             continue;
+             }
+             ast_delete(tfield);
          }
  
-         if (!localblock && isfield) {
-             parseerror(parser, "missing semicolon");
-             ast_value_delete(typevar);
-             return false;
+ skipvar:
+         if (parser->tok == ';') {
+             ast_delete(basetype);
+             if (!parser_next(parser)) {
+                 parseerror(parser, "error after variable declaration");
+                 return false;
+             }
+             return true;
+         }
+         if (parser->tok == ',')
+             goto another;
+         if (!var || (!localblock && !nofields && basetype->expression.vtype == TYPE_FIELD)) {
+             parseerror(parser, "missing comma or semicolon while parsing variables");
+             break;
          }
  
-         /* NOTE: only 'typevar' needs to be deleted from here on, so 'cleanup' won't be used
-          * to avoid having too many gotos
-          */
          if (localblock && opts_standard == COMPILER_QCC) {
              if (parsewarning(parser, WARN_LOCAL_CONSTANTS,
                               "initializing expression turns variable `%s` into a constant in this standard",
                               var->name) )
              {
-                 ast_value_delete(typevar);
-                 return false;
+                 break;
              }
          }
  
-         if (parser->tok != '=') {
-             if (opts_standard == COMPILER_QCC)
-                 parseerror(parser, "missing semicolon");
-             else
+         if (parser->tok != '{') {
+             if (parser->tok != '=') {
                  parseerror(parser, "missing semicolon or initializer");
-             ast_value_delete(typevar);
-             return false;
-         }
+                 break;
+             }
  
-         if (!parser_next(parser)) {
-             ast_value_delete(typevar);
-             return false;
+             if (!parser_next(parser)) {
+                 parseerror(parser, "error parsing initializer");
+                 break;
+             }
+         }
+         else if (opts_standard == COMPILER_QCC) {
+             parseerror(parser, "expected '=' before function body in this standard");
          }
  
          if (parser->tok == '#') {
  
              if (localblock) {
                  parseerror(parser, "cannot declare builtins within functions");
-                 ast_value_delete(typevar);
-                 return false;
+                 break;
              }
-             if (!isfunc) {
+             if (var->expression.vtype != TYPE_FUNCTION) {
                  parseerror(parser, "unexpected builtin number, '%s' is not a function", var->name);
-                 ast_value_delete(typevar);
-                 return false;
+                 break;
              }
              if (!parser_next(parser)) {
                  parseerror(parser, "expected builtin number");
-                 ast_value_delete(typevar);
-                 return false;
+                 break;
              }
              if (parser->tok != TOKEN_INTCONST) {
                  parseerror(parser, "builtin number must be an integer constant");
-                 ast_value_delete(typevar);
-                 return false;
+                 break;
              }
              if (parser_token(parser)->constval.i <= 0) {
-                 parseerror(parser, "builtin number must be positive integer greater than zero");
-                 ast_value_delete(typevar);
-                 return false;
+                 parseerror(parser, "builtin number must be an integer greater than zero");
+                 break;
              }
  
              func = ast_function_new(ast_ctx(var), var->name, var);
              if (!func) {
                  parseerror(parser, "failed to allocate function for `%s`", var->name);
-                 ast_value_delete(typevar);
-                 return false;
+                 break;
              }
              if (!parser_t_functions_add(parser, func)) {
                  parseerror(parser, "failed to allocate slot for function `%s`", var->name);
                  ast_function_delete(func);
                  var->constval.vfunc = NULL;
-                 ast_value_delete(typevar);
-                 return false;
+                 break;
              }
  
              func->builtin = -parser_token(parser)->constval.i;
  
              if (!parser_next(parser)) {
-                 ast_value_delete(typevar);
-                 return false;
+                 parseerror(parser, "expected comma or semicolon");
+                 ast_function_delete(func);
+                 var->constval.vfunc = NULL;
+                 break;
              }
          }
          else if (parser->tok == '{' || parser->tok == '[')
          {
-             ast_value_delete(typevar);
              if (localblock) {
                  parseerror(parser, "cannot declare functions within functions");
-                 return false;
+                 break;
              }
  
-             if (!parse_function_body(parser, var)) {
-                 return false;
-             }
+             if (!parse_function_body(parser, var))
+                 break;
+             ast_delete(basetype);
              return true;
          } else {
              ast_expression *cexp;
              ast_value      *cval;
  
              cexp = parse_expression_leave(parser, true);
-             if (!cexp) {
-                 ast_value_delete(typevar);
-                 return false;
-             }
+             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);
              }
          }
  
+ another:
          if (parser->tok == ',') {
-             /* another */
+             if (!parser_next(parser)) {
+                 parseerror(parser, "expected another variable");
+                 break;
+             }
+             if (parser->tok != TOKEN_IDENT) {
+                 parseerror(parser, "expected another variable");
+                 break;
+             }
+             var = ast_value_copy(basetype);
+             cleanvar = true;
+             ast_value_set_name(var, parser_tokval(parser));
+             if (!parser_next(parser)) {
+                 parseerror(parser, "error parsing variable declaration");
+                 break;
+             }
              continue;
          }
  
          if (parser->tok != ';') {
-             parseerror(parser, "missing semicolon");
-             ast_value_delete(typevar);
-             return false;
+             parseerror(parser, "missing semicolon after variables");
+             break;
          }
  
-         (void)parser_next(parser);
+         if (!parser_next(parser)) {
+             parseerror(parser, "parse error after variable declaration");
+             break;
+         }
  
-         ast_value_delete(typevar);
+         ast_delete(basetype);
          return true;
      }
  
+     if (cleanvar && var)
+         ast_delete(var);
+     ast_delete(basetype);
+     return false;
  cleanup:
-     ast_delete(typevar);
-     if (var && cleanvar) ast_delete(var);
+     ast_delete(basetype);
+     if (cleanvar && var)
+         ast_delete(var);
      if (varent.name) mem_d(varent.name);
      if (ve[0].name)  mem_d(ve[0].name);
      if (ve[1].name)  mem_d(ve[1].name);
      if (ve[0].var)   mem_d(ve[0].var);
      if (ve[1].var)   mem_d(ve[1].var);
      if (ve[2].var)   mem_d(ve[2].var);
      return retval;
  }
  
@@@ -2821,11 -3491,18 +3491,18 @@@ static bool parser_global_statement(par
  {
      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 == '$')
@@@ -2847,16 -3524,35 +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));
+     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(const char *filename)
 +bool parser_compile()
  {
 -    parser->lex = lex_open(filename);
 -    if (!parser->lex) {
 -        printf("failed to open file \"%s\"\n", filename);
 -        return false;
 -    }
 -
      /* initial lexer/parser state */
      parser->lex->flags.noops = true;
  
      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;
@@@ -3023,7 -3699,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;
          }
  
              field = (ast_value*)parser->fields[i].var;
              isconst = field->isconst;
              field->isconst = false;
-             if (!ast_global_codegen((ast_value*)field, ir)) {
+             if (!ast_global_codegen((ast_value*)field, ir, true)) {
 -                printf("failed to generate field %s\n", field->name);
 +                con_out("failed to generate field %s\n", field->name);
                  ir_builder_delete(ir);
                  return false;
              }
                                                     "unused global: `%s`", asvalue->name);
                  }
              }
-             if (!ast_global_codegen(asvalue, ir)) {
+             if (!ast_global_codegen(asvalue, ir, false)) {
 -                printf("failed to generate global %s\n", parser->globals[i].name);
 +                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)) {
+             if (!ast_global_codegen(parser->imm_float[i], ir, false)) {
 -                printf("failed to generate global %s\n", parser->imm_float[i]->name);
 +                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)) {
+             if (!ast_global_codegen(parser->imm_string[i], ir, false)) {
 -                printf("failed to generate global %s\n", parser->imm_string[i]->name);
 +                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)) {
+             if (!ast_global_codegen(parser->imm_vector[i], ir, false)) {
 -                printf("failed to generate global %s\n", parser->imm_vector[i]->name);
 +                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;
              }
  
          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;
              }
          return retval;
      }
  
 -    printf("*** there were compile errors\n");
 +    con_out("*** there were compile errors\n");
      return false;
  }