#include "block.h" #include "expr/expr.h" #include "iterate.h" struct cond_group_parser_state { struct parser_state s_base; bool s_inline; unsigned int s_prev_token; struct ivy_ast_cond_node *s_cur_branch; b_queue s_branches; struct ivy_ast_node *s_prev_node; }; static void init_state(struct ivy_parser *ctx, struct parser_state *sp, uintptr_t arg) { struct cond_group_parser_state *state = (struct cond_group_parser_state *)sp; 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 = parser_get_state(ctx, struct cond_group_parser_state); if (state->s_cur_branch) { return PARSE_RESULT(IVY_ERR_BAD_SYNTAX, 0); } state->s_cur_branch = (struct ivy_ast_cond_node *)ast_node_create(IVY_AST_COND); if (!state->s_cur_branch) { return PARSE_RESULT(IVY_ERR_NO_MEMORY, 0); } if (state->s_prev_node) { /* this is an inline if-else */ state->s_inline = true; state->s_cur_branch->n_body = state->s_prev_node; state->s_prev_node = NULL; } else { state->s_inline = false; } struct expr_parser_state *expr = (struct expr_parser_state *)parser_push_state( ctx, IVY_AST_EXPR, 0); state->s_prev_token = IVY_KW_IF; expr->s_subexpr_depth = 1; return PARSE_RESULT(IVY_OK, 0); } 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 PARSE_RESULT(IVY_ERR_BAD_SYNTAX, 0); } 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); } 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( 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 PARSE_RESULT(IVY_ERR_BAD_SYNTAX, 0); } if (state->s_inline) { return PARSE_RESULT(IVY_ERR_BAD_SYNTAX, 0); } if (state->s_prev_token != IVY_KW_IF && state->s_prev_token != IVY_KW_ELIF) { return PARSE_RESULT(IVY_ERR_BAD_SYNTAX, 0); } /* previous component was the if-condition. */ if (!state->s_prev_node) { return PARSE_RESULT(IVY_ERR_BAD_SYNTAX, 0); } state->s_cur_branch->n_cond = state->s_prev_node; state->s_prev_node = NULL; /* next component will be a block. */ struct block_parser_state *block = (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; return PARSE_RESULT(IVY_OK, 0); } static struct token_parse_result parse_else( struct ivy_parser *ctx, struct ivy_token *tok) { enum ivy_status status; struct cond_group_parser_state *state = parser_get_state(ctx, struct cond_group_parser_state); if (!state->s_cur_branch) { return PARSE_RESULT(IVY_ERR_BAD_SYNTAX, 0); } if (!state->s_inline) { /* previous component was either an expression or a block, and is the if branch body. */ if (!state->s_prev_node) { 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; status = flush_current_branch(state); if (status != IVY_OK) { return PARSE_RESULT(status, 0); } /* next component should be a block. */ } else { /* previous component was the if-condition. */ if (!state->s_prev_node) { return PARSE_RESULT(IVY_ERR_BAD_SYNTAX, 0); } if (state->s_prev_token != IVY_KW_IF) { return PARSE_RESULT(IVY_ERR_BAD_SYNTAX, 0); } state->s_cur_branch->n_cond = state->s_prev_node; state->s_prev_node = NULL; status = flush_current_branch(state); if (status != IVY_OK) { return PARSE_RESULT(status, 0); } /* next component will be an expression. */ struct expr_parser_state *expr = (struct expr_parser_state *)parser_push_state( ctx, IVY_AST_EXPR, 0); /* set the sub-expression depth to be non-zero so the expression * parser doesn't consume the expression separator. */ expr->s_subexpr_depth = 1; } state->s_prev_token = IVY_KW_ELSE; return PARSE_RESULT(IVY_OK, 0); } static struct token_parse_result parse_expr_begin( 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_prev_token != IVY_KW_ELSE && state->s_prev_token != IVY_KW_THEN) { /* expression can only follow else and then keywords. */ return PARSE_RESULT(IVY_ERR_BAD_SYNTAX, 0); } struct block_parser_state *block = (struct block_parser_state *)parser_push_state( ctx, IVY_AST_BLOCK, 0); block_add_terminator(block, IVY_KW_END); return PARSE_RESULT(IVY_OK, PARSE_REPEAT_TOKEN); } static enum ivy_status finalise_cond_group(struct cond_group_parser_state *state) { struct ivy_ast_cond_group_node *group = (struct ivy_ast_cond_group_node *)state->s_base.s_node; if (state->s_inline) { /* we have just reached the end of either the 'if' or the 'else' expression. */ if (!state->s_cur_branch) { /* not currently parsing a conditional branch. */ return IVY_ERR_BAD_SYNTAX; } if (!state->s_prev_node) { /* else expression is empty. */ return IVY_ERR_BAD_SYNTAX; } if (state->s_prev_token == IVY_KW_ELSE) { /* this is the else expression, s_prev_node is the else body. */ state->s_cur_branch->n_body = state->s_prev_node; } else if (state->s_prev_token == IVY_KW_IF) { /* this is the if expression, s_prev_node is the if condition. */ state->s_cur_branch->n_cond = state->s_prev_node; } state->s_prev_node = NULL; flush_current_branch(state); group->n_branches = state->s_branches; state->s_branches = B_QUEUE_INIT; return IVY_OK; } else { /* we have just reached the 'end' keyword. */ if (!state->s_cur_branch) { /* not currently parsing a conditional branch. */ return IVY_ERR_BAD_SYNTAX; } if (!state->s_prev_node) { /* else expression is empty. */ return IVY_ERR_BAD_SYNTAX; } state->s_cur_branch->n_body = state->s_prev_node; state->s_prev_node = NULL; flush_current_branch(state); group->n_branches = state->s_branches; state->s_branches = B_QUEUE_INIT; return IVY_OK; } } static struct token_parse_result parse_punct_terminator( 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_inline) { /* only inline conditional can be ended with punctuation. */ return PARSE_RESULT(IVY_ERR_BAD_SYNTAX, 0); } enum ivy_status status = finalise_cond_group(state); if (status != IVY_OK) { return PARSE_RESULT(status, 0); } parser_pop_state(ctx, STATE_ADD_NODE_TO_PARENT); return PARSE_RESULT(IVY_OK, PARSE_REPEAT_TOKEN); } static struct token_parse_result parse_end( struct ivy_parser *ctx, struct ivy_token *tok) { struct cond_group_parser_state *state = parser_get_state(ctx, struct cond_group_parser_state); enum ivy_status status = finalise_cond_group(state); if (status != IVY_OK) { return PARSE_RESULT(status, 0); } int flags = 0; if (state->s_inline) { flags = PARSE_REPEAT_TOKEN; } parser_pop_state(ctx, STATE_ADD_NODE_TO_PARENT); return PARSE_RESULT(IVY_OK, flags); } static enum ivy_status add_child( struct parser_state *parent, struct ivy_ast_node *child) { struct cond_group_parser_state *state = (struct cond_group_parser_state *)parent; if (state->s_prev_node) { return IVY_ERR_BAD_SYNTAX; } state->s_prev_node = child; return IVY_OK; } static void cond_group_collect_children( struct ivy_ast_node *node, struct ivy_ast_node_iterator *iterator) { struct ivy_ast_cond_group_node *group = (struct ivy_ast_cond_group_node *)node; b_queue_entry *entry = b_queue_first(&group->n_branches); while (entry) { struct ivy_ast_node *branch = b_unbox(struct ivy_ast_node, entry, n_entry); ast_node_iterator_enqueue_node(iterator, node, branch); entry = b_queue_next(entry); } } static void cond_collect_children( struct ivy_ast_node *node, struct ivy_ast_node_iterator *iterator) { struct ivy_ast_cond_node *cond = (struct ivy_ast_cond_node *)node; if (cond->n_cond) { ast_node_iterator_enqueue_node(iterator, node, cond->n_cond); } if (cond->n_body) { ast_node_iterator_enqueue_node(iterator, node, cond->n_body); } } struct ast_node_type cond_group_node_ops = { .n_init_state = init_state, .n_add_child = add_child, .n_collect_children = cond_group_collect_children, .n_state_size = sizeof(struct cond_group_parser_state), .n_node_size = sizeof(struct ivy_ast_cond_group_node), .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), }, .n_symbol_parsers = { SYM_PARSER(DOT, parse_punct_terminator), SYM_PARSER(COMMA, parse_punct_terminator), SYM_PARSER(RIGHT_PAREN, parse_punct_terminator), SYM_PARSER(BANG, parse_punct_terminator), }, .n_expr_parser = { .expr_begin = parse_expr_begin, }, }; struct ast_node_type cond_node_ops = { .n_collect_children = cond_collect_children, .n_state_size = sizeof(struct parser_state), .n_node_size = sizeof(struct ivy_ast_cond_node), };