X-Git-Url: https://de.git.xonotic.org/?a=blobdiff_plain;f=parser.c;h=b193566f77e0ee5dbc27256794668b6c23692197;hb=24a21d08167dae7657d1785ec5db7c1727c7d238;hp=bb85edf3c75bb9e5907154f4f39a3e057071bf82;hpb=0bee75940616ea487cf52d1ea82c9f7a90c8d4e6;p=xonotic%2Fgmqcc.git diff --git a/parser.c b/parser.c index bb85edf..b193566 100644 --- a/parser.c +++ b/parser.c @@ -4,48 +4,125 @@ #include "gmqcc.h" #include "lexer.h" +typedef struct { + char *name; + ast_expression *var; +} varentry_t; + typedef struct { lex_file *lex; int tok; - MEM_VECTOR_MAKE(ast_value*, globals); + MEM_VECTOR_MAKE(varentry_t, globals); + MEM_VECTOR_MAKE(varentry_t, fields); MEM_VECTOR_MAKE(ast_function*, functions); MEM_VECTOR_MAKE(ast_value*, imm_float); MEM_VECTOR_MAKE(ast_value*, imm_string); MEM_VECTOR_MAKE(ast_value*, imm_vector); + ast_value *imm_float_zero; + ast_value *imm_vector_zero; + ast_function *function; - MEM_VECTOR_MAKE(ast_value*, locals); + MEM_VECTOR_MAKE(varentry_t, locals); size_t blocklocal; size_t errors; + + /* 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 + */ + qcint memberof; } parser_t; -MEM_VEC_FUNCTIONS(parser_t, ast_value*, globals) +MEM_VEC_FUNCTIONS(parser_t, varentry_t, globals) +MEM_VEC_FUNCTIONS(parser_t, varentry_t, fields) MEM_VEC_FUNCTIONS(parser_t, ast_value*, imm_float) MEM_VEC_FUNCTIONS(parser_t, ast_value*, imm_string) MEM_VEC_FUNCTIONS(parser_t, ast_value*, imm_vector) -MEM_VEC_FUNCTIONS(parser_t, ast_value*, locals) +MEM_VEC_FUNCTIONS(parser_t, varentry_t, locals) MEM_VEC_FUNCTIONS(parser_t, ast_function*, functions) +static void parser_pop_local(parser_t *parser); +static bool parser_variable(parser_t *parser, ast_block *localblock); +static ast_block* parser_parse_block(parser_t *parser); +static ast_expression* parser_parse_statement_or_block(parser_t *parser); +static ast_expression* parser_expression_leave(parser_t *parser, bool stopatcomma); +static ast_expression* parser_expression(parser_t *parser, bool stopatcomma); + void parseerror(parser_t *parser, const char *fmt, ...) { va_list ap; parser->errors++; - if (parser) - printf("error %s:%lu: ", parser->lex->tok->ctx.file, (unsigned long)parser->lex->tok->ctx.line); - else - printf("error: "); + va_start(ap, fmt); + vprintmsg(LVL_ERROR, parser->lex->tok->ctx.file, parser->lex->tok->ctx.line, "parse error", fmt, ap); + va_end(ap); +} + +/* returns true if it counts as an error */ +bool GMQCC_WARN parsewarning(parser_t *parser, int warntype, const char *fmt, ...) +{ + va_list ap; + int lvl = LVL_WARNING; + + if (!OPTS_WARN(warntype)) + return false; + + if (opts_werror) { + parser->errors++; + lvl = LVL_ERROR; + } va_start(ap, fmt); - vprintf(fmt, ap); + vprintmsg(lvl, parser->lex->tok->ctx.file, parser->lex->tok->ctx.line, "warning", fmt, ap); va_end(ap); - printf("\n"); + return opts_werror; +} + +/********************************************************************** + * some maths used for constant folding + */ + +vector vec3_add(vector a, vector b) +{ + vector out; + out.x = a.x + b.x; + out.y = a.y + b.y; + out.z = a.z + b.z; + return out; +} + +vector vec3_sub(vector a, vector b) +{ + vector out; + out.x = a.x - b.x; + out.y = a.y - b.y; + out.z = a.z - b.z; + return out; +} + +qcfloat vec3_mulvv(vector a, vector b) +{ + return (a.x * b.x + a.y * b.y + a.z * b.z); +} + +vector vec3_mulvf(vector a, float b) +{ + vector out; + out.x = a.x * b; + out.y = a.y * b; + out.z = a.z * b; + return out; } +/********************************************************************** + * parsing + */ + bool parser_next(parser_t *parser) { /* lex_do kills the previous token */ @@ -85,6 +162,24 @@ ast_value* parser_const_float(parser_t *parser, double d) return out; } +ast_value* parser_const_float_0(parser_t *parser) +{ + if (!parser->imm_float_zero) + parser->imm_float_zero = parser_const_float(parser, 0); + return parser->imm_float_zero; +} + +char *parser_strdup(const char *str) +{ + if (str && !*str) { + /* actually dup empty strings */ + char *out = mem_a(1); + *out = 0; + return out; + } + return util_strdup(str); +} + ast_value* parser_const_string(parser_t *parser, const char *str) { size_t i; @@ -95,7 +190,7 @@ ast_value* parser_const_string(parser_t *parser, const char *str) } out = ast_value_new(parser_ctx(parser), "#IMMEDIATE", TYPE_STRING); out->isconst = true; - out->constval.vstring = util_strdup(str); + out->constval.vstring = parser_strdup(str); if (!parser_t_imm_string_add(parser, out)) { ast_value_delete(out); return NULL; @@ -121,36 +216,64 @@ ast_value* parser_const_vector(parser_t *parser, vector v) return out; } -ast_value* parser_find_global(parser_t *parser, const char *name) +ast_value* parser_const_vector_f(parser_t *parser, float x, float y, float z) +{ + vector v; + v.x = x; + v.y = y; + v.z = z; + return parser_const_vector(parser, v); +} + +ast_value* parser_const_vector_0(parser_t *parser) +{ + if (!parser->imm_vector_zero) + parser->imm_vector_zero = parser_const_vector_f(parser, 0, 0, 0); + return parser->imm_vector_zero; +} + +ast_expression* parser_find_field(parser_t *parser, const char *name) +{ + size_t i; + for (i = 0; i < parser->fields_count; ++i) { + if (!strcmp(parser->fields[i].name, name)) + return parser->fields[i].var; + } + return NULL; +} + +ast_expression* parser_find_global(parser_t *parser, const char *name) { size_t i; for (i = 0; i < parser->globals_count; ++i) { - if (!strcmp(parser->globals[i]->name, name)) - return parser->globals[i]; + if (!strcmp(parser->globals[i].name, name)) + return parser->globals[i].var; } return NULL; } -ast_value* parser_find_local(parser_t *parser, const char *name, size_t upto) +ast_expression* parser_find_local(parser_t *parser, const char *name, size_t upto) { size_t i; ast_value *fun; for (i = parser->locals_count; i > upto;) { --i; - if (!strcmp(parser->locals[i]->name, name)) - return parser->locals[i]; + if (!strcmp(parser->locals[i].name, name)) + return parser->locals[i].var; } + if (!parser->function) + return NULL; fun = parser->function->vtype; for (i = 0; i < fun->expression.params_count; ++i) { if (!strcmp(fun->expression.params[i]->name, name)) - return fun->expression.params[i]; + return (ast_expression*)(fun->expression.params[i]); } return NULL; } -ast_value* parser_find_var(parser_t *parser, const char *name) +ast_expression* parser_find_var(parser_t *parser, const char *name) { - ast_value *v; + ast_expression *v; v = parser_find_local(parser, name, 0); if (!v) v = parser_find_global(parser, name); return v; @@ -178,6 +301,8 @@ static ast_value *parser_parse_type(parser_t *parser, int basetype, bool *isfunc *isfunc = true; while (true) { ast_value *param; + ast_value *fld; + bool isfield = false; bool dummy; if (!parser_next(parser)) @@ -186,6 +311,14 @@ static ast_value *parser_parse_type(parser_t *parser, int basetype, bool *isfunc if (parser->tok == ')') break; + if (parser->tok == '.') { + isfield = true; + if (!parser_next(parser)) { + parseerror(parser, "expected field parameter type"); + goto on_error; + } + } + temptype = parser_token(parser)->constval.t; if (!parser_next(parser)) goto on_error; @@ -204,6 +337,12 @@ static ast_value *parser_parse_type(parser_t *parser, int basetype, bool *isfunc goto on_error; } + if (isfield) { + fld = ast_value_new(ctx, param->name, TYPE_FIELD); + fld->expression.next = (ast_expression*)param; + param = fld; + } + if (!paramlist_t_p_add(¶ms, param)) { parseerror(parser, "Out of memory while parsing typename"); goto on_error; @@ -290,6 +429,12 @@ static sy_elem syparen(lex_ctx ctx, int p, size_t off) { return e; } +#ifdef DEBUGSHUNT +# define DEBUGSHUNTDO(x) x +#else +# define DEBUGSHUNTDO(x) +#endif + static bool parser_sy_pop(parser_t *parser, shunt *sy) { const oper_info *op; @@ -297,7 +442,9 @@ static bool parser_sy_pop(parser_t *parser, shunt *sy) ast_expression *out = NULL; ast_expression *exprs[3]; ast_block *blocks[3]; - size_t i; + ast_value *asvalue[3]; + size_t i, assignop; + qcint generated_op = 0; if (!sy->ops_count) { parseerror(parser, "internal error: missing operator"); @@ -312,8 +459,11 @@ static bool parser_sy_pop(parser_t *parser, shunt *sy) op = &operators[sy->ops[sy->ops_count-1].etype - 1]; ctx = sy->ops[sy->ops_count-1].ctx; + DEBUGSHUNTDO(printf("apply %s\n", op->op)); + if (sy->out_count < op->operands) { - parseerror(parser, "internal error: not enough operands: %i", sy->out_count); + parseerror(parser, "internal error: not enough operands: %i (operator %s (%i))", sy->out_count, + op->op, (int)op->id); return false; } @@ -323,6 +473,7 @@ static bool parser_sy_pop(parser_t *parser, shunt *sy) for (i = 0; i < op->operands; ++i) { exprs[i] = sy->out[sy->out_count+i].out; blocks[i] = sy->out[sy->out_count+i].block; + asvalue[i] = (ast_value*)exprs[i]; } if (blocks[0] && !blocks[0]->exprs_count && op->id != opid1(',')) { @@ -330,12 +481,40 @@ static bool parser_sy_pop(parser_t *parser, shunt *sy) return false; } +#define NotSameType(T) \ + (exprs[0]->expression.vtype != exprs[1]->expression.vtype || \ + exprs[0]->expression.vtype != T) +#define CanConstFold1(A) \ + (ast_istype((A), ast_value) && ((ast_value*)(A))->isconst) +#define CanConstFold(A, B) \ + (CanConstFold1(A) && CanConstFold1(B)) +#define ConstV(i) (asvalue[(i)]->constval.vvec) +#define ConstF(i) (asvalue[(i)]->constval.vfloat) +#define ConstS(i) (asvalue[(i)]->constval.vstring) switch (op->id) { default: - parseerror(parser, "internal error: unhandled operand"); + parseerror(parser, "internal error: unhandled operator: %s (%i)", op->op, (int)op->id); return false; + case opid1('.'): + if (exprs[0]->expression.vtype == TYPE_ENTITY) { + if (exprs[1]->expression.vtype != TYPE_FIELD) { + parseerror(parser, "type error: right hand of member-operand should be an entity-field"); + return false; + } + out = (ast_expression*)ast_entfield_new(ctx, exprs[0], exprs[1]); + } + else if (exprs[0]->expression.vtype == TYPE_VECTOR) { + parseerror(parser, "internal error: vector access is not supposed to be handled at this point"); + return false; + } + else { + parseerror(parser, "type error: member-of operator on something that is not an entity or vector"); + return false; + } + break; + case opid1(','): if (blocks[0]) { if (!ast_block_exprs_add(blocks[0], exprs[1])) @@ -354,43 +533,122 @@ static bool parser_sy_pop(parser_t *parser, shunt *sy) sy->out[sy->out_count++] = syblock(ctx, blocks[0]); return true; + case opid2('-','P'): + switch (exprs[0]->expression.vtype) { + case TYPE_FLOAT: + if (CanConstFold1(exprs[0])) + out = (ast_expression*)parser_const_float(parser, -ConstF(0)); + else + out = (ast_expression*)ast_binary_new(ctx, INSTR_SUB_F, + (ast_expression*)parser_const_float_0(parser), + exprs[0]); + break; + case TYPE_VECTOR: + if (CanConstFold1(exprs[0])) + out = (ast_expression*)parser_const_vector_f(parser, + -ConstV(0).x, -ConstV(0).y, -ConstV(0).z); + else + out = (ast_expression*)ast_binary_new(ctx, INSTR_SUB_V, + (ast_expression*)parser_const_vector_0(parser), + exprs[0]); + break; + default: + parseerror(parser, "invalid types used in expression: cannot negate type %s", + type_name[exprs[0]->expression.vtype]); + return false; + } + break; + + case opid2('!','P'): + switch (exprs[0]->expression.vtype) { + case TYPE_FLOAT: + if (CanConstFold1(exprs[0])) + out = (ast_expression*)parser_const_float(parser, !ConstF(0)); + else + out = (ast_expression*)ast_unary_new(ctx, INSTR_NOT_F, exprs[0]); + break; + case TYPE_VECTOR: + if (CanConstFold1(exprs[0])) + out = (ast_expression*)parser_const_float(parser, + (!ConstV(0).x && !ConstV(0).y && !ConstV(0).z)); + else + out = (ast_expression*)ast_unary_new(ctx, INSTR_NOT_V, exprs[0]); + break; + case TYPE_STRING: + if (CanConstFold1(exprs[0])) + out = (ast_expression*)parser_const_float(parser, !ConstS(0) || !*ConstS(0)); + else + out = (ast_expression*)ast_unary_new(ctx, INSTR_NOT_S, exprs[0]); + break; + /* we don't constant-fold NOT for these types */ + case TYPE_ENTITY: + out = (ast_expression*)ast_unary_new(ctx, INSTR_NOT_ENT, exprs[0]); + break; + case TYPE_FUNCTION: + out = (ast_expression*)ast_unary_new(ctx, INSTR_NOT_FNC, exprs[0]); + break; + default: + parseerror(parser, "invalid types used in expression: cannot logically negate type %s", + type_name[exprs[0]->expression.vtype]); + return false; + } + break; + case opid1('+'): - if (exprs[0]->expression.vtype != exprs[1]->expression.vtype) { - parseerror(parser, "Cannot add type %s and %s", + if (exprs[0]->expression.vtype != exprs[1]->expression.vtype || + (exprs[0]->expression.vtype != TYPE_VECTOR && exprs[0]->expression.vtype != TYPE_FLOAT) ) + { + parseerror(parser, "invalid types used in expression: cannot add type %s and %s", type_name[exprs[0]->expression.vtype], type_name[exprs[1]->expression.vtype]); return false; } switch (exprs[0]->expression.vtype) { case TYPE_FLOAT: - out = (ast_expression*)ast_binary_new(ctx, INSTR_ADD_F, exprs[0], exprs[1]); + if (CanConstFold(exprs[0], exprs[1])) + { + out = (ast_expression*)parser_const_float(parser, ConstF(0) + ConstF(1)); + } + else + out = (ast_expression*)ast_binary_new(ctx, INSTR_ADD_F, exprs[0], exprs[1]); break; case TYPE_VECTOR: - out = (ast_expression*)ast_binary_new(ctx, INSTR_ADD_V, exprs[0], exprs[1]); + if (CanConstFold(exprs[0], exprs[1])) + out = (ast_expression*)parser_const_vector(parser, vec3_add(ConstV(0), ConstV(1))); + else + out = (ast_expression*)ast_binary_new(ctx, INSTR_ADD_V, exprs[0], exprs[1]); break; default: - parseerror(parser, "Cannot add type %s and %s", + parseerror(parser, "invalid types used in expression: cannot add type %s and %s", type_name[exprs[0]->expression.vtype], type_name[exprs[1]->expression.vtype]); return false; }; break; case opid1('-'): - if (exprs[0]->expression.vtype != exprs[1]->expression.vtype) { - parseerror(parser, "Cannot subtract type %s from %s", + if (exprs[0]->expression.vtype != exprs[1]->expression.vtype || + (exprs[0]->expression.vtype != TYPE_VECTOR && exprs[0]->expression.vtype != TYPE_FLOAT) ) + { + parseerror(parser, "invalid types used in expression: cannot subtract type %s from %s", type_name[exprs[1]->expression.vtype], type_name[exprs[0]->expression.vtype]); return false; } switch (exprs[0]->expression.vtype) { case TYPE_FLOAT: - out = (ast_expression*)ast_binary_new(ctx, INSTR_SUB_F, exprs[0], exprs[1]); + if (CanConstFold(exprs[0], exprs[1])) + out = (ast_expression*)parser_const_float(parser, ConstF(0) - ConstF(1)); + else + out = (ast_expression*)ast_binary_new(ctx, INSTR_SUB_F, exprs[0], exprs[1]); break; case TYPE_VECTOR: - out = (ast_expression*)ast_binary_new(ctx, INSTR_SUB_V, exprs[0], exprs[1]); + if (CanConstFold(exprs[0], exprs[1])) + out = (ast_expression*)parser_const_vector(parser, vec3_sub(ConstV(0), ConstV(1))); + else + out = (ast_expression*)ast_binary_new(ctx, INSTR_SUB_V, exprs[0], exprs[1]); break; default: - parseerror(parser, "Cannot add type %s from %s", + parseerror(parser, "invalid types used in expression: cannot subtract type %s from %s", type_name[exprs[1]->expression.vtype], type_name[exprs[0]->expression.vtype]); return false; @@ -403,7 +661,7 @@ static bool parser_sy_pop(parser_t *parser, shunt *sy) exprs[1]->expression.vtype != TYPE_VECTOR && exprs[1]->expression.vtype != TYPE_FLOAT) { - parseerror(parser, "Cannot multiply type %s from %s", + parseerror(parser, "invalid types used in expression: cannot multiply types %s and %s", type_name[exprs[1]->expression.vtype], type_name[exprs[0]->expression.vtype]); return false; @@ -411,48 +669,191 @@ static bool parser_sy_pop(parser_t *parser, shunt *sy) switch (exprs[0]->expression.vtype) { case TYPE_FLOAT: if (exprs[1]->expression.vtype == TYPE_VECTOR) - out = (ast_expression*)ast_binary_new(ctx, INSTR_MUL_FV, exprs[0], exprs[1]); + { + if (CanConstFold(exprs[0], exprs[1])) + out = (ast_expression*)parser_const_vector(parser, vec3_mulvf(ConstV(1), ConstF(0))); + else + out = (ast_expression*)ast_binary_new(ctx, INSTR_MUL_FV, exprs[0], exprs[1]); + } else - out = (ast_expression*)ast_binary_new(ctx, INSTR_MUL_F, exprs[0], exprs[1]); + { + if (CanConstFold(exprs[0], exprs[1])) + out = (ast_expression*)parser_const_float(parser, ConstF(0) * ConstF(1)); + else + out = (ast_expression*)ast_binary_new(ctx, INSTR_MUL_F, exprs[0], exprs[1]); + } break; case TYPE_VECTOR: if (exprs[1]->expression.vtype == TYPE_FLOAT) - out = (ast_expression*)ast_binary_new(ctx, INSTR_MUL_VF, exprs[0], exprs[1]); + { + if (CanConstFold(exprs[0], exprs[1])) + out = (ast_expression*)parser_const_vector(parser, vec3_mulvf(ConstV(0), ConstF(1))); + else + out = (ast_expression*)ast_binary_new(ctx, INSTR_MUL_VF, exprs[0], exprs[1]); + } else - out = (ast_expression*)ast_binary_new(ctx, INSTR_MUL_V, exprs[0], exprs[1]); + { + if (CanConstFold(exprs[0], exprs[1])) + out = (ast_expression*)parser_const_float(parser, vec3_mulvv(ConstV(0), ConstV(1))); + else + out = (ast_expression*)ast_binary_new(ctx, INSTR_MUL_V, exprs[0], exprs[1]); + } break; default: - parseerror(parser, "Cannot add type %s from %s", + parseerror(parser, "invalid types used in expression: cannot multiply types %s and %s", type_name[exprs[1]->expression.vtype], type_name[exprs[0]->expression.vtype]); return false; }; break; case opid1('/'): - if (exprs[0]->expression.vtype != exprs[1]->expression.vtype || - exprs[0]->expression.vtype != TYPE_FLOAT) - { - parseerror(parser, "Cannot divide types %s and %s", + if (NotSameType(TYPE_FLOAT)) { + parseerror(parser, "invalid types used in expression: cannot divide types %s and %s", + type_name[exprs[0]->expression.vtype], + type_name[exprs[1]->expression.vtype]); + return false; + } + if (CanConstFold(exprs[0], exprs[1])) + out = (ast_expression*)parser_const_float(parser, ConstF(0) / ConstF(1)); + else + out = (ast_expression*)ast_binary_new(ctx, INSTR_DIV_F, exprs[0], exprs[1]); + break; + case opid1('%'): + case opid2('%','='): + parseerror(parser, "qc does not have a modulo operator"); + return false; + case opid1('|'): + case opid1('&'): + if (NotSameType(TYPE_FLOAT)) { + parseerror(parser, "invalid types used in expression: cannot perform bit operations between types %s and %s", + type_name[exprs[0]->expression.vtype], + type_name[exprs[1]->expression.vtype]); + return false; + } + if (CanConstFold(exprs[0], exprs[1])) + out = (ast_expression*)parser_const_float(parser, + (op->id == opid1('|') ? (float)( ((qcint)ConstF(0)) | ((qcint)ConstF(1)) ) : + (float)( ((qcint)ConstF(0)) & ((qcint)ConstF(1)) ) )); + else + out = (ast_expression*)ast_binary_new(ctx, + (op->id == opid1('|') ? INSTR_BITOR : INSTR_BITAND), + exprs[0], exprs[1]); + break; + case opid1('^'): + parseerror(parser, "TODO: bitxor"); + return false; + + case opid2('<','<'): + case opid2('>','>'): + case opid3('<','<','='): + case opid3('>','>','='): + parseerror(parser, "TODO: shifts"); + return false; + + case opid2('|','|'): + generated_op += 1; /* INSTR_OR */ + case opid2('&','&'): + generated_op += INSTR_AND; + if (NotSameType(TYPE_FLOAT)) { + parseerror(parser, "invalid types used in expression: cannot perform logical operations between types %s and %s", type_name[exprs[0]->expression.vtype], type_name[exprs[1]->expression.vtype]); + parseerror(parser, "TODO: logical ops for arbitrary types using INSTR_NOT"); + parseerror(parser, "TODO: optional early out"); return false; } - out = (ast_expression*)ast_binary_new(ctx, INSTR_DIV_F, exprs[0], exprs[1]); + if (opts_standard == COMPILER_GMQCC) + printf("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)))); + else + out = (ast_expression*)ast_binary_new(ctx, generated_op, exprs[0], exprs[1]); break; + case opid1('>'): + generated_op += 1; /* INSTR_GT */ + case opid1('<'): + generated_op += 1; /* INSTR_LT */ + case opid2('>', '='): + generated_op += 1; /* INSTR_GE */ + case opid2('<', '='): + generated_op += INSTR_LE; + if (NotSameType(TYPE_FLOAT)) { + parseerror(parser, "invalid types used in expression: cannot perform comparison between types %s and %s", + type_name[exprs[0]->expression.vtype], + type_name[exprs[1]->expression.vtype]); + return false; + } + out = (ast_expression*)ast_binary_new(ctx, generated_op, exprs[0], exprs[1]); + break; + case opid2('!', '='): + if (exprs[0]->expression.vtype != exprs[1]->expression.vtype) { + parseerror(parser, "invalid types used in expression: cannot perform comparison between types %s and %s", + type_name[exprs[0]->expression.vtype], + type_name[exprs[1]->expression.vtype]); + return false; + } + out = (ast_expression*)ast_binary_new(ctx, type_ne_instr[exprs[0]->expression.vtype], exprs[0], exprs[1]); + break; + case opid2('=', '='): + if (exprs[0]->expression.vtype != exprs[1]->expression.vtype) { + parseerror(parser, "invalid types used in expression: cannot perform comparison between types %s and %s", + type_name[exprs[0]->expression.vtype], + type_name[exprs[1]->expression.vtype]); + return false; + } + out = (ast_expression*)ast_binary_new(ctx, type_eq_instr[exprs[0]->expression.vtype], exprs[0], exprs[1]); + break; case opid1('='): - out = (ast_expression*)ast_store_new(ctx, - type_store_instr[exprs[0]->expression.vtype], - exprs[0], exprs[1]); + if (ast_istype(exprs[0], ast_entfield)) + assignop = type_storep_instr[exprs[0]->expression.vtype]; + else + assignop = type_store_instr[exprs[0]->expression.vtype]; + out = (ast_expression*)ast_store_new(ctx, assignop, exprs[0], exprs[1]); + break; + case opid2('+','='): + case opid2('-','='): + if (exprs[0]->expression.vtype != exprs[1]->expression.vtype || + (exprs[0]->expression.vtype != TYPE_VECTOR && exprs[0]->expression.vtype != TYPE_FLOAT) ) + { + parseerror(parser, "invalid types used in expression: cannot add or subtract type %s and %s", + type_name[exprs[0]->expression.vtype], + type_name[exprs[1]->expression.vtype]); + return false; + } + if (ast_istype(exprs[0], ast_entfield)) + assignop = type_storep_instr[exprs[0]->expression.vtype]; + else + assignop = type_store_instr[exprs[0]->expression.vtype]; + switch (exprs[0]->expression.vtype) { + case TYPE_FLOAT: + out = (ast_expression*)ast_binstore_new(ctx, assignop, + (op->id == opid2('+','=') ? INSTR_ADD_F : INSTR_SUB_F), + exprs[0], exprs[1]); + break; + case TYPE_VECTOR: + out = (ast_expression*)ast_binstore_new(ctx, assignop, + (op->id == opid2('+','=') ? INSTR_ADD_V : INSTR_SUB_V), + exprs[0], exprs[1]); + break; + default: + parseerror(parser, "invalid types used in expression: cannot add or subtract type %s and %s", + type_name[exprs[0]->expression.vtype], + type_name[exprs[1]->expression.vtype]); + return false; + }; break; } +#undef NotSameType if (!out) { parseerror(parser, "failed to apply operand %s", op->op); return false; } + DEBUGSHUNTDO(printf("applied %s\n", op->op)); sy->out[sy->out_count++] = syexp(ctx, out); return true; } @@ -518,7 +919,7 @@ static bool parser_close_call(parser_t *parser, shunt *sy) sy->out[fid] = syexp(call->expression.node.context, (ast_expression*)call); if (fun->expression.vtype != TYPE_FUNCTION) { - parseerror(parser, "not a function"); + parseerror(parser, "not a function (%s)", type_name[fun->expression.vtype]); return false; } @@ -561,145 +962,268 @@ static bool parser_close_paren(parser_t *parser, shunt *sy, bool functions_only) return true; } -static ast_expression* parser_expression(parser_t *parser) +static void parser_reclassify_token(parser_t *parser) +{ + size_t i; + for (i = 0; i < operator_count; ++i) { + if (!strcmp(parser_tokval(parser), operators[i].op)) { + parser->tok = TOKEN_OPERATOR; + return; + } + } +} + +static ast_expression* parser_expression_leave(parser_t *parser, bool stopatcomma) { ast_expression *expr = NULL; shunt sy; bool wantop = false; + bool gotmemberof = false; + + /* count the parens because an if starts with one, so the + * end of a condition is an unmatched closing paren + */ + int parens = 0; MEM_VECTOR_INIT(&sy, out); MEM_VECTOR_INIT(&sy, ops); + parser->lex->flags.noops = false; + + parser_reclassify_token(parser); + while (true) { - if (!wantop) + if (gotmemberof) + gotmemberof = false; + else + parser->memberof = 0; + + if (parser->tok == TOKEN_IDENT) { - bool nextwant = true; - if (parser->tok == TOKEN_IDENT) + ast_expression *var; + if (wantop) { + parseerror(parser, "expected operator or end of statement"); + goto onerr; + } + wantop = true; + /* variable */ + if (opts_standard == COMPILER_GMQCC) { - /* variable */ - ast_value *var = parser_find_var(parser, parser_tokval(parser)); - if (!var) { - parseerror(parser, "unexpected ident: %s", parser_tokval(parser)); - goto onerr; + if (parser->memberof == TYPE_ENTITY) { + /* still get vars first since there could be a fieldpointer */ + var = parser_find_var(parser, parser_tokval(parser)); + if (!var) + var = parser_find_field(parser, parser_tokval(parser)); } - if (!shunt_out_add(&sy, syexp(parser_ctx(parser), (ast_expression*)var))) { - parseerror(parser, "out of memory"); + else if (parser->memberof == TYPE_VECTOR) + { + parseerror(parser, "TODO: implement effective vector member access"); goto onerr; } - } - else if (parser->tok == TOKEN_FLOATCONST) { - ast_value *val = parser_const_float(parser, (parser_token(parser)->constval.f)); - if (!val) - return false; - if (!shunt_out_add(&sy, syexp(parser_ctx(parser), (ast_expression*)val))) { - parseerror(parser, "out of memory"); + else if (parser->memberof) { + parseerror(parser, "namespace for member not found"); goto onerr; } + else + var = parser_find_var(parser, parser_tokval(parser)); + } else { + var = parser_find_var(parser, parser_tokval(parser)); + if (!var) + var = parser_find_field(parser, parser_tokval(parser)); } - else if (parser->tok == TOKEN_INTCONST) { - ast_value *val = parser_const_float(parser, (double)(parser_token(parser)->constval.i)); - if (!val) - return false; - if (!shunt_out_add(&sy, syexp(parser_ctx(parser), (ast_expression*)val))) { - parseerror(parser, "out of memory"); - goto onerr; - } + if (!var) { + parseerror(parser, "unexpected ident: %s", parser_tokval(parser)); + goto onerr; } - else if (parser->tok == TOKEN_STRINGCONST) { - ast_value *val = parser_const_string(parser, parser_tokval(parser)); - if (!val) - return false; - if (!shunt_out_add(&sy, syexp(parser_ctx(parser), (ast_expression*)val))) { - parseerror(parser, "out of memory"); - goto onerr; - } + if (!shunt_out_add(&sy, syexp(parser_ctx(parser), var))) { + parseerror(parser, "out of memory"); + goto onerr; } - else if (parser->tok == TOKEN_VECTORCONST) { - ast_value *val = parser_const_vector(parser, parser_token(parser)->constval.v); - if (!val) - return false; - if (!shunt_out_add(&sy, syexp(parser_ctx(parser), (ast_expression*)val))) { - parseerror(parser, "out of memory"); - goto onerr; - } + DEBUGSHUNTDO(printf("push %s\n", parser_tokval(parser))); + } + else if (parser->tok == TOKEN_FLOATCONST) { + ast_value *val; + if (wantop) { + parseerror(parser, "expected operator or end of statement, got constant"); + goto onerr; } - else if (parser->tok == '(') { - nextwant = false; /* not expecting an operator next */ - if (!shunt_ops_add(&sy, syparen(parser_ctx(parser), 1, 0))) { - parseerror(parser, "out of memory"); - goto onerr; - } + wantop = true; + val = parser_const_float(parser, (parser_token(parser)->constval.f)); + if (!val) + return false; + if (!shunt_out_add(&sy, syexp(parser_ctx(parser), (ast_expression*)val))) { + parseerror(parser, "out of memory"); + goto onerr; } - else if (parser->tok == ')') { - /* allowed for function calls */ - if (!parser_close_paren(parser, &sy, true)) - goto onerr; + DEBUGSHUNTDO(printf("push %g\n", parser_token(parser)->constval.f)); + } + else if (parser->tok == TOKEN_INTCONST) { + ast_value *val; + if (wantop) { + parseerror(parser, "expected operator or end of statement, got constant"); + goto onerr; } - else { - /* TODO: prefix operators */ - parseerror(parser, "expected statement"); + wantop = true; + val = parser_const_float(parser, (double)(parser_token(parser)->constval.i)); + if (!val) + return false; + if (!shunt_out_add(&sy, syexp(parser_ctx(parser), (ast_expression*)val))) { + parseerror(parser, "out of memory"); goto onerr; } - wantop = nextwant; - parser->lex->flags.noops = !wantop; - } else { - if (parser->tok == '(') { - /* we expected an operator, this is the function-call operator */ - if (!shunt_ops_add(&sy, syparen(parser_ctx(parser), 'f', sy.out_count-1))) { - parseerror(parser, "out of memory"); - goto onerr; - } + DEBUGSHUNTDO(printf("push %i\n", parser_token(parser)->constval.i)); + } + else if (parser->tok == TOKEN_STRINGCONST) { + ast_value *val; + if (wantop) { + parseerror(parser, "expected operator or end of statement, got constant"); + goto onerr; + } + wantop = true; + val = parser_const_string(parser, parser_tokval(parser)); + if (!val) + return false; + if (!shunt_out_add(&sy, syexp(parser_ctx(parser), (ast_expression*)val))) { + parseerror(parser, "out of memory"); + goto onerr; + } + DEBUGSHUNTDO(printf("push string\n")); + } + else if (parser->tok == TOKEN_VECTORCONST) { + ast_value *val; + if (wantop) { + parseerror(parser, "expected operator or end of statement, got constant"); + goto onerr; } - else if (parser->tok == ')') { + wantop = true; + val = parser_const_vector(parser, parser_token(parser)->constval.v); + if (!val) + return false; + if (!shunt_out_add(&sy, syexp(parser_ctx(parser), (ast_expression*)val))) { + parseerror(parser, "out of memory"); + goto onerr; + } + DEBUGSHUNTDO(printf("push '%g %g %g'\n", + parser_token(parser)->constval.v.x, + parser_token(parser)->constval.v.y, + parser_token(parser)->constval.v.z)); + } + 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")); + --parens; + if (parens < 0) + break; /* we do expect an operator next */ /* closing an opening paren */ if (!parser_close_paren(parser, &sy, false)) goto onerr; + } else { + DEBUGSHUNTDO(printf("do[nop] )\n")); + --parens; + if (parens < 0) + break; + /* allowed for function calls */ + if (!parser_close_paren(parser, &sy, true)) + goto onerr; } - else if (parser->tok != TOKEN_OPERATOR) { + wantop = true; + } + else if (parser->tok != TOKEN_OPERATOR) { + if (wantop) { parseerror(parser, "expected operator or end of statement"); goto onerr; } - else { - /* classify the operator */ - /* TODO: suffix operators */ - const oper_info *op; - const oper_info *olast = NULL; - size_t o; - for (o = 0; o < operator_count; ++o) { - if (!(operators[o].flags & OP_PREFIX) && - !(operators[o].flags & OP_SUFFIX) && /* remove this */ - !strcmp(parser_tokval(parser), operators[o].op)) - { - break; - } - } - if (o == operator_count) { - /* no operator found... must be the end of the statement */ + break; + } + else + { + /* classify the operator */ + /* TODO: suffix operators */ + const oper_info *op; + const oper_info *olast = NULL; + size_t o; + for (o = 0; o < operator_count; ++o) { + if ((!(operators[o].flags & OP_PREFIX) == wantop) && + !(operators[o].flags & OP_SUFFIX) && /* remove this */ + !strcmp(parser_tokval(parser), operators[o].op)) + { break; } - /* found an operator */ - op = &operators[o]; - - if (sy.ops_count && !sy.ops[sy.ops_count-1].paren) + } + if (o == operator_count) { + /* no operator found... must be the end of the statement */ + break; + } + /* found an operator */ + op = &operators[o]; + + /* when declaring variables, a comma starts a new variable */ + if (op->id == opid1(',') && !parens && stopatcomma) + break; + + if (op->id == opid1('.')) { + /* for gmqcc standard: open up the namespace of the previous type */ + ast_expression *prevex = sy.out[sy.out_count-1].out; + if (!prevex) { + parseerror(parser, "unexpected member operator"); + goto onerr; + } + if (prevex->expression.vtype == TYPE_ENTITY) + parser->memberof = TYPE_ENTITY; + else if (prevex->expression.vtype == TYPE_VECTOR) + parser->memberof = TYPE_VECTOR; + else { + parseerror(parser, "type error: type has no members"); + goto onerr; + } + gotmemberof = true; + } + + if (sy.ops_count && !sy.ops[sy.ops_count-1].paren) + olast = &operators[sy.ops[sy.ops_count-1].etype-1]; + + while (olast && ( + (op->prec < olast->prec) || + (op->assoc == ASSOC_LEFT && op->prec <= olast->prec) ) ) + { + if (!parser_sy_pop(parser, &sy)) + goto onerr; + if (sy.ops_count && !sy.ops[sy.ops_count-1].paren) olast = &operators[sy.ops[sy.ops_count-1].etype-1]; + else + olast = NULL; + } - while (olast && ( - (op->prec < olast->prec) || - (op->assoc == ASSOC_LEFT && op->prec <= olast->prec) ) ) - { - if (!parser_sy_pop(parser, &sy)) + if (op->id == opid1('(')) { + if (wantop) { + DEBUGSHUNTDO(printf("push (\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))) { + parseerror(parser, "out of memory"); + goto onerr; + } + } else { + ++parens; + if (!shunt_ops_add(&sy, syparen(parser_ctx(parser), 1, 0))) { + parseerror(parser, "out of memory"); goto onerr; - if (sy.ops_count && !sy.ops[sy.ops_count-1].paren) - olast = &operators[sy.ops[sy.ops_count-1].etype-1]; + } + DEBUGSHUNTDO(printf("push (\n")); } - + wantop = false; + } else { + DEBUGSHUNTDO(printf("push operator %s\n", op->op)); if (!shunt_ops_add(&sy, syop(parser_ctx(parser), op))) goto onerr; + wantop = false; } - wantop = false; - parser->lex->flags.noops = true; } if (!parser_next(parser)) { goto onerr; @@ -708,10 +1232,6 @@ static ast_expression* parser_expression(parser_t *parser) break; } } - if (!parser_next(parser)) { - parseerror(parser, "Unexpected end of file"); - goto onerr; - } while (sy.ops_count) { if (!parser_sy_pop(parser, &sy)) @@ -726,6 +1246,7 @@ static ast_expression* parser_expression(parser_t *parser) expr = sy.out[0].out; MEM_VECTOR_CLEAR(&sy, out); MEM_VECTOR_CLEAR(&sy, ops); + DEBUGSHUNTDO(printf("shunt done\n")); return expr; onerr: @@ -735,19 +1256,338 @@ onerr: return NULL; } -static bool parser_variable(parser_t *parser, ast_block *localblock); -static bool parser_body_do(parser_t *parser, ast_block *block) +static ast_expression* parser_expression(parser_t *parser, bool stopatcomma) +{ + ast_expression *e = parser_expression_leave(parser, stopatcomma); + if (!e) + return NULL; + if (!parser_next(parser)) { + ast_delete(e); + return NULL; + } + return e; +} + +static bool parser_parse_if(parser_t *parser, ast_block *block, ast_expression **out) +{ + ast_ifthen *ifthen; + ast_expression *cond, *ontrue, *onfalse = NULL; + + lex_ctx ctx = parser_ctx(parser); + + /* skip the 'if' and check for opening paren */ + if (!parser_next(parser) || parser->tok != '(') { + parseerror(parser, "expected 'if' condition in parenthesis"); + return false; + } + /* parse into the expression */ + if (!parser_next(parser)) { + parseerror(parser, "expected 'if' condition after opening paren"); + return false; + } + /* parse the condition */ + cond = parser_expression_leave(parser, false); + if (!cond) + return false; + /* closing paren */ + if (parser->tok != ')') { + parseerror(parser, "expected closing paren after 'if' condition"); + ast_delete(cond); + return false; + } + /* parse into the 'then' branch */ + if (!parser_next(parser)) { + parseerror(parser, "expected statement for on-true branch of 'if'"); + ast_delete(cond); + return false; + } + ontrue = parser_parse_statement_or_block(parser); + if (!ontrue) { + ast_delete(cond); + return false; + } + /* check for an else */ + if (!strcmp(parser_tokval(parser), "else")) { + /* parse into the 'else' branch */ + if (!parser_next(parser)) { + parseerror(parser, "expected on-false branch after 'else'"); + ast_delete(ontrue); + ast_delete(cond); + return false; + } + onfalse = parser_parse_statement_or_block(parser); + if (!onfalse) { + ast_delete(ontrue); + ast_delete(cond); + return false; + } + } + + ifthen = ast_ifthen_new(ctx, cond, ontrue, onfalse); + *out = (ast_expression*)ifthen; + return true; +} + +static bool parser_parse_while(parser_t *parser, ast_block *block, ast_expression **out) +{ + ast_loop *aloop; + ast_expression *cond, *ontrue; + + lex_ctx ctx = parser_ctx(parser); + + /* skip the 'while' and check for opening paren */ + if (!parser_next(parser) || parser->tok != '(') { + parseerror(parser, "expected 'while' condition in parenthesis"); + return false; + } + /* parse into the expression */ + if (!parser_next(parser)) { + parseerror(parser, "expected 'while' condition after opening paren"); + return false; + } + /* parse the condition */ + cond = parser_expression_leave(parser, false); + if (!cond) + return false; + /* closing paren */ + if (parser->tok != ')') { + parseerror(parser, "expected closing paren after 'while' condition"); + ast_delete(cond); + return false; + } + /* parse into the 'then' branch */ + if (!parser_next(parser)) { + parseerror(parser, "expected while-loop body"); + ast_delete(cond); + return false; + } + ontrue = parser_parse_statement_or_block(parser); + if (!ontrue) { + ast_delete(cond); + return false; + } + + aloop = ast_loop_new(ctx, NULL, cond, NULL, NULL, ontrue); + *out = (ast_expression*)aloop; + return true; +} + +static bool parser_parse_dowhile(parser_t *parser, ast_block *block, ast_expression **out) +{ + ast_loop *aloop; + ast_expression *cond, *ontrue; + + lex_ctx ctx = parser_ctx(parser); + + /* skip the 'do' and get the body */ + if (!parser_next(parser)) { + parseerror(parser, "expected loop body"); + return false; + } + ontrue = parser_parse_statement_or_block(parser); + if (!ontrue) + return false; + + /* expect the "while" */ + if (parser->tok != TOKEN_KEYWORD || + strcmp(parser_tokval(parser), "while")) + { + parseerror(parser, "expected 'while' and condition"); + ast_delete(ontrue); + return false; + } + + /* skip the 'while' and check for opening paren */ + if (!parser_next(parser) || parser->tok != '(') { + parseerror(parser, "expected 'while' condition in parenthesis"); + ast_delete(ontrue); + return false; + } + /* parse into the expression */ + if (!parser_next(parser)) { + parseerror(parser, "expected 'while' condition after opening paren"); + ast_delete(ontrue); + return false; + } + /* parse the condition */ + cond = parser_expression_leave(parser, false); + if (!cond) + return false; + /* closing paren */ + if (parser->tok != ')') { + parseerror(parser, "expected closing paren after 'while' condition"); + ast_delete(ontrue); + ast_delete(cond); + return false; + } + /* parse on */ + if (!parser_next(parser) || parser->tok != ';') { + parseerror(parser, "expected semicolon after condition"); + ast_delete(ontrue); + ast_delete(cond); + return false; + } + + if (!parser_next(parser)) { + parseerror(parser, "parse error"); + ast_delete(ontrue); + ast_delete(cond); + return false; + } + + aloop = ast_loop_new(ctx, NULL, NULL, cond, NULL, ontrue); + *out = (ast_expression*)aloop; + return true; +} + +static bool parser_parse_for(parser_t *parser, ast_block *block, ast_expression **out) +{ + ast_loop *aloop; + ast_expression *initexpr, *cond, *increment, *ontrue; + size_t oldblocklocal; + + lex_ctx ctx = parser_ctx(parser); + + oldblocklocal = parser->blocklocal; + parser->blocklocal = parser->locals_count; + + initexpr = NULL; + cond = NULL; + increment = NULL; + ontrue = NULL; + + /* skip the 'while' and check for opening paren */ + if (!parser_next(parser) || parser->tok != '(') { + parseerror(parser, "expected 'for' expressions in parenthesis"); + goto onerr; + } + /* parse into the expression */ + if (!parser_next(parser)) { + parseerror(parser, "expected 'for' initializer after opening paren"); + goto onerr; + } + + if (parser->tok == TOKEN_TYPENAME) { + if (opts_standard != COMPILER_GMQCC) { + if (parsewarning(parser, WARN_EXTENSIONS, + "current standard does not allow variable declarations in for-loop initializers")) + goto onerr; + } + + parseerror(parser, "TODO: assignment of new variables to be non-const"); + goto onerr; + if (!parser_variable(parser, block)) + goto onerr; + } + else if (parser->tok != ';') + { + initexpr = parser_expression_leave(parser, false); + if (!initexpr) + goto onerr; + } + + /* move on to condition */ + if (parser->tok != ';') { + parseerror(parser, "expected semicolon after for-loop initializer"); + goto onerr; + } + if (!parser_next(parser)) { + parseerror(parser, "expected for-loop condition"); + goto onerr; + } + + /* parse the condition */ + if (parser->tok != ';') { + cond = parser_expression_leave(parser, false); + if (!cond) + goto onerr; + } + + /* move on to incrementor */ + if (parser->tok != ';') { + parseerror(parser, "expected semicolon after for-loop initializer"); + goto onerr; + } + if (!parser_next(parser)) { + parseerror(parser, "expected for-loop condition"); + goto onerr; + } + + /* parse the incrementor */ + if (parser->tok != ')') { + increment = parser_expression_leave(parser, false); + if (!increment) + goto onerr; + } + + /* closing paren */ + if (parser->tok != ')') { + parseerror(parser, "expected closing paren after 'for-loop' incrementor"); + goto onerr; + } + /* parse into the 'then' branch */ + if (!parser_next(parser)) { + parseerror(parser, "expected for-loop body"); + goto onerr; + } + ontrue = parser_parse_statement_or_block(parser); + if (!ontrue) { + goto onerr; + } + + aloop = ast_loop_new(ctx, initexpr, cond, NULL, increment, ontrue); + *out = (ast_expression*)aloop; + + while (parser->locals_count > parser->blocklocal) + parser_pop_local(parser); + parser->blocklocal = oldblocklocal; + return true; +onerr: + if (initexpr) ast_delete(initexpr); + if (cond) ast_delete(cond); + if (increment) ast_delete(increment); + while (parser->locals_count > parser->blocklocal) + parser_pop_local(parser); + parser->blocklocal = oldblocklocal; + return false; +} + +static bool parser_parse_statement(parser_t *parser, ast_block *block, ast_expression **out) { if (parser->tok == TOKEN_TYPENAME) { /* local variable */ + if (!block) { + parseerror(parser, "cannot declare a variable from here"); + return false; + } + if (opts_standard == COMPILER_QCC) { + if (parsewarning(parser, WARN_EXTENSIONS, "missing 'local' keyword when declaring a local variable")) + return false; + } if (!parser_variable(parser, block)) return false; + *out = NULL; return true; } else if (parser->tok == TOKEN_KEYWORD) { - if (!strcmp(parser_tokval(parser), "return")) + if (!strcmp(parser_tokval(parser), "local")) + { + if (!block) { + parseerror(parser, "cannot declare a local variable here"); + return false; + } + if (!parser_next(parser)) { + parseerror(parser, "expected variable declaration"); + return false; + } + if (!parser_variable(parser, block)) + return false; + *out = NULL; + return true; + } + else if (!strcmp(parser_tokval(parser), "return")) { ast_expression *exp = NULL; ast_return *ret = NULL; @@ -759,7 +1599,7 @@ static bool parser_body_do(parser_t *parser, ast_block *block) } if (parser->tok != ';') { - exp = parser_expression(parser); + exp = parser_expression(parser, false); if (!exp) return false; @@ -773,10 +1613,7 @@ static bool parser_body_do(parser_t *parser, ast_block *block) return false; } - if (!ast_block_exprs_add(block, (ast_expression*)ret)) { - ast_delete(ret); - return false; - } + *out = (ast_expression*)ret; } else if (!parser_next(parser)) { parseerror(parser, "expected semicolon"); if (expected->expression.next->expression.vtype != TYPE_VOID) { @@ -785,28 +1622,54 @@ static bool parser_body_do(parser_t *parser, ast_block *block) } return true; } + else if (!strcmp(parser_tokval(parser), "if")) + { + return parser_parse_if(parser, block, out); + } + else if (!strcmp(parser_tokval(parser), "while")) + { + return parser_parse_while(parser, block, out); + } + else if (!strcmp(parser_tokval(parser), "do")) + { + return parser_parse_dowhile(parser, block, out); + } + else if (!strcmp(parser_tokval(parser), "for")) + { + if (opts_standard == COMPILER_QCC) { + if (parsewarning(parser, WARN_EXTENSIONS, "for loops are not recognized in the original Quake C standard, to enable try an alternate standard --std=?")) + return false; + } + return parser_parse_for(parser, block, out); + } parseerror(parser, "Unexpected keyword"); return false; } else if (parser->tok == '{') { - /* a block */ - parseerror(parser, "TODO: inner blocks: %s", parser_tokval(parser)); - return false; + ast_block *inner; + inner = parser_parse_block(parser); + if (!inner) + return false; + *out = (ast_expression*)inner; + return true; } else { - ast_expression *exp = parser_expression(parser); + ast_expression *exp = parser_expression(parser, false); if (!exp) return false; - if (!ast_block_exprs_add(block, exp)) { - ast_delete(exp); - return false; - } + *out = exp; return true; } } +static void parser_pop_local(parser_t *parser) +{ + parser->locals_count--; + mem_d(parser->locals[parser->locals_count].name); +} + static ast_block* parser_parse_block(parser_t *parser) { size_t oldblocklocal; @@ -824,10 +1687,19 @@ static ast_block* parser_parse_block(parser_t *parser) while (parser->tok != TOKEN_EOF && parser->tok < TOKEN_ERROR) { + ast_expression *expr; if (parser->tok == '}') break; - if (!parser_body_do(parser, block)) { + if (!parser_parse_statement(parser, block, &expr)) { + ast_block_delete(block); + block = NULL; + goto cleanup; + } + if (!expr) + continue; + if (!ast_block_exprs_add(block, expr)) { + ast_delete(expr); ast_block_delete(block); block = NULL; goto cleanup; @@ -842,16 +1714,31 @@ static ast_block* parser_parse_block(parser_t *parser) } cleanup: + while (parser->locals_count > parser->blocklocal) + parser_pop_local(parser); parser->blocklocal = oldblocklocal; + /* unroll the local vector */ return block; } +static ast_expression* parser_parse_statement_or_block(parser_t *parser) +{ + ast_expression *expr; + if (parser->tok == '{') + return (ast_expression*)parser_parse_block(parser); + if (!parser_parse_statement(parser, NULL, &expr)) + return NULL; + return expr; +} + static bool parser_variable(parser_t *parser, ast_block *localblock) { bool isfunc = false; ast_function *func = NULL; lex_ctx ctx; ast_value *var; + varentry_t varent; + ast_expression *olddecl; int basetype = parser_token(parser)->constval.t; @@ -862,8 +1749,9 @@ static bool parser_variable(parser_t *parser, ast_block *localblock) return false; } - isfunc = false; - func = NULL; + olddecl = NULL; + isfunc = false; + func = NULL; ctx = parser_ctx(parser); var = parser_parse_type(parser, basetype, &isfunc); @@ -875,16 +1763,20 @@ static bool parser_variable(parser_t *parser, ast_block *localblock) return false; } - if (!localblock && parser_find_global(parser, parser_tokval(parser))) { - ast_value_delete(var); - parseerror(parser, "global already exists: %s\n", parser_tokval(parser)); - return false; - } + if (!isfunc) { + if (!localblock && (olddecl = parser_find_global(parser, parser_tokval(parser)))) { + ast_value_delete(var); + parseerror(parser, "global %s already declared here: %s:%i\n", + parser_tokval(parser), ast_ctx(olddecl).file, (int)ast_ctx(olddecl).line); + return false; + } - if (localblock && parser_find_local(parser, parser_tokval(parser), parser->blocklocal)) { - ast_value_delete(var); - parseerror(parser, "local variable already exists: %s\n", parser_tokval(parser)); - return false; + if (localblock && parser_find_local(parser, parser_tokval(parser), parser->blocklocal)) { + ast_value_delete(var); + parseerror(parser, "local %s already declared here: %s:%i\n", + parser_tokval(parser), ast_ctx(olddecl).file, (int)ast_ctx(olddecl).line); + return false; + } } if (!ast_value_set_name(var, parser_tokval(parser))) { @@ -896,6 +1788,30 @@ static bool parser_variable(parser_t *parser, ast_block *localblock) if (isfunc) { /* a function was defined */ ast_value *fval; + ast_value *proto = NULL; + + if (!localblock) + olddecl = parser_find_global(parser, parser_tokval(parser)); + else + olddecl = parser_find_local(parser, parser_tokval(parser), parser->blocklocal); + + if (olddecl) { + /* we had a prototype */ + if (!ast_istype(olddecl, ast_value)) { + /* theoretically not possible you think? + * well: + * vector v; + * void() v_x = {} + * got it? + */ + parseerror(parser, "cannot declare a function with the same name as a vector's member: %s", + parser_tokval(parser)); + ast_value_delete(var); + return false; + } + + proto = (ast_value*)olddecl; + } /* turn var into a value of TYPE_FUNCTION, with the old var * as return type @@ -912,27 +1828,94 @@ static bool parser_variable(parser_t *parser, ast_block *localblock) fval->expression.next = (ast_expression*)var; MEM_VECTOR_MOVE(&var->expression, params, &fval->expression, params); - if (!parser_t_functions_add(parser, func)) { - ast_value_delete(var); - if (fval) ast_value_delete(fval); - if (func) ast_function_delete(func); - return false; + /* we compare the type late here, but it's easier than + * messing with the parameter-vector etc. earlier + */ + if (proto) { + 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_function_delete(func); + ast_value_delete(fval); + return false; + } + ast_function_delete(func); + ast_value_delete(fval); + var = proto; + func = var->constval.vfunc; + } + else + { + if (!parser_t_functions_add(parser, func)) { + ast_function_delete(func); + ast_value_delete(fval); + return false; + } } var = fval; } - if ( (!localblock && !parser_t_globals_add(parser, var)) || - ( localblock && !parser_t_locals_add(parser, var)) ) + varent.name = util_strdup(var->name); + varent.var = (ast_expression*)var; + if (var->expression.vtype == TYPE_VECTOR) { - ast_value_delete(var); - return false; + size_t len = strlen(varent.name); + varentry_t vx, vy, vz; + vx.var = (ast_expression*)ast_member_new(var->expression.node.context, (ast_expression*)var, 0); + vy.var = (ast_expression*)ast_member_new(var->expression.node.context, (ast_expression*)var, 1); + vz.var = (ast_expression*)ast_member_new(var->expression.node.context, (ast_expression*)var, 2); + vx.name = (char*)mem_a(len+3); + vy.name = (char*)mem_a(len+3); + vz.name = (char*)mem_a(len+3); + memcpy(vx.name, varent.name, len); + memcpy(vy.name, varent.name, len); + memcpy(vz.name, varent.name, len); + vx.name[len] = vy.name[len] = vz.name[len] = '_'; + vx.name[len+1] = 'x'; + vy.name[len+1] = 'y'; + vz.name[len+1] = 'z'; + vx.name[len+2] = vy.name[len+2] = vz.name[len+2] = 0; + + if (!localblock) { + (void)!parser_t_globals_add(parser, varent); + (void)!parser_t_globals_add(parser, vx); + (void)!parser_t_globals_add(parser, vy); + (void)!parser_t_globals_add(parser, vz); + } else { + (void)!parser_t_locals_add(parser, varent); + (void)!parser_t_locals_add(parser, vx); + (void)!parser_t_locals_add(parser, vy); + (void)!parser_t_locals_add(parser, vz); + if (!ast_block_locals_add(localblock, var) || + !ast_block_collect(localblock, vx.var) || + !ast_block_collect(localblock, vy.var) || + !ast_block_collect(localblock, vz.var)) + { + parser_pop_local(parser); + parser_pop_local(parser); + parser_pop_local(parser); + parser_pop_local(parser); + ast_value_delete(var); + return false; + } + } } - if (localblock && !ast_block_locals_add(localblock, var)) + else { - parser->locals_count--; - ast_value_delete(var); - return false; + if ( (!localblock && !parser_t_globals_add(parser, varent)) || + ( localblock && !parser_t_locals_add(parser, varent)) ) + { + ast_value_delete(var); + return false; + } + if (localblock && !ast_block_locals_add(localblock, var)) + { + parser_pop_local(parser); + ast_value_delete(var); + return false; + } } if (!parser_next(parser)) { @@ -982,6 +1965,9 @@ static bool parser_variable(parser_t *parser, ast_block *localblock) } func->builtin = -parser_token(parser)->constval.i; + + if (!parser_next(parser)) + return false; } else if (parser->tok == '{') { /* function body */ ast_block *block; @@ -1003,21 +1989,36 @@ static bool parser_variable(parser_t *parser, ast_block *localblock) ast_block_delete(block); return false; } + + if (parser->tok == ';') + return parser_next(parser) || parser->tok == TOKEN_EOF; + else if (opts_standard == COMPILER_QCC) + parseerror(parser, "missing semicolon after function body (mandatory with -std=qcc)"); return true; } else { - parseerror(parser, "TODO, const assignment"); + ast_expression *cexp; + ast_value *cval; + + cexp = parser_expression_leave(parser, true); + 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; + memcpy(&var->constval, &cval->constval, sizeof(var->constval)); + memset(&cval->constval, 0, sizeof(cval->constval)); + ast_unref(cval); + } } - if (!parser_next(parser)) - return false; - if (parser->tok == ',') { /* another */ continue; } if (parser->tok != ';') { - parseerror(parser, "expected semicolon"); + parseerror(parser, "missing semicolon"); return false; } @@ -1040,8 +2041,160 @@ static bool parser_do(parser_t *parser) } else if (parser->tok == '.') { + ast_value *var; + ast_value *typevar; + ast_value *fld; + ast_expression *oldex; + bool isfunc = false; + int basetype; + lex_ctx ctx = parser_ctx(parser); + varentry_t varent; + /* entity-member declaration */ - return false; + if (!parser_next(parser) || parser->tok != TOKEN_TYPENAME) { + parseerror(parser, "expected member variable definition"); + return false; + } + + /* remember the base/return type */ + basetype = parser_token(parser)->constval.t; + + /* parse into the declaration */ + if (!parser_next(parser)) { + parseerror(parser, "expected field def"); + return false; + } + + /* parse the field type fully */ + typevar = var = parser_parse_type(parser, basetype, &isfunc); + if (!var) + return false; + + while (true) { + var = ast_value_copy(typevar); + /* now the field name */ + if (parser->tok != TOKEN_IDENT) { + parseerror(parser, "expected field name"); + ast_delete(var); + return false; + } + + /* check for an existing field + * in original qc we also have to check for an existing + * global named like the field + */ + if (opts_standard == COMPILER_QCC) { + if (parser_find_global(parser, parser_tokval(parser))) { + parseerror(parser, "cannot declare a field and a global of the same name with -std=qcc"); + ast_delete(var); + return false; + } + } + + if (isfunc) { + ast_value *fval; + fval = ast_value_new(ctx, var->name, TYPE_FUNCTION); + if (!fval) { + ast_value_delete(var); + return false; + } + fval->expression.next = (ast_expression*)var; + MEM_VECTOR_MOVE(&var->expression, params, &fval->expression, params); + var = fval; + } + + /* turn it into a field */ + fld = ast_value_new(ctx, parser_tokval(parser), TYPE_FIELD); + fld->expression.next = (ast_expression*)var; + + if ( (oldex = parser_find_field(parser, parser_tokval(parser)))) { + if (ast_istype(oldex, ast_member)) { + parseerror(parser, "cannot declare a field with the same name as a vector component, component %s has been declared here: %s:%i", + parser_tokval(parser), ast_ctx(oldex).file, (int)ast_ctx(oldex).line); + ast_delete(fld); + return false; + } + if (!ast_istype(oldex, ast_value)) { + /* not possible / sanity check */ + parseerror(parser, "internal error: %s is not an ast_value", parser_tokval(parser)); + ast_delete(fld); + return false; + } + + if (!ast_compare_type(oldex, (ast_expression*)fld)) { + parseerror(parser, "field %s has previously been declared with a different type here: %s:%i", + parser_tokval(parser), ast_ctx(oldex).file, (int)ast_ctx(oldex).line); + ast_delete(fld); + return false; + } else { + if (parsewarning(parser, WARN_FIELD_REDECLARED, "field %s has already been declared here: %s:%i", + parser_tokval(parser), ast_ctx(oldex).file, (int)ast_ctx(oldex).line)) + { + ast_delete(fld); + return false; + } + } + + ast_delete(fld); + goto nextfield; + } + + varent.var = (ast_expression*)fld; + varent.name = util_strdup(fld->name); + (void)!parser_t_fields_add(parser, varent); + + if (var->expression.vtype == TYPE_VECTOR) + { + /* create _x, _y and _z fields as well */ + size_t len; + varentry_t vx, vy, vz; + + len = strlen(varent.name); + vx.var = (ast_expression*)ast_member_new(ast_ctx(fld), (ast_expression*)fld, 0); + vy.var = (ast_expression*)ast_member_new(ast_ctx(fld), (ast_expression*)fld, 1); + vz.var = (ast_expression*)ast_member_new(ast_ctx(fld), (ast_expression*)fld, 2); + vx.name = (char*)mem_a(len+3); + vy.name = (char*)mem_a(len+3); + vz.name = (char*)mem_a(len+3); + memcpy(vx.name, varent.name, len); + memcpy(vy.name, varent.name, len); + memcpy(vz.name, varent.name, len); + vx.name[len] = vy.name[len] = vz.name[len] = '_'; + vx.name[len+1] = 'x'; + vy.name[len+1] = 'y'; + vz.name[len+1] = 'z'; + vx.name[len+2] = vy.name[len+2] = vz.name[len+2] = 0; + (void)!parser_t_fields_add(parser, vx); + (void)!parser_t_fields_add(parser, vy); + (void)!parser_t_fields_add(parser, vz); + } + +nextfield: + if (!parser_next(parser)) { + parseerror(parser, "expected semicolon or another field name"); + return false; + } + if (parser->tok == ';') + break; + if (parser->tok != ',' || !parser_next(parser)) { + parseerror(parser, "expected semicolon or another field name"); + return false; + } + } + ast_delete(typevar); + + /* skip the semicolon */ + if (!parser_next(parser)) + return parser->tok == TOKEN_EOF; + + return true; + } + else if (parser->tok == '$') + { + if (!parser_next(parser)) { + parseerror(parser, "parse error"); + return false; + } } else { @@ -1059,10 +2212,7 @@ bool parser_init() if (!parser) return false; - memset(parser, 0, sizeof(parser)); - - MEM_VECTOR_INIT(parser, globals); - MEM_VECTOR_INIT(parser, locals); + memset(parser, 0, sizeof(*parser)); return true; } @@ -1084,16 +2234,17 @@ bool parser_compile(const char *filename) if (!parser_do(parser)) { if (parser->tok == TOKEN_EOF) parseerror(parser, "unexpected eof"); - else + else if (!parser->errors) parseerror(parser, "parse error\n"); lex_close(parser->lex); - mem_d(parser); + parser->lex = NULL; return false; } } } lex_close(parser->lex); + parser->lex = NULL; return !parser->errors; } @@ -1113,10 +2264,21 @@ void parser_cleanup() for (i = 0; i < parser->imm_float_count; ++i) { ast_delete(parser->imm_float[i]); } + for (i = 0; i < parser->fields_count; ++i) { + ast_delete(parser->fields[i].var); + mem_d(parser->fields[i].name); + } for (i = 0; i < parser->globals_count; ++i) { - ast_delete(parser->globals[i]); + ast_delete(parser->globals[i].var); + mem_d(parser->globals[i].name); } + MEM_VECTOR_CLEAR(parser, functions); + MEM_VECTOR_CLEAR(parser, imm_vector); + MEM_VECTOR_CLEAR(parser, imm_string); + MEM_VECTOR_CLEAR(parser, imm_float); MEM_VECTOR_CLEAR(parser, globals); + MEM_VECTOR_CLEAR(parser, fields); + MEM_VECTOR_CLEAR(parser, locals); mem_d(parser); } @@ -1155,9 +2317,37 @@ bool parser_finish(const char *output) return false; } } + for (i = 0; i < parser->fields_count; ++i) { + ast_value *field; + bool isconst; + if (!ast_istype(parser->fields[i].var, ast_value)) + continue; + field = (ast_value*)parser->fields[i].var; + isconst = field->isconst; + field->isconst = false; + if (!ast_global_codegen((ast_value*)field, ir)) { + printf("failed to generate field %s\n", field->name); + ir_builder_delete(ir); + return false; + } + if (isconst) { + ir_value *ifld; + ast_expression *subtype; + field->isconst = true; + subtype = field->expression.next; + ifld = ir_builder_create_field(ir, field->name, subtype->expression.vtype); + if (subtype->expression.vtype == TYPE_FIELD) + ifld->fieldtype = subtype->expression.next->expression.vtype; + else if (subtype->expression.vtype == TYPE_FUNCTION) + ifld->outtype = subtype->expression.next->expression.vtype; + (void)!ir_value_set_field(field->ir_v, ifld); + } + } for (i = 0; i < parser->globals_count; ++i) { - if (!ast_global_codegen(parser->globals[i], ir)) { - printf("failed to generate global %s\n", parser->globals[i]->name); + if (!ast_istype(parser->globals[i].var, ast_value)) + continue; + if (!ast_global_codegen((ast_value*)(parser->globals[i].var), ir)) { + printf("failed to generate global %s\n", parser->globals[i].name); ir_builder_delete(ir); return false; } @@ -1175,7 +2365,8 @@ bool parser_finish(const char *output) } } - ir_builder_dump(ir, printf); + if (opts_dump) + ir_builder_dump(ir, printf); if (!ir_builder_generate(ir, output)) { printf("*** failed to generate output file\n");