X-Git-Url: https://git.xonotic.org/?p=xonotic%2Fgmqcc.git;a=blobdiff_plain;f=parser.c;h=a54094a841f32beb80f736f756f7b00fee1226f8;hp=cef598573dcf5f72cc0f7afe4cf01135633aa556;hb=36c5722273f1ea87603621c6ee20b7178a7a641b;hpb=ba207cc04caa361d775055cd614ef73cae300872 diff --git a/parser.c b/parser.c index cef5985..a54094a 100644 --- a/parser.c +++ b/parser.c @@ -135,16 +135,6 @@ static bool GMQCC_WARN parsewarning(parser_t *parser, int warntype, const char * 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; -} - /********************************************************************** * some maths used for constant folding */ @@ -640,7 +630,7 @@ static bool parser_sy_apply_operator(parser_t *parser, shunt *sy) { #if 0 /* This is not broken in fteqcc anymore */ - if (opts.standard != COMPILER_GMQCC) { + if (OPTS_OPTION_U32(OPTION_STANDARD) != COMPILER_GMQCC) { /* this error doesn't need to make us bail out */ (void)!parsewarning(parser, WARN_EXTENSIONS, "accessing array-field members of an entity without parenthesis\n" @@ -1794,7 +1784,7 @@ static bool parse_sya_operand(parser_t *parser, shunt *sy, bool with_labels) * We should also consider adding correction tables for * other things as well. */ - if (opts.correction) { + if (OPTS_OPTION_BOOL(OPTION_CORRECTION)) { correction_t corr; correct_init(&corr); @@ -1991,9 +1981,13 @@ static ast_expression* parse_expression_leave(parser_t *parser, bool stopatcomma } } else if (parser->tok == ')') { + while (vec_size(sy.paren) && vec_last(sy.paren) == PAREN_TERNARY2) { + if (!parser_sy_apply_operator(parser, &sy)) + goto onerr; + } + if (!vec_size(sy.paren)) + break; if (wantop) { - if (!vec_size(sy.paren)) - break; if (vec_last(sy.paren) == PAREN_TERNARY1) { parseerror(parser, "mismatched parentheses (closing paren in ternary expression?)"); goto onerr; @@ -2002,8 +1996,6 @@ static ast_expression* parse_expression_leave(parser_t *parser, bool stopatcomma goto onerr; } else { /* must be a function call without parameters */ - if (!vec_size(sy.paren)) - break; if (vec_last(sy.paren) != PAREN_FUNC) { parseerror(parser, "closing paren in invalid position"); goto onerr; @@ -2022,14 +2014,19 @@ static ast_expression* parse_expression_leave(parser_t *parser, bool stopatcomma goto onerr; } else if (parser->tok == ']') { + while (vec_size(sy.paren) && vec_last(sy.paren) == PAREN_TERNARY2) { + if (!parser_sy_apply_operator(parser, &sy)) + goto onerr; + } if (!vec_size(sy.paren)) break; - if (!vec_size(sy.paren) || vec_last(sy.paren) != PAREN_INDEX) { + if (vec_last(sy.paren) != PAREN_INDEX) { parseerror(parser, "mismatched parentheses, unexpected ']'"); goto onerr; } if (!parser_close_paren(parser, &sy)) goto onerr; + wantop = true; } else if (!wantop) { if (!parse_sya_operand(parser, &sy, with_labels)) @@ -2198,6 +2195,12 @@ static ast_expression* process_condition(parser_t *parser, ast_expression *cond, ast_unary *unary; ast_expression *prev; + if (cond->expression.vtype == TYPE_VOID || cond->expression.vtype >= TYPE_VARIANT) { + char ty[1024]; + ast_type_to_string(cond, ty, sizeof(ty)); + compile_error(ast_ctx(cond), "invalid type for if() condition: %s", ty); + } + if (OPTS_FLAG(FALSE_EMPTY_STRINGS) && cond->expression.vtype == TYPE_STRING) { prev = cond; @@ -2632,7 +2635,7 @@ static bool parse_for_go(parser_t *parser, ast_block *block, ast_expression **ou if (typevar || parser->tok == TOKEN_TYPENAME) { #if 0 - if (opts.standard != COMPILER_GMQCC) { + if (OPTS_OPTION_U32(OPTION_STANDARD) != COMPILER_GMQCC) { if (parsewarning(parser, WARN_EXTENSIONS, "current standard does not allow variable declarations in for-loop initializers")) goto onerr; @@ -2677,11 +2680,12 @@ static bool parse_for_go(parser_t *parser, ast_block *block, ast_expression **ou /* parse the incrementor */ if (parser->tok != ')') { + lex_ctx condctx = parser_ctx(parser); increment = parse_expression_leave(parser, false, false, false); if (!increment) goto onerr; if (!ast_side_effects(increment)) { - if (genwarning(ast_ctx(increment), WARN_EFFECTLESS_STATEMENT, "statement has no effect")) + if (compile_warning(condctx, WARN_EFFECTLESS_STATEMENT, "statement has no effect")) goto onerr; } } @@ -3288,15 +3292,26 @@ static bool parse_eol(parser_t *parser) 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; - } +/* + * Traditionally you'd implement warning, error, and message + * directives in the preprocessor. However, like #pragma, these + * shouldn't depend on -fftepp to utilize. So they're instead + * implemented here. + */ +enum { + PARSE_DIRECTIVE_ERROR, + PARSE_DIRECTIVE_MESSAGE, + PARSE_DIRECTIVE_WARNING, + PARSE_DIRECTIVE_COUNT +}; + +static const char *parser_directives[PARSE_DIRECTIVE_COUNT] = { + "error", + "message", + "warning" +}; + +static bool parse_pragma_do(parser_t *parser) { if (!parse_skipwhite(parser) || parser->tok != TOKEN_IDENT) { parseerror(parser, "expected pragma, got `%s`", parser_tokval(parser)); return false; @@ -3307,35 +3322,99 @@ static bool parse_pragma_do(parser_t *parser) 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 - { + } else { (void)!parsewarning(parser, WARN_UNKNOWN_PRAGMAS, "ignoring #pragma %s", parser_tokval(parser)); return false; } - return true; } -static bool parse_pragma(parser_t *parser) +static bool parse_directive_or_pragma_do(parser_t *parser, bool *pragma) { + + size_t type = PARSE_DIRECTIVE_COUNT; + + if (!parser_next(parser) || parser->tok != TOKEN_IDENT) { + parseerror(parser, "expected `pragma, error, message, warning` after `#`, got `%s`", + parser_tokval(parser)); + + return false; + } + + if (!strcmp(parser_tokval(parser), "pragma" )) { + *pragma = true; + + return parse_pragma_do(parser); + } + + if (!strcmp(parser_tokval(parser), "error" )) type = PARSE_DIRECTIVE_ERROR; + if (!strcmp(parser_tokval(parser), "message")) type = PARSE_DIRECTIVE_MESSAGE; + if (!strcmp(parser_tokval(parser), "warning")) type = PARSE_DIRECTIVE_WARNING; + + switch (type) { + case PARSE_DIRECTIVE_ERROR: + case PARSE_DIRECTIVE_MESSAGE: + case PARSE_DIRECTIVE_WARNING: + *pragma = false; + + if (!parse_skipwhite(parser) || parser->tok != TOKEN_STRINGCONST) { + parseerror(parser, "expected %s, got `%`", parser_directives[type], parser_tokval(parser)); + return false; + } + + switch (type) { + case PARSE_DIRECTIVE_ERROR: + con_cprintmsg(&parser->lex->tok.ctx, LVL_ERROR, "error", parser_tokval(parser)); + compile_errors ++; /* hack */ + break; + /*break;*/ + + case PARSE_DIRECTIVE_MESSAGE: + con_cprintmsg(&parser->lex->tok.ctx, LVL_MSG, "message", parser_tokval(parser)); + break; + + case PARSE_DIRECTIVE_WARNING: + con_cprintmsg(&parser->lex->tok.ctx, LVL_WARNING, "warning", parser_tokval(parser)); + break; + } + + if (!parse_eol(parser)) { + parseerror(parser, "parse error after `%` directive", parser_directives[type]); + return false; + } + + return (type != PARSE_DIRECTIVE_ERROR); + } + + parseerror(parser, "invalid directive `%s`", parser_tokval(parser)); + return false; +} + +static bool parse_directive_or_pragma(parser_t *parser) { bool rv; + bool pragma; /* true when parsing pragma */ + parser->lex->flags.preprocessing = true; - parser->lex->flags.mergelines = true; - rv = parse_pragma_do(parser); + parser->lex->flags.mergelines = true; + + rv = parse_directive_or_pragma_do(parser, &pragma); + if (parser->tok != TOKEN_EOL) { - parseerror(parser, "junk after pragma"); + parseerror(parser, "junk after %s", (pragma) ? "pragma" : "directive"); rv = false; } parser->lex->flags.preprocessing = false; - parser->lex->flags.mergelines = false; + parser->lex->flags.mergelines = false; + if (!parser_next(parser)) { - parseerror(parser, "parse error after pragma"); + parseerror(parser, "parse error after %s", (pragma) ? "pragma" : "directive"); rv = false; } return rv; @@ -3361,7 +3440,7 @@ static bool parse_statement(parser_t *parser, ast_block *block, ast_expression * parseerror(parser, "cannot declare a variable from here"); return false; } - if (opts.standard == COMPILER_QCC) { + if (OPTS_OPTION_U32(OPTION_STANDARD) == COMPILER_QCC) { if (parsewarning(parser, WARN_EXTENSIONS, "missing 'local' keyword when declaring a local variable")) return false; } @@ -3428,7 +3507,7 @@ static bool parse_statement(parser_t *parser, ast_block *block, ast_expression * } else if (!strcmp(parser_tokval(parser), "for")) { - if (opts.standard == COMPILER_QCC) { + if (OPTS_OPTION_U32(OPTION_STANDARD) == COMPILER_QCC) { if (parsewarning(parser, WARN_EXTENSIONS, "for loops are not recognized in the original Quake C standard, to enable try an alternate standard --std=?")) return false; } @@ -3527,12 +3606,13 @@ static bool parse_statement(parser_t *parser, ast_block *block, ast_expression * } else { + lex_ctx ctx = parser_ctx(parser); ast_expression *exp = parse_expression(parser, false, false); if (!exp) return false; *out = exp; if (!ast_side_effects(exp)) { - if (genwarning(ast_ctx(exp), WARN_EFFECTLESS_STATEMENT, "statement has no effect")) + if (compile_warning(ctx, WARN_EFFECTLESS_STATEMENT, "statement has no effect")) return false; } return true; @@ -4029,7 +4109,7 @@ static bool parse_function_body(parser_t *parser, ast_value *var) if (parser->tok == ';') return parser_next(parser); - else if (opts.standard == COMPILER_QCC) + else if (OPTS_OPTION_U32(OPTION_STANDARD) == COMPILER_QCC) parseerror(parser, "missing semicolon after function body (mandatory with -std=qcc)"); return retval; @@ -4545,7 +4625,7 @@ static ast_value *parse_parameter_list(parser_t *parser, ast_value *var) vec_free(params); /* sanity check */ - if (vec_size(params) > 8 && opts.standard == COMPILER_QCC) + if (vec_size(params) > 8 && OPTS_OPTION_U32(OPTION_STANDARD) == COMPILER_QCC) (void)!parsewarning(parser, WARN_EXTENSIONS, "more than 8 parameters are not supported by this standard"); /* parse-out */ @@ -4759,7 +4839,7 @@ static ast_value *parse_typename(parser_t *parser, ast_value **storebase, ast_va } /* now there may be function parens again */ - if (parser->tok == '(' && opts.standard == COMPILER_QCC) + if (parser->tok == '(' && OPTS_OPTION_U32(OPTION_STANDARD) == COMPILER_QCC) parseerror(parser, "C-style function syntax is not allowed in -std=qcc"); if (parser->tok == '(' && wasarray) parseerror(parser, "arrays as part of a return type is not supported"); @@ -4898,7 +4978,7 @@ static bool parse_variable(parser_t *parser, ast_block *localblock, bool nofield /* Part 0: finish the type */ if (parser->tok == '(') { - if (opts.standard == COMPILER_QCC) + if (OPTS_OPTION_U32(OPTION_STANDARD) == COMPILER_QCC) parseerror(parser, "C-style function syntax is not allowed in -std=qcc"); var = parse_parameter_list(parser, var); if (!var) { @@ -4921,7 +5001,7 @@ static bool parse_variable(parser_t *parser, ast_block *localblock, bool nofield } /* for functions returning functions */ while (parser->tok == '(') { - if (opts.standard == COMPILER_QCC) + if (OPTS_OPTION_U32(OPTION_STANDARD) == COMPILER_QCC) parseerror(parser, "C-style function syntax is not allowed in -std=qcc"); var = parse_parameter_list(parser, var); if (!var) { @@ -4991,7 +5071,7 @@ static bool parse_variable(parser_t *parser, ast_block *localblock, bool nofield goto cleanup; */ } - if ((opts.standard == COMPILER_QCC || opts.standard == COMPILER_FTEQCC) && + if ((OPTS_OPTION_U32(OPTION_STANDARD) == COMPILER_QCC || OPTS_OPTION_U32(OPTION_STANDARD) == COMPILER_FTEQCC) && (old = parser_find_global(parser, var->name))) { parseerror(parser, "cannot declare a field and a global of the same name with -std=qcc"); @@ -5063,7 +5143,7 @@ static bool parse_variable(parser_t *parser, ast_block *localblock, bool nofield ast_delete(var); var = proto; } - if (opts.standard == COMPILER_QCC && + if (OPTS_OPTION_U32(OPTION_STANDARD) == COMPILER_QCC && (old = parser_find_field(parser, var->name))) { parseerror(parser, "cannot declare a field and a global of the same name with -std=qcc"); @@ -5094,7 +5174,7 @@ static bool parse_variable(parser_t *parser, ast_block *localblock, bool nofield retval = false; goto cleanup; } - if (opts.standard != COMPILER_GMQCC) { + if (OPTS_OPTION_U32(OPTION_STANDARD) != COMPILER_GMQCC) { ast_delete(var); var = NULL; goto skipvar; @@ -5274,7 +5354,7 @@ skipvar: break; } - if (localblock && opts.standard == COMPILER_QCC) { + if (localblock && OPTS_OPTION_U32(OPTION_STANDARD) == COMPILER_QCC) { if (parsewarning(parser, WARN_LOCAL_CONSTANTS, "initializing expression turns variable `%s` into a constant in this standard", var->name) ) @@ -5294,7 +5374,7 @@ skipvar: break; } } - else if (opts.standard == COMPILER_QCC) { + else if (OPTS_OPTION_U32(OPTION_STANDARD) == COMPILER_QCC) { parseerror(parser, "expected '=' before function body in this standard"); } @@ -5515,7 +5595,7 @@ static bool parser_global_statement(parser_t *parser) } else if (parser->tok == '#') { - return parse_pragma(parser); + return parse_directive_or_pragma(parser); } else if (parser->tok == '$') { @@ -5655,7 +5735,7 @@ bool parser_init() parser->const_vec[1] = ast_value_new(empty_ctx, "", TYPE_NOEXPR); parser->const_vec[2] = ast_value_new(empty_ctx, "", TYPE_NOEXPR); - if (opts.add_info) { + if (OPTS_OPTION_BOOL(OPTION_ADD_INFO)) { parser->reserved_version = ast_value_new(empty_ctx, "reserved:version", TYPE_STRING); parser->reserved_version->cvq = CV_CONST; parser->reserved_version->hasvalue = true; @@ -5840,8 +5920,8 @@ bool parser_finish(const char *output) 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); + retval = retval && !compile_warning(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); @@ -5944,7 +6024,7 @@ bool parser_finish(const char *output) return false; } } - if (opts.dump) + if (OPTS_OPTION_BOOL(OPTION_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)) { @@ -5961,7 +6041,7 @@ bool parser_finish(const char *output) } if (retval) { - if (opts.dumpfin) + if (OPTS_OPTION_BOOL(OPTION_DUMPFIN)) ir_builder_dump(ir, con_out); generate_checksum(parser);