diff --git a/bshell/parse/syntax/if-else.c b/bshell/parse/syntax/if-else.c new file mode 100644 index 0000000..5195173 --- /dev/null +++ b/bshell/parse/syntax/if-else.c @@ -0,0 +1,110 @@ +#include "../syntax.h" + +static bool add_branch( + struct if_ast_node *group, + struct ast_node *cond, + struct ast_node *body) +{ + struct if_branch_ast_node *branch + = (struct if_branch_ast_node *)ast_node_create(AST_IF_BRANCH); + if (!branch) { + return false; + } + + branch->n_cond = cond; + branch->n_body = body; + fx_queue_push_back(&group->n_branches, &branch->n_base.n_entry); + + return true; +} + +bool parse_if(struct parse_ctx *ctx, struct ast_node **out) +{ + if (!parse_keyword(ctx, KW_IF)) { + return false; + } + + if (!parse_symbol(ctx, SYM_LEFT_PAREN)) { + report_error(ctx, "expected `(` after `if`"); + return false; + } + + struct ast_node *if_cond = NULL, *if_body = NULL; + if (!parse_expr(ctx, &if_cond)) { + report_error(ctx, "invalid if condition"); + return false; + } + + if (!parse_symbol(ctx, SYM_RIGHT_PAREN)) { + report_error(ctx, "expected `)` after if-condition"); + ast_node_destroy(if_cond); + return false; + } + + if (!parse_block(ctx, &if_body)) { + report_error(ctx, "invalid if body"); + ast_node_destroy(if_cond); + return false; + } + + struct if_ast_node *if_group + = (struct if_ast_node *)ast_node_create(AST_IF); + if (!if_group) { + ctx->p_status = BSHELL_ERR_NO_MEMORY; + ast_node_destroy(if_cond); + ast_node_destroy(if_body); + return false; + } + + if (!add_branch(if_group, if_cond, if_body)) { + ctx->p_status = BSHELL_ERR_NO_MEMORY; + ast_node_destroy(if_cond); + ast_node_destroy(if_body); + ast_node_destroy((struct ast_node *)if_group); + return false; + } + + bool done = false; + while (!done) { + struct ast_node *cond = NULL, *body = NULL; + if (parse_keyword(ctx, KW_ELSE)) { + done = true; + } else if (parse_keyword(ctx, KW_ELSEIF)) { + if (!parse_expr(ctx, &cond)) { + report_error( + ctx, + "invalid conditional expression"); + ast_node_destroy((struct ast_node *)if_group); + return false; + } + } else { + done = true; + break; + } + + if (!parse_block(ctx, &body)) { + report_error(ctx, "invalid conditional body"); + if (cond) { + ast_node_destroy(cond); + } + + ast_node_destroy((struct ast_node *)if_group); + return false; + } + + if (!add_branch(if_group, cond, body)) { + report_error(ctx, "failed to add branch to if-group"); + if (cond) { + ast_node_destroy(cond); + } + + ast_node_destroy(body); + ast_node_destroy((struct ast_node *)if_group); + return false; + } + } + + *out = (struct ast_node *)if_group; + + return true; +}