X-Git-Url: https://git.xonotic.org/?a=blobdiff_plain;f=parser.c;h=1d9ae74577f9b9063f6d22c6d81da788b406f5c3;hb=b6ab0207b1b18a9a8eebffcfab5c61d7302b027d;hp=aad3b6a1b585ba9d51060e44b156e1d0492e1e5f;hpb=5ef88dbda75a5c3b20b2a5cced9fbbd7e7e55c84;p=xonotic%2Fgmqcc.git diff --git a/parser.c b/parser.c index aad3b6a..1d9ae74 100644 --- a/parser.c +++ b/parser.c @@ -20,6 +20,9 @@ typedef struct { 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(varentry_t, locals); size_t blocklocal; @@ -45,8 +48,8 @@ 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); -static ast_expression* parser_expression(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, ...) { @@ -57,12 +60,10 @@ void parseerror(parser_t *parser, const char *fmt, ...) va_start(ap, fmt); vprintmsg(LVL_ERROR, parser->lex->tok->ctx.file, parser->lex->tok->ctx.line, "parse error", fmt, ap); va_end(ap); - - printf("\n"); } /* returns true if it counts as an error */ -bool parsewarning(parser_t *parser, int warntype, const char *fmt, ...) +bool GMQCC_WARN parsewarning(parser_t *parser, int warntype, const char *fmt, ...) { va_list ap; int lvl = LVL_WARNING; @@ -70,7 +71,7 @@ bool parsewarning(parser_t *parser, int warntype, const char *fmt, ...) if (!OPTS_WARN(warntype)) return false; - if (OPTS_WARN(WARN_ERROR)) { + if (opts_werror) { parser->errors++; lvl = LVL_ERROR; } @@ -79,9 +80,49 @@ bool parsewarning(parser_t *parser, int warntype, const char *fmt, ...) vprintmsg(lvl, parser->lex->tok->ctx.file, parser->lex->tok->ctx.line, "warning", fmt, ap); va_end(ap); - return OPTS_WARN(WARN_ERROR); + 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 */ @@ -121,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; @@ -131,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; @@ -157,6 +216,22 @@ ast_value* parser_const_vector(parser_t *parser, vector v) return out; } +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; @@ -186,6 +261,8 @@ ast_expression* parser_find_local(parser_t *parser, const char *name, size_t upt 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)) @@ -224,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)) @@ -232,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; @@ -250,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; @@ -349,6 +442,7 @@ static bool parser_sy_pop(parser_t *parser, shunt *sy) ast_expression *out = NULL; ast_expression *exprs[3]; ast_block *blocks[3]; + ast_value *asvalue[3]; size_t i, assignop; qcint generated_op = 0; @@ -368,7 +462,8 @@ static bool parser_sy_pop(parser_t *parser, shunt *sy) 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; } @@ -378,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(',')) { @@ -388,10 +484,17 @@ static bool parser_sy_pop(parser_t *parser, shunt *sy) #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('.'): @@ -430,55 +533,122 @@ static bool parser_sy_pop(parser_t *parser, shunt *sy) sy->out[sy->out_count++] = syblock(ctx, blocks[0]); return true; - case opid1('+'): - if (exprs[0]->expression.vtype != exprs[1]->expression.vtype) { - parseerror(parser, "Cannot add type %s and %s", - type_name[exprs[0]->expression.vtype], - type_name[exprs[1]->expression.vtype]); + 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; } - if (exprs[0]->expression.vtype != TYPE_VECTOR && exprs[0]->expression.vtype != TYPE_FLOAT) { - parseerror(parser, "type error: %s - %s not defined", + 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 || + (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, "type error: cannot add type %s to %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, "type error: 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; } - if (exprs[0]->expression.vtype != TYPE_VECTOR && exprs[0]->expression.vtype != TYPE_FLOAT) { - parseerror(parser, "type error: %s - %s not defined", - 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_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, "type error: cannot subtract 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; @@ -491,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, "type error: cannot multiply type %s by %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; @@ -499,18 +669,38 @@ 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, "type error: cannot multiplye type %s by %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; @@ -518,12 +708,15 @@ static bool parser_sy_pop(parser_t *parser, shunt *sy) break; case opid1('/'): if (NotSameType(TYPE_FLOAT)) { - parseerror(parser, "type error: cannot divide types %s and %s", + 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; } - out = (ast_expression*)ast_binary_new(ctx, INSTR_DIV_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_DIV_F, exprs[0], exprs[1]); break; case opid1('%'): case opid2('%','='): @@ -532,14 +725,19 @@ static bool parser_sy_pop(parser_t *parser, shunt *sy) case opid1('|'): case opid1('&'): if (NotSameType(TYPE_FLOAT)) { - parseerror(parser, "type error: cannot perform bit operations on types %s and %s", + 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; } - out = (ast_expression*)ast_binary_new(ctx, - (op->id == opid1('|') ? INSTR_BITOR : INSTR_BITAND), - exprs[0], exprs[1]); + 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"); @@ -557,7 +755,7 @@ static bool parser_sy_pop(parser_t *parser, shunt *sy) case opid2('&','&'): generated_op += INSTR_AND; if (NotSameType(TYPE_FLOAT)) { - parseerror(parser, "type error: cannot apply logical operation on types %s and %s", + 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"); @@ -566,7 +764,11 @@ static bool parser_sy_pop(parser_t *parser, shunt *sy) } if (opts_standard == COMPILER_GMQCC) printf("TODO: early out logic\n"); - out = (ast_expression*)ast_binary_new(ctx, generated_op, exprs[0], exprs[1]); + 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('>'): @@ -578,7 +780,7 @@ static bool parser_sy_pop(parser_t *parser, shunt *sy) case opid2('<', '='): generated_op += INSTR_LE; if (NotSameType(TYPE_FLOAT)) { - parseerror(parser, "type error: cannot compare types %s and %s", + 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; @@ -587,7 +789,7 @@ static bool parser_sy_pop(parser_t *parser, shunt *sy) break; case opid2('!', '='): if (exprs[0]->expression.vtype != exprs[1]->expression.vtype) { - parseerror(parser, "type error: cannot compare types %s and %s", + 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; @@ -596,7 +798,7 @@ static bool parser_sy_pop(parser_t *parser, shunt *sy) break; case opid2('=', '='): if (exprs[0]->expression.vtype != exprs[1]->expression.vtype) { - parseerror(parser, "type error: cannot compare types %s and %s", + 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; @@ -612,27 +814,32 @@ static bool parser_sy_pop(parser_t *parser, shunt *sy) out = (ast_expression*)ast_store_new(ctx, assignop, exprs[0], exprs[1]); break; case opid2('+','='): - if (exprs[0]->expression.vtype != exprs[1]->expression.vtype) { - parseerror(parser, "Cannot add type %s and %s", - type_name[exprs[0]->expression.vtype], - type_name[exprs[1]->expression.vtype]); - return false; - } - if (exprs[0]->expression.vtype != TYPE_VECTOR && exprs[0]->expression.vtype != TYPE_FLOAT) { - parseerror(parser, "type error: %s - %s not defined", + 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_binary_new(ctx, INSTR_ADD_F, exprs[0], exprs[1]); + 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_binary_new(ctx, INSTR_ADD_V, exprs[0], exprs[1]); + 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, "type error: cannot add type %s to %s", + 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; @@ -712,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; } @@ -755,7 +962,18 @@ static bool parser_close_paren(parser_t *parser, shunt *sy, bool functions_only) return true; } -static ast_expression* parser_expression_leave(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; @@ -770,129 +988,133 @@ static ast_expression* parser_expression_leave(parser_t *parser) MEM_VECTOR_INIT(&sy, out); MEM_VECTOR_INIT(&sy, ops); + parser->lex->flags.noops = false; + + parser_reclassify_token(parser); + while (true) { if (gotmemberof) gotmemberof = false; else parser->memberof = 0; - if (!wantop) + + 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_expression *var; - if (opts_standard == COMPILER_GMQCC) - { - if (parser->memberof == TYPE_ENTITY) - var = parser_find_field(parser, parser_tokval(parser)); - else if (parser->memberof == TYPE_VECTOR) - { - parseerror(parser, "TODO: implement effective vector member access"); - goto onerr; - } - else if (parser->memberof) { - parseerror(parser, "namespace for member not found"); - goto onerr; - } - else - var = parser_find_var(parser, parser_tokval(parser)); - } else { + 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 (!var) { - parseerror(parser, "unexpected ident: %s", parser_tokval(parser)); + else if (parser->memberof == TYPE_VECTOR) + { + parseerror(parser, "TODO: implement effective vector member access"); goto onerr; } - if (!shunt_out_add(&sy, syexp(parser_ctx(parser), var))) { - parseerror(parser, "out of memory"); + else if (parser->memberof) { + parseerror(parser, "namespace for member not found"); goto onerr; } - DEBUGSHUNTDO(printf("push %s\n", parser_tokval(parser))); + 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_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"); - goto onerr; - } - DEBUGSHUNTDO(printf("push %g\n", parser_token(parser)->constval.f)); + if (!var) { + parseerror(parser, "unexpected ident: %s", parser_tokval(parser)); + goto onerr; } - 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; - } - DEBUGSHUNTDO(printf("push %i\n", parser_token(parser)->constval.i)); + if (!shunt_out_add(&sy, syexp(parser_ctx(parser), var))) { + parseerror(parser, "out of memory"); + 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; - } - DEBUGSHUNTDO(printf("push string\n")); + 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 == 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 '%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 == '(') { - ++parens; - 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; - } - DEBUGSHUNTDO(printf("push (\n")); + 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 == ')') { - DEBUGSHUNTDO(printf("do[nop] )\n")); - --parens; - if (parens < 0) - break; - /* 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 { - bool nextwant = false; - if (parser->tok == '(') { - 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; - } + 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) @@ -901,71 +1123,107 @@ static ast_expression* parser_expression_leave(parser_t *parser) /* closing an opening paren */ if (!parser_close_paren(parser, &sy, false)) goto onerr; - nextwant = true; + } 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 (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 (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; - if (sy.ops_count && !sy.ops[sy.ops_count-1].paren) - olast = &operators[sy.ops[sy.ops_count-1].etype-1]; - else - olast = NULL; + } + } else { + ++parens; + if (!shunt_ops_add(&sy, syparen(parser_ctx(parser), 1, 0))) { + parseerror(parser, "out of memory"); + goto onerr; + } + 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 = nextwant; - parser->lex->flags.noops = !wantop; } if (!parser_next(parser)) { goto onerr; @@ -988,7 +1246,7 @@ static ast_expression* parser_expression_leave(parser_t *parser) expr = sy.out[0].out; MEM_VECTOR_CLEAR(&sy, out); MEM_VECTOR_CLEAR(&sy, ops); - DEBUGSHUNTDO(printf("shut done\n")); + DEBUGSHUNTDO(printf("shunt done\n")); return expr; onerr: @@ -998,9 +1256,9 @@ onerr: return NULL; } -static ast_expression* parser_expression(parser_t *parser) +static ast_expression* parser_expression(parser_t *parser, bool stopatcomma) { - ast_expression *e = parser_expression_leave(parser); + ast_expression *e = parser_expression_leave(parser, stopatcomma); if (!e) return NULL; if (!parser_next(parser)) { @@ -1028,7 +1286,7 @@ static bool parser_parse_if(parser_t *parser, ast_block *block, ast_expression * return false; } /* parse the condition */ - cond = parser_expression_leave(parser); + cond = parser_expression_leave(parser, false); if (!cond) return false; /* closing paren */ @@ -1088,7 +1346,7 @@ static bool parser_parse_while(parser_t *parser, ast_block *block, ast_expressio return false; } /* parse the condition */ - cond = parser_expression_leave(parser); + cond = parser_expression_leave(parser, false); if (!cond) return false; /* closing paren */ @@ -1114,6 +1372,74 @@ static bool parser_parse_while(parser_t *parser, ast_block *block, ast_expressio 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; @@ -1155,7 +1481,7 @@ static bool parser_parse_for(parser_t *parser, ast_block *block, ast_expression } else if (parser->tok != ';') { - initexpr = parser_expression_leave(parser); + initexpr = parser_expression_leave(parser, false); if (!initexpr) goto onerr; } @@ -1172,11 +1498,9 @@ static bool parser_parse_for(parser_t *parser, ast_block *block, ast_expression /* parse the condition */ if (parser->tok != ';') { - printf("going cond!\n"); - cond = parser_expression_leave(parser); + cond = parser_expression_leave(parser, false); if (!cond) goto onerr; - printf("going cond!\n"); } /* move on to incrementor */ @@ -1191,7 +1515,7 @@ static bool parser_parse_for(parser_t *parser, ast_block *block, ast_expression /* parse the incrementor */ if (parser->tok != ')') { - increment = parser_expression_leave(parser); + increment = parser_expression_leave(parser, false); if (!increment) goto onerr; } @@ -1237,6 +1561,10 @@ static bool parser_parse_statement(parser_t *parser, ast_block *block, ast_expre 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; @@ -1244,7 +1572,22 @@ static bool parser_parse_statement(parser_t *parser, ast_block *block, ast_expre } 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; @@ -1256,7 +1599,7 @@ static bool parser_parse_statement(parser_t *parser, ast_block *block, ast_expre } if (parser->tok != ';') { - exp = parser_expression(parser); + exp = parser_expression(parser, false); if (!exp) return false; @@ -1287,8 +1630,16 @@ static bool parser_parse_statement(parser_t *parser, ast_block *block, ast_expre { 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"); @@ -1305,7 +1656,7 @@ static bool parser_parse_statement(parser_t *parser, ast_block *block, ast_expre } else { - ast_expression *exp = parser_expression(parser); + ast_expression *exp = parser_expression(parser, false); if (!exp) return false; *out = exp; @@ -1387,6 +1738,7 @@ static bool parser_variable(parser_t *parser, ast_block *localblock) lex_ctx ctx; ast_value *var; varentry_t varent; + ast_expression *olddecl; int basetype = parser_token(parser)->constval.t; @@ -1397,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); @@ -1410,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))) { @@ -1431,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 @@ -1447,11 +1828,30 @@ 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; @@ -1466,12 +1866,12 @@ static bool parser_variable(parser_t *parser, ast_block *localblock) 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 = mem_a(len+3); - vy.name = mem_a(len+3); - vz.name = mem_a(len+3); - strcpy(vx.name, varent.name); - strcpy(vy.name, varent.name); - strcpy(vz.name, varent.name); + 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'; @@ -1488,6 +1888,18 @@ static bool parser_variable(parser_t *parser, ast_block *localblock) (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; + } } } else @@ -1498,12 +1910,12 @@ static bool parser_variable(parser_t *parser, ast_block *localblock) 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 (localblock && !ast_block_locals_add(localblock, var)) + { + parser_pop_local(parser); + ast_value_delete(var); + return false; + } } if (!parser_next(parser)) { @@ -1553,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; @@ -1574,13 +1989,28 @@ 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; - if (!parser_next(parser)) - return false; + 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->tok == ',') { /* another */ @@ -1588,7 +2018,7 @@ static bool parser_variable(parser_t *parser, ast_block *localblock) } if (parser->tok != ';') { - parseerror(parser, "expected semicolon"); + parseerror(parser, "missing semicolon"); return false; } @@ -1613,6 +2043,7 @@ static bool parser_do(parser_t *parser) { ast_value *var; ast_value *fld; + ast_expression *oldex; bool isfunc = false; int basetype; lex_ctx ctx = parser_ctx(parser); @@ -1638,69 +2069,115 @@ static bool parser_do(parser_t *parser) if (!var) return false; - /* 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"); + while (true) { + /* now the field name */ + if (parser->tok != TOKEN_IDENT) { + parseerror(parser, "expected field name"); ast_delete(var); return false; } - } - if (parser_find_field(parser, parser_tokval(parser))) { - parseerror(parser, "field %s already exists", parser_tokval(parser)); - ast_delete(var); - return false; - } - /* if it was a function, turn it into a function */ - if (isfunc) { - ast_value *fval; - /* turn var into a value of TYPE_FUNCTION, with the old var - * as return type + /* check for an existing field + * in original qc we also have to check for an existing + * global named like the field */ - fval = ast_value_new(ctx, var->name, TYPE_FUNCTION); - if (!fval) { - ast_value_delete(var); - ast_value_delete(fval); - return false; + 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; + } } - fval->expression.next = (ast_expression*)var; - MEM_VECTOR_MOVE(&var->expression, params, &fval->expression, params); + 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; + } - var = fval; - } + /* turn it into a field */ + fld = ast_value_new(ctx, parser_tokval(parser), TYPE_FIELD); + fld->expression.next = (ast_expression*)var; - /* 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; + } - varent.var = (ast_expression*)fld; - if (var->expression.vtype == TYPE_VECTOR) - { - /* create _x, _y and _z fields as well */ - parseerror(parser, "TODO: vector field members (_x,_y,_z)"); - 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; + } + } - varent.name = util_strdup(fld->name); - (void)!parser_t_fields_add(parser, varent); + ast_delete(fld); + goto nextfield; + } - /* end with a semicolon */ - if (!parser_next(parser) || parser->tok != ';') { - parseerror(parser, "semicolon expected"); - return false; + 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; + } } /* skip the semicolon */ @@ -1709,6 +2186,13 @@ static bool parser_do(parser_t *parser) return true; } + else if (parser->tok == '$') + { + if (!parser_next(parser)) { + parseerror(parser, "parse error"); + return false; + } + } else { parseerror(parser, "unexpected token: %s", parser->lex->tok->value); @@ -1725,10 +2209,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; } @@ -1779,11 +2260,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].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); } @@ -1870,7 +2361,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");