X-Git-Url: https://git.xonotic.org/?p=xonotic%2Fgmqcc.git;a=blobdiff_plain;f=parser.c;h=53220b51d7cb5f7f25a32a63d2cbdb69fb5b1afc;hp=d64741543f25c6133d0f18c49c50b3b3b40c4986;hb=755ee5462fb3d59b181da79d7c7f7eeba32bda2c;hpb=fa401b6f568ef80a40194722e3ddc103f7378adb diff --git a/parser.c b/parser.c index d647415..53220b5 100644 --- a/parser.c +++ b/parser.c @@ -32,6 +32,7 @@ #define PARSER_HT_SIZE 1024 #define TYPEDEF_HT_SIZE 16 +enum parser_pot { POT_PAREN, POT_TERNARY1, POT_TERNARY2 }; typedef struct { lex_file *lex; int tok; @@ -50,6 +51,7 @@ typedef struct { ast_value *imm_float_zero; ast_value *imm_float_one; ast_value *imm_vector_zero; + ast_value *nil; size_t crc_globals; size_t crc_fields; @@ -59,8 +61,10 @@ typedef struct { /* All the labels the function defined... * Should they be in ast_function instead? */ - ast_label **labels; - ast_goto **gotos; + ast_label **labels; + ast_goto **gotos; + const char **breaks; + const char **continues; /* A list of hashtables for each scope */ ht *variables; @@ -75,8 +79,6 @@ typedef struct { size_t *_blocktypedefs; lex_ctx *_block_ctx; - size_t errors; - /* we store the '=' operator info */ const oper_info *assign_op; @@ -90,7 +92,7 @@ typedef struct { * If we reach a 'comma' operator in a ternary without a paren, * we shall trigger -Wternary-precedence. */ - enum { POT_PAREN, POT_TERNARY1, POT_TERNARY2 } *pot; + enum parser_pot *pot; /* pragma flags */ bool noref; @@ -102,7 +104,7 @@ static void parser_enterblock(parser_t *parser); static bool parser_leaveblock(parser_t *parser); static void parser_addlocal(parser_t *parser, const char *name, ast_expression *e); static bool parse_typedef(parser_t *parser); -static bool parse_variable(parser_t *parser, ast_block *localblock, bool nofields, int qualifier, ast_value *cached_typedef, bool noref, bool noreturn); +static bool parse_variable(parser_t *parser, ast_block *localblock, bool nofields, int qualifier, ast_value *cached_typedef, bool noref, bool is_static, uint32_t qflags); static ast_block* parse_block(parser_t *parser); static bool parse_block_into(parser_t *parser, ast_block *block); static bool parse_statement_or_block(parser_t *parser, ast_expression **out); @@ -112,34 +114,31 @@ static ast_expression* parse_expression(parser_t *parser, bool stopatcomma); static void parseerror(parser_t *parser, const char *fmt, ...) { - va_list ap; - - parser->errors++; - - va_start(ap, fmt); - con_vprintmsg(LVL_ERROR, parser->lex->tok.ctx.file, parser->lex->tok.ctx.line, "parse error", fmt, ap); - va_end(ap); + va_list ap; + va_start(ap, fmt); + vcompile_error(parser->lex->tok.ctx, fmt, ap); + va_end(ap); } /* returns true if it counts as an error */ static bool GMQCC_WARN parsewarning(parser_t *parser, int warntype, const char *fmt, ...) { bool r; - va_list ap; - va_start(ap, fmt); - r = vcompile_warning(parser->lex->tok.ctx, warntype, fmt, ap); - va_end(ap); - return r; + va_list ap; + va_start(ap, fmt); + r = vcompile_warning(parser->lex->tok.ctx, warntype, fmt, ap); + va_end(ap); + return r; } static bool GMQCC_WARN genwarning(lex_ctx ctx, int warntype, const char *fmt, ...) { bool r; - va_list ap; - va_start(ap, fmt); - r = vcompile_warning(ctx, warntype, fmt, ap); - va_end(ap); - return r; + va_list ap; + va_start(ap, fmt); + r = vcompile_warning(ctx, warntype, fmt, ap); + va_end(ap); + return r; } /********************************************************************** @@ -234,7 +233,7 @@ static char *parser_strdup(const char *str) { if (str && !*str) { /* actually dup empty strings */ - char *out = mem_a(1); + char *out = (char*)mem_a(1); *out = 0; return out; } @@ -296,12 +295,12 @@ static ast_value* parser_const_vector_0(parser_t *parser) static ast_expression* parser_find_field(parser_t *parser, const char *name) { - return util_htget(parser->htfields, name); + return ( ast_expression*)util_htget(parser->htfields, name); } static ast_expression* parser_find_global(parser_t *parser, const char *name) { - return util_htget(parser->htglobals, name); + return (ast_expression*)util_htget(parser->htglobals, name); } static ast_expression* parser_find_param(parser_t *parser, const char *name) @@ -328,7 +327,7 @@ static ast_expression* parser_find_local(parser_t *parser, const char *name, siz *isparam = false; for (i = vec_size(parser->variables); i > upto;) { --i; - if ( (e = util_htgeth(parser->variables[i], name, hash)) ) + if ( (e = (ast_expression*)util_htgeth(parser->variables[i], name, hash)) ) return e; } *isparam = true; @@ -761,10 +760,11 @@ static bool parser_sy_apply_operator(parser_t *parser, shunt *sy) break; case opid1('*'): if (exprs[0]->expression.vtype != exprs[1]->expression.vtype && - exprs[0]->expression.vtype != TYPE_VECTOR && - exprs[0]->expression.vtype != TYPE_FLOAT && - exprs[1]->expression.vtype != TYPE_VECTOR && - exprs[1]->expression.vtype != TYPE_FLOAT) + !(exprs[0]->expression.vtype == TYPE_VECTOR && + exprs[1]->expression.vtype == TYPE_FLOAT) && + !(exprs[1]->expression.vtype == TYPE_VECTOR && + exprs[0]->expression.vtype == TYPE_FLOAT) + ) { parseerror(parser, "invalid types used in expression: cannot multiply types %s and %s", type_name[exprs[1]->expression.vtype], @@ -800,6 +800,64 @@ static bool parser_sy_apply_operator(parser_t *parser, shunt *sy) { if (CanConstFold(exprs[0], exprs[1])) out = (ast_expression*)parser_const_float(parser, vec3_mulvv(ConstV(0), ConstV(1))); + else if (OPTS_OPTIMIZATION(OPTIM_VECTOR_COMPONENTS) && CanConstFold1(exprs[0])) { + vector vec = ConstV(0); + if (!vec.y && !vec.z) { /* 'n 0 0' * v */ + ++opts_optimizationcount[OPTIM_VECTOR_COMPONENTS]; + out = (ast_expression*)ast_member_new(ctx, exprs[1], 0, NULL); + out->expression.node.keep = false; + ((ast_member*)out)->rvalue = true; + if (vec.x != 1) + out = (ast_expression*)ast_binary_new(ctx, INSTR_MUL_F, (ast_expression*)parser_const_float(parser, vec.x), out); + } + else if (!vec.x && !vec.z) { /* '0 n 0' * v */ + ++opts_optimizationcount[OPTIM_VECTOR_COMPONENTS]; + out = (ast_expression*)ast_member_new(ctx, exprs[1], 1, NULL); + out->expression.node.keep = false; + ((ast_member*)out)->rvalue = true; + if (vec.y != 1) + out = (ast_expression*)ast_binary_new(ctx, INSTR_MUL_F, (ast_expression*)parser_const_float(parser, vec.y), out); + } + else if (!vec.x && !vec.y) { /* '0 n 0' * v */ + ++opts_optimizationcount[OPTIM_VECTOR_COMPONENTS]; + out = (ast_expression*)ast_member_new(ctx, exprs[1], 2, NULL); + out->expression.node.keep = false; + ((ast_member*)out)->rvalue = true; + if (vec.z != 1) + out = (ast_expression*)ast_binary_new(ctx, INSTR_MUL_F, (ast_expression*)parser_const_float(parser, vec.z), out); + } + else + out = (ast_expression*)ast_binary_new(ctx, INSTR_MUL_V, exprs[0], exprs[1]); + } + else if (OPTS_OPTIMIZATION(OPTIM_VECTOR_COMPONENTS) && CanConstFold1(exprs[1])) { + vector vec = ConstV(1); + if (!vec.y && !vec.z) { /* v * 'n 0 0' */ + ++opts_optimizationcount[OPTIM_VECTOR_COMPONENTS]; + out = (ast_expression*)ast_member_new(ctx, exprs[0], 0, NULL); + out->expression.node.keep = false; + ((ast_member*)out)->rvalue = true; + if (vec.x != 1) + out = (ast_expression*)ast_binary_new(ctx, INSTR_MUL_F, out, (ast_expression*)parser_const_float(parser, vec.x)); + } + else if (!vec.x && !vec.z) { /* v * '0 n 0' */ + ++opts_optimizationcount[OPTIM_VECTOR_COMPONENTS]; + out = (ast_expression*)ast_member_new(ctx, exprs[0], 1, NULL); + out->expression.node.keep = false; + ((ast_member*)out)->rvalue = true; + if (vec.y != 1) + out = (ast_expression*)ast_binary_new(ctx, INSTR_MUL_F, out, (ast_expression*)parser_const_float(parser, vec.y)); + } + else if (!vec.x && !vec.y) { /* v * '0 n 0' */ + ++opts_optimizationcount[OPTIM_VECTOR_COMPONENTS]; + out = (ast_expression*)ast_member_new(ctx, exprs[0], 2, NULL); + out->expression.node.keep = false; + ((ast_member*)out)->rvalue = true; + if (vec.z != 1) + out = (ast_expression*)ast_binary_new(ctx, INSTR_MUL_F, out, (ast_expression*)parser_const_float(parser, vec.z)); + } + else + out = (ast_expression*)ast_binary_new(ctx, INSTR_MUL_V, exprs[0], exprs[1]); + } else out = (ast_expression*)ast_binary_new(ctx, INSTR_MUL_V, exprs[0], exprs[1]); } @@ -890,23 +948,27 @@ static bool parser_sy_apply_operator(parser_t *parser, shunt *sy) parseerror(parser, "invalid types for logical operation with -fperl-logic: %s and %s", ty1, ty2); return false; } - if (OPTS_FLAG(CORRECT_LOGIC)) { - /* non-floats need to be NOTed */ - for (i = 0; i < 2; ++i) { - if (exprs[i]->expression.vtype != TYPE_FLOAT) { - if (type_not_instr[exprs[i]->expression.vtype] == AINSTR_END) { - ast_type_to_string(exprs[0], ty1, sizeof(ty1)); - ast_type_to_string(exprs[1], ty2, sizeof(ty2)); - parseerror(parser, "invalid types for logical operation with -fcorrect-logic: %s and %s", ty1, ty2); - return false; - } - out = (ast_expression*)ast_unary_new(ctx, type_not_instr[exprs[i]->expression.vtype], exprs[i]); - if (!out) break; - out = (ast_expression*)ast_unary_new(ctx, INSTR_NOT_F, out); - if (!out) break; - exprs[i] = out; out = NULL; - if (OPTS_FLAG(PERL_LOGIC)) { - } + for (i = 0; i < 2; ++i) { + if (OPTS_FLAG(CORRECT_LOGIC) && exprs[i]->expression.vtype == TYPE_VECTOR) { + out = (ast_expression*)ast_unary_new(ctx, INSTR_NOT_V, exprs[i]); + if (!out) break; + out = (ast_expression*)ast_unary_new(ctx, INSTR_NOT_F, out); + if (!out) break; + exprs[i] = out; out = NULL; + if (OPTS_FLAG(PERL_LOGIC)) { + /* here we want to keep the right expressions' type */ + break; + } + } + else if (OPTS_FLAG(FALSE_EMPTY_STRINGS) && exprs[i]->expression.vtype == TYPE_STRING) { + out = (ast_expression*)ast_unary_new(ctx, INSTR_NOT_S, exprs[i]); + if (!out) break; + out = (ast_expression*)ast_unary_new(ctx, INSTR_NOT_F, out); + if (!out) break; + exprs[i] = out; out = NULL; + if (OPTS_FLAG(PERL_LOGIC)) { + /* here we want to keep the right expressions' type */ + break; } } } @@ -987,11 +1049,8 @@ static bool parser_sy_apply_operator(parser_t *parser, shunt *sy) field->expression.next->expression.vtype == TYPE_FUNCTION && exprs[1]->expression.vtype == TYPE_FUNCTION) { - if (parsewarning(parser, WARN_ASSIGN_FUNCTION_TYPES, - "invalid types in assignment: cannot assign %s to %s", ty2, ty1)) - { - parser->errors++; - } + (void)!parsewarning(parser, WARN_ASSIGN_FUNCTION_TYPES, + "invalid types in assignment: cannot assign %s to %s", ty2, ty1); } else parseerror(parser, "invalid types in assignment: cannot assign %s to %s", ty2, ty1); @@ -1014,18 +1073,17 @@ static bool parser_sy_apply_operator(parser_t *parser, shunt *sy) ast_type_to_string(exprs[1], ty2, sizeof(ty2)); parseerror(parser, "invalid types in assignment: cannot assign %s to %s", ty2, ty1); } - else if (!ast_compare_type(exprs[0], exprs[1])) { + else if (exprs[1]->expression.vtype != TYPE_NIL && + !ast_compare_type(exprs[0], exprs[1])) + { ast_type_to_string(exprs[0], ty1, sizeof(ty1)); ast_type_to_string(exprs[1], ty2, sizeof(ty2)); if (OPTS_FLAG(ASSIGN_FUNCTION_TYPES) && exprs[0]->expression.vtype == TYPE_FUNCTION && exprs[1]->expression.vtype == TYPE_FUNCTION) { - if (parsewarning(parser, WARN_ASSIGN_FUNCTION_TYPES, - "invalid types in assignment: cannot assign %s to %s", ty2, ty1)) - { - parser->errors++; - } + (void)!parsewarning(parser, WARN_ASSIGN_FUNCTION_TYPES, + "invalid types in assignment: cannot assign %s to %s", ty2, ty1); } else parseerror(parser, "invalid types in assignment: cannot assign %s to %s", ty2, ty1); @@ -1301,8 +1359,7 @@ static bool parser_close_call(parser_t *parser, shunt *sy) params->exprs = NULL; ast_delete(params); } - if (!ast_call_check_types(call)) - parser->errors++; + (void)!ast_call_check_types(call); } else { parseerror(parser, "invalid function call"); return false; @@ -1475,7 +1532,7 @@ static ast_expression* parse_expression_leave(parser_t *parser, bool stopatcomma val = parser_const_string(parser, parser_tokval(parser), true); wantop = true; if (!val) - return false; + return NULL; vec_push(sy.out, syexp(parser_ctx(parser), (ast_expression*)val)); DEBUGSHUNTDO(con_out("push string\n")); @@ -1552,7 +1609,7 @@ static ast_expression* parse_expression_leave(parser_t *parser, bool stopatcomma wantop = true; val = parser_const_float(parser, (parser_token(parser)->constval.f)); if (!val) - return false; + return NULL; vec_push(sy.out, syexp(parser_ctx(parser), (ast_expression*)val)); DEBUGSHUNTDO(con_out("push %g\n", parser_token(parser)->constval.f)); } @@ -1565,7 +1622,7 @@ static ast_expression* parse_expression_leave(parser_t *parser, bool stopatcomma wantop = true; val = parser_const_float(parser, (double)(parser_token(parser)->constval.i)); if (!val) - return false; + return NULL; vec_push(sy.out, syexp(parser_ctx(parser), (ast_expression*)val)); DEBUGSHUNTDO(con_out("push %i\n", parser_token(parser)->constval.i)); } @@ -1578,7 +1635,7 @@ static ast_expression* parse_expression_leave(parser_t *parser, bool stopatcomma wantop = true; val = parser_const_string(parser, parser_tokval(parser), false); if (!val) - return false; + return NULL; vec_push(sy.out, syexp(parser_ctx(parser), (ast_expression*)val)); DEBUGSHUNTDO(con_out("push string\n")); } @@ -1591,7 +1648,7 @@ static ast_expression* parse_expression_leave(parser_t *parser, bool stopatcomma wantop = true; val = parser_const_vector(parser, parser_token(parser)->constval.v); if (!val) - return false; + return NULL; vec_push(sy.out, syexp(parser_ctx(parser), (ast_expression*)val)); DEBUGSHUNTDO(con_out("push '%g %g %g'\n", parser_token(parser)->constval.v.x, @@ -1670,7 +1727,7 @@ static ast_expression* parse_expression_leave(parser_t *parser, bool stopatcomma const oper_info *olast = NULL; size_t o; for (o = 0; o < operator_count; ++o) { - if ((!(operators[o].flags & OP_PREFIX) == wantop) && + if (((!(operators[o].flags & OP_PREFIX) == !!wantop)) && /* !(operators[o].flags & OP_SUFFIX) && / * remove this */ !strcmp(parser_tokval(parser), operators[o].op)) { @@ -1868,10 +1925,8 @@ static bool parser_leaveblock(parser_t *parser) ast_value *v = (ast_value*)e; vec_pop(parser->_locals); if (ast_istype(e, ast_value) && !v->uses) { - if (compile_warning(ast_ctx(v), WARN_UNUSED_VARIABLE, "unused variable: `%s`", v->name)) { - parser->errors++; + if (compile_warning(ast_ctx(v), WARN_UNUSED_VARIABLE, "unused variable: `%s`", v->name)) rv = false; - } } } @@ -1899,7 +1954,8 @@ static ast_expression* process_condition(parser_t *parser, ast_expression *cond, ast_unary *unary; ast_expression *prev; - if (OPTS_FLAG(FALSE_EMPTY_STRINGS) && cond->expression.vtype == TYPE_STRING) { + if (OPTS_FLAG(FALSE_EMPTY_STRINGS) && cond->expression.vtype == TYPE_STRING) + { prev = cond; cond = (ast_expression*)ast_unary_new(ast_ctx(cond), INSTR_NOT_S, cond); if (!cond) { @@ -1909,22 +1965,15 @@ static ast_expression* process_condition(parser_t *parser, ast_expression *cond, } ifnot = !ifnot; } - if (OPTS_FLAG(CORRECT_LOGIC) && - !(cond->expression.vtype == TYPE_STRING && OPTS_FLAG(TRUE_EMPTY_STRINGS))) + else if (OPTS_FLAG(CORRECT_LOGIC) && cond->expression.vtype == TYPE_VECTOR) { - /* non-floats need to use NOT; except for strings on -ftrue-empty-strings */ - unary = (ast_unary*)cond; - if (!ast_istype(cond, ast_unary) || unary->op < INSTR_NOT_F || unary->op > INSTR_NOT_FNC) + /* vector types need to be cast to true booleans */ + ast_binary *bin = (ast_binary*)cond; + if (!OPTS_FLAG(PERL_LOGIC) || !ast_istype(cond, ast_binary) || !(bin->op == INSTR_AND || bin->op == INSTR_OR)) { - /* use the right NOT_ */ + /* in perl-logic, AND and OR take care of the -fcorrect-logic */ prev = cond; - cond = (ast_expression*)ast_unary_new(ast_ctx(cond), type_not_instr[cond->expression.vtype], cond); - - /* - * cppcheck: it thinks there is a possible null pointer dereference - * otherwise it would be "redundant" to check it ast_unary_new returned - * null, it's wrong. - */ + cond = (ast_expression*)ast_unary_new(ast_ctx(cond), INSTR_NOT_V, cond); if (!cond) { ast_unref(prev); parseerror(parser, "internal error: failed to process condition"); @@ -1936,7 +1985,6 @@ static ast_expression* process_condition(parser_t *parser, ast_expression *cond, unary = (ast_unary*)cond; while (ast_istype(cond, ast_unary) && unary->op == INSTR_NOT_F) - /*&& unary->operand->expression.vtype != TYPE_STRING) */ { cond = unary->operand; unary->operand = NULL; @@ -2034,7 +2082,61 @@ static bool parse_if(parser_t *parser, ast_block *block, ast_expression **out) return true; } +static bool parse_while_go(parser_t *parser, ast_block *block, ast_expression **out); static bool parse_while(parser_t *parser, ast_block *block, ast_expression **out) +{ + bool rv; + char *label = NULL; + + /* skip the 'while' and get the body */ + if (!parser_next(parser)) { + if (OPTS_FLAG(LOOP_LABELS)) + parseerror(parser, "expected loop label or 'while' condition in parenthesis"); + else + parseerror(parser, "expected 'while' condition in parenthesis"); + return false; + } + + if (parser->tok == ':') { + if (!OPTS_FLAG(LOOP_LABELS)) + parseerror(parser, "labeled loops not activated, try using -floop-labels"); + if (!parser_next(parser) || parser->tok != TOKEN_IDENT) { + parseerror(parser, "expected loop label"); + return false; + } + label = util_strdup(parser_tokval(parser)); + if (!parser_next(parser)) { + mem_d(label); + parseerror(parser, "expected 'while' condition in parenthesis"); + return false; + } + } + + if (parser->tok != '(') { + parseerror(parser, "expected 'while' condition in parenthesis"); + return false; + } + + vec_push(parser->breaks, label); + vec_push(parser->continues, label); + + rv = parse_while_go(parser, block, out); + if (label) + mem_d(label); + if (vec_last(parser->breaks) != label || vec_last(parser->continues) != label) { + parseerror(parser, "internal error: label stack corrupted"); + rv = false; + ast_delete(*out); + *out = NULL; + } + else { + vec_pop(parser->breaks); + vec_pop(parser->continues); + } + return rv; +} + +static bool parse_while_go(parser_t *parser, ast_block *block, ast_expression **out) { ast_loop *aloop; ast_expression *cond, *ontrue; @@ -2045,11 +2147,6 @@ static bool parse_while(parser_t *parser, ast_block *block, ast_expression **out (void)block; /* not touching */ - /* 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"); @@ -2086,7 +2183,56 @@ static bool parse_while(parser_t *parser, ast_block *block, ast_expression **out return true; } +static bool parse_dowhile_go(parser_t *parser, ast_block *block, ast_expression **out); static bool parse_dowhile(parser_t *parser, ast_block *block, ast_expression **out) +{ + bool rv; + char *label = NULL; + + /* skip the 'do' and get the body */ + if (!parser_next(parser)) { + if (OPTS_FLAG(LOOP_LABELS)) + parseerror(parser, "expected loop label or body"); + else + parseerror(parser, "expected loop body"); + return false; + } + + if (parser->tok == ':') { + if (!OPTS_FLAG(LOOP_LABELS)) + parseerror(parser, "labeled loops not activated, try using -floop-labels"); + if (!parser_next(parser) || parser->tok != TOKEN_IDENT) { + parseerror(parser, "expected loop label"); + return false; + } + label = util_strdup(parser_tokval(parser)); + if (!parser_next(parser)) { + mem_d(label); + parseerror(parser, "expected loop body"); + return false; + } + } + + vec_push(parser->breaks, label); + vec_push(parser->continues, label); + + rv = parse_dowhile_go(parser, block, out); + if (label) + mem_d(label); + if (vec_last(parser->breaks) != label || vec_last(parser->continues) != label) { + parseerror(parser, "internal error: label stack corrupted"); + rv = false; + ast_delete(*out); + *out = NULL; + } + else { + vec_pop(parser->breaks); + vec_pop(parser->continues); + } + return rv; +} + +static bool parse_dowhile_go(parser_t *parser, ast_block *block, ast_expression **out) { ast_loop *aloop; ast_expression *cond, *ontrue; @@ -2097,11 +2243,6 @@ static bool parse_dowhile(parser_t *parser, ast_block *block, ast_expression **o (void)block; /* not touching */ - /* skip the 'do' and get the body */ - if (!parser_next(parser)) { - parseerror(parser, "expected loop body"); - return false; - } if (!parse_statement_or_block(parser, &ontrue)) return false; @@ -2162,7 +2303,60 @@ static bool parse_dowhile(parser_t *parser, ast_block *block, ast_expression **o return true; } +static bool parse_for_go(parser_t *parser, ast_block *block, ast_expression **out); static bool parse_for(parser_t *parser, ast_block *block, ast_expression **out) +{ + bool rv; + char *label = NULL; + + /* skip the 'for' and check for opening paren */ + if (!parser_next(parser)) { + if (OPTS_FLAG(LOOP_LABELS)) + parseerror(parser, "expected loop label or 'for' expressions in parenthesis"); + else + parseerror(parser, "expected 'for' expressions in parenthesis"); + return false; + } + + if (parser->tok == ':') { + if (!OPTS_FLAG(LOOP_LABELS)) + parseerror(parser, "labeled loops not activated, try using -floop-labels"); + if (!parser_next(parser) || parser->tok != TOKEN_IDENT) { + parseerror(parser, "expected loop label"); + return false; + } + label = util_strdup(parser_tokval(parser)); + if (!parser_next(parser)) { + mem_d(label); + parseerror(parser, "expected 'for' expressions in parenthesis"); + return false; + } + } + + if (parser->tok != '(') { + parseerror(parser, "expected 'for' expressions in parenthesis"); + return false; + } + + vec_push(parser->breaks, label); + vec_push(parser->continues, label); + + rv = parse_for_go(parser, block, out); + if (label) + mem_d(label); + if (vec_last(parser->breaks) != label || vec_last(parser->continues) != label) { + parseerror(parser, "internal error: label stack corrupted"); + rv = false; + ast_delete(*out); + *out = NULL; + } + else { + vec_pop(parser->breaks); + vec_pop(parser->continues); + } + return rv; +} +static bool parse_for_go(parser_t *parser, ast_block *block, ast_expression **out) { ast_loop *aloop; ast_expression *initexpr, *cond, *increment, *ontrue; @@ -2180,11 +2374,6 @@ static bool parse_for(parser_t *parser, ast_block *block, ast_expression **out) 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"); @@ -2201,7 +2390,7 @@ static bool parse_for(parser_t *parser, ast_block *block, ast_expression **out) "current standard does not allow variable declarations in for-loop initializers")) goto onerr; } - if (!parse_variable(parser, block, true, CV_VAR, typevar, false, false)) + if (!parse_variable(parser, block, true, CV_VAR, typevar, false, false, 0)) goto onerr; } else if (parser->tok != ';') @@ -2327,11 +2516,39 @@ static bool parse_return(parser_t *parser, ast_block *block, ast_expression **ou static bool parse_break_continue(parser_t *parser, ast_block *block, ast_expression **out, bool is_continue) { - lex_ctx ctx = parser_ctx(parser); + size_t i; + unsigned int levels = 0; + lex_ctx ctx = parser_ctx(parser); + const char **loops = (is_continue ? parser->continues : parser->breaks); (void)block; /* not touching */ + if (!parser_next(parser)) { + parseerror(parser, "expected semicolon or loop label"); + return false; + } - if (!parser_next(parser) || parser->tok != ';') { + if (parser->tok == TOKEN_IDENT) { + if (!OPTS_FLAG(LOOP_LABELS)) + parseerror(parser, "labeled loops not activated, try using -floop-labels"); + i = vec_size(loops); + while (i--) { + if (loops[i] && !strcmp(loops[i], parser_tokval(parser))) + break; + if (!i) { + parseerror(parser, "no such loop to %s: `%s`", + (is_continue ? "continue" : "break out of"), + parser_tokval(parser)); + return false; + } + ++levels; + } + if (!parser_next(parser)) { + parseerror(parser, "expected semicolon"); + return false; + } + } + + if (parser->tok != ';') { parseerror(parser, "expected semicolon"); return false; } @@ -2339,22 +2556,72 @@ static bool parse_break_continue(parser_t *parser, ast_block *block, ast_express if (!parser_next(parser)) parseerror(parser, "parse error"); - *out = (ast_expression*)ast_breakcont_new(ctx, is_continue); + *out = (ast_expression*)ast_breakcont_new(ctx, is_continue, levels); return true; } /* returns true when it was a variable qualifier, false otherwise! * on error, cvq is set to CV_WRONG */ -static bool parse_var_qualifiers(parser_t *parser, bool with_local, int *cvq, bool *noref, bool *noreturn) +static bool parse_qualifiers(parser_t *parser, bool with_local, int *cvq, bool *noref, bool *is_static, uint32_t *_flags) { - bool had_const = false; - bool had_var = false; - bool had_noref = false; - bool had_noreturn = false; - + bool had_const = false; + bool had_var = false; + bool had_noref = false; + bool had_attrib = false; + bool had_static = false; + uint32_t flags = 0; + + *cvq = CV_NONE; for (;;) { - if (!strcmp(parser_tokval(parser), "const")) + if (parser->tok == TOKEN_ATTRIBUTE_OPEN) { + had_attrib = true; + /* parse an attribute */ + if (!parser_next(parser)) { + parseerror(parser, "expected attribute after `[[`"); + *cvq = CV_WRONG; + return false; + } + if (!strcmp(parser_tokval(parser), "noreturn")) { + flags |= AST_FLAG_NORETURN; + if (!parser_next(parser) || parser->tok != TOKEN_ATTRIBUTE_CLOSE) { + parseerror(parser, "`noreturn` attribute has no parameters, expected `]]`"); + *cvq = CV_WRONG; + return false; + } + } + else if (!strcmp(parser_tokval(parser), "noref")) { + had_noref = true; + if (!parser_next(parser) || parser->tok != TOKEN_ATTRIBUTE_CLOSE) { + parseerror(parser, "`noref` attribute has no parameters, expected `]]`"); + *cvq = CV_WRONG; + return false; + } + } + else if (!strcmp(parser_tokval(parser), "inline")) { + flags |= AST_FLAG_INLINE; + if (!parser_next(parser) || parser->tok != TOKEN_ATTRIBUTE_CLOSE) { + parseerror(parser, "`noref` attribute has no parameters, expected `]]`"); + *cvq = CV_WRONG; + return false; + } + } + else + { + /* Skip tokens until we hit a ]] */ + (void)!parsewarning(parser, WARN_UNKNOWN_ATTRIBUTE, "unknown attribute starting with `%s`", parser_tokval(parser)); + while (parser->tok != TOKEN_ATTRIBUTE_CLOSE) { + if (!parser_next(parser)) { + parseerror(parser, "error inside attribute"); + *cvq = CV_WRONG; + return false; + } + } + } + } + else if (!strcmp(parser_tokval(parser), "static")) + had_static = true; + else if (!strcmp(parser_tokval(parser), "const")) had_const = true; else if (!strcmp(parser_tokval(parser), "var")) had_var = true; @@ -2362,9 +2629,7 @@ static bool parse_var_qualifiers(parser_t *parser, bool with_local, int *cvq, bo had_var = true; else if (!strcmp(parser_tokval(parser), "noref")) had_noref = true; - else if (!strcmp(parser_tokval(parser), "noreturn")) - had_noreturn = true; - else if (!had_const && !had_var && !had_noref && !had_noreturn) { + else if (!had_const && !had_var && !had_noref && !had_attrib && !had_static && !flags) { return false; } else @@ -2378,8 +2643,9 @@ static bool parse_var_qualifiers(parser_t *parser, bool with_local, int *cvq, bo *cvq = CV_VAR; else *cvq = CV_NONE; - *noref = had_noref; - *noreturn = had_noreturn; + *noref = had_noref; + *is_static = had_static; + *_flags = flags; return true; onerr: parseerror(parser, "parse error after variable qualifier"); @@ -2387,7 +2653,59 @@ onerr: return true; } +static bool parse_switch_go(parser_t *parser, ast_block *block, ast_expression **out); static bool parse_switch(parser_t *parser, ast_block *block, ast_expression **out) +{ + bool rv; + char *label = NULL; + + /* skip the 'while' and get the body */ + if (!parser_next(parser)) { + if (OPTS_FLAG(LOOP_LABELS)) + parseerror(parser, "expected loop label or 'switch' operand in parenthesis"); + else + parseerror(parser, "expected 'switch' operand in parenthesis"); + return false; + } + + if (parser->tok == ':') { + if (!OPTS_FLAG(LOOP_LABELS)) + parseerror(parser, "labeled loops not activated, try using -floop-labels"); + if (!parser_next(parser) || parser->tok != TOKEN_IDENT) { + parseerror(parser, "expected loop label"); + return false; + } + label = util_strdup(parser_tokval(parser)); + if (!parser_next(parser)) { + mem_d(label); + parseerror(parser, "expected 'switch' operand in parenthesis"); + return false; + } + } + + if (parser->tok != '(') { + parseerror(parser, "expected 'switch' operand in parenthesis"); + return false; + } + + vec_push(parser->breaks, label); + + rv = parse_switch_go(parser, block, out); + if (label) + mem_d(label); + if (vec_last(parser->breaks) != label) { + parseerror(parser, "internal error: label stack corrupted"); + rv = false; + ast_delete(*out); + *out = NULL; + } + else { + vec_pop(parser->breaks); + } + return rv; +} + +static bool parse_switch_go(parser_t *parser, ast_block *block, ast_expression **out) { ast_expression *operand; ast_value *opval; @@ -2396,19 +2714,14 @@ static bool parse_switch(parser_t *parser, ast_block *block, ast_expression **ou ast_switch_case swcase; int cvq; - bool noref, noreturn; + bool noref, is_static; + uint32_t qflags = 0; lex_ctx ctx = parser_ctx(parser); (void)block; /* not touching */ (void)opval; - /* parse over the opening paren */ - if (!parser_next(parser) || parser->tok != '(') { - parseerror(parser, "expected switch operand in parenthesis"); - return false; - } - /* parse into the expression */ if (!parser_next(parser)) { parseerror(parser, "expected switch operand"); @@ -2448,19 +2761,19 @@ static bool parse_switch(parser_t *parser, ast_block *block, ast_expression **ou if (parser->tok == TOKEN_IDENT) typevar = parser_find_typedef(parser, parser_tokval(parser), 0); if (typevar || parser->tok == TOKEN_TYPENAME) { - if (!parse_variable(parser, block, false, CV_NONE, typevar, false, false)) { + if (!parse_variable(parser, block, false, CV_NONE, typevar, false, false, 0)) { ast_delete(switchnode); return false; } continue; } - if (parse_var_qualifiers(parser, true, &cvq, &noref, &noreturn)) + if (parse_qualifiers(parser, true, &cvq, &noref, &is_static, &qflags)) { if (cvq == CV_WRONG) { ast_delete(switchnode); return false; } - if (!parse_variable(parser, block, false, cvq, NULL, noref, noreturn)) { + if (!parse_variable(parser, block, false, cvq, NULL, noref, is_static, qflags)) { ast_delete(switchnode); return false; } @@ -2473,11 +2786,6 @@ static bool parse_switch(parser_t *parser, ast_block *block, ast_expression **ou while (parser->tok != '}') { ast_block *caseblock; - if (parser->tok != TOKEN_KEYWORD) { - ast_delete(switchnode); - parseerror(parser, "expected 'case' or 'default'"); - return false; - } if (!strcmp(parser_tokval(parser), "case")) { if (!parser_next(parser)) { ast_delete(switchnode); @@ -2506,6 +2814,11 @@ static bool parse_switch(parser_t *parser, ast_block *block, ast_expression **ou return false; } } + else { + ast_delete(switchnode); + parseerror(parser, "expected 'case' or 'default'"); + return false; + } /* Now the colon and body */ if (parser->tok != ':') { @@ -2675,8 +2988,9 @@ static bool parse_pragma(parser_t *parser) static bool parse_statement(parser_t *parser, ast_block *block, ast_expression **out, bool allow_cases) { - bool noref, noreturn; + bool noref, is_static; int cvq = CV_NONE; + uint32_t qflags = 0; ast_value *typevar = NULL; *out = NULL; @@ -2695,15 +3009,15 @@ static bool parse_statement(parser_t *parser, ast_block *block, ast_expression * if (parsewarning(parser, WARN_EXTENSIONS, "missing 'local' keyword when declaring a local variable")) return false; } - if (!parse_variable(parser, block, false, CV_NONE, typevar, false, false)) + if (!parse_variable(parser, block, false, CV_NONE, typevar, false, false, 0)) return false; return true; } - else if (parse_var_qualifiers(parser, !!block, &cvq, &noref, &noreturn)) + else if (parse_qualifiers(parser, !!block, &cvq, &noref, &is_static, &qflags)) { if (cvq == CV_WRONG) return false; - return parse_variable(parser, block, true, cvq, NULL, noref, noreturn); + return parse_variable(parser, block, true, cvq, NULL, noref, is_static, qflags); } else if (parser->tok == TOKEN_KEYWORD) { @@ -2932,7 +3246,7 @@ static bool create_vector_members(ast_value *var, ast_member **me) size_t len = strlen(var->name); for (i = 0; i < 3; ++i) { - char *name = mem_a(len+3); + char *name = (char*)mem_a(len+3); memcpy(name, var->name, len); name[len+0] = '_'; name[len+1] = 'x'+i; @@ -3669,9 +3983,9 @@ static ast_value *parse_parameter_list(parser_t *parser, ast_value *var) goto on_error; vec_push(params, param); if (param->expression.vtype >= TYPE_VARIANT) { - char typename[1024]; - ast_type_to_string((ast_expression*)param, typename, sizeof(typename)); - parseerror(parser, "type not supported as part of a parameter list: %s", typename); + char tname[1024]; /* typename is reserved in C++ */ + ast_type_to_string((ast_expression*)param, tname, sizeof(tname)); + parseerror(parser, "type not supported as part of a parameter list: %s", tname); goto on_error; } } @@ -3959,7 +4273,45 @@ static bool parse_typedef(parser_t *parser) return true; } -static bool parse_variable(parser_t *parser, ast_block *localblock, bool nofields, int qualifier, ast_value *cached_typedef, bool noref, bool noreturn) +static const char *cvq_to_str(int cvq) { + switch (cvq) { + case CV_NONE: return "none"; + case CV_VAR: return "`var`"; + case CV_CONST: return "`const`"; + default: return ""; + } +} + +static bool parser_check_qualifiers(parser_t *parser, const ast_value *var, const ast_value *proto) +{ + bool av, ao; + if (proto->cvq != var->cvq) { + if (!(proto->cvq == CV_CONST && var->cvq == CV_NONE && + !OPTS_FLAG(INITIALIZED_NONCONSTANTS) && + parser->tok == '=')) + { + return !parsewarning(parser, WARN_DIFFERENT_QUALIFIERS, + "`%s` declared with different qualifiers: %s\n" + " -> previous declaration here: %s:%i uses %s", + var->name, cvq_to_str(var->cvq), + ast_ctx(proto).file, ast_ctx(proto).line, + cvq_to_str(proto->cvq)); + } + } + av = (var ->expression.flags & AST_FLAG_NORETURN); + ao = (proto->expression.flags & AST_FLAG_NORETURN); + if (av != ao) { + return !parsewarning(parser, WARN_DIFFERENT_ATTRIBUTES, + "`%s` declared with different attributes%s\n" + " -> previous declaration here: %s:%i", + var->name, (av ? ": noreturn" : ""), + ast_ctx(proto).file, ast_ctx(proto).line, + (ao ? ": noreturn" : "")); + } + return true; +} + +static bool parse_variable(parser_t *parser, ast_block *localblock, bool nofields, int qualifier, ast_value *cached_typedef, bool noref, bool is_static, uint32_t qflags) { ast_value *var; ast_value *proto; @@ -3976,6 +4328,9 @@ static bool parse_variable(parser_t *parser, ast_block *localblock, bool nofield ast_member *me[3]; + if (!localblock && is_static) + parseerror(parser, "`static` qualifier is not supported in global scope"); + /* get the first complete variable */ var = parse_typename(parser, &basetype, cached_typedef); if (!var) { @@ -4023,17 +4378,20 @@ static bool parse_variable(parser_t *parser, ast_block *localblock, bool nofield } var->cvq = qualifier; - /* in a noref section we simply bump the usecount */ - if (noref || parser->noref) - var->uses++; - if (noreturn) - var->expression.flags |= AST_FLAG_NORETURN; + var->expression.flags |= qflags; /* Part 1: * check for validity: (end_sys_..., multiple-definitions, prototypes, ...) * Also: if there was a prototype, `var` will be deleted and set to `proto` which * is then filled with the previous definition and the parameter-names replaced. */ + if (!strcmp(var->name, "nil")) { + if (OPTS_FLAG(UNTYPED_NIL)) { + if (!localblock || !OPTS_FLAG(PERMISSIVE)) + parseerror(parser, "name `nil` not allowed (try -fpermissive)"); + } else + (void)!parsewarning(parser, WARN_RESERVED_NAMES, "variable name `nil` is reserved"); + } if (!localblock) { /* Deal with end_sys_ vars */ was_end = false; @@ -4111,6 +4469,12 @@ static bool parse_variable(parser_t *parser, ast_block *localblock, bool nofield /* we need the new parameter-names */ for (i = 0; i < vec_size(proto->expression.params); ++i) ast_value_set_name(proto->expression.params[i], var->expression.params[i]->name); + if (!parser_check_qualifiers(parser, var, proto)) { + retval = false; + proto = NULL; + goto cleanup; + } + proto->expression.flags |= var->expression.flags; ast_delete(var); var = proto; } @@ -4138,6 +4502,12 @@ static bool parse_variable(parser_t *parser, ast_block *localblock, bool nofield proto = NULL; goto cleanup; } + if (!parser_check_qualifiers(parser, var, proto)) { + retval = false; + proto = NULL; + goto cleanup; + } + proto->expression.flags |= var->expression.flags; ast_delete(var); var = proto; } @@ -4181,6 +4551,10 @@ static bool parse_variable(parser_t *parser, ast_block *localblock, bool nofield } } + /* in a noref section we simply bump the usecount */ + if (noref || parser->noref) + var->uses++; + /* Part 2: * Create the global/local, and deal with vector types. */ @@ -4222,16 +4596,55 @@ static bool parse_variable(parser_t *parser, ast_block *localblock, bool nofield } } } else { - vec_push(localblock->locals, var); - parser_addlocal(parser, var->name, (ast_expression*)var); - if (isvector) { - for (i = 0; i < 3; ++i) { - parser_addlocal(parser, me[i]->name, (ast_expression*)me[i]); - ast_block_collect(localblock, (ast_expression*)me[i]); + if (is_static) { + /* a static adds itself to be generated like any other global + * but is added to the local namespace instead + */ + char *defname = NULL; + size_t prefix_len, ln; + + ln = strlen(parser->function->name); + vec_append(defname, ln, parser->function->name); + + vec_append(defname, 2, "::"); + /* remember the length up to here */ + prefix_len = vec_size(defname); + + /* Add it to the local scope */ + util_htset(vec_last(parser->variables), var->name, (void*)var); + /* now rename the global */ + ln = strlen(var->name); + vec_append(defname, ln, var->name); + ast_value_set_name(var, defname); + + /* push it to the to-be-generated globals */ + vec_push(parser->globals, (ast_expression*)var); + + /* same game for the vector members */ + if (isvector) { + for (i = 0; i < 3; ++i) { + util_htset(vec_last(parser->variables), me[i]->name, (void*)(me[i])); + + vec_shrinkto(defname, prefix_len); + ln = strlen(me[i]->name); + vec_append(defname, ln, me[i]->name); + ast_member_set_name(me[i], defname); + + vec_push(parser->globals, (ast_expression*)me[i]); + } + } + vec_free(defname); + } else { + vec_push(localblock->locals, var); + parser_addlocal(parser, var->name, (ast_expression*)var); + if (isvector) { + for (i = 0; i < 3; ++i) { + parser_addlocal(parser, me[i]->name, (ast_expression*)me[i]); + ast_block_collect(localblock, (ast_expression*)me[i]); + } } } } - } me[0] = me[1] = me[2] = NULL; cleanvar = false; @@ -4399,8 +4812,12 @@ skipvar: if (!localblock) { cval = (ast_value*)cexp; - if (!ast_istype(cval, ast_value) || ((!cval->hasvalue || cval->cvq != CV_CONST) && !cval->isfield)) + if (cval != parser->nil && + (!ast_istype(cval, ast_value) || ((!cval->hasvalue || cval->cvq != CV_CONST) && !cval->isfield)) + ) + { parseerror(parser, "cannot initialize a global constant variable with a non-constant expression"); + } else { if (opts.standard != COMPILER_GMQCC && @@ -4409,17 +4826,22 @@ skipvar: { var->cvq = CV_CONST; } - var->hasvalue = true; - if (cval->expression.vtype == TYPE_STRING) - var->constval.vstring = parser_strdup(cval->constval.vstring); - else if (cval->expression.vtype == TYPE_FIELD) - var->constval.vfield = cval; + if (cval == parser->nil) + var->expression.flags |= AST_FLAG_INITIALIZED; else - memcpy(&var->constval, &cval->constval, sizeof(var->constval)); - ast_unref(cval); + { + var->hasvalue = true; + if (cval->expression.vtype == TYPE_STRING) + var->constval.vstring = parser_strdup(cval->constval.vstring); + else if (cval->expression.vtype == TYPE_FIELD) + var->constval.vfield = cval; + else + memcpy(&var->constval, &cval->constval, sizeof(var->constval)); + ast_unref(cval); + } } } else { - bool cvq; + int cvq; shunt sy = { NULL, NULL }; cvq = var->cvq; var->cvq = CV_NONE; @@ -4492,23 +4914,24 @@ cleanup: static bool parser_global_statement(parser_t *parser) { - int cvq = CV_WRONG; - bool noref = false; - bool noreturn = false; - ast_value *istype = NULL; + int cvq = CV_WRONG; + bool noref = false; + bool is_static = false; + uint32_t qflags = 0; + ast_value *istype = NULL; if (parser->tok == TOKEN_IDENT) istype = parser_find_typedef(parser, parser_tokval(parser), 0); if (istype || parser->tok == TOKEN_TYPENAME || parser->tok == '.') { - return parse_variable(parser, NULL, false, CV_NONE, istype, false, false); + return parse_variable(parser, NULL, false, CV_NONE, istype, false, false, 0); } - else if (parse_var_qualifiers(parser, false, &cvq, &noref, &noreturn)) + else if (parse_qualifiers(parser, false, &cvq, &noref, &is_static, &qflags)) { if (cvq == CV_WRONG) return false; - return parse_variable(parser, NULL, true, cvq, NULL, noref, noreturn); + return parse_variable(parser, NULL, true, cvq, NULL, noref, is_static, qflags); } else if (parser->tok == TOKEN_KEYWORD) { @@ -4565,62 +4988,63 @@ static void generate_checksum(parser_t *parser) size_t i; ast_value *value; - crc = progdefs_crc_both(crc, "\n/* file generated by qcc, do not modify */\n\ntypedef struct\n{"); - crc = progdefs_crc_sum(crc, "\tint\tpad[28];\n"); - /* - progdefs_crc_file("\tint\tpad;\n"); - progdefs_crc_file("\tint\tofs_return[3];\n"); - progdefs_crc_file("\tint\tofs_parm0[3];\n"); - progdefs_crc_file("\tint\tofs_parm1[3];\n"); - progdefs_crc_file("\tint\tofs_parm2[3];\n"); - progdefs_crc_file("\tint\tofs_parm3[3];\n"); - progdefs_crc_file("\tint\tofs_parm4[3];\n"); - progdefs_crc_file("\tint\tofs_parm5[3];\n"); - progdefs_crc_file("\tint\tofs_parm6[3];\n"); - progdefs_crc_file("\tint\tofs_parm7[3];\n"); - */ - for (i = 0; i < parser->crc_globals; ++i) { - if (!ast_istype(parser->globals[i], ast_value)) - continue; - value = (ast_value*)(parser->globals[i]); - switch (value->expression.vtype) { - case TYPE_FLOAT: crc = progdefs_crc_both(crc, "\tfloat\t"); break; - case TYPE_VECTOR: crc = progdefs_crc_both(crc, "\tvec3_t\t"); break; - case TYPE_STRING: crc = progdefs_crc_both(crc, "\tstring_t\t"); break; - case TYPE_FUNCTION: crc = progdefs_crc_both(crc, "\tfunc_t\t"); break; - default: - crc = progdefs_crc_both(crc, "\tint\t"); - break; - } - crc = progdefs_crc_both(crc, value->name); - crc = progdefs_crc_both(crc, ";\n"); - } - crc = progdefs_crc_both(crc, "} globalvars_t;\n\ntypedef struct\n{\n"); - for (i = 0; i < parser->crc_fields; ++i) { - if (!ast_istype(parser->fields[i], ast_value)) - continue; - value = (ast_value*)(parser->fields[i]); - switch (value->expression.next->expression.vtype) { - case TYPE_FLOAT: crc = progdefs_crc_both(crc, "\tfloat\t"); break; - case TYPE_VECTOR: crc = progdefs_crc_both(crc, "\tvec3_t\t"); break; - case TYPE_STRING: crc = progdefs_crc_both(crc, "\tstring_t\t"); break; - case TYPE_FUNCTION: crc = progdefs_crc_both(crc, "\tfunc_t\t"); break; - default: - crc = progdefs_crc_both(crc, "\tint\t"); - break; - } - crc = progdefs_crc_both(crc, value->name); - crc = progdefs_crc_both(crc, ";\n"); - } - crc = progdefs_crc_both(crc, "} entvars_t;\n\n"); - - code_crc = crc; + crc = progdefs_crc_both(crc, "\n/* file generated by qcc, do not modify */\n\ntypedef struct\n{"); + crc = progdefs_crc_sum(crc, "\tint\tpad[28];\n"); + /* + progdefs_crc_file("\tint\tpad;\n"); + progdefs_crc_file("\tint\tofs_return[3];\n"); + progdefs_crc_file("\tint\tofs_parm0[3];\n"); + progdefs_crc_file("\tint\tofs_parm1[3];\n"); + progdefs_crc_file("\tint\tofs_parm2[3];\n"); + progdefs_crc_file("\tint\tofs_parm3[3];\n"); + progdefs_crc_file("\tint\tofs_parm4[3];\n"); + progdefs_crc_file("\tint\tofs_parm5[3];\n"); + progdefs_crc_file("\tint\tofs_parm6[3];\n"); + progdefs_crc_file("\tint\tofs_parm7[3];\n"); + */ + for (i = 0; i < parser->crc_globals; ++i) { + if (!ast_istype(parser->globals[i], ast_value)) + continue; + value = (ast_value*)(parser->globals[i]); + switch (value->expression.vtype) { + case TYPE_FLOAT: crc = progdefs_crc_both(crc, "\tfloat\t"); break; + case TYPE_VECTOR: crc = progdefs_crc_both(crc, "\tvec3_t\t"); break; + case TYPE_STRING: crc = progdefs_crc_both(crc, "\tstring_t\t"); break; + case TYPE_FUNCTION: crc = progdefs_crc_both(crc, "\tfunc_t\t"); break; + default: + crc = progdefs_crc_both(crc, "\tint\t"); + break; + } + crc = progdefs_crc_both(crc, value->name); + crc = progdefs_crc_both(crc, ";\n"); + } + crc = progdefs_crc_both(crc, "} globalvars_t;\n\ntypedef struct\n{\n"); + for (i = 0; i < parser->crc_fields; ++i) { + if (!ast_istype(parser->fields[i], ast_value)) + continue; + value = (ast_value*)(parser->fields[i]); + switch (value->expression.next->expression.vtype) { + case TYPE_FLOAT: crc = progdefs_crc_both(crc, "\tfloat\t"); break; + case TYPE_VECTOR: crc = progdefs_crc_both(crc, "\tvec3_t\t"); break; + case TYPE_STRING: crc = progdefs_crc_both(crc, "\tstring_t\t"); break; + case TYPE_FUNCTION: crc = progdefs_crc_both(crc, "\tfunc_t\t"); break; + default: + crc = progdefs_crc_both(crc, "\tint\t"); + break; + } + crc = progdefs_crc_both(crc, value->name); + crc = progdefs_crc_both(crc, ";\n"); + } + crc = progdefs_crc_both(crc, "} entvars_t;\n\n"); + + code_crc = crc; } static parser_t *parser; bool parser_init() { + lex_ctx empty_ctx; size_t i; parser = (parser_t*)mem_a(sizeof(parser_t)); @@ -4645,6 +5069,13 @@ bool parser_init() vec_push(parser->variables, parser->htglobals = util_htnew(PARSER_HT_SIZE)); vec_push(parser->typedefs, util_htnew(TYPEDEF_HT_SIZE)); vec_push(parser->_blocktypedefs, 0); + + empty_ctx.file = ""; + empty_ctx.line = 0; + parser->nil = ast_value_new(empty_ctx, "nil", TYPE_NIL); + parser->nil->cvq = CV_CONST; + if (OPTS_FLAG(UNTYPED_NIL)) + util_htset(parser->htglobals, "nil", (void*)parser->nil); return true; } @@ -4660,7 +5091,7 @@ bool parser_compile() if (!parser_global_statement(parser)) { if (parser->tok == TOKEN_EOF) parseerror(parser, "unexpected eof"); - else if (!parser->errors) + else if (compile_errors) parseerror(parser, "there have been errors, bailing out"); lex_close(parser->lex); parser->lex = NULL; @@ -4677,7 +5108,7 @@ bool parser_compile() lex_close(parser->lex); parser->lex = NULL; - return !parser->errors; + return !compile_errors; } bool parser_compile_file(const char *filename) @@ -4690,7 +5121,7 @@ bool parser_compile_file(const char *filename) return parser_compile(); } -bool parser_compile_string_len(const char *name, const char *str, size_t len) +bool parser_compile_string(const char *name, const char *str, size_t len) { parser->lex = lex_open_string(str, len, name); if (!parser->lex) { @@ -4700,16 +5131,6 @@ bool parser_compile_string_len(const char *name, const char *str, size_t len) return parser_compile(); } -bool parser_compile_string(const char *name, const char *str) -{ - parser->lex = lex_open_string(str, strlen(str), name); - if (!parser->lex) { - con_err("failed to create lexer for string \"%s\"\n", name); - return false; - } - return parser_compile(); -} - void parser_cleanup() { size_t i; @@ -4762,6 +5183,8 @@ void parser_cleanup() vec_free(parser->labels); vec_free(parser->gotos); + vec_free(parser->breaks); + vec_free(parser->continues); mem_d(parser); } @@ -4772,133 +5195,149 @@ bool parser_finish(const char *output) ir_builder *ir; bool retval = true; - if (!parser->errors) - { - ir = ir_builder_new("gmqcc_out"); - if (!ir) { - con_out("failed to allocate builder\n"); + if (compile_errors) { + con_out("*** there were compile errors\n"); + return false; + } + + ir = ir_builder_new("gmqcc_out"); + if (!ir) { + con_out("failed to allocate builder\n"); + return false; + } + + for (i = 0; i < vec_size(parser->fields); ++i) { + ast_value *field; + bool hasvalue; + if (!ast_istype(parser->fields[i], ast_value)) + continue; + field = (ast_value*)parser->fields[i]; + hasvalue = field->hasvalue; + field->hasvalue = false; + if (!ast_global_codegen((ast_value*)field, ir, true)) { + con_out("failed to generate field %s\n", field->name); + ir_builder_delete(ir); return false; } - - for (i = 0; i < vec_size(parser->fields); ++i) { - ast_value *field; - bool hasvalue; - if (!ast_istype(parser->fields[i], ast_value)) - continue; - field = (ast_value*)parser->fields[i]; - hasvalue = field->hasvalue; - field->hasvalue = false; - if (!ast_global_codegen((ast_value*)field, ir, true)) { - con_out("failed to generate field %s\n", field->name); - ir_builder_delete(ir); - return false; - } - if (hasvalue) { - ir_value *ifld; - ast_expression *subtype; - field->hasvalue = 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 < vec_size(parser->globals); ++i) { - ast_value *asvalue; - if (!ast_istype(parser->globals[i], ast_value)) - continue; - asvalue = (ast_value*)(parser->globals[i]); - if (!asvalue->uses && !asvalue->hasvalue && asvalue->expression.vtype != TYPE_FUNCTION) { - retval = retval && !genwarning(ast_ctx(asvalue), WARN_UNUSED_VARIABLE, - "unused global: `%s`", asvalue->name); - } - if (!ast_global_codegen(asvalue, ir, false)) { - con_out("failed to generate global %s\n", asvalue->name); - ir_builder_delete(ir); - return false; - } + if (hasvalue) { + ir_value *ifld; + ast_expression *subtype; + field->hasvalue = 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 < vec_size(parser->imm_float); ++i) { - if (!ast_global_codegen(parser->imm_float[i], ir, false)) { - con_out("failed to generate global %s\n", parser->imm_float[i]->name); - ir_builder_delete(ir); - return false; - } + } + for (i = 0; i < vec_size(parser->globals); ++i) { + ast_value *asvalue; + if (!ast_istype(parser->globals[i], ast_value)) + continue; + asvalue = (ast_value*)(parser->globals[i]); + if (!asvalue->uses && !asvalue->hasvalue && asvalue->expression.vtype != TYPE_FUNCTION) { + retval = retval && !genwarning(ast_ctx(asvalue), WARN_UNUSED_VARIABLE, + "unused global: `%s`", asvalue->name); + } + if (!ast_global_codegen(asvalue, ir, false)) { + con_out("failed to generate global %s\n", asvalue->name); + ir_builder_delete(ir); + return false; } - for (i = 0; i < vec_size(parser->imm_string); ++i) { - if (!ast_global_codegen(parser->imm_string[i], ir, false)) { - con_out("failed to generate global %s\n", parser->imm_string[i]->name); - ir_builder_delete(ir); - return false; - } + } + for (i = 0; i < vec_size(parser->imm_float); ++i) { + if (!ast_global_codegen(parser->imm_float[i], ir, false)) { + con_out("failed to generate global %s\n", parser->imm_float[i]->name); + ir_builder_delete(ir); + return false; } - for (i = 0; i < vec_size(parser->imm_vector); ++i) { - if (!ast_global_codegen(parser->imm_vector[i], ir, false)) { - con_out("failed to generate global %s\n", parser->imm_vector[i]->name); - ir_builder_delete(ir); - return false; - } + } + for (i = 0; i < vec_size(parser->imm_string); ++i) { + if (!ast_global_codegen(parser->imm_string[i], ir, false)) { + con_out("failed to generate global %s\n", parser->imm_string[i]->name); + ir_builder_delete(ir); + return false; } - for (i = 0; i < vec_size(parser->globals); ++i) { - ast_value *asvalue; - if (!ast_istype(parser->globals[i], ast_value)) - continue; - asvalue = (ast_value*)(parser->globals[i]); - if (!ast_generate_accessors(asvalue, ir)) { - ir_builder_delete(ir); - return false; - } + } + for (i = 0; i < vec_size(parser->imm_vector); ++i) { + if (!ast_global_codegen(parser->imm_vector[i], ir, false)) { + con_out("failed to generate global %s\n", parser->imm_vector[i]->name); + ir_builder_delete(ir); + return false; + } + } + for (i = 0; i < vec_size(parser->globals); ++i) { + ast_value *asvalue; + if (!ast_istype(parser->globals[i], ast_value)) + continue; + asvalue = (ast_value*)(parser->globals[i]); + if (!(asvalue->expression.flags & AST_FLAG_INITIALIZED)) + { + if (asvalue->cvq == CV_CONST && !asvalue->hasvalue) + (void)!compile_warning(ast_ctx(asvalue), WARN_UNINITIALIZED_CONSTANT, + "uninitialized constant: `%s`", + asvalue->name); + else if ((asvalue->cvq == CV_NONE || asvalue->cvq == CV_CONST) && !asvalue->hasvalue) + (void)!compile_warning(ast_ctx(asvalue), WARN_UNINITIALIZED_GLOBAL, + "uninitialized global: `%s`", + asvalue->name); + } + if (!ast_generate_accessors(asvalue, ir)) { + ir_builder_delete(ir); + return false; } - for (i = 0; i < vec_size(parser->fields); ++i) { - ast_value *asvalue; - asvalue = (ast_value*)(parser->fields[i]->expression.next); + } + for (i = 0; i < vec_size(parser->fields); ++i) { + ast_value *asvalue; + asvalue = (ast_value*)(parser->fields[i]->expression.next); - if (!ast_istype((ast_expression*)asvalue, ast_value)) - continue; - if (asvalue->expression.vtype != TYPE_ARRAY) - continue; - if (!ast_generate_accessors(asvalue, ir)) { - ir_builder_delete(ir); - return false; - } + if (!ast_istype((ast_expression*)asvalue, ast_value)) + continue; + if (asvalue->expression.vtype != TYPE_ARRAY) + continue; + if (!ast_generate_accessors(asvalue, ir)) { + ir_builder_delete(ir); + return false; } - for (i = 0; i < vec_size(parser->functions); ++i) { - if (!ast_function_codegen(parser->functions[i], ir)) { - con_out("failed to generate function %s\n", parser->functions[i]->name); - ir_builder_delete(ir); - return false; - } + } + for (i = 0; i < vec_size(parser->functions); ++i) { + if (!ast_function_codegen(parser->functions[i], ir)) { + con_out("failed to generate function %s\n", parser->functions[i]->name); + ir_builder_delete(ir); + return false; } - if (opts.dump) - ir_builder_dump(ir, con_out); - for (i = 0; i < vec_size(parser->functions); ++i) { - if (!ir_function_finalize(parser->functions[i]->ir_func)) { - con_out("failed to finalize function %s\n", parser->functions[i]->name); - ir_builder_delete(ir); - return false; - } + } + if (opts.dump) + ir_builder_dump(ir, con_out); + for (i = 0; i < vec_size(parser->functions); ++i) { + if (!ir_function_finalize(parser->functions[i]->ir_func)) { + con_out("failed to finalize function %s\n", parser->functions[i]->name); + ir_builder_delete(ir); + return false; } + } - if (retval) { - if (opts.dumpfin) - ir_builder_dump(ir, con_out); + if (compile_Werrors) { + con_out("*** there were warnings treated as errors\n"); + compile_show_werrors(); + retval = false; + } - generate_checksum(parser); + if (retval) { + if (opts.dumpfin) + ir_builder_dump(ir, con_out); - if (!ir_builder_generate(ir, output)) { - con_out("*** failed to generate output file\n"); - ir_builder_delete(ir); - return false; - } - } + generate_checksum(parser); - ir_builder_delete(ir); - return retval; + if (!ir_builder_generate(ir, output)) { + con_out("*** failed to generate output file\n"); + ir_builder_delete(ir); + return false; + } } - con_out("*** there were compile errors\n"); - return false; + ir_builder_delete(ir); + return retval; }