X-Git-Url: https://git.xonotic.org/?p=xonotic%2Fgmqcc.git;a=blobdiff_plain;f=parser.c;h=eaa14903d7681416e69b53fdbbc76c121bc678a0;hp=0e43f50c526e91daa3303c0623c485a68ab4617c;hb=15d1277158f9e4e235129fedcf2b8b79c4b833b9;hpb=072bff44e6d574f5f32657875adcbe48fc27cb5b diff --git a/parser.c b/parser.c index 0e43f50..eaa1490 100644 --- a/parser.c +++ b/parser.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2012, 2013 + * Copyright (C) 2012, 2013, 2014 * Wolfgang Bumiller * Dale Weiler * @@ -2803,6 +2803,11 @@ 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 */ +typedef struct { + const char *name; + size_t flag; +} attribute_t; + static bool parse_qualifiers(parser_t *parser, bool with_local, int *cvq, bool *noref, bool *is_static, uint32_t *_flags, char **message) { bool had_const = false; @@ -2812,8 +2817,18 @@ static bool parse_qualifiers(parser_t *parser, bool with_local, int *cvq, bool * bool had_static = false; uint32_t flags = 0; - *cvq = CV_NONE; + static attribute_t attributes[] = { + { "noreturn", AST_FLAG_NORETURN }, + { "inline", AST_FLAG_INLINE }, + { "eraseable", AST_FLAG_ERASEABLE }, + { "accumulate", AST_FLAG_ACCUMULATE }, + { "last", AST_FLAG_FINAL_DECL } + }; + + *cvq = CV_NONE; + for (;;) { + size_t i; if (parser->tok == TOKEN_ATTRIBUTE_OPEN) { had_attrib = true; /* parse an attribute */ @@ -2822,15 +2837,25 @@ static bool parse_qualifiers(parser_t *parser, bool with_local, int *cvq, bool * *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; + + for (i = 0; i < GMQCC_ARRAY_COUNT(attributes); i++) { + if (!strcmp(parser_tokval(parser), attributes[i].name)) { + flags |= attributes[i].flag; + if (!parser_next(parser) || parser->tok != TOKEN_ATTRIBUTE_CLOSE) { + parseerror(parser, "`%s` attribute has no parameters, expected `]]`", + attributes[i].name); + *cvq = CV_WRONG; + return false; + } + break; } } - else if (!strcmp(parser_tokval(parser), "noref")) { + + if (i != GMQCC_ARRAY_COUNT(attributes)) + goto leave; + + + 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 `]]`"); @@ -2838,30 +2863,6 @@ static bool parse_qualifiers(parser_t *parser, bool with_local, int *cvq, bool * return false; } } - else if (!strcmp(parser_tokval(parser), "inline")) { - flags |= AST_FLAG_INLINE; - if (!parser_next(parser) || parser->tok != TOKEN_ATTRIBUTE_CLOSE) { - parseerror(parser, "`inline` attribute has no parameters, expected `]]`"); - *cvq = CV_WRONG; - return false; - } - } - else if (!strcmp(parser_tokval(parser), "eraseable")) { - flags |= AST_FLAG_ERASEABLE; - if (!parser_next(parser) || parser->tok != TOKEN_ATTRIBUTE_CLOSE) { - parseerror(parser, "`eraseable` attribute has no parameters, expected `]]`"); - *cvq = CV_WRONG; - return false; - } - } - else if (!strcmp(parser_tokval(parser), "accumulate")) { - flags |= AST_FLAG_ACCUMULATE; - if (!parser_next(parser) || parser->tok != TOKEN_ATTRIBUTE_CLOSE) { - parseerror(parser, "`accumulate` attribute has no parameters, expected `]]`"); - *cvq = CV_WRONG; - return false; - } - } else if (!strcmp(parser_tokval(parser), "alias") && !(flags & AST_FLAG_ALIAS)) { flags |= AST_FLAG_ALIAS; *message = NULL; @@ -2943,6 +2944,47 @@ static bool parse_qualifiers(parser_t *parser, bool with_local, int *cvq, bool * return false; } } + else if (!strcmp(parser_tokval(parser), "coverage") && !(flags & AST_FLAG_COVERAGE)) { + flags |= AST_FLAG_COVERAGE; + if (!parser_next(parser)) { + error_in_coverage: + parseerror(parser, "parse error in coverage attribute"); + *cvq = CV_WRONG; + return false; + } + if (parser->tok == '(') { + if (!parser_next(parser)) { + bad_coverage_arg: + parseerror(parser, "invalid parameter for coverage() attribute\n" + "valid are: block"); + *cvq = CV_WRONG; + return false; + } + if (parser->tok != ')') { + do { + if (parser->tok != TOKEN_IDENT) + goto bad_coverage_arg; + if (!strcmp(parser_tokval(parser), "block")) + flags |= AST_FLAG_BLOCK_COVERAGE; + else if (!strcmp(parser_tokval(parser), "none")) + flags &= ~(AST_FLAG_COVERAGE_MASK); + else + goto bad_coverage_arg; + if (!parser_next(parser)) + goto error_in_coverage; + if (parser->tok == ',') { + if (!parser_next(parser)) + goto error_in_coverage; + } + } while (parser->tok != ')'); + } + if (parser->tok != ')' || !parser_next(parser)) + goto error_in_coverage; + } else { + /* without parameter [[coverage]] equals [[coverage(block)]] */ + flags |= AST_FLAG_BLOCK_COVERAGE; + } + } else { /* Skip tokens until we hit a ]] */ @@ -2971,6 +3013,8 @@ static bool parse_qualifiers(parser_t *parser, bool with_local, int *cvq, bool * } else break; + + leave: if (!parser_next(parser)) goto onerr; } @@ -4363,6 +4407,7 @@ static bool parser_create_array_accessor(parser_t *parser, ast_value *array, con parseerror(parser, "failed to create accessor function value"); return false; } + fval->expression.flags &= ~(AST_FLAG_COVERAGE_MASK); func = ast_function_new(ast_ctx(array), funcname, fval); if (!func) { @@ -5160,6 +5205,8 @@ static bool parse_variable(parser_t *parser, ast_block *localblock, bool nofield } var->cvq = qualifier; + if (qflags & AST_FLAG_COVERAGE) /* specified in QC, drop our default */ + var->expression.flags &= ~(AST_FLAG_COVERAGE_MASK); var->expression.flags |= qflags; /* @@ -5289,6 +5336,12 @@ static bool parse_variable(parser_t *parser, ast_block *localblock, bool nofield retval = false; goto cleanup; } + if (old->flags & AST_FLAG_FINAL_DECL) { + parseerror(parser, "cannot redeclare variable `%s`, declared final here: %s:%i", + var->name, ast_ctx(old).file, ast_ctx(old).line); + retval = false; + goto cleanup; + } proto = (ast_value*)old; if (!ast_istype(old, ast_value)) { parseerror(parser, "internal error: not an ast_value"); @@ -5302,6 +5355,11 @@ static bool parse_variable(parser_t *parser, ast_block *localblock, bool nofield goto cleanup; } proto->expression.flags |= var->expression.flags; + /* copy the context for finals, + * so the error can show where it was actually made 'final' + */ + if (proto->expression.flags & AST_FLAG_FINAL_DECL) + ast_ctx(old) = ast_ctx(var); ast_delete(var); var = proto; } @@ -6187,11 +6245,51 @@ void parser_cleanup(parser_t *parser) mem_d(parser); } +static bool parser_set_coverage_func(parser_t *parser, ir_builder *ir) { + size_t i; + ast_expression *expr; + ast_value *cov; + ast_function *func; + + if (!OPTS_OPTION_BOOL(OPTION_COVERAGE)) + return true; + + func = NULL; + for (i = 0; i != vec_size(parser->functions); ++i) { + if (!strcmp(parser->functions[i]->name, "coverage")) { + func = parser->functions[i]; + break; + } + } + if (!func) { + if (OPTS_OPTION_BOOL(OPTION_COVERAGE)) { + con_out("coverage support requested but no coverage() builtin declared\n"); + ir_builder_delete(ir); + return false; + } + return true; + } + + cov = func->vtype; + expr = (ast_expression*)cov; + + if (expr->vtype != TYPE_FUNCTION || vec_size(expr->params) != 0) { + char ty[1024]; + ast_type_to_string(expr, ty, sizeof(ty)); + con_out("invalid type for coverage(): %s\n", ty); + ir_builder_delete(ir); + return false; + } + + ir->coverage_func = func->ir_func->value; + return true; +} + bool parser_finish(parser_t *parser, const char *output) { - size_t i; - ir_builder *ir; - bool retval = true; + size_t i; + ir_builder *ir; + bool retval = true; if (compile_errors) { con_out("*** there were compile errors\n"); @@ -6273,6 +6371,10 @@ bool parser_finish(parser_t *parser, const char *output) if (!fold_generate(parser->fold, ir)) return false; + /* before generating any functions we need to set the coverage_func */ + if (!parser_set_coverage_func(parser, ir)) + return false; + for (i = 0; i < vec_size(parser->globals); ++i) { ast_value *asvalue; if (!ast_istype(parser->globals[i], ast_value))