diff --git a/lang/ast/expr/arith.c b/lang/ast/expr/arith.c index d14e4cf..5a4c0d2 100644 --- a/lang/ast/expr/arith.c +++ b/lang/ast/expr/arith.c @@ -1,5 +1,6 @@ -#include "../node.h" +#include "../block.h" #include "../debug.h" +#include "../node.h" #include "expr.h" #include @@ -481,6 +482,40 @@ struct token_parse_result arith_parse_in( return PARSE_RESULT(IVY_ERR_BAD_SYNTAX, 0); } +struct token_parse_result arith_parse_do( + struct ivy_parser *ctx, struct ivy_token *tok) +{ + struct expr_parser_state *state + = parser_get_state(ctx, struct expr_parser_state); + + bool terminator = false; + if (state->s_terminator == IVY_KW_DO) { + terminator = true; + } + + if (state->s_sub_type == EXPR_SUBTYPE_KEYWORD_MSG + || state->s_sub_type == EXPR_SUBTYPE_KEYWORD_ARG) { + terminator = true; + } + + if (terminator) { + state->s_prev_token = IVY_KW_DO; + struct token_parse_result result + = expr_finalise_and_return(ctx, state); + result.r_flags = PARSE_REPEAT_TOKEN; + return result; + } + + /* 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. */ + + state->s_prev_token = IVY_KW_DO; + return PARSE_RESULT(IVY_OK, 0); +} + struct token_parse_result arith_parse_ident( struct ivy_parser *ctx, struct ivy_token *tok) { diff --git a/lang/ast/expr/expr.c b/lang/ast/expr/expr.c index d6ccef5..68a4af6 100644 --- a/lang/ast/expr/expr.c +++ b/lang/ast/expr/expr.c @@ -40,15 +40,16 @@ struct ast_node_type expr_node_ops = { }, .n_keyword_parsers = { /* statement keywords */ + KW_PARSER(FOR, stmt_parse_for), KW_PARSER(WHILE, stmt_parse_while), KW_PARSER(MATCH, stmt_parse_match), KW_PARSER(IF, stmt_parse_if), KW_PARSER(THEN, stmt_parse_end), KW_PARSER(ELSE, stmt_parse_end), - KW_PARSER(DO, stmt_parse_end), KW_PARSER(END, stmt_parse_end), - /* operator keywords */ + /* operator/block keywords */ KW_PARSER(IN, arith_parse_in), + KW_PARSER(DO, arith_parse_do), } }; diff --git a/lang/ast/expr/expr.h b/lang/ast/expr/expr.h index 88deb2c..d62c60a 100644 --- a/lang/ast/expr/expr.h +++ b/lang/ast/expr/expr.h @@ -128,9 +128,13 @@ extern struct token_parse_result arith_parse_equal_right_angle( struct ivy_parser *ctx, struct ivy_token *tok); extern struct token_parse_result arith_parse_in( struct ivy_parser *ctx, struct ivy_token *tok); +extern struct token_parse_result arith_parse_do( + struct ivy_parser *ctx, struct ivy_token *tok); /* statement parser callbacks */ +extern struct token_parse_result stmt_parse_for( + struct ivy_parser *ctx, struct ivy_token *tok); extern struct token_parse_result stmt_parse_while( struct ivy_parser *ctx, struct ivy_token *tok); extern struct token_parse_result stmt_parse_match( diff --git a/lang/ast/expr/stmt.c b/lang/ast/expr/stmt.c index 29b3891..72150fd 100644 --- a/lang/ast/expr/stmt.c +++ b/lang/ast/expr/stmt.c @@ -6,6 +6,42 @@ #include #include +struct token_parse_result stmt_parse_for( + struct ivy_parser *ctx, struct ivy_token *tok) +{ + struct expr_parser_state *state + = parser_get_state(ctx, struct expr_parser_state); + + if (state->s_sub_type == EXPR_SUBTYPE_KEYWORD_ARG) { + /* keyword messages have a higher precedence than inline + * conditionals, so treat this as a statement terminator. */ + struct token_parse_result result + = expr_finalise_and_return(ctx, state); + result.r_flags |= PARSE_REPEAT_TOKEN; + return result; + } + + struct ivy_ast_node *expr = NULL; + struct token_parse_result result + = expr_finalise(ctx, state, IVY_PRECEDENCE_IF_ELSE, &expr); + if (result.r_status != IVY_OK) { + return result; + } + + state->s_prev_token = IVY_KW_FOR; + + if (b_queue_empty(&state->s_operator_stack) + && b_queue_empty(&state->s_output_queue)) { + parser_pop_state(ctx, 0); + } + + /* if expr is NULL, this is an if-then-else-end statement, + * otherwise, this is an expr-if-else-expr. */ + parser_push_state(ctx, IVY_AST_FOR_LOOP, (uintptr_t)expr); + + return PARSE_RESULT(IVY_OK, PARSE_REPEAT_TOKEN); +} + struct token_parse_result stmt_parse_while( struct ivy_parser *ctx, struct ivy_token *tok) { diff --git a/lang/ast/for.c b/lang/ast/for.c new file mode 100644 index 0000000..c646c2d --- /dev/null +++ b/lang/ast/for.c @@ -0,0 +1,264 @@ +#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->s_terminator = 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->s_terminator = IVY_KW_DO; + + 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) { + 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->s_terminator = 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->s_terminator = 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, + }, +}; diff --git a/lang/ast/node.c b/lang/ast/node.c index 8af5543..2584156 100644 --- a/lang/ast/node.c +++ b/lang/ast/node.c @@ -24,6 +24,7 @@ extern struct ast_node_type cond_group_node_ops; extern struct ast_node_type cond_node_ops; extern struct ast_node_type match_node_ops; extern struct ast_node_type while_loop_node_ops; +extern struct ast_node_type for_loop_node_ops; extern struct ast_node_type return_node_ops; extern struct ast_node_type discard_node_ops; @@ -47,6 +48,7 @@ static const struct ast_node_type *node_ops[] = { [IVY_AST_COND] = &cond_node_ops, [IVY_AST_MATCH] = &match_node_ops, [IVY_AST_WHILE_LOOP] = &while_loop_node_ops, + [IVY_AST_FOR_LOOP] = &for_loop_node_ops, [IVY_AST_RETURN] = &return_node_ops, [IVY_AST_DISCARD] = &discard_node_ops, }; diff --git a/lang/include/ivy/lang/ast.h b/lang/include/ivy/lang/ast.h index 750808a..49890f5 100644 --- a/lang/include/ivy/lang/ast.h +++ b/lang/include/ivy/lang/ast.h @@ -200,8 +200,8 @@ struct ivy_ast_for_loop_node { struct ivy_ast_node *n_iterator; /* the object being iterated over, an expression that evaluates to an object that can be iterated. */ struct ivy_ast_node *n_iterable; - /* queue of struct ivy_ast_node. expressions to evaluate as part of the loop. */ - b_queue n_body; + /* expression to evaluate as part of the loop. */ + struct ivy_ast_node *n_body; }; struct ivy_ast_while_loop_node {