mirror of
https://github.com/DarkPlacesEngine/gmqcc.git
synced 2025-02-21 02:40:56 +00:00
Implemented computed goto + added goto test for testsuite (tests both normal and computed goto statements).
This commit is contained in:
parent
467a4740da
commit
a421d9a33b
3 changed files with 103 additions and 7 deletions
64
parser.c
64
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)) {
|
||||
|
|
34
tests/goto.qc
Normal file
34
tests/goto.qc
Normal file
|
@ -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");
|
||||
}
|
12
tests/goto.tmpl
Normal file
12
tests/goto.tmpl
Normal file
|
@ -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
|
Loading…
Reference in a new issue