From cbec21b90fa172791c4f4ef29a095fe0820cac77 Mon Sep 17 00:00:00 2001 From: Max Wash Date: Thu, 24 Apr 2025 09:45:26 +0100 Subject: [PATCH] lang: implement elif keyword for more complex if-statements --- lang/ast/block.c | 1 + lang/ast/cond.c | 60 ++++++++++++++++++++++++++++++++----- lang/ast/expr/expr.c | 1 + lang/include/ivy/lang/lex.h | 5 ++-- lang/lex.c | 4 ++- 5 files changed, 60 insertions(+), 11 deletions(-) diff --git a/lang/ast/block.c b/lang/ast/block.c index 6e918c7..eb13ad6 100644 --- a/lang/ast/block.c +++ b/lang/ast/block.c @@ -160,6 +160,7 @@ struct ast_node_type block_node_ops = { KW_PARSER(VAR, parse_var), KW_PARSER(END, parse_end), KW_PARSER(ELSE, parse_else), + KW_PARSER(ELIF, parse_else), /* same behaviour as ELSE */ KW_PARSER_FALLBACK(parse_keyword), }, .n_symbol_parsers = { diff --git a/lang/ast/cond.c b/lang/ast/cond.c index 7b70e16..28c154a 100644 --- a/lang/ast/cond.c +++ b/lang/ast/cond.c @@ -20,6 +20,24 @@ static void init_state(struct ivy_parser *ctx, struct parser_state *sp, uintptr_ state->s_prev_node = (struct ivy_ast_node *)arg; } +static enum ivy_status flush_current_branch(struct cond_group_parser_state *state) +{ + if (!state->s_cur_branch) { + return IVY_ERR_INTERNAL_FAILURE; + } + + b_queue_push_back(&state->s_branches, &state->s_cur_branch->n_base.n_entry); + + state->s_cur_branch + = (struct ivy_ast_cond_node *)ast_node_create(IVY_AST_COND); + + if (!state->s_cur_branch) { + return IVY_ERR_NO_MEMORY; + } + + return IVY_OK; +} + struct token_parse_result parse_if(struct ivy_parser *ctx, struct ivy_token *tok) { struct cond_group_parser_state *state @@ -54,22 +72,45 @@ struct token_parse_result parse_if(struct ivy_parser *ctx, struct ivy_token *tok return PARSE_RESULT(IVY_OK, 0); } -static enum ivy_status flush_current_branch(struct cond_group_parser_state *state) +struct token_parse_result parse_elif(struct ivy_parser *ctx, struct ivy_token *tok) { + struct cond_group_parser_state *state + = parser_get_state(ctx, struct cond_group_parser_state); + if (!state->s_cur_branch) { - return IVY_ERR_INTERNAL_FAILURE; + return PARSE_RESULT(IVY_ERR_BAD_SYNTAX, 0); } - b_queue_push_back(&state->s_branches, &state->s_cur_branch->n_base.n_entry); + if (state->s_inline) { + return PARSE_RESULT(IVY_ERR_BAD_SYNTAX, 0); + } + + if (state->s_prev_token != IVY_KW_THEN) { + return PARSE_RESULT(IVY_ERR_BAD_SYNTAX, 0); + } + + state->s_cur_branch->n_body = state->s_prev_node; + state->s_prev_node = NULL; + + enum ivy_status status = flush_current_branch(state); + if (status != IVY_OK) { + return PARSE_RESULT(status, 0); + } state->s_cur_branch = (struct ivy_ast_cond_node *)ast_node_create(IVY_AST_COND); - if (!state->s_cur_branch) { - return IVY_ERR_NO_MEMORY; + return PARSE_RESULT(IVY_ERR_NO_MEMORY, 0); } - return IVY_OK; + struct expr_parser_state *expr + = (struct expr_parser_state *)parser_push_state( + ctx, IVY_AST_EXPR, 0); + + state->s_prev_token = IVY_KW_ELIF; + expr->s_subexpr_depth = 1; + + return PARSE_RESULT(IVY_OK, 0); } static struct token_parse_result parse_then( @@ -86,7 +127,7 @@ static struct token_parse_result parse_then( return PARSE_RESULT(IVY_ERR_BAD_SYNTAX, 0); } - if (state->s_prev_token != IVY_KW_IF) { + if (state->s_prev_token != IVY_KW_IF && state->s_prev_token != IVY_KW_ELIF) { return PARSE_RESULT(IVY_ERR_BAD_SYNTAX, 0); } @@ -103,6 +144,8 @@ static struct token_parse_result parse_then( = (struct block_parser_state *)parser_push_state( ctx, IVY_AST_BLOCK, 0); /* set the sub-expression depth to be non-zero so the expression parser doesn't consume the expression separator. */ + block_add_terminator(block, IVY_KW_ELIF); + block_add_terminator(block, IVY_KW_ELSE); block_add_terminator(block, IVY_KW_END); state->s_prev_token = IVY_KW_THEN; @@ -138,7 +181,7 @@ static struct token_parse_result parse_else( return PARSE_RESULT(status, 0); } - /* next component should either be an if keyword or a block. */ + /* next component should be a block. */ } else { /* previous component was the if-condition. */ if (!state->s_prev_node) { @@ -332,6 +375,7 @@ struct ast_node_type cond_group_node_ops = { .n_keyword_parsers = { KW_PARSER(IF, parse_if), KW_PARSER(THEN, parse_then), + KW_PARSER(ELIF, parse_elif), KW_PARSER(ELSE, parse_else), KW_PARSER(END, parse_end), }, diff --git a/lang/ast/expr/expr.c b/lang/ast/expr/expr.c index 043a46f..5ff28e2 100644 --- a/lang/ast/expr/expr.c +++ b/lang/ast/expr/expr.c @@ -84,6 +84,7 @@ struct ast_node_type expr_node_ops = { KW_PARSER(IF, stmt_parse_if), KW_PARSER(THEN, stmt_parse_end), KW_PARSER(ELSE, stmt_parse_end), + KW_PARSER(ELIF, stmt_parse_end), KW_PARSER(CATCH, stmt_parse_end), KW_PARSER(FINALLY, stmt_parse_end), KW_PARSER(END, stmt_parse_end), diff --git a/lang/include/ivy/lang/lex.h b/lang/include/ivy/lang/lex.h index b246df1..07dbe4a 100644 --- a/lang/include/ivy/lang/lex.h +++ b/lang/include/ivy/lang/lex.h @@ -34,13 +34,14 @@ enum ivy_keyword { IVY_KW_THROW, IVY_KW_CATCH, IVY_KW_FINALLY, - IVY_KW_IF, - IVY_KW_THEN, IVY_KW_AND, IVY_KW_OR, IVY_KW_UNDERSTANDS, IVY_KW_IS, IVY_KW_NOT, + IVY_KW_IF, + IVY_KW_THEN, + IVY_KW_ELIF, IVY_KW_ELSE, IVY_KW_WHILE, IVY_KW_FOR, diff --git a/lang/lex.c b/lang/lex.c index bbb30d3..176587d 100644 --- a/lang/lex.c +++ b/lang/lex.c @@ -31,12 +31,13 @@ static struct lex_token_def keywords[] = { LEX_TOKEN_DEF(IVY_KW_FINALLY, "finally"), LEX_TOKEN_DEF(IVY_KW_UNDERSTANDS, "understands"), LEX_TOKEN_DEF(IVY_KW_IF, "if"), + LEX_TOKEN_DEF(IVY_KW_ELIF, "elif"), + LEX_TOKEN_DEF(IVY_KW_ELSE, "else"), LEX_TOKEN_DEF(IVY_KW_THEN, "then"), LEX_TOKEN_DEF(IVY_KW_AND, "and"), LEX_TOKEN_DEF(IVY_KW_OR, "or"), LEX_TOKEN_DEF(IVY_KW_IS, "is"), LEX_TOKEN_DEF(IVY_KW_NOT, "not"), - LEX_TOKEN_DEF(IVY_KW_ELSE, "else"), LEX_TOKEN_DEF(IVY_KW_WHILE, "while"), LEX_TOKEN_DEF(IVY_KW_FOR, "for"), LEX_TOKEN_DEF(IVY_KW_MATCH, "match"), @@ -1140,6 +1141,7 @@ const char *ivy_keyword_to_string(enum ivy_keyword keyword) ENUM_STR(IVY_KW_CATCH); ENUM_STR(IVY_KW_FINALLY); ENUM_STR(IVY_KW_IF); + ENUM_STR(IVY_KW_ELIF); ENUM_STR(IVY_KW_THEN); ENUM_STR(IVY_KW_AND); ENUM_STR(IVY_KW_OR);