mirror of
https://github.com/DarkPlacesEngine/gmqcc.git
synced 2024-11-23 20:33:05 +00:00
parsing of loop labels
This commit is contained in:
parent
c6e7e80f23
commit
ebb7cb2ae3
4 changed files with 185 additions and 17 deletions
3
ast.c
3
ast.c
|
@ -764,12 +764,13 @@ void ast_loop_delete(ast_loop *self)
|
|||
mem_d(self);
|
||||
}
|
||||
|
||||
ast_breakcont* ast_breakcont_new(lex_ctx ctx, bool iscont)
|
||||
ast_breakcont* ast_breakcont_new(lex_ctx ctx, bool iscont, unsigned int levels)
|
||||
{
|
||||
ast_instantiate(ast_breakcont, ctx, ast_breakcont_delete);
|
||||
ast_expression_init((ast_expression*)self, (ast_expression_codegen*)&ast_breakcont_codegen);
|
||||
|
||||
self->is_continue = iscont;
|
||||
self->levels = levels;
|
||||
|
||||
return self;
|
||||
}
|
||||
|
|
5
ast.h
5
ast.h
|
@ -474,9 +474,10 @@ bool ast_loop_codegen(ast_loop*, ast_function*, bool lvalue, ir_value**);
|
|||
struct ast_breakcont_s
|
||||
{
|
||||
ast_expression_common expression;
|
||||
bool is_continue;
|
||||
bool is_continue;
|
||||
unsigned int levels;
|
||||
};
|
||||
ast_breakcont* ast_breakcont_new(lex_ctx ctx, bool iscont);
|
||||
ast_breakcont* ast_breakcont_new(lex_ctx ctx, bool iscont, unsigned int levels);
|
||||
void ast_breakcont_delete(ast_breakcont*);
|
||||
|
||||
bool ast_breakcont_codegen(ast_breakcont*, ast_function*, bool lvalue, ir_value**);
|
||||
|
|
1
opts.def
1
opts.def
|
@ -45,6 +45,7 @@
|
|||
GMQCC_DEFINE_FLAG(FALSE_EMPTY_STRINGS)
|
||||
GMQCC_DEFINE_FLAG(UTF8)
|
||||
GMQCC_DEFINE_FLAG(BAIL_ON_WERROR)
|
||||
GMQCC_DEFINE_FLAG(LOOP_LABELS)
|
||||
#endif
|
||||
|
||||
/* warning flags */
|
||||
|
|
193
parser.c
193
parser.c
|
@ -60,8 +60,9 @@ typedef struct {
|
|||
/* All the labels the function defined...
|
||||
* Should they be in ast_function instead?
|
||||
*/
|
||||
ast_label **labels;
|
||||
ast_goto **gotos;
|
||||
ast_label **labels;
|
||||
ast_goto **gotos;
|
||||
const char **loops;
|
||||
|
||||
/* A list of hashtables for each scope */
|
||||
ht *variables;
|
||||
|
@ -2018,7 +2019,58 @@ static bool parse_if(parser_t *parser, ast_block *block, ast_expression **out)
|
|||
return true;
|
||||
}
|
||||
|
||||
static bool parse_while_go(parser_t *parser, ast_block *block, ast_expression **out);
|
||||
static bool parse_while(parser_t *parser, ast_block *block, ast_expression **out)
|
||||
{
|
||||
bool rv;
|
||||
char *label = NULL;
|
||||
|
||||
/* skip the 'while' and get the body */
|
||||
if (!parser_next(parser)) {
|
||||
if (OPTS_FLAG(LOOP_LABELS))
|
||||
parseerror(parser, "expected loop label or 'while' condition in parenthesis");
|
||||
else
|
||||
parseerror(parser, "expected 'while' condition in parenthesis");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (parser->tok == ':') {
|
||||
if (!OPTS_FLAG(LOOP_LABELS))
|
||||
parseerror(parser, "labeled loops not activated, try using -floop-labels");
|
||||
if (!parser_next(parser) || parser->tok != TOKEN_IDENT) {
|
||||
parseerror(parser, "expected loop label");
|
||||
return false;
|
||||
}
|
||||
label = util_strdup(parser_tokval(parser));
|
||||
if (!parser_next(parser)) {
|
||||
mem_d(label);
|
||||
parseerror(parser, "expected 'while' condition in parenthesis");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (parser->tok != '(') {
|
||||
parseerror(parser, "expected 'while' condition in parenthesis");
|
||||
return false;
|
||||
}
|
||||
|
||||
vec_push(parser->loops, label);
|
||||
|
||||
rv = parse_while_go(parser, block, out);
|
||||
if (label)
|
||||
mem_d(label);
|
||||
if (vec_last(parser->loops) != label) {
|
||||
parseerror(parser, "internal error: label stack corrupted");
|
||||
rv = false;
|
||||
ast_delete(*out);
|
||||
*out = NULL;
|
||||
}
|
||||
else
|
||||
vec_pop(parser->loops);
|
||||
return rv;
|
||||
}
|
||||
|
||||
static bool parse_while_go(parser_t *parser, ast_block *block, ast_expression **out)
|
||||
{
|
||||
ast_loop *aloop;
|
||||
ast_expression *cond, *ontrue;
|
||||
|
@ -2070,7 +2122,53 @@ static bool parse_while(parser_t *parser, ast_block *block, ast_expression **out
|
|||
return true;
|
||||
}
|
||||
|
||||
static bool parse_dowhile_go(parser_t *parser, ast_block *block, ast_expression **out);
|
||||
static bool parse_dowhile(parser_t *parser, ast_block *block, ast_expression **out)
|
||||
{
|
||||
bool rv;
|
||||
char *label = NULL;
|
||||
|
||||
/* skip the 'do' and get the body */
|
||||
if (!parser_next(parser)) {
|
||||
if (OPTS_FLAG(LOOP_LABELS))
|
||||
parseerror(parser, "expected loop label or body");
|
||||
else
|
||||
parseerror(parser, "expected loop body");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (parser->tok == ':') {
|
||||
if (!OPTS_FLAG(LOOP_LABELS))
|
||||
parseerror(parser, "labeled loops not activated, try using -floop-labels");
|
||||
if (!parser_next(parser) || parser->tok != TOKEN_IDENT) {
|
||||
parseerror(parser, "expected loop label");
|
||||
return false;
|
||||
}
|
||||
label = util_strdup(parser_tokval(parser));
|
||||
if (!parser_next(parser)) {
|
||||
mem_d(label);
|
||||
parseerror(parser, "expected loop body");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
vec_push(parser->loops, label);
|
||||
|
||||
rv = parse_dowhile_go(parser, block, out);
|
||||
if (label)
|
||||
mem_d(label);
|
||||
if (vec_last(parser->loops) != label) {
|
||||
parseerror(parser, "internal error: label stack corrupted");
|
||||
rv = false;
|
||||
ast_delete(*out);
|
||||
*out = NULL;
|
||||
}
|
||||
else
|
||||
vec_pop(parser->loops);
|
||||
return rv;
|
||||
}
|
||||
|
||||
static bool parse_dowhile_go(parser_t *parser, ast_block *block, ast_expression **out)
|
||||
{
|
||||
ast_loop *aloop;
|
||||
ast_expression *cond, *ontrue;
|
||||
|
@ -2081,11 +2179,6 @@ static bool parse_dowhile(parser_t *parser, ast_block *block, ast_expression **o
|
|||
|
||||
(void)block; /* not touching */
|
||||
|
||||
/* skip the 'do' and get the body */
|
||||
if (!parser_next(parser)) {
|
||||
parseerror(parser, "expected loop body");
|
||||
return false;
|
||||
}
|
||||
if (!parse_statement_or_block(parser, &ontrue))
|
||||
return false;
|
||||
|
||||
|
@ -2146,7 +2239,57 @@ static bool parse_dowhile(parser_t *parser, ast_block *block, ast_expression **o
|
|||
return true;
|
||||
}
|
||||
|
||||
static bool parse_for_go(parser_t *parser, ast_block *block, ast_expression **out);
|
||||
static bool parse_for(parser_t *parser, ast_block *block, ast_expression **out)
|
||||
{
|
||||
bool rv;
|
||||
char *label = NULL;
|
||||
|
||||
/* skip the 'for' and check for opening paren */
|
||||
if (!parser_next(parser)) {
|
||||
if (OPTS_FLAG(LOOP_LABELS))
|
||||
parseerror(parser, "expected loop label or 'for' expressions in parenthesis");
|
||||
else
|
||||
parseerror(parser, "expected 'for' expressions in parenthesis");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (parser->tok == ':') {
|
||||
if (!OPTS_FLAG(LOOP_LABELS))
|
||||
parseerror(parser, "labeled loops not activated, try using -floop-labels");
|
||||
if (!parser_next(parser) || parser->tok != TOKEN_IDENT) {
|
||||
parseerror(parser, "expected loop label");
|
||||
return false;
|
||||
}
|
||||
label = util_strdup(parser_tokval(parser));
|
||||
if (!parser_next(parser)) {
|
||||
mem_d(label);
|
||||
parseerror(parser, "expected 'for' expressions in parenthesis");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (parser->tok != '(') {
|
||||
parseerror(parser, "expected 'for' expressions in parenthesis");
|
||||
return false;
|
||||
}
|
||||
|
||||
vec_push(parser->loops, label);
|
||||
|
||||
rv = parse_for_go(parser, block, out);
|
||||
if (label)
|
||||
mem_d(label);
|
||||
if (vec_last(parser->loops) != label) {
|
||||
parseerror(parser, "internal error: label stack corrupted");
|
||||
rv = false;
|
||||
ast_delete(*out);
|
||||
*out = NULL;
|
||||
}
|
||||
else
|
||||
vec_pop(parser->loops);
|
||||
return rv;
|
||||
}
|
||||
static bool parse_for_go(parser_t *parser, ast_block *block, ast_expression **out)
|
||||
{
|
||||
ast_loop *aloop;
|
||||
ast_expression *initexpr, *cond, *increment, *ontrue;
|
||||
|
@ -2164,11 +2307,6 @@ static bool parse_for(parser_t *parser, ast_block *block, ast_expression **out)
|
|||
increment = NULL;
|
||||
ontrue = NULL;
|
||||
|
||||
/* skip the 'while' and check for opening paren */
|
||||
if (!parser_next(parser) || parser->tok != '(') {
|
||||
parseerror(parser, "expected 'for' expressions in parenthesis");
|
||||
goto onerr;
|
||||
}
|
||||
/* parse into the expression */
|
||||
if (!parser_next(parser)) {
|
||||
parseerror(parser, "expected 'for' initializer after opening paren");
|
||||
|
@ -2311,11 +2449,38 @@ static bool parse_return(parser_t *parser, ast_block *block, ast_expression **ou
|
|||
|
||||
static bool parse_break_continue(parser_t *parser, ast_block *block, ast_expression **out, bool is_continue)
|
||||
{
|
||||
size_t i;
|
||||
unsigned int levels = 0;
|
||||
lex_ctx ctx = parser_ctx(parser);
|
||||
|
||||
(void)block; /* not touching */
|
||||
if (!parser_next(parser)) {
|
||||
parseerror(parser, "expected semicolon or loop label");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!parser_next(parser) || parser->tok != ';') {
|
||||
if (parser->tok == TOKEN_IDENT) {
|
||||
if (!OPTS_FLAG(LOOP_LABELS))
|
||||
parseerror(parser, "labeled loops not activated, try using -floop-labels");
|
||||
i = vec_size(parser->loops);
|
||||
while (i--) {
|
||||
if (parser->loops[i] && !strcmp(parser->loops[i], parser_tokval(parser)))
|
||||
break;
|
||||
if (!i) {
|
||||
parseerror(parser, "no such loop to %s: `%s`",
|
||||
(is_continue ? "continue" : "break out of"),
|
||||
parser_tokval(parser));
|
||||
return false;
|
||||
}
|
||||
++levels;
|
||||
}
|
||||
if (!parser_next(parser)) {
|
||||
parseerror(parser, "expected semicolon");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (parser->tok != ';') {
|
||||
parseerror(parser, "expected semicolon");
|
||||
return false;
|
||||
}
|
||||
|
@ -2323,7 +2488,7 @@ static bool parse_break_continue(parser_t *parser, ast_block *block, ast_express
|
|||
if (!parser_next(parser))
|
||||
parseerror(parser, "parse error");
|
||||
|
||||
*out = (ast_expression*)ast_breakcont_new(ctx, is_continue);
|
||||
*out = (ast_expression*)ast_breakcont_new(ctx, is_continue, levels);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in a new issue