X-Git-Url: https://git.xonotic.org/?a=blobdiff_plain;f=parser.c;h=769cced2b2f13783029b313a97055a0bad2f662b;hb=f140251e85c41477280e9eb787ecb4195c3d3ac9;hp=c3c4fa2c62c1fd275911daebb031aa6295a0124e;hpb=02ec45363e86b5ab0f74535be979d2e9f9942748;p=xonotic%2Fgmqcc.git diff --git a/parser.c b/parser.c index c3c4fa2..769cced 100644 --- a/parser.c +++ b/parser.c @@ -93,13 +93,16 @@ typedef struct { * we shall trigger -Wternary-precedence. */ enum { POT_PAREN, POT_TERNARY1, POT_TERNARY2 } *pot; + + /* pragma flags */ + bool noref; } parser_t; 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); +static bool parse_variable(parser_t *parser, ast_block *localblock, bool nofields, int qualifier, ast_value *cached_typedef, bool noref); 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); @@ -133,7 +136,7 @@ static bool GMQCC_WARN parsewarning(parser_t *parser, int warntype, const char * } va_start(ap, fmt); - con_vprintmsg(lvl, parser->lex->tok.ctx.file, parser->lex->tok.ctx.line, "warning", fmt, ap); + con_vprintmsg(lvl, parser->lex->tok.ctx.file, parser->lex->tok.ctx.line, (opts_werror ? "error" : "warning"), fmt, ap); va_end(ap); return opts_werror; @@ -151,7 +154,7 @@ static bool GMQCC_WARN genwarning(lex_ctx ctx, int warntype, const char *fmt, .. lvl = LVL_ERROR; va_start(ap, fmt); - con_vprintmsg(lvl, ctx.file, ctx.line, "warning", fmt, ap); + con_vprintmsg(lvl, ctx.file, ctx.line, (opts_werror ? "error" : "warning"), fmt, ap); va_end(ap); return opts_werror; @@ -481,7 +484,7 @@ static bool rotate_entfield_array_index_nodes(ast_expression **out) return true; } -static bool parser_sy_pop(parser_t *parser, shunt *sy) +static bool parser_sy_apply_operator(parser_t *parser, shunt *sy) { const oper_info *op; lex_ctx ctx; @@ -1314,7 +1317,7 @@ static bool parser_close_paren(parser_t *parser, shunt *sy, bool functions_only) /* pop off the parenthesis */ vec_shrinkby(sy->ops, 1); /* then apply the index operator */ - if (!parser_sy_pop(parser, sy)) + if (!parser_sy_apply_operator(parser, sy)) return false; return true; } @@ -1330,7 +1333,7 @@ static bool parser_close_paren(parser_t *parser, shunt *sy, bool functions_only) vec_shrinkby(sy->ops, 1); return true; } - if (!parser_sy_pop(parser, sy)) + if (!parser_sy_apply_operator(parser, sy)) return false; } return true; @@ -1618,7 +1621,7 @@ static ast_expression* parse_expression_leave(parser_t *parser, bool stopatcomma (op->prec < olast->prec) || (op->assoc == ASSOC_LEFT && op->prec <= olast->prec) ) ) { - if (!parser_sy_pop(parser, &sy)) + if (!parser_sy_apply_operator(parser, &sy)) goto onerr; if (vec_size(sy.ops) && !vec_last(sy.ops).paren) olast = &operators[vec_last(sy.ops).etype-1]; @@ -1705,7 +1708,7 @@ static ast_expression* parse_expression_leave(parser_t *parser, bool stopatcomma } while (vec_size(sy.ops)) { - if (!parser_sy_pop(parser, &sy)) + if (!parser_sy_apply_operator(parser, &sy)) goto onerr; } @@ -2028,7 +2031,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)) + if (!parse_variable(parser, block, true, CV_VAR, typevar, false)) goto onerr; } else if (parser->tok != ';') @@ -2165,6 +2168,46 @@ static bool parse_break_continue(parser_t *parser, ast_block *block, ast_express 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 had_const = false; + bool had_var = false; + bool had_noref = false; + + for (;;) { + if (!strcmp(parser_tokval(parser), "const")) + had_const = true; + else if (!strcmp(parser_tokval(parser), "var")) + had_var = true; + else if (with_local && !strcmp(parser_tokval(parser), "local")) + had_var = true; + else if (!strcmp(parser_tokval(parser), "noref")) + had_noref = true; + else if (!had_const && !had_var && !had_noref) { + return false; + } + else + break; + if (!parser_next(parser)) + goto onerr; + } + if (had_const) + *cvq = CV_CONST; + else if (had_var) + *cvq = CV_VAR; + else + *cvq = CV_NONE; + *noref = had_noref; + return true; +onerr: + parseerror(parser, "parse error after variable qualifier"); + *cvq = CV_WRONG; + return true; +} + static bool parse_switch(parser_t *parser, ast_block *block, ast_expression **out) { ast_expression *operand; @@ -2173,6 +2216,9 @@ static bool parse_switch(parser_t *parser, ast_block *block, ast_expression **ou ast_switch *switchnode; ast_switch_case swcase; + int cvq; + bool noref; + lex_ctx ctx = parser_ctx(parser); (void)block; /* not touching */ @@ -2223,40 +2269,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)) { + if (!parse_variable(parser, block, false, CV_NONE, typevar, false)) { ast_delete(switchnode); return false; } continue; } - if (!strcmp(parser_tokval(parser), "var") || - !strcmp(parser_tokval(parser), "local")) + if (parse_var_qualifiers(parser, true, &cvq, &noref)) { - if (!parser_next(parser)) { - parseerror(parser, "expected variable declaration"); + if (cvq == CV_WRONG) { ast_delete(switchnode); return false; } - if (!parse_variable(parser, block, false, CV_VAR, NULL)) { - ast_delete(switchnode); - return false; - } - continue; - } - if (!strcmp(parser_tokval(parser), "const")) { - if (!parser_next(parser)) { - parseerror(parser, "expected variable declaration"); - ast_delete(switchnode); - return false; - } - if (!strcmp(parser_tokval(parser), "var")) { - if (!parser_next(parser)) { - parseerror(parser, "expected variable declaration"); - ast_delete(switchnode); - return false; - } - } - if (!parse_variable(parser, block, false, CV_CONST, NULL)) { + if (!parse_variable(parser, block, false, cvq, NULL, noref)) { ast_delete(switchnode); return false; } @@ -2398,10 +2423,81 @@ static bool parse_goto(parser_t *parser, ast_expression **out) return true; } +static bool parse_skipwhite(parser_t *parser) +{ + do { + if (!parser_next(parser)) + return false; + } while (parser->tok == TOKEN_WHITE && parser->tok < TOKEN_ERROR); + return parser->tok < TOKEN_ERROR; +} + +static bool parse_eol(parser_t *parser) +{ + if (!parse_skipwhite(parser)) + return false; + return parser->tok == TOKEN_EOL; +} + +static bool parse_pragma_do(parser_t *parser) +{ + if (!parser_next(parser) || + parser->tok != TOKEN_IDENT || + strcmp(parser_tokval(parser), "pragma")) + { + parseerror(parser, "expected `pragma` keyword after `#`, got `%s`", parser_tokval(parser)); + return false; + } + if (!parse_skipwhite(parser) || parser->tok != TOKEN_IDENT) { + parseerror(parser, "expected pragma, got `%s`", parser_tokval(parser)); + return false; + } + + if (!strcmp(parser_tokval(parser), "noref")) { + if (!parse_skipwhite(parser) || parser->tok != TOKEN_INTCONST) { + parseerror(parser, "`noref` pragma requires an argument: 0 or 1"); + return false; + } + parser->noref = !!parser_token(parser)->constval.i; + if (!parse_eol(parser)) { + parseerror(parser, "parse error after `noref` pragma"); + return false; + } + } + else + { + parseerror(parser, "unrecognized hash-keyword: `%s`", parser_tokval(parser)); + return false; + } + + return true; +} + +static bool parse_pragma(parser_t *parser) +{ + bool rv; + parser->lex->flags.preprocessing = true; + parser->lex->flags.mergelines = true; + rv = parse_pragma_do(parser); + if (parser->tok != TOKEN_EOL) { + parseerror(parser, "junk after pragma"); + rv = false; + } + parser->lex->flags.preprocessing = false; + parser->lex->flags.mergelines = false; + if (!parser_next(parser)) { + parseerror(parser, "parse error after pragma"); + rv = false; + } + return rv; +} + static bool parse_statement(parser_t *parser, ast_block *block, ast_expression **out, bool allow_cases) { - int cvq; + bool noref; + int cvq = CV_NONE; ast_value *typevar = NULL; + *out = NULL; if (parser->tok == TOKEN_IDENT) @@ -2418,43 +2514,19 @@ 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)) + if (!parse_variable(parser, block, false, CV_NONE, typevar, false)) return false; - *out = NULL; return true; } - else if (parser->tok == TOKEN_IDENT && !strcmp(parser_tokval(parser), "var")) + else if (parse_var_qualifiers(parser, !!block, &cvq, &noref)) { - goto ident_var; + if (cvq == CV_WRONG) + return false; + return parse_variable(parser, block, true, cvq, NULL, noref); } else if (parser->tok == TOKEN_KEYWORD) { - if (!strcmp(parser_tokval(parser), "local") || - !strcmp(parser_tokval(parser), "const") || - !strcmp(parser_tokval(parser), "var")) - { -ident_var: - if (parser_tokval(parser)[0] == 'c') - cvq = CV_CONST; - else if (parser_tokval(parser)[0] == 'v') - cvq = CV_VAR; - else - cvq = CV_NONE; - - if (!block) { - parseerror(parser, "cannot declare a local variable here"); - return false; - } - if (!parser_next(parser)) { - parseerror(parser, "expected variable declaration"); - return false; - } - if (!parse_variable(parser, block, true, cvq, NULL)) - return false; - *out = NULL; - return true; - } - else if (!strcmp(parser_tokval(parser), "__builtin_debug_printtype")) + if (!strcmp(parser_tokval(parser), "__builtin_debug_printtype")) { char ty[1024]; ast_value *tdef; @@ -3689,7 +3761,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) +static bool parse_variable(parser_t *parser, ast_block *localblock, bool nofields, int qualifier, ast_value *cached_typedef, bool noref) { ast_value *var; ast_value *proto; @@ -3753,6 +3825,9 @@ 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++; /* Part 1: * check for validity: (end_sys_..., multiple-definitions, prototypes, ...) @@ -3763,10 +3838,12 @@ static bool parse_variable(parser_t *parser, ast_block *localblock, bool nofield /* Deal with end_sys_ vars */ was_end = false; if (!strcmp(var->name, "end_sys_globals")) { + var->uses++; parser->crc_globals = vec_size(parser->globals); was_end = true; } else if (!strcmp(var->name, "end_sys_fields")) { + var->uses++; parser->crc_fields = vec_size(parser->fields); was_end = true; } @@ -4150,7 +4227,7 @@ skipvar: vec_push(sy.out, syexp(ast_ctx(var), (ast_expression*)var)); vec_push(sy.out, syexp(ast_ctx(cexp), (ast_expression*)cexp)); vec_push(sy.ops, syop(ast_ctx(var), parser->assign_op)); - if (!parser_sy_pop(parser, &sy)) + if (!parser_sy_apply_operator(parser, &sy)) ast_unref(cexp); else { if (vec_size(sy.out) != 1 && vec_size(sy.ops) != 0) @@ -4215,48 +4292,26 @@ cleanup: static bool parser_global_statement(parser_t *parser) { + int cvq = CV_WRONG; + bool noref = 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); + return parse_variable(parser, NULL, false, CV_NONE, istype, false); } - else if (parser->tok == TOKEN_IDENT && !strcmp(parser_tokval(parser), "var")) + else if (parse_var_qualifiers(parser, false, &cvq, &noref)) { - if (!strcmp(parser_tokval(parser), "var")) { - if (!parser_next(parser)) { - parseerror(parser, "expected variable declaration after 'var'"); - return false; - } - if (parser->tok == TOKEN_KEYWORD && !strcmp(parser_tokval(parser), "const")) { - (void)!parsewarning(parser, WARN_CONST_VAR, "ignoring `const` after 'var' qualifier"); - if (!parser_next(parser)) { - parseerror(parser, "expected variable declaration after 'const var'"); - return false; - } - } - return parse_variable(parser, NULL, true, CV_VAR, NULL); - } + if (cvq == CV_WRONG) + return false; + return parse_variable(parser, NULL, true, cvq, NULL, noref); } else if (parser->tok == TOKEN_KEYWORD) { - if (!strcmp(parser_tokval(parser), "const")) { - if (!parser_next(parser)) { - parseerror(parser, "expected variable declaration after 'const'"); - return false; - } - if (parser->tok == TOKEN_IDENT && !strcmp(parser_tokval(parser), "var")) { - (void)!parsewarning(parser, WARN_CONST_VAR, "ignoring `var` after const qualifier"); - if (!parser_next(parser)) { - parseerror(parser, "expected variable declaration after 'const var'"); - return false; - } - } - return parse_variable(parser, NULL, true, CV_CONST, NULL); - } - else if (!strcmp(parser_tokval(parser), "typedef")) { + if (!strcmp(parser_tokval(parser), "typedef")) { if (!parser_next(parser)) { parseerror(parser, "expected type definition after 'typedef'"); return false; @@ -4266,6 +4321,10 @@ static bool parser_global_statement(parser_t *parser) parseerror(parser, "unrecognized keyword `%s`", parser_tokval(parser)); return false; } + else if (parser->tok == '#') + { + return parse_pragma(parser); + } else if (parser->tok == '$') { if (!parser_next(parser)) { @@ -4552,12 +4611,8 @@ bool parser_finish(const char *output) continue; asvalue = (ast_value*)(parser->globals[i]); if (!asvalue->uses && !asvalue->hasvalue && asvalue->expression.vtype != TYPE_FUNCTION) { - if (strcmp(asvalue->name, "end_sys_globals") && - strcmp(asvalue->name, "end_sys_fields")) - { - retval = retval && !genwarning(ast_ctx(asvalue), WARN_UNUSED_VARIABLE, - "unused global: `%s`", asvalue->name); - } + 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);