X-Git-Url: https://git.xonotic.org/?a=blobdiff_plain;f=parser.c;h=99b7eb7bace4f651eb2fbf37d221cd18a14444cb;hb=5778b6ffb60296c5081fda4aa62bd566add98d0e;hp=805c28cf53536557f41ad780cfb987b439bbaa93;hpb=906f319673f6a2eddb62e59acc970b9497f3e96a;p=xonotic%2Fgmqcc.git diff --git a/parser.c b/parser.c index 805c28c..99b7eb7 100644 --- a/parser.c +++ b/parser.c @@ -20,6 +20,9 @@ typedef struct { ast_value **imm_string; ast_value **imm_vector; + /* must be deleted first, they reference immediates and values */ + ast_value **accessors; + ast_value *imm_float_zero; ast_value *imm_vector_zero; @@ -48,6 +51,7 @@ static bool parse_variable(parser_t *parser, ast_block *localblock, bool nofield static ast_block* parse_block(parser_t *parser, bool warnreturn); static bool parse_block_into(parser_t *parser, ast_block *block, bool warnreturn); static ast_expression* parse_statement_or_block(parser_t *parser); +static bool parse_statement(parser_t *parser, ast_block *block, ast_expression **out, bool allow_cases); static ast_expression* parse_expression_leave(parser_t *parser, bool stopatcomma); static ast_expression* parse_expression(parser_t *parser, bool stopatcomma); @@ -1328,7 +1332,10 @@ static ast_expression* parse_expression_leave(parser_t *parser, bool stopatcomma if (!parser_next(parser)) { goto onerr; } - if (parser->tok == ';' || (!parens && parser->tok == ']')) { + if (parser->tok == ';' || + (!parens && parser->tok == ']') || + parser->tok == ':') + { break; } } @@ -1675,7 +1682,208 @@ onerr: return false; } -static bool parse_statement(parser_t *parser, ast_block *block, ast_expression **out) +static bool parse_return(parser_t *parser, ast_block *block, ast_expression **out) +{ + ast_expression *exp = NULL; + ast_return *ret = NULL; + ast_value *expected = parser->function->vtype; + + if (!parser_next(parser)) { + parseerror(parser, "expected return expression"); + return false; + } + + if (parser->tok != ';') { + exp = parse_expression(parser, false); + if (!exp) + return false; + + if (exp->expression.vtype != expected->expression.next->expression.vtype) { + parseerror(parser, "return with invalid expression"); + } + + ret = ast_return_new(exp->expression.node.context, exp); + if (!ret) { + ast_delete(exp); + return false; + } + } else { + if (!parser_next(parser)) + parseerror(parser, "parse error"); + if (expected->expression.next->expression.vtype != TYPE_VOID) { + if (opts_standard != COMPILER_GMQCC) + (void)!parsewarning(parser, WARN_MISSING_RETURN_VALUES, "return without value"); + else + parseerror(parser, "return without value"); + } + ret = ast_return_new(parser_ctx(parser), NULL); + } + *out = (ast_expression*)ret; + return true; +} + +static bool parse_break_continue(parser_t *parser, ast_block *block, ast_expression **out, bool is_continue) +{ + lex_ctx ctx = parser_ctx(parser); + + if (!parser_next(parser) || parser->tok != ';') { + parseerror(parser, "expected semicolon"); + return false; + } + + if (!parser_next(parser)) + parseerror(parser, "parse error"); + + *out = (ast_expression*)ast_breakcont_new(ctx, is_continue); + return true; +} + +static bool parse_switch(parser_t *parser, ast_block *block, ast_expression **out) +{ + ast_expression *operand; + ast_value *opval; + ast_switch *switchnode; + ast_switch_case swcase; + + lex_ctx ctx = parser_ctx(parser); + + /* 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"); + return false; + } + /* parse the operand */ + operand = parse_expression_leave(parser, false); + if (!operand) + return false; + + if (!OPTS_FLAG(RELAXED_SWITCH)) { + opval = (ast_value*)operand; + if (!ast_istype(operand, ast_value) || !opval->isconst) { + parseerror(parser, "case on non-constant values need to be explicitly enabled via -frelaxed-switch"); + ast_unref(operand); + return false; + } + } + + switchnode = ast_switch_new(ctx, operand); + + /* closing paren */ + if (parser->tok != ')') { + ast_delete(switchnode); + parseerror(parser, "expected closing paren after 'switch' operand"); + return false; + } + + /* parse over the opening paren */ + if (!parser_next(parser) || parser->tok != '{') { + ast_delete(switchnode); + parseerror(parser, "expected list of cases"); + return false; + } + + if (!parser_next(parser)) { + ast_delete(switchnode); + parseerror(parser, "expected 'case' or 'default'"); + return false; + } + + /* case list! */ + while (parser->tok != '}') { + ast_block *block; + + 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); + parseerror(parser, "expected expression for case"); + return false; + } + swcase.value = parse_expression_leave(parser, false); + if (!swcase.value) { + ast_delete(switchnode); + parseerror(parser, "expected expression for case"); + return false; + } + } + else if (!strcmp(parser_tokval(parser), "default")) { + swcase.value = NULL; + if (!parser_next(parser)) { + ast_delete(switchnode); + parseerror(parser, "expected colon"); + return false; + } + } + + /* Now the colon and body */ + if (parser->tok != ':') { + if (swcase.value) ast_unref(swcase.value); + ast_delete(switchnode); + parseerror(parser, "expected colon"); + return false; + } + + if (!parser_next(parser)) { + if (swcase.value) ast_unref(swcase.value); + ast_delete(switchnode); + parseerror(parser, "expected statements or case"); + return false; + } + block = ast_block_new(parser_ctx(parser)); + if (!block) { + if (swcase.value) ast_unref(swcase.value); + ast_delete(switchnode); + return false; + } + swcase.code = (ast_expression*)block; + vec_push(switchnode->cases, swcase); + while (true) { + ast_expression *expr; + if (parser->tok == '}') + break; + if (parser->tok == TOKEN_KEYWORD) { + if (!strcmp(parser_tokval(parser), "case") || + !strcmp(parser_tokval(parser), "default")) + { + break; + } + } + if (!parse_statement(parser, block, &expr, true)) { + ast_delete(switchnode); + return false; + } + if (!expr) + continue; + vec_push(block->exprs, expr); + } + } + + /* closing paren */ + if (parser->tok != '}') { + ast_delete(switchnode); + parseerror(parser, "expected closing paren of case list"); + return false; + } + if (!parser_next(parser)) { + ast_delete(switchnode); + parseerror(parser, "parse error after switch"); + return false; + } + *out = (ast_expression*)switchnode; + return true; +} + +static bool parse_statement(parser_t *parser, ast_block *block, ast_expression **out, bool allow_cases) { if (parser->tok == TOKEN_TYPENAME || parser->tok == '.') { @@ -1712,42 +1920,7 @@ static bool parse_statement(parser_t *parser, ast_block *block, ast_expression * } else if (!strcmp(parser_tokval(parser), "return")) { - ast_expression *exp = NULL; - ast_return *ret = NULL; - ast_value *expected = parser->function->vtype; - - if (!parser_next(parser)) { - parseerror(parser, "expected return expression"); - return false; - } - - if (parser->tok != ';') { - exp = parse_expression(parser, false); - if (!exp) - return false; - - if (exp->expression.vtype != expected->expression.next->expression.vtype) { - parseerror(parser, "return with invalid expression"); - } - - ret = ast_return_new(exp->expression.node.context, exp); - if (!ret) { - ast_delete(exp); - return false; - } - } else { - if (!parser_next(parser)) - parseerror(parser, "parse error"); - if (expected->expression.next->expression.vtype != TYPE_VOID) { - if (opts_standard != COMPILER_GMQCC) - (void)!parsewarning(parser, WARN_MISSING_RETURN_VALUES, "return without value"); - else - parseerror(parser, "return without value"); - } - ret = ast_return_new(parser_ctx(parser), NULL); - } - *out = (ast_expression*)ret; - return true; + return parse_return(parser, block, out); } else if (!strcmp(parser_tokval(parser), "if")) { @@ -1769,6 +1942,27 @@ static bool parse_statement(parser_t *parser, ast_block *block, ast_expression * } return parse_for(parser, block, out); } + else if (!strcmp(parser_tokval(parser), "break")) + { + return parse_break_continue(parser, block, out, false); + } + else if (!strcmp(parser_tokval(parser), "continue")) + { + return parse_break_continue(parser, block, out, true); + } + else if (!strcmp(parser_tokval(parser), "switch")) + { + return parse_switch(parser, block, out); + } + else if (!strcmp(parser_tokval(parser), "case") || + !strcmp(parser_tokval(parser), "default")) + { + if (!allow_cases) { + parseerror(parser, "unexpected 'case' label"); + return false; + } + return true; + } parseerror(parser, "Unexpected keyword"); return false; } @@ -1804,9 +1998,11 @@ static bool GMQCC_WARN parser_pop_local(parser_t *parser) varentry_t *ve; ve = &vec_last(parser->locals); - if (ast_istype(ve->var, ast_value) && !(((ast_value*)(ve->var))->uses)) { - if (parsewarning(parser, WARN_UNUSED_VARIABLE, "unused variable: `%s`", ve->name)) - rv = false; + if (!parser->errors) { + if (ast_istype(ve->var, ast_value) && !(((ast_value*)(ve->var))->uses)) { + if (parsewarning(parser, WARN_UNUSED_VARIABLE, "unused variable: `%s`", ve->name)) + rv = false; + } } mem_d(ve->name); vec_pop(parser->locals); @@ -1832,7 +2028,7 @@ static bool parse_block_into(parser_t *parser, ast_block *block, bool warnreturn if (parser->tok == '}') break; - if (!parse_statement(parser, block, &expr)) { + if (!parse_statement(parser, block, &expr, false)) { /* parseerror(parser, "parse error"); */ block = NULL; goto cleanup; @@ -1884,7 +2080,7 @@ static ast_expression* parse_statement_or_block(parser_t *parser) ast_expression *expr = NULL; if (parser->tok == '{') return (ast_expression*)parse_block(parser, false); - if (!parse_statement(parser, NULL, &expr)) + if (!parse_statement(parser, NULL, &expr, false)) return NULL; return expr; } @@ -2396,6 +2592,7 @@ static bool parser_create_array_accessor(parser_t *parser, ast_value *array, con { ast_function *func = NULL; ast_value *fval = NULL; + ast_block *body = NULL; fval = ast_value_new(ast_ctx(array), funcname, TYPE_FUNCTION); if (!fval) { @@ -2410,15 +2607,25 @@ static bool parser_create_array_accessor(parser_t *parser, ast_value *array, con return false; } + body = ast_block_new(ast_ctx(array)); + if (!body) { + parseerror(parser, "failed to create block for array accessor"); + ast_delete(fval); + ast_delete(func); + return false; + } + + vec_push(func->blocks, body); *out = fval; + vec_push(parser->accessors, fval); + return true; } static bool parser_create_array_setter(parser_t *parser, ast_value *array, const char *funcname) { ast_expression *root = NULL; - ast_block *body = NULL; ast_value *index = NULL; ast_value *value = NULL; ast_function *func; @@ -2434,12 +2641,6 @@ static bool parser_create_array_setter(parser_t *parser, ast_value *array, const func = fval->constval.vfunc; fval->expression.next = (ast_expression*)ast_value_new(ast_ctx(array), "", TYPE_VOID); - body = ast_block_new(ast_ctx(array)); - if (!body) { - parseerror(parser, "failed to create block for array accessor"); - goto cleanup; - } - index = ast_value_new(ast_ctx(array), "index", TYPE_FLOAT); value = ast_value_copy((ast_value*)array->expression.next); @@ -2457,12 +2658,10 @@ static bool parser_create_array_setter(parser_t *parser, ast_value *array, const goto cleanup; } - vec_push(body->exprs, root); - vec_push(func->blocks, body); + vec_push(func->blocks[0]->exprs, root); array->setter = fval; return true; cleanup: - if (body) ast_delete(body); if (index) ast_delete(index); if (value) ast_delete(value); if (root) ast_delete(root); @@ -2474,7 +2673,6 @@ cleanup: static bool parser_create_array_field_setter(parser_t *parser, ast_value *array, const char *funcname) { ast_expression *root = NULL; - ast_block *body = NULL; ast_value *entity = NULL; ast_value *index = NULL; ast_value *value = NULL; @@ -2491,12 +2689,6 @@ static bool parser_create_array_field_setter(parser_t *parser, ast_value *array, func = fval->constval.vfunc; fval->expression.next = (ast_expression*)ast_value_new(ast_ctx(array), "", TYPE_VOID); - body = ast_block_new(ast_ctx(array)); - if (!body) { - parseerror(parser, "failed to create block for array accessor"); - goto cleanup; - } - entity = ast_value_new(ast_ctx(array), "entity", TYPE_ENTITY); index = ast_value_new(ast_ctx(array), "index", TYPE_FLOAT); value = ast_value_copy((ast_value*)array->expression.next); @@ -2515,12 +2707,10 @@ static bool parser_create_array_field_setter(parser_t *parser, ast_value *array, goto cleanup; } - vec_push(body->exprs, root); - vec_push(func->blocks, body); + vec_push(func->blocks[0]->exprs, root); array->setter = fval; return true; cleanup: - if (body) ast_delete(body); if (entity) ast_delete(entity); if (index) ast_delete(index); if (value) ast_delete(value); @@ -2533,7 +2723,6 @@ cleanup: static bool parser_create_array_getter(parser_t *parser, ast_value *array, const ast_expression *elemtype, const char *funcname) { ast_expression *root = NULL; - ast_block *body = NULL; ast_value *index = NULL; ast_value *fval; ast_function *func; @@ -2551,12 +2740,6 @@ static bool parser_create_array_getter(parser_t *parser, ast_value *array, const func = fval->constval.vfunc; fval->expression.next = ast_type_copy(ast_ctx(array), elemtype); - body = ast_block_new(ast_ctx(array)); - if (!body) { - parseerror(parser, "failed to create block for array accessor"); - goto cleanup; - } - index = ast_value_new(ast_ctx(array), "index", TYPE_FLOAT); if (!index) { @@ -2571,12 +2754,10 @@ static bool parser_create_array_getter(parser_t *parser, ast_value *array, const goto cleanup; } - vec_push(body->exprs, root); - vec_push(func->blocks, body); + vec_push(func->blocks[0]->exprs, root); array->getter = fval; return true; cleanup: - if (body) ast_delete(body); if (index) ast_delete(index); if (root) ast_delete(root); ast_delete(func); @@ -2649,8 +2830,8 @@ static ast_value *parse_parameter_list(parser_t *parser, ast_value *var) } /* sanity check */ - if (vec_size(params) > 8) - parseerror(parser, "more than 8 parameters are currently not supported"); + if (vec_size(params) > 8 && opts_standard == COMPILER_QCC) + (void)!parsewarning(parser, WARN_EXTENSIONS, "more than 8 parameters are not supported by this standard"); /* parse-out */ if (!parser_next(parser)) { @@ -3431,7 +3612,17 @@ bool parser_compile_file(const char *filename) { parser->lex = lex_open(filename); if (!parser->lex) { - con_out("failed to open file \"%s\"\n", filename); + con_err("failed to open file \"%s\"\n", filename); + return false; + } + return parser_compile(); +} + +bool parser_compile_string_len(const char *name, const char *str, size_t len) +{ + parser->lex = lex_open_string(str, len, name); + if (!parser->lex) { + con_err("failed to create lexer for string \"%s\"\n", name); return false; } return parser_compile(); @@ -3441,7 +3632,7 @@ bool parser_compile_string(const char *name, const char *str) { parser->lex = lex_open_string(str, strlen(str), name); if (!parser->lex) { - con_out("failed to create lexer for string \"%s\"\n", name); + con_err("failed to create lexer for string \"%s\"\n", name); return false; } return parser_compile(); @@ -3450,6 +3641,11 @@ bool parser_compile_string(const char *name, const char *str) void parser_cleanup() { size_t i; + for (i = 0; i < vec_size(parser->accessors); ++i) { + ast_delete(parser->accessors[i]->constval.vfunc); + parser->accessors[i]->constval.vfunc = NULL; + ast_delete(parser->accessors[i]); + } for (i = 0; i < vec_size(parser->functions); ++i) { ast_delete(parser->functions[i]); } @@ -3470,6 +3666,7 @@ void parser_cleanup() ast_delete(parser->globals[i].var); mem_d(parser->globals[i].name); } + vec_free(parser->accessors); vec_free(parser->functions); vec_free(parser->imm_vector); vec_free(parser->imm_string);