X-Git-Url: https://git.xonotic.org/?a=blobdiff_plain;f=parser.c;h=4058a8f99b042c349cfa54fa3aa89717121a2fb3;hb=a2504f645585404bd26b92fb163dd514a8bcada6;hp=ee4718519ece62aae2edf5b6c65ab6b66320fd9e;hpb=b7117e08ce279d2378ba1fc803e44be683b1ad89;p=xonotic%2Fgmqcc.git diff --git a/parser.c b/parser.c index ee47185..4058a8f 100644 --- a/parser.c +++ b/parser.c @@ -26,14 +26,13 @@ #include "gmqcc.h" #include "lexer.h" -#define PARSER_HT_FIELDS 0 -#define PARSER_HT_GLOBALS 1 /* beginning of locals */ #define PARSER_HT_LOCALS 2 #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; @@ -92,17 +91,19 @@ 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; } parser_t; +static const ast_expression *intrinsic_debug_typestring = (ast_expression*)0x10; + 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); +static bool parse_variable(parser_t *parser, ast_block *localblock, bool nofields, int qualifier, ast_value *cached_typedef, bool noref, bool noreturn, bool is_static); 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 +113,34 @@ static ast_expression* parse_expression(parser_t *parser, bool stopatcomma); static void parseerror(parser_t *parser, const char *fmt, ...) { - va_list ap; + va_list ap; - parser->errors++; + parser->errors++; - va_start(ap, fmt); + 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_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 +235,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 +297,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 +329,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; @@ -466,6 +467,32 @@ static bool rotate_entfield_array_index_nodes(ast_expression **out) return true; } +static bool immediate_is_true(lex_ctx ctx, ast_value *v) +{ + switch (v->expression.vtype) { + case TYPE_FLOAT: + return !!v->constval.vfloat; + case TYPE_INTEGER: + return !!v->constval.vint; + case TYPE_VECTOR: + if (OPTS_FLAG(CORRECT_LOGIC)) + return v->constval.vvec.x && + v->constval.vvec.y && + v->constval.vvec.z; + else + return !!(v->constval.vvec.x); + case TYPE_STRING: + if (!v->constval.vstring) + return false; + if (v->constval.vstring && OPTS_FLAG(TRUE_EMPTY_STRINGS)) + return true; + return !!v->constval.vstring[0]; + default: + compile_error(ctx, "internal error: immediate_is_true on invalid type"); + return !!v->constval.vfunc; + } +} + static bool parser_sy_apply_operator(parser_t *parser, shunt *sy) { const oper_info *op; @@ -524,7 +551,8 @@ static bool parser_sy_apply_operator(parser_t *parser, shunt *sy) (exprs[0]->expression.vtype != exprs[1]->expression.vtype || \ exprs[0]->expression.vtype != T) #define CanConstFold1(A) \ - (ast_istype((A), ast_value) && ((ast_value*)(A))->hasvalue && (((ast_value*)(A))->cvq == CV_CONST)) + (ast_istype((A), ast_value) && ((ast_value*)(A))->hasvalue && (((ast_value*)(A))->cvq == CV_CONST) &&\ + (A)->expression.vtype != TYPE_FUNCTION) #define CanConstFold(A, B) \ (CanConstFold1(A) && CanConstFold1(B)) #define ConstV(i) (asvalue[(i)]->constval.vvec) @@ -646,10 +674,17 @@ static bool parser_sy_apply_operator(parser_t *parser, shunt *sy) 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]); + if (CanConstFold1(exprs[0])) { + if (OPTS_FLAG(TRUE_EMPTY_STRINGS)) + out = (ast_expression*)parser_const_float(parser, !ConstS(0)); + else + out = (ast_expression*)parser_const_float(parser, !ConstS(0) || !*ConstS(0)); + } else { + if (OPTS_FLAG(TRUE_EMPTY_STRINGS)) + out = (ast_expression*)ast_unary_new(ctx, INSTR_NOT_F, exprs[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: @@ -835,13 +870,53 @@ static bool parser_sy_apply_operator(parser_t *parser, shunt *sy) return false; } #endif - if (opts.standard == COMPILER_GMQCC) - con_out("TODO: early out logic\n"); if (CanConstFold(exprs[0], exprs[1])) - out = (ast_expression*)parser_const_float(parser, - (generated_op == INSTR_OR ? (ConstF(0) || ConstF(1)) : (ConstF(0) && ConstF(1)))); + { + if (OPTS_FLAG(PERL_LOGIC)) { + if (immediate_is_true(ctx, asvalue[0])) + out = exprs[1]; + } + else + out = (ast_expression*)parser_const_float(parser, + ( (generated_op == INSTR_OR) + ? (immediate_is_true(ctx, asvalue[0]) || immediate_is_true(ctx, asvalue[1])) + : (immediate_is_true(ctx, asvalue[0]) && immediate_is_true(ctx, asvalue[1])) ) + ? 0 : 1); + } else + { + if (OPTS_FLAG(PERL_LOGIC) && !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)); + parseerror(parser, "invalid types for logical operation with -fperl-logic: %s and %s", ty1, ty2); + return false; + } + 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; + } + } + } out = (ast_expression*)ast_binary_new(ctx, generated_op, exprs[0], exprs[1]); + } break; case opid2('?',':'): @@ -1192,9 +1267,25 @@ static bool parser_close_call(parser_t *parser, shunt *sy) fun = sy->out[fid].out; + if (fun == intrinsic_debug_typestring) { + char ty[1024]; + if (fid+2 != vec_size(sy->out) || + vec_last(sy->out).block) + { + parseerror(parser, "intrinsic __builtin_debug_typestring requires exactly 1 parameter"); + return false; + } + ast_type_to_string(vec_last(sy->out).out, ty, sizeof(ty)); + ast_unref(vec_last(sy->out).out); + sy->out[fid] = syexp(ast_ctx(vec_last(sy->out).out), + (ast_expression*)parser_const_string(parser, ty, false)); + vec_shrinkby(sy->out, 1); + return true; + } + call = ast_call_new(sy->ops[vec_size(sy->ops)].ctx, fun); if (!call) { - parseerror(parser, "out of memory"); + parseerror(parser, "internal error: failed to create ast_call node"); return false; } @@ -1235,7 +1326,7 @@ static bool parser_close_call(parser_t *parser, shunt *sy) return false; } else { if (vec_size(fun->expression.params) != paramcount && - !(fun->expression.variadic && + !((fun->expression.flags & AST_FLAG_VARIADIC) && vec_size(fun->expression.params) < paramcount)) { ast_value *fval; @@ -1251,9 +1342,9 @@ static bool parser_close_call(parser_t *parser, shunt *sy) fval->name, ast_ctx(fun).file, (int)ast_ctx(fun).line); else parseerror(parser, "too %s parameters for function call: expected %i, got %i\n" - " -> `%s` has been declared here: %s:%i", - fewmany, fval->name, (int)vec_size(fun->expression.params), (int)paramcount, - fval->name, ast_ctx(fun).file, (int)ast_ctx(fun).line); + " -> it has been declared here: %s:%i", + fewmany, (int)vec_size(fun->expression.params), (int)paramcount, + ast_ctx(fun).file, (int)ast_ctx(fun).line); return false; } else @@ -1267,9 +1358,9 @@ static bool parser_close_call(parser_t *parser, shunt *sy) else return !parsewarning(parser, WARN_TOO_FEW_PARAMETERS, "too %s parameters for function call: expected %i, got %i\n" - " -> `%s` has been declared here: %s:%i", - fewmany, fval->name, (int)vec_size(fun->expression.params), (int)paramcount, - fval->name, ast_ctx(fun).file, (int)ast_ctx(fun).line); + " -> it has been declared here: %s:%i", + fewmany, (int)vec_size(fun->expression.params), (int)paramcount, + ast_ctx(fun).file, (int)ast_ctx(fun).line); } } } @@ -1389,7 +1480,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")); @@ -1432,16 +1523,27 @@ static ast_expression* parse_expression_leave(parser_t *parser, bool stopatcomma var = parser_find_field(parser, parser_tokval(parser)); } if (!var) { - parseerror(parser, "unexpected ident: %s", parser_tokval(parser)); - goto onerr; - } - if (ast_istype(var, ast_value)) { - ((ast_value*)var)->uses++; + /* intrinsics */ + if (!strcmp(parser_tokval(parser), "__builtin_debug_typestring")) { + var = (ast_expression*)intrinsic_debug_typestring; + + } + else + { + parseerror(parser, "unexpected ident: %s", parser_tokval(parser)); + goto onerr; + } } - else if (ast_istype(var, ast_member)) { - ast_member *mem = (ast_member*)var; - if (ast_istype(mem->owner, ast_value)) - ((ast_value*)(mem->owner))->uses++; + else + { + if (ast_istype(var, ast_value)) { + ((ast_value*)var)->uses++; + } + else if (ast_istype(var, ast_member)) { + ast_member *mem = (ast_member*)var; + if (ast_istype(mem->owner, ast_value)) + ((ast_value*)(mem->owner))->uses++; + } } vec_push(sy.out, syexp(parser_ctx(parser), var)); DEBUGSHUNTDO(con_out("push %s\n", parser_tokval(parser))); @@ -1455,7 +1557,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)); } @@ -1468,7 +1570,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)); } @@ -1481,7 +1583,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")); } @@ -1494,7 +1596,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, @@ -1663,7 +1765,6 @@ static ast_expression* parse_expression_leave(parser_t *parser, bool stopatcomma vec_push(sy.ops, syparen(parser_ctx(parser), SY_PAREN_INDEX, 0)); wantop = false; } else if (op->id == opid2('?',':')) { - wantop = false; vec_push(sy.ops, syop(parser_ctx(parser), op)); vec_push(sy.ops, syparen(parser_ctx(parser), SY_PAREN_TERNARY, 0)); wantop = false; @@ -1797,10 +1898,62 @@ static void parser_addlocal(parser_t *parser, const char *name, ast_expression * util_htset(vec_last(parser->variables), name, (void*)e); } +static ast_expression* process_condition(parser_t *parser, ast_expression *cond, bool *_ifnot) +{ + bool ifnot = false; + ast_unary *unary; + ast_expression *prev; + + 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) { + ast_unref(prev); + parseerror(parser, "internal error: failed to process condition"); + return NULL; + } + ifnot = !ifnot; + } + else if (OPTS_FLAG(CORRECT_LOGIC) && cond->expression.vtype == TYPE_VECTOR) + { + /* 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)) + { + /* in perl-logic, AND and OR take care of the -fcorrect-logic */ + prev = cond; + 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"); + return NULL; + } + ifnot = !ifnot; + } + } + + unary = (ast_unary*)cond; + while (ast_istype(cond, ast_unary) && unary->op == INSTR_NOT_F) + { + cond = unary->operand; + unary->operand = NULL; + ast_delete(unary); + ifnot = !ifnot; + unary = (ast_unary*)cond; + } + + if (!cond) + parseerror(parser, "internal error: failed to process condition"); + + if (ifnot) *_ifnot = !*_ifnot; + return cond; +} + static bool parse_if(parser_t *parser, ast_block *block, ast_expression **out) { ast_ifthen *ifthen; - ast_expression *cond, *ontrue, *onfalse = NULL; + ast_expression *cond, *ontrue = NULL, *onfalse = NULL; bool ifnot = false; lex_ctx ctx = parser_ctx(parser); @@ -1864,6 +2017,13 @@ static bool parse_if(parser_t *parser, ast_block *block, ast_expression **out) } } + cond = process_condition(parser, cond, &ifnot); + if (!cond) { + if (ontrue) ast_delete(ontrue); + if (onfalse) ast_delete(onfalse); + return false; + } + if (ifnot) ifthen = ast_ifthen_new(ctx, cond, onfalse, ontrue); else @@ -1877,6 +2037,8 @@ static bool parse_while(parser_t *parser, ast_block *block, ast_expression **out ast_loop *aloop; ast_expression *cond, *ontrue; + bool ifnot = false; + lex_ctx ctx = parser_ctx(parser); (void)block; /* not touching */ @@ -1912,7 +2074,12 @@ static bool parse_while(parser_t *parser, ast_block *block, ast_expression **out return false; } - aloop = ast_loop_new(ctx, NULL, cond, NULL, NULL, ontrue); + cond = process_condition(parser, cond, &ifnot); + if (!cond) { + ast_delete(ontrue); + return false; + } + aloop = ast_loop_new(ctx, NULL, cond, ifnot, NULL, false, NULL, ontrue); *out = (ast_expression*)aloop; return true; } @@ -1922,6 +2089,8 @@ static bool parse_dowhile(parser_t *parser, ast_block *block, ast_expression **o ast_loop *aloop; ast_expression *cond, *ontrue; + bool ifnot = false; + lex_ctx ctx = parser_ctx(parser); (void)block; /* not touching */ @@ -1981,7 +2150,12 @@ static bool parse_dowhile(parser_t *parser, ast_block *block, ast_expression **o return false; } - aloop = ast_loop_new(ctx, NULL, NULL, cond, NULL, ontrue); + cond = process_condition(parser, cond, &ifnot); + if (!cond) { + ast_delete(ontrue); + return false; + } + aloop = ast_loop_new(ctx, NULL, NULL, false, cond, ifnot, NULL, ontrue); *out = (ast_expression*)aloop; return true; } @@ -1991,7 +2165,9 @@ static bool parse_for(parser_t *parser, ast_block *block, ast_expression **out) ast_loop *aloop; ast_expression *initexpr, *cond, *increment, *ontrue; ast_value *typevar; - bool retval = true; + + bool retval = true; + bool ifnot = false; lex_ctx ctx = parser_ctx(parser); @@ -2023,7 +2199,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)) + if (!parse_variable(parser, block, true, CV_VAR, typevar, false, false, false)) goto onerr; } else if (parser->tok != ';') @@ -2084,7 +2260,12 @@ static bool parse_for(parser_t *parser, ast_block *block, ast_expression **out) if (!parse_statement_or_block(parser, &ontrue)) goto onerr; - aloop = ast_loop_new(ctx, initexpr, cond, NULL, increment, ontrue); + if (cond) { + cond = process_condition(parser, cond, &ifnot); + if (!cond) + goto onerr; + } + aloop = ast_loop_new(ctx, initexpr, cond, ifnot, NULL, false, increment, ontrue); *out = (ast_expression*)aloop; if (!parser_leaveblock(parser)) @@ -2122,7 +2303,7 @@ static bool parse_return(parser_t *parser, ast_block *block, ast_expression **ou parseerror(parser, "return with invalid expression"); } - ret = ast_return_new(exp->expression.node.context, exp); + ret = ast_return_new(ctx, exp); if (!ret) { ast_delete(exp); return false; @@ -2163,14 +2344,57 @@ static bool parse_break_continue(parser_t *parser, ast_block *block, ast_express /* 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) +static bool parse_var_qualifiers(parser_t *parser, bool with_local, int *cvq, bool *noref, bool *noreturn, bool *is_static) { - bool had_const = false; - bool had_var = false; - bool had_noref = false; - + bool had_const = false; + bool had_var = false; + bool had_noref = false; + bool had_noreturn = false; + bool had_attrib = false; + bool had_static = false; + + *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")) { + had_noreturn = true; + 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 + { + /* 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; @@ -2178,7 +2402,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 (!had_const && !had_var && !had_noref) { + else if (!had_const && !had_var && !had_noref && !had_noreturn && !had_attrib && !had_static) { return false; } else @@ -2192,7 +2416,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; + *noref = had_noref; + *noreturn = had_noreturn; + *is_static = had_static; return true; onerr: parseerror(parser, "parse error after variable qualifier"); @@ -2209,7 +2435,7 @@ static bool parse_switch(parser_t *parser, ast_block *block, ast_expression **ou ast_switch_case swcase; int cvq; - bool noref; + bool noref, noreturn, is_static; lex_ctx ctx = parser_ctx(parser); @@ -2261,19 +2487,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)) { + if (!parse_variable(parser, block, false, CV_NONE, typevar, false, false, false)) { ast_delete(switchnode); return false; } continue; } - if (parse_var_qualifiers(parser, true, &cvq, &noref)) + if (parse_var_qualifiers(parser, true, &cvq, &noref, &noreturn, &is_static)) { if (cvq == CV_WRONG) { ast_delete(switchnode); return false; } - if (!parse_variable(parser, block, false, cvq, NULL, noref)) { + if (!parse_variable(parser, block, false, cvq, NULL, noref, noreturn, is_static)) { ast_delete(switchnode); return false; } @@ -2304,8 +2530,7 @@ static bool parse_switch(parser_t *parser, ast_block *block, ast_expression **ou return false; } if (!OPTS_FLAG(RELAXED_SWITCH)) { - opval = (ast_value*)swcase.value; - if (!ast_istype(swcase.value, ast_value)) { /* || opval->cvq != CV_CONST) { */ + if (!ast_istype(swcase.value, ast_value)) { /* || ((ast_value*)swcase.value)->cvq != CV_CONST) { */ parseerror(parser, "case on non-constant values need to be explicitly enabled via -frelaxed-switch"); ast_unref(operand); return false; @@ -2489,7 +2714,7 @@ 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; + bool noref, noreturn, is_static; int cvq = CV_NONE; ast_value *typevar = NULL; @@ -2509,15 +2734,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)) + if (!parse_variable(parser, block, false, CV_NONE, typevar, false, false, false)) return false; return true; } - else if (parse_var_qualifiers(parser, !!block, &cvq, &noref)) + else if (parse_var_qualifiers(parser, !!block, &cvq, &noref, &noreturn, &is_static)) { if (cvq == CV_WRONG) return false; - return parse_variable(parser, block, true, cvq, NULL, noref); + return parse_variable(parser, block, true, cvq, NULL, noref, noreturn, is_static); } else if (parser->tok == TOKEN_KEYWORD) { @@ -2746,7 +2971,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; @@ -2788,7 +3013,7 @@ static bool parse_function_body(parser_t *parser, ast_value *var) return false; } - if (var->expression.variadic) { + if (var->expression.flags & AST_FLAG_VARIADIC) { if (parsewarning(parser, WARN_VARIADIC_FUNCTION, "variadic function with implementation will not be able to access additional parameters")) { @@ -3483,9 +3708,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; } } @@ -3507,7 +3732,8 @@ static ast_value *parse_parameter_list(parser_t *parser, ast_value *var) /* now turn 'var' into a function type */ fval = ast_value_new(ctx, "", TYPE_FUNCTION); fval->expression.next = (ast_expression*)var; - fval->expression.variadic = variadic; + if (variadic) + fval->expression.flags |= AST_FLAG_VARIADIC; var = fval; var->expression.params = params; @@ -3772,7 +3998,7 @@ 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) +static bool parse_variable(parser_t *parser, ast_block *localblock, bool nofields, int qualifier, ast_value *cached_typedef, bool noref, bool noreturn, bool is_static) { ast_value *var; ast_value *proto; @@ -3789,6 +4015,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) { @@ -3839,6 +4068,8 @@ 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++; + if (noreturn) + var->expression.flags |= AST_FLAG_NORETURN; /* Part 1: * check for validity: (end_sys_..., multiple-definitions, prototypes, ...) @@ -4033,16 +4264,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; @@ -4184,7 +4454,6 @@ skipvar: } else if (parser->tok == '{' || parser->tok == '[') { - size_t i; if (localblock) { parseerror(parser, "cannot declare functions within functions"); break; @@ -4304,22 +4573,24 @@ cleanup: static bool parser_global_statement(parser_t *parser) { - int cvq = CV_WRONG; - bool noref = false; - ast_value *istype = NULL; + int cvq = CV_WRONG; + bool noref = false; + bool noreturn = false; + bool is_static = false; + 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); + return parse_variable(parser, NULL, false, CV_NONE, istype, false, false, false); } - else if (parse_var_qualifiers(parser, false, &cvq, &noref)) + else if (parse_var_qualifiers(parser, false, &cvq, &noref, &noreturn, &is_static)) { if (cvq == CV_WRONG) return false; - return parse_variable(parser, NULL, true, cvq, NULL, noref); + return parse_variable(parser, NULL, true, cvq, NULL, noref, noreturn, is_static); } else if (parser->tok == TOKEN_KEYWORD) { @@ -4376,56 +4647,56 @@ 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; @@ -4501,7 +4772,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) { @@ -4511,16 +4782,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;