From: Dale Weiler Date: Wed, 2 Jan 2013 21:32:57 +0000 (+0000) Subject: Implemented computed goto + added goto test for testsuite (tests both normal and... X-Git-Tag: before-library~387 X-Git-Url: https://git.xonotic.org/?p=xonotic%2Fgmqcc.git;a=commitdiff_plain;h=a421d9a33bf371492b5ccba8294a6df31f397412 Implemented computed goto + added goto test for testsuite (tests both normal and computed goto statements). --- diff --git a/parser.c b/parser.c index 4256682..8701a8f 100644 --- a/parser.c +++ b/parser.c @@ -300,6 +300,17 @@ static ast_expression* parser_find_field(parser_t *parser, const char *name) return ( ast_expression*)util_htget(parser->htfields, name); } +static ast_expression* parser_find_label(parser_t *parser, const char *name) { + size_t i; + if (!parser->labels) + return NULL; + + for(i = 0; i < vec_size(parser->labels); i++) + if (!strcmp(parser->labels[i]->name, name)) + return (ast_expression*)parser->labels[i]; + return NULL; +} + static ast_expression* parser_find_global(parser_t *parser, const char *name) { return (ast_expression*)util_htget(parser->htglobals, name); @@ -1573,18 +1584,19 @@ static ast_expression* parse_expression_leave(parser_t *parser, bool stopatcomma parseerror(parser, "namespace for member not found"); goto onerr; } - else - var = parser_find_var(parser, parser_tokval(parser)); + else if (!(var = parser_find_var(parser, parser_tokval(parser)))) + var = (ast_expression*)parser_find_label(parser, parser_tokval(parser)); } else { var = parser_find_var(parser, parser_tokval(parser)); if (!var) var = parser_find_field(parser, parser_tokval(parser)); + if (!var) + var = parser_find_label(parser, parser_tokval(parser)); } if (!var) { /* intrinsics */ if (!strcmp(parser_tokval(parser), "__builtin_debug_typestring")) { var = (ast_expression*)intrinsic_debug_typestring; - } else { @@ -2957,16 +2969,54 @@ static bool parse_switch_go(parser_t *parser, ast_block *block, ast_expression * return true; } +/* parse computed goto sides */ +static ast_expression *parse_goto_computed(parser_t *parser, ast_expression *side) { + ast_expression *on_true; + ast_expression *on_false; + + if (!side) + return NULL; + + if (ast_istype(side, ast_ternary)) { + on_true = parse_goto_computed(parser, ((ast_ternary*)side)->on_true); + on_false = parse_goto_computed(parser, ((ast_ternary*)side)->on_false); + + return (ast_expression*)ast_ifthen_new(parser_ctx(parser), ((ast_ternary*)side)->cond, on_true, on_false); + } else if (ast_istype(side, ast_label)) { + ast_goto *gt = ast_goto_new(parser_ctx(parser), ((ast_label*)side)->name); + ast_goto_set_label(gt, ((ast_label*)side)); + return (ast_expression*)gt; + } + return NULL; +} + static bool parse_goto(parser_t *parser, ast_expression **out) { size_t i; - ast_goto *gt; + ast_goto *gt = NULL; - if (!parser_next(parser) || parser->tok != TOKEN_IDENT) { - parseerror(parser, "expected label name after `goto`"); + if (!parser_next(parser)) return false; + + if (parser->tok != TOKEN_IDENT) { + ast_expression *expression; + /* could be an expression i.e computed goto :-) */ + if (parser->tok != '(') { + parseerror(parser, "expected label name after `goto`"); + return false; + } + + /* failed to parse expression for goto */ + if (!(expression = parse_expression(parser, false))) + return false; + + if (!(*out = parse_goto_computed(parser, expression))) + return false; + + return true; } + /* not computed goto */ gt = ast_goto_new(parser_ctx(parser), parser_tokval(parser)); for (i = 0; i < vec_size(parser->labels); ++i) { @@ -2979,7 +3029,7 @@ static bool parse_goto(parser_t *parser, ast_expression **out) vec_push(parser->gotos, gt); if (!parser_next(parser) || parser->tok != ';') { - parseerror(parser, "semicolon expected after goto label"); + parseerror(parser, "semicolon expected after goto label got"); return false; } if (!parser_next(parser)) { diff --git a/tests/goto.qc b/tests/goto.qc new file mode 100644 index 0000000..7c9f297 --- /dev/null +++ b/tests/goto.qc @@ -0,0 +1,34 @@ +void(string, ...) print = #1; + +// correct execution order: +// label_3 +// label_2 +// label_4 +// label_3 +// label_1 +// label_5 +void main() { + float x = 1; + float y = 2; + + goto label_3; + + :label_1; print("label_1", "\n"); goto label_5; + :label_2; print("label_2", "\n"); goto label_4; + :label_3; print("label_3", "\n"); + + // will goto label_2 + goto (x == y) ? label_1 : label_2; + + :label_4; print("label_4", "\n"); + { + x = 1; + y = 1; + + // will goto label_1 + // then goes label_5 + goto label_3; + } + + :label_5; print("label_5", "\n"); +} diff --git a/tests/goto.tmpl b/tests/goto.tmpl new file mode 100644 index 0000000..4acf179 --- /dev/null +++ b/tests/goto.tmpl @@ -0,0 +1,12 @@ +I: goto.qc +D: test goto (both normal and computed) +T: -execute +C: -std=gmqcc +F: goto failed +S: goto worked +M: label_3 +M: label_2 +M: label_4 +M: label_3 +M: label_1 +M: label_5