#include "block.h" #include "expr/expr.h" #include "iterate.h" struct for_parser_state { struct parser_state s_base; bool s_inline; unsigned int s_prev_token; struct ivy_ast_node *s_body, *s_iterator, *s_iterable; struct ivy_ast_node *s_prev_node; }; static void init_state(struct ivy_parser *ctx, struct parser_state *sp, uintptr_t arg) { struct for_parser_state *state = (struct for_parser_state *)sp; state->s_prev_node = (struct ivy_ast_node *)arg; } struct token_parse_result parse_for(struct ivy_parser *ctx, struct ivy_token *tok) { struct for_parser_state *state = parser_get_state(ctx, struct for_parser_state); if (state->s_prev_node) { /* this is an inline for-loop */ state->s_inline = true; state->s_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_FOR; expr_add_terminator(expr, IVY_KW_IN); expr->s_subexpr_depth = 1; return PARSE_RESULT(IVY_OK, 0); } static struct token_parse_result parse_in( struct ivy_parser *ctx, struct ivy_token *tok) { struct for_parser_state *state = parser_get_state(ctx, struct for_parser_state); if (state->s_prev_token != IVY_KW_FOR) { return PARSE_RESULT(IVY_ERR_BAD_SYNTAX, 0); } /* previous component was the for iterator */ if (!state->s_prev_node) { return PARSE_RESULT(IVY_ERR_BAD_SYNTAX, 0); } state->s_iterator = state->s_prev_node; state->s_prev_node = NULL; /* next component will be the for iterable. */ 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; expr_add_terminator(expr, IVY_KW_DO); expr_add_terminator(expr, IVY_SYM_RIGHT_PAREN); state->s_prev_token = IVY_KW_IN; return PARSE_RESULT(IVY_OK, 0); } static struct token_parse_result parse_do( struct ivy_parser *ctx, struct ivy_token *tok) { struct for_parser_state *state = parser_get_state(ctx, struct for_parser_state); if (state->s_inline) { struct ivy_diag *diag = parser_push_diag( ctx, IVY_LANG_E_INCORRECT_INLINE_FOR_LOOP, IVY_LANG_MSG_DO_UNEXPECTED_IN_INLINE_FOR, tok); ivy_diag_push_msg( diag, IVY_LANG_MSG_PREVIOUS_EXPRESSION_IS_FOR_BODY); const struct ivy_char_cell *x = &state->s_body->n_end; const struct ivy_char_cell *z = &tok->t_end; const struct ivy_diag_amendment a[] = { IVY_DIAG_ADD(x->c_row, x->c_col + 1, "."), }; const size_t nr_a = sizeof a / sizeof a[0]; const struct ivy_diag_highlight hl[] = { IVY_DIAG_HL( HINT, x->c_row, x->c_col + 1, x->c_row, x->c_col + 1), }; const size_t nr_hl = sizeof hl / sizeof hl[0]; ivy_diag_push_snippet(diag, x->c_row, z->c_row, a, nr_a, hl, nr_hl); return PARSE_RESULT(IVY_ERR_BAD_SYNTAX, 0); } if (state->s_prev_token != IVY_KW_IN) { return PARSE_RESULT(IVY_ERR_BAD_SYNTAX, 0); } /* previous component was the for iterable. */ if (!state->s_prev_node) { return PARSE_RESULT(IVY_ERR_BAD_SYNTAX, 0); } state->s_iterable = 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); block_add_terminator(block, IVY_KW_END); state->s_prev_token = IVY_KW_DO; return PARSE_RESULT(IVY_OK, 0); } static struct token_parse_result parse_expr_begin( struct ivy_parser *ctx, struct ivy_token *tok) { struct for_parser_state *state = parser_get_state(ctx, struct for_parser_state); if (state->s_prev_token != IVY_KW_DO) { /* expression can only follow do keyword. */ 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_for_loop(struct for_parser_state *state) { struct ivy_ast_for_loop_node *loop = (struct ivy_ast_for_loop_node *)state->s_base.s_node; if (state->s_inline) { /* we have just reached the end of the for iterable. */ if (!state->s_prev_node) { /* for condition is empty. */ return IVY_ERR_BAD_SYNTAX; } state->s_iterable = state->s_prev_node; state->s_prev_node = NULL; loop->n_iterator = state->s_iterator; loop->n_iterable = state->s_iterable; loop->n_body = state->s_body; state->s_iterator = NULL; state->s_iterable = NULL; state->s_body = NULL; return IVY_OK; } if (!state->s_prev_node) { /* for body is empty. */ return IVY_ERR_BAD_SYNTAX; } state->s_body = state->s_prev_node; state->s_prev_node = NULL; loop->n_iterator = state->s_iterator; loop->n_iterable = state->s_iterable; loop->n_body = state->s_body; state->s_iterator = NULL; state->s_iterable = NULL; state->s_body = NULL; return IVY_OK; } static struct token_parse_result parse_punct_terminator( struct ivy_parser *ctx, struct ivy_token *tok) { struct for_parser_state *state = parser_get_state(ctx, struct for_parser_state); if (!state->s_inline) { /* only inline for loop can be ended with punctuation. */ return PARSE_RESULT(IVY_ERR_BAD_SYNTAX, 0); } enum ivy_status status = finalise_for_loop(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 for_parser_state *state = parser_get_state(ctx, struct for_parser_state); if (state->s_inline) { /* inline for loop must be terminated with punctuation. */ return PARSE_RESULT(IVY_ERR_BAD_SYNTAX, 0); } enum ivy_status status = finalise_for_loop(state); if (status != IVY_OK) { return PARSE_RESULT(status, 0); } parser_pop_state(ctx, STATE_ADD_NODE_TO_PARENT); return PARSE_RESULT(IVY_OK, 0); } static enum ivy_status add_child( struct parser_state *parent, struct ivy_ast_node *child) { struct for_parser_state *state = (struct for_parser_state *)parent; if (state->s_prev_node) { return IVY_ERR_BAD_SYNTAX; } state->s_prev_node = child; return IVY_OK; } static void collect_children( struct ivy_ast_node *node, struct ivy_ast_node_iterator *iterator) { struct ivy_ast_for_loop_node *loop = (struct ivy_ast_for_loop_node *)node; if (loop->n_iterator) { ast_node_iterator_enqueue_node(iterator, node, loop->n_iterator); } if (loop->n_iterable) { ast_node_iterator_enqueue_node(iterator, node, loop->n_iterable); } if (loop->n_body) { ast_node_iterator_enqueue_node(iterator, node, loop->n_body); } } struct ast_node_type for_loop_node_ops = { .n_init_state = init_state, .n_add_child = add_child, .n_collect_children = collect_children, .n_state_size = sizeof(struct for_parser_state), .n_node_size = sizeof(struct ivy_ast_for_loop_node), .n_keyword_parsers = { KW_PARSER(FOR, parse_for), KW_PARSER(IN, parse_in), KW_PARSER(DO, parse_do), 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, }, };