handling if/else, shunting yard now leaves unmatched closing paren-tokens on the parser for this purpose

This commit is contained in:
Wolfgang (Blub) Bumiller 2012-08-11 11:17:46 +02:00
parent 3e4f490a59
commit ce082551d8
2 changed files with 76 additions and 1 deletions

View file

@ -623,6 +623,8 @@ int lex_do(lex_file *lex)
} else if (!strcmp(v, "for") ||
!strcmp(v, "while") ||
!strcmp(v, "do") ||
!strcmp(v, "if") ||
!strcmp(v, "else") ||
!strcmp(v, "var") ||
!strcmp(v, "return") ||
!strcmp(v, "const"))

View file

@ -572,6 +572,11 @@ static ast_expression* parser_expression(parser_t *parser)
shunt sy;
bool wantop = false;
/* count the parens because an if starts with one, so the
* end of a condition is an unmatched closing paren
*/
int parens = 0;
MEM_VECTOR_INIT(&sy, out);
MEM_VECTOR_INIT(&sy, ops);
@ -630,6 +635,7 @@ static ast_expression* parser_expression(parser_t *parser)
}
}
else if (parser->tok == '(') {
++parens;
nextwant = false; /* not expecting an operator next */
if (!shunt_ops_add(&sy, syparen(parser_ctx(parser), 1, 0))) {
parseerror(parser, "out of memory");
@ -637,6 +643,9 @@ static ast_expression* parser_expression(parser_t *parser)
}
}
else if (parser->tok == ')') {
--parens;
if (parens < 0)
break;
/* allowed for function calls */
if (!parser_close_paren(parser, &sy, true))
goto onerr;
@ -650,6 +659,7 @@ static ast_expression* parser_expression(parser_t *parser)
parser->lex->flags.noops = !wantop;
} else {
if (parser->tok == '(') {
++parens;
/* we expected an operator, this is the function-call operator */
if (!shunt_ops_add(&sy, syparen(parser_ctx(parser), 'f', sy.out_count-1))) {
parseerror(parser, "out of memory");
@ -657,6 +667,9 @@ static ast_expression* parser_expression(parser_t *parser)
}
}
else if (parser->tok == ')') {
--parens;
if (parens < 0)
break;
/* we do expect an operator next */
/* closing an opening paren */
if (!parser_close_paren(parser, &sy, false))
@ -713,7 +726,7 @@ static ast_expression* parser_expression(parser_t *parser)
break;
}
}
if (!parser_next(parser)) {
if (parens >= 0 && !parser_next(parser)) {
parseerror(parser, "Unexpected end of file");
goto onerr;
}
@ -742,6 +755,7 @@ onerr:
static bool parser_variable(parser_t *parser, ast_block *localblock);
static ast_block* parser_parse_block(parser_t *parser);
static ast_expression* parser_parse_statement_or_block(parser_t *parser);
static bool parser_parse_statement(parser_t *parser, ast_block *block, ast_expression **out)
{
if (parser->tok == TOKEN_TYPENAME)
@ -793,6 +807,65 @@ static bool parser_parse_statement(parser_t *parser, ast_block *block, ast_expre
}
return true;
}
else if (!strcmp(parser_tokval(parser), "if"))
{
ast_ifthen *ifthen;
ast_expression *cond, *ontrue, *onfalse = NULL;
lex_ctx ctx = parser_ctx(parser);
/* skip the 'if' and check for opening paren */
if (!parser_next(parser) || parser->tok != '(') {
parseerror(parser, "expected 'if' condition in parenthesis");
return false;
}
/* parse into the expression */
if (!parser_next(parser)) {
parseerror(parser, "expected 'if' condition after opening paren");
return false;
}
/* parse the condition */
cond = parser_expression(parser);
if (!cond)
return false;
/* closing paren */
if (parser->tok != ')') {
parseerror(parser, "expected closing paren after 'if' condition");
ast_delete(cond);
return false;
}
/* parse into the 'then' branch */
if (!parser_next(parser)) {
parseerror(parser, "expected statement for on-true branch of 'if'");
ast_delete(cond);
return false;
}
ontrue = parser_parse_statement_or_block(parser);
if (!ontrue) {
ast_delete(cond);
return false;
}
/* check for an else */
if (!strcmp(parser_tokval(parser), "else")) {
/* parse into the 'else' branch */
if (!parser_next(parser)) {
parseerror(parser, "expected on-false branch after 'else'");
ast_delete(ontrue);
ast_delete(cond);
return false;
}
onfalse = parser_parse_statement_or_block(parser);
if (!onfalse) {
ast_delete(ontrue);
ast_delete(cond);
return false;
}
}
ifthen = ast_ifthen_new(ctx, cond, ontrue, onfalse);
*out = (ast_expression*)ifthen;
return true;
}
parseerror(parser, "Unexpected keyword");
return false;
}