+ INSTR_DONE,
+ INSTR_MUL_F,
+ INSTR_MUL_V,
+ INSTR_MUL_FV,
+ INSTR_MUL_VF,
+ INSTR_DIV_F,
+ INSTR_ADD_F,
+ INSTR_ADD_V,
+ INSTR_SUB_F,
+ INSTR_SUB_V,
+ INSTR_EQ_F,
+ INSTR_EQ_V,
+ INSTR_EQ_S,
+ INSTR_EQ_E,
+ INSTR_EQ_FNC,
+ INSTR_NE_F,
+ INSTR_NE_V,
+ INSTR_NE_S,
+ INSTR_NE_E,
+ INSTR_NE_FNC,
+ INSTR_LE,
+ INSTR_GE,
+ INSTR_LT,
+ INSTR_GT,
+ INSTR_LOAD_F,
+ INSTR_LOAD_V,
+ INSTR_LOAD_S,
+ INSTR_LOAD_ENT,
+ INSTR_LOAD_FLD,
+ INSTR_LOAD_FNC,
+ INSTR_ADDRESS,
+ INSTR_STORE_F,
+ INSTR_STORE_V,
+ INSTR_STORE_S,
+ INSTR_STORE_ENT,
+ INSTR_STORE_FLD,
+ INSTR_STORE_FNC,
+ INSTR_STOREP_F,
+ INSTR_STOREP_V,
+ INSTR_STOREP_S,
+ INSTR_STOREP_ENT,
+ INSTR_STOREP_FLD,
+ INSTR_STOREP_FNC,
+ INSTR_RETURN,
+ INSTR_NOT_F,
+ INSTR_NOT_V,
+ INSTR_NOT_S,
+ INSTR_NOT_ENT,
+ INSTR_NOT_FNC,
+ INSTR_IF,
+ INSTR_IFNOT,
+ INSTR_CALL0,
+ INSTR_CALL1,
+ INSTR_CALL2,
+ INSTR_CALL3,
+ INSTR_CALL4,
+ INSTR_CALL5,
+ INSTR_CALL6,
+ INSTR_CALL7,
+ INSTR_CALL8,
+ INSTR_STATE,
+ INSTR_GOTO,
+ INSTR_AND,
+ INSTR_OR,
+ INSTR_BITAND,
+ INSTR_BITOR,
+
+ /*
+ * Virtual instructions used by the assembler
+ * keep at the end but before virtual instructions
+ * for the IR below.
+ */
+ AINSTR_END,
+
+ /*
+ * Virtual instructions used by the IR
+ * Keep at the end!
+ */
+ VINSTR_PHI,
+ VINSTR_JUMP,
+ VINSTR_COND
+};
+
+/*
+ * The symbols below are created by the following
+ * expanded macros:
+ *
+ * VECTOR_MAKE(prog_section_statement, code_statements);
+ * VECTOR_MAKE(prog_section_def, code_defs );
+ * VECTOR_MAKE(prog_section_field, code_fields );
+ * VECTOR_MAKE(prog_section_function, code_functions );
+ * VECTOR_MAKE(int, code_globals );
+ * VECTOR_MAKE(char, code_chars );
+ */
+VECTOR_PROT(prog_section_statement, code_statements);
+VECTOR_PROT(prog_section_statement, code_statements);
+VECTOR_PROT(prog_section_def, code_defs );
+VECTOR_PROT(prog_section_field, code_fields );
+VECTOR_PROT(prog_section_function, code_functions );
+VECTOR_PROT(int, code_globals );
+VECTOR_PROT(char, code_chars );
+
+typedef float qcfloat;
+typedef int32_t qcint;
+
+/*
+ * code_write -- writes out the compiled file
+ * code_init -- prepares the code file
+ */
+bool code_write (const char *filename);
+void code_init ();
+uint32_t code_genstring (const char *string);
+uint32_t code_cachedstring(const char *string);
+qcint code_alloc_field (size_t qcsize);
+
+/*===================================================================*/
+/*========================= assembler.c =============================*/
+/*===================================================================*/
+static const struct {
+ const char *m; /* menomic */
+ const size_t o; /* operands */
+ const size_t l; /* menomic len */
+} asm_instr[] = {
+ { "DONE" , 1, 4 },
+ { "MUL_F" , 3, 5 },
+ { "MUL_V" , 3, 5 },
+ { "MUL_FV" , 3, 6 },
+ { "MUL_VF" , 3, 6 },
+ { "DIV" , 0, 3 },
+ { "ADD_F" , 3, 5 },
+ { "ADD_V" , 3, 5 },
+ { "SUB_F" , 3, 5 },
+ { "DUB_V" , 3, 5 },
+ { "EQ_F" , 0, 4 },
+ { "EQ_V" , 0, 4 },
+ { "EQ_S" , 0, 4 },
+ { "EQ_E" , 0, 4 },
+ { "EQ_FNC" , 0, 6 },
+ { "NE_F" , 0, 4 },
+ { "NE_V" , 0, 4 },
+ { "NE_S" , 0, 4 },
+ { "NE_E" , 0, 4 },
+ { "NE_FNC" , 0, 6 },
+ { "LE" , 0, 2 },
+ { "GE" , 0, 2 },
+ { "LT" , 0, 2 },
+ { "GT" , 0, 2 },
+ { "FIELD_F" , 0, 7 },
+ { "FIELD_V" , 0, 7 },
+ { "FIELD_S" , 0, 7 },
+ { "FIELD_ENT" , 0, 9 },
+ { "FIELD_FLD" , 0, 9 },
+ { "FIELD_FNC" , 0, 9 },
+ { "ADDRESS" , 0, 7 },
+ { "STORE_F" , 0, 7 },
+ { "STORE_V" , 0, 7 },
+ { "STORE_S" , 0, 7 },
+ { "STORE_ENT" , 0, 9 },
+ { "STORE_FLD" , 0, 9 },
+ { "STORE_FNC" , 0, 9 },
+ { "STOREP_F" , 0, 8 },
+ { "STOREP_V" , 0, 8 },
+ { "STOREP_S" , 0, 8 },
+ { "STOREP_ENT", 0, 10},
+ { "STOREP_FLD", 0, 10},
+ { "STOREP_FNC", 0, 10},
+ { "RETURN" , 0, 6 },
+ { "NOT_F" , 0, 5 },
+ { "NOT_V" , 0, 5 },
+ { "NOT_S" , 0, 5 },
+ { "NOT_ENT" , 0, 7 },
+ { "NOT_FNC" , 0, 7 },
+ { "IF" , 0, 2 },
+ { "IFNOT" , 0, 5 },
+ { "CALL0" , 1, 5 },
+ { "CALL1" , 2, 5 },
+ { "CALL2" , 3, 5 },
+ { "CALL3" , 4, 5 },
+ { "CALL4" , 5, 5 },
+ { "CALL5" , 6, 5 },
+ { "CALL6" , 7, 5 },
+ { "CALL7" , 8, 5 },
+ { "CALL8" , 9, 5 },
+ { "STATE" , 0, 5 },
+ { "GOTO" , 0, 4 },
+ { "AND" , 0, 3 },
+ { "OR" , 0, 2 },
+ { "BITAND" , 0, 6 },
+ { "BITOR" , 0, 5 },
+
+ { "END" , 0, 3 } /* virtual assembler instruction */
+};
+
+void asm_init (const char *, FILE **);
+void asm_close(FILE *);
+void asm_parse(FILE *);
+/*===================================================================*/
+/*============================= ast.c ===============================*/
+/*===================================================================*/
+#define MEM_VECTOR_PROTO(Towner, Tmem, mem) \
+ bool GMQCC_WARN Towner##_##mem##_add(Towner*, Tmem); \
+ bool GMQCC_WARN Towner##_##mem##_remove(Towner*, size_t)
+
+#define MEM_VECTOR_PROTO_ALL(Towner, Tmem, mem) \
+ MEM_VECTOR_PROTO(Towner, Tmem, mem); \
+ bool GMQCC_WARN Towner##_##mem##_find(Towner*, Tmem, size_t*); \
+ void Towner##_##mem##_clear(Towner*)
+
+#define MEM_VECTOR_MAKE(Twhat, name) \
+ Twhat *name; \
+ size_t name##_count; \
+ size_t name##_alloc
+
+#define MEM_VEC_FUN_ADD(Tself, Twhat, mem) \
+bool GMQCC_WARN Tself##_##mem##_add(Tself *self, Twhat f) \
+{ \
+ Twhat *reall; \
+ if (self->mem##_count == self->mem##_alloc) { \
+ if (!self->mem##_alloc) { \
+ self->mem##_alloc = 16; \
+ } else { \
+ self->mem##_alloc *= 2; \
+ } \
+ reall = (Twhat*)mem_a(sizeof(Twhat) * self->mem##_alloc); \
+ if (!reall) { \
+ return false; \
+ } \
+ memcpy(reall, self->mem, sizeof(Twhat) * self->mem##_count); \
+ mem_d(self->mem); \
+ self->mem = reall; \
+ } \
+ self->mem[self->mem##_count++] = f; \
+ return true; \
+}
+
+#define MEM_VEC_FUN_REMOVE(Tself, Twhat, mem) \
+bool GMQCC_WARN Tself##_##mem##_remove(Tself *self, size_t idx) \
+{ \
+ size_t i; \
+ Twhat *reall; \
+ if (idx >= self->mem##_count) { \
+ return true; /* huh... */ \
+ } \
+ for (i = idx; i < self->mem##_count-1; ++i) { \
+ self->mem[i] = self->mem[i+1]; \
+ } \
+ self->mem##_count--; \
+ if (self->mem##_count < self->mem##_count/2) { \
+ self->mem##_alloc /= 2; \
+ reall = (Twhat*)mem_a(sizeof(Twhat) * self->mem##_count); \
+ if (!reall) { \
+ return false; \
+ } \
+ memcpy(reall, self->mem, sizeof(Twhat) * self->mem##_count); \
+ mem_d(self->mem); \
+ self->mem = reall; \
+ } \
+ return true; \
+}
+
+#define MEM_VEC_FUN_FIND(Tself, Twhat, mem) \
+bool GMQCC_WARN Tself##_##mem##_find(Tself *self, Twhat obj, size_t *idx) \
+{ \
+ size_t i; \
+ for (i = 0; i < self->mem##_count; ++i) { \
+ if (self->mem[i] == obj) { \
+ if (idx) { \
+ *idx = i; \
+ } \
+ return true; \
+ } \
+ } \
+ return false; \
+}
+
+#define MEM_VEC_FUN_APPEND(Tself, Twhat, mem) \
+bool GMQCC_WARN Tself##_##mem##_append(Tself *s, Twhat *p, size_t c) \
+{ \
+ Twhat *reall; \
+ if (s->mem##_count+c >= s->mem##_alloc) { \
+ if (!s->mem##_alloc) { \
+ s->mem##_alloc = c < 16 ? 16 : c; \
+ } else { \
+ s->mem##_alloc *= 2; \
+ if (s->mem##_count+c >= s->mem##_alloc) { \
+ s->mem##_alloc = s->mem##_count+c; \
+ } \
+ } \
+ reall = (Twhat*)mem_a(sizeof(Twhat) * s->mem##_alloc); \
+ if (!reall) { \
+ return false; \
+ } \
+ memcpy(reall, s->mem, sizeof(Twhat) * s->mem##_count); \
+ mem_d(s->mem); \
+ s->mem = reall; \
+ } \
+ memcpy(&s->mem[s->mem##_count], p, c*sizeof(*p)); \
+ s->mem##_count += c; \
+ return true; \
+}
+
+#define MEM_VEC_FUN_RESIZE(Tself, Twhat, mem) \
+bool GMQCC_WARN Tself##_##mem##_resize(Tself *s, size_t c) \
+{ \
+ Twhat *reall; \
+ if (c > s->mem##_alloc) { \
+ reall = (Twhat*)mem_a(sizeof(Twhat) * c); \
+ if (!reall) { return false; } \
+ memcpy(reall, s->mem, sizeof(Twhat) * s->mem##_count); \
+ s->mem##_alloc = c; \
+ mem_d(s->mem); \
+ s->mem = reall; \
+ return true; \
+ } \
+ s->mem##_count = c; \
+ if (c < (s->mem##_alloc / 2)) { \
+ reall = (Twhat*)mem_a(sizeof(Twhat) * c); \
+ if (!reall) { return false; } \
+ memcpy(reall, s->mem, sizeof(Twhat) * c); \
+ mem_d(s->mem); \
+ s->mem = reall; \
+ } \
+ return true; \
+}
+
+#define MEM_VEC_FUN_CLEAR(Tself, mem) \
+void Tself##_##mem##_clear(Tself *self) \
+{ \
+ if (!self->mem) \
+ return; \
+ mem_d((void*) self->mem); \
+ self->mem = NULL; \
+ self->mem##_count = 0; \
+ self->mem##_alloc = 0; \
+}
+
+#define MEM_VECTOR_CLEAR(owner, mem) \
+ if ((owner)->mem) \
+ mem_d((void*)((owner)->mem)); \
+ (owner)->mem = NULL; \
+ (owner)->mem##_count = 0; \
+ (owner)->mem##_alloc = 0
+
+#define MEM_VECTOR_INIT(owner, mem) \
+{ \
+ (owner)->mem = NULL; \
+ (owner)->mem##_count = 0; \
+ (owner)->mem##_alloc = 0; \
+}
+
+#define MEM_VECTOR_MOVE(from, mem, to, tm) \
+{ \
+ (to)->tm = (from)->mem; \
+ (to)->tm##_count = (from)->mem##_count; \
+ (to)->tm##_alloc = (from)->mem##_alloc; \
+ (from)->mem = NULL; \
+ (from)->mem##_count = 0; \
+ (from)->mem##_alloc = 0; \
+}
+
+#define MEM_VEC_FUNCTIONS(Tself, Twhat, mem) \
+MEM_VEC_FUN_REMOVE(Tself, Twhat, mem) \
+MEM_VEC_FUN_ADD(Tself, Twhat, mem)
+
+#define MEM_VEC_FUNCTIONS_ALL(Tself, Twhat, mem) \
+MEM_VEC_FUNCTIONS(Tself, Twhat, mem) \
+MEM_VEC_FUN_CLEAR(Tself, mem) \
+MEM_VEC_FUN_FIND(Tself, Twhat, mem)
+
+enum store_types {
+ store_global,
+ store_local, /* local, assignable for now, should get promoted later */
+ store_param, /* parameters, they are locals with a fixed position */
+ store_value, /* unassignable */
+ store_return /* unassignable, at OFS_RETURN */
+};
+
+typedef struct {
+ qcfloat x, y, z;
+} vector;
+
+vector vec3_add (vector, vector);
+vector vec3_sub (vector, vector);
+qcfloat vec3_mulvv(vector, vector);
+vector vec3_mulvf(vector, float);
+
+/*
+ * A shallow copy of a lex_file to remember where which ast node
+ * came from.
+ */
+typedef struct {
+ const char *file;
+ size_t line;
+} lex_ctx;
+
+/*===================================================================*/
+/*============================= exec.c ==============================*/
+/*===================================================================*/
+
+/* darkplaces has (or will have) a 64 bit prog loader
+ * where the 32 bit qc program is autoconverted on load.
+ * Since we may want to support that as well, let's redefine
+ * float and int here.
+ */
+typedef union {
+ qcint _int;
+ qcint string;
+ qcint function;
+ qcint edict;
+ qcfloat _float;
+ qcfloat vector[3];
+ qcint ivector[3];
+} qcany;
+
+typedef char qcfloat_size_is_correct [sizeof(qcfloat) == 4 ?1:-1];
+typedef char qcint_size_is_correct [sizeof(qcint) == 4 ?1:-1];
+
+enum {
+ VMERR_OK,
+ VMERR_TEMPSTRING_ALLOC,
+
+ VMERR_END