From 209c47da68ab62e604e69a12e0cc6eb77eae88a2 Mon Sep 17 00:00:00 2001 From: Max Wash Date: Sat, 7 Dec 2024 20:56:57 +0000 Subject: [PATCH] lang: ast: implement parsing of static and dynamic package initialisers --- lang/ast/expr/arith.c | 24 +- lang/ast/expr/stmt.c | 14 ++ lang/ast/node.c | 12 + lang/ast/package.c | 454 ++++++++++++++++++++++++++++++++++++ lang/include/ivy/lang/ast.h | 21 +- 5 files changed, 518 insertions(+), 7 deletions(-) create mode 100644 lang/ast/package.c diff --git a/lang/ast/expr/arith.c b/lang/ast/expr/arith.c index 526c161..7a94903 100644 --- a/lang/ast/expr/arith.c +++ b/lang/ast/expr/arith.c @@ -505,8 +505,10 @@ struct token_parse_result arith_parse_in( = parser_get_state(ctx, struct expr_parser_state); if (state->s_terminator == IVY_KW_IN) { - state->s_prev_token = IVY_KW_IN; - return expr_finalise_and_return(ctx, state); + /* 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; } return PARSE_RESULT(IVY_ERR_BAD_SYNTAX, 0); @@ -644,7 +646,23 @@ struct token_parse_result arith_parse_right_paren( struct token_parse_result arith_parse_left_brace( struct ivy_parser *ctx, struct ivy_token *tok) { - return PARSE_RESULT(IVY_ERR_NOT_SUPPORTED, 0); + struct expr_parser_state *state + = parser_get_state(ctx, struct expr_parser_state); + + if (state->s_type == EXPR_TYPE_NONE) { + state->s_type = EXPR_TYPE_ARITH; + } + + if (state->s_type != EXPR_TYPE_ARITH) { + return PARSE_RESULT(IVY_ERR_BAD_SYNTAX, 0); + } + + if (state->s_prev_component != EXPR_CMP_OPERATOR && state->s_prev_component != EXPR_CMP_NONE) { + return PARSE_RESULT(IVY_ERR_BAD_SYNTAX, 0); + } + + parser_push_state(ctx, IVY_AST_PKG, 0); + return PARSE_RESULT(IVY_OK, 0); } struct token_parse_result arith_parse_right_brace( diff --git a/lang/ast/expr/stmt.c b/lang/ast/expr/stmt.c index 72150fd..0f24e26 100644 --- a/lang/ast/expr/stmt.c +++ b/lang/ast/expr/stmt.c @@ -12,6 +12,13 @@ struct token_parse_result stmt_parse_for( struct expr_parser_state *state = parser_get_state(ctx, struct expr_parser_state); + if (state->s_terminator == IVY_KW_FOR) { + /* 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; + } + 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. */ @@ -119,6 +126,13 @@ struct token_parse_result stmt_parse_if( struct expr_parser_state *state = parser_get_state(ctx, struct expr_parser_state); + if (state->s_terminator == IVY_KW_IF) { + /* 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; + } + 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. */ diff --git a/lang/ast/node.c b/lang/ast/node.c index a5a82d2..f9b1696 100644 --- a/lang/ast/node.c +++ b/lang/ast/node.c @@ -31,6 +31,10 @@ extern struct ast_node_type for_loop_node_ops; extern struct ast_node_type return_node_ops; extern struct ast_node_type property_node_ops; extern struct ast_node_type lambda_node_ops; +extern struct ast_node_type pkg_node_ops; +extern struct ast_node_type pkg_static_node_ops; +extern struct ast_node_type pkg_static_item_node_ops; +extern struct ast_node_type pkg_dynamic_node_ops; extern struct ast_node_type discard_node_ops; static const struct ast_node_type *node_ops[] = { @@ -59,6 +63,10 @@ static const struct ast_node_type *node_ops[] = { [IVY_AST_RETURN] = &return_node_ops, [IVY_AST_PROPERTY] = &property_node_ops, [IVY_AST_LAMBDA] = &lambda_node_ops, + [IVY_AST_PKG] = &pkg_node_ops, + [IVY_AST_PKG_STATIC] = &pkg_static_node_ops, + [IVY_AST_PKG_STATIC_ITEM] = &pkg_static_item_node_ops, + [IVY_AST_PKG_DYNAMIC] = &pkg_dynamic_node_ops, [IVY_AST_DISCARD] = &discard_node_ops, }; static const size_t nr_node_ops = sizeof node_ops / sizeof node_ops[0]; @@ -267,6 +275,10 @@ const char *ivy_ast_node_type_to_string(enum ivy_ast_node_type v) ENUM_STR(IVY_AST_COND); ENUM_STR(IVY_AST_TUPLE); ENUM_STR(IVY_AST_BLOCK); + ENUM_STR(IVY_AST_PKG); + ENUM_STR(IVY_AST_PKG_STATIC); + ENUM_STR(IVY_AST_PKG_STATIC_ITEM); + ENUM_STR(IVY_AST_PKG_DYNAMIC); ENUM_STR(IVY_AST_RETURN); ENUM_STR(IVY_AST_TYPE_COUNT); default: diff --git a/lang/ast/package.c b/lang/ast/package.c new file mode 100644 index 0000000..1b08774 --- /dev/null +++ b/lang/ast/package.c @@ -0,0 +1,454 @@ +#include "block.h" +#include "ctx.h" +#include "expr/expr.h" +#include "iterate.h" +#include "node.h" + +#include +#include + +enum package_type { + PACKAGE_NONE = 0, + /* package constructed using a list of key-value pairs. */ + PACKAGE_STATIC, + /* package constructed using an (X for X in Y if Z) pattern. */ + PACKAGE_DYNAMIC, +}; + +struct package_parser_state { + struct parser_state s_base; + enum package_type s_type; + + struct ivy_ast_node *s_prev_node; + unsigned int s_prev; + + /* only used for PACKAGE_STATIC */ + b_queue s_items; + struct ivy_ast_node *s_next_index; + unsigned int s_next_implicit_index; + + /* only used for PACKAGE_DYNAMIC */ + struct ivy_ast_node *s_transform; + struct ivy_ast_node *s_item; + struct ivy_ast_node *s_source; + struct ivy_ast_node *s_cond; +}; + +static enum ivy_status add_package_item(struct package_parser_state *state, struct ivy_ast_node *index, struct ivy_ast_node *value) +{ + struct ivy_ast_pkg_static_item_node *item = (struct ivy_ast_pkg_static_item_node *)ast_node_create(IVY_AST_PKG_STATIC_ITEM); + if (!item) { + return IVY_ERR_NO_MEMORY; + } + + item->n_value = value; + + if (index) { + item->n_index = index; + } else { + item->n_implicit_index = state->s_next_implicit_index++; + } + + b_queue_push_back(&state->s_items, &item->n_base.n_entry); + + state->s_next_index = NULL; + state->s_prev_node = NULL; + + return IVY_OK; +} + +static struct ivy_ast_node *finalise_package(struct package_parser_state *state) +{ + struct ivy_ast_pkg_static_node *s = NULL; + struct ivy_ast_pkg_dynamic_node *d = NULL; + + switch (state->s_type) { + case PACKAGE_STATIC: + s = (struct ivy_ast_pkg_static_node *)ast_node_create(IVY_AST_PKG_STATIC); + + if (!s) { + return NULL; + } + + s->n_items = state->s_items; + state->s_items = B_QUEUE_INIT; + return (struct ivy_ast_node *)s; + case PACKAGE_DYNAMIC: + d = (struct ivy_ast_pkg_dynamic_node *)ast_node_create(IVY_AST_PKG_DYNAMIC); + if (!d) { + return NULL; + } + + d->n_transform = state->s_transform; + d->n_item = state->s_item; + d->n_source = state->s_source; + d->n_cond = state->s_cond; + + state->s_transform = NULL; + state->s_item = NULL; + state->s_source = NULL; + state->s_cond = NULL; + + return (struct ivy_ast_node *)d; + default: + return NULL; + } +} + +static struct token_parse_result parse_equal_right_angle( + struct ivy_parser *ctx, struct ivy_token *tok) +{ + struct package_parser_state *state = parser_get_state(ctx, struct package_parser_state); + + if (state->s_type == PACKAGE_NONE) { + state->s_type = PACKAGE_STATIC; + } + + if (state->s_type != PACKAGE_STATIC) { + /* this token cannot be used in this context. */ + return PARSE_RESULT(IVY_ERR_BAD_SYNTAX, 0); + } + + if (state->s_prev != IVY_SYM_LEFT_BRACE && state->s_prev != IVY_SYM_COMMA) { + /* token unexpected at this time. */ + return PARSE_RESULT(IVY_ERR_BAD_SYNTAX, 0); + } + + if (!state->s_prev_node) { + /* empty index expression. */ + return PARSE_RESULT(IVY_ERR_BAD_SYNTAX, 0); + } + + state->s_prev = IVY_SYM_EQUAL_RIGHT_ANGLE; + state->s_next_index = state->s_prev_node; + state->s_prev_node = NULL; + + struct expr_parser_state *expr = (struct expr_parser_state *)parser_push_state(ctx, IVY_AST_EXPR, 0); + expr->s_terminator = IVY_SYM_COMMA; + expr->s_subexpr_depth = 1; + + return PARSE_RESULT(IVY_OK, 0); +} + +static struct token_parse_result parse_comma( + struct ivy_parser *ctx, struct ivy_token *tok) +{ + struct package_parser_state *state = parser_get_state(ctx, struct package_parser_state); + + if (state->s_type == PACKAGE_NONE) { + state->s_type = PACKAGE_STATIC; + } + + if (state->s_type != PACKAGE_STATIC) { + /* this token cannot be used in this context. */ + return PARSE_RESULT(IVY_ERR_BAD_SYNTAX, 0); + } + + if (state->s_prev != IVY_SYM_LEFT_BRACE && state->s_prev != IVY_SYM_COMMA && state->s_prev != IVY_SYM_EQUAL_RIGHT_ANGLE) { + /* token unexpected at this time. */ + return PARSE_RESULT(IVY_ERR_BAD_SYNTAX, 0); + } + + if (!state->s_prev_node) { + /* empty value expression. */ + return PARSE_RESULT(IVY_ERR_BAD_SYNTAX, 0); + } + + enum ivy_status status = add_package_item(state, state->s_next_index, state->s_prev_node); + if (status != IVY_OK) { + return PARSE_RESULT(status, 0); + } + + state->s_prev = IVY_SYM_COMMA; + + struct expr_parser_state *expr = (struct expr_parser_state *)parser_push_state(ctx, IVY_AST_EXPR, 0); + expr->s_terminator = IVY_SYM_EQUAL_RIGHT_ANGLE; + expr->s_subexpr_depth = 1; + + return PARSE_RESULT(IVY_OK, 0); +} + +static struct token_parse_result parse_right_brace( + struct ivy_parser *ctx, struct ivy_token *tok) +{ + struct package_parser_state *state = parser_get_state(ctx, struct package_parser_state); + + if (state->s_type == PACKAGE_NONE) { + state->s_type = PACKAGE_STATIC; + } + + enum ivy_status status = IVY_OK; + + switch (state->s_type) { + case PACKAGE_STATIC: + if (state->s_prev != IVY_SYM_LEFT_BRACE && state->s_prev != IVY_SYM_COMMA && state->s_prev != IVY_SYM_EQUAL_RIGHT_ANGLE) { + /* token unexpected at this time. */ + return PARSE_RESULT(IVY_ERR_BAD_SYNTAX, 0); + } + + if (state->s_prev_node) { + status = add_package_item(state, state->s_next_index, state->s_prev_node); + } + + break; + case PACKAGE_DYNAMIC: + if (!state->s_prev_node) { + /* empty expression */ + status = IVY_ERR_BAD_SYNTAX; + break; + } + + switch (state->s_prev) { + case IVY_KW_IN: + state->s_source = state->s_prev_node; + break; + case IVY_KW_IF: + state->s_cond = state->s_prev_node; + break; + default: + /* token unexpected at this time. */ + status = IVY_ERR_BAD_SYNTAX; + break; + } + break; + default: + /* not sure how we got here. */ + status = IVY_ERR_INTERNAL_FAILURE; + break; + } + + if (status != IVY_OK) { + return PARSE_RESULT(status, 0); + } + + struct ivy_ast_node *pkg = finalise_package(state); + if (!pkg) { + return PARSE_RESULT(IVY_ERR_NO_MEMORY, 0); + } + + parser_replace_current_node(ctx, pkg); + parser_pop_state(ctx, STATE_ADD_NODE_TO_PARENT); + + return PARSE_RESULT(IVY_OK, 0); +} + +static struct token_parse_result parse_for( + struct ivy_parser *ctx, struct ivy_token *tok) +{ + struct package_parser_state *state = parser_get_state(ctx, struct package_parser_state); + + if (state->s_type == PACKAGE_NONE) { + state->s_type = PACKAGE_DYNAMIC; + } + + if (state->s_type != PACKAGE_DYNAMIC) { + /* this token cannot be used in this context. */ + return PARSE_RESULT(IVY_ERR_BAD_SYNTAX, 0); + } + + if (state->s_prev != IVY_SYM_LEFT_BRACE) { + /* token unexpected at this time. */ + return PARSE_RESULT(IVY_ERR_BAD_SYNTAX, 0); + } + + if (!state->s_prev_node) { + /* empty expression. */ + return PARSE_RESULT(IVY_ERR_BAD_SYNTAX, 0); + } + + state->s_prev = IVY_KW_FOR; + state->s_transform = state->s_prev_node; + state->s_prev_node = NULL; + + struct expr_parser_state *expr = (struct expr_parser_state *)parser_push_state(ctx, IVY_AST_EXPR, 0); + 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 package_parser_state *state = parser_get_state(ctx, struct package_parser_state); + + if (state->s_type != PACKAGE_DYNAMIC) { + /* this token cannot be used in this context. */ + return PARSE_RESULT(IVY_ERR_BAD_SYNTAX, 0); + } + + if (state->s_prev != IVY_KW_FOR) { + /* token unexpected at this time. */ + return PARSE_RESULT(IVY_ERR_BAD_SYNTAX, 0); + } + + if (!state->s_prev_node) { + /* empty expression. */ + return PARSE_RESULT(IVY_ERR_BAD_SYNTAX, 0); + } + + state->s_prev = IVY_KW_IN; + state->s_item = state->s_prev_node; + state->s_prev_node = NULL; + + struct expr_parser_state *expr = (struct expr_parser_state *)parser_push_state(ctx, IVY_AST_EXPR, 0); + expr->s_terminator = IVY_KW_IF; + expr->s_subexpr_depth = 1; + + return PARSE_RESULT(IVY_OK, 0); +} + +static struct token_parse_result parse_if( + struct ivy_parser *ctx, struct ivy_token *tok) +{ + struct package_parser_state *state = parser_get_state(ctx, struct package_parser_state); + + if (state->s_type != PACKAGE_DYNAMIC) { + /* this token cannot be used in this context. */ + return PARSE_RESULT(IVY_ERR_BAD_SYNTAX, 0); + } + + if (state->s_prev != IVY_KW_IN) { + /* token unexpected at this time. */ + return PARSE_RESULT(IVY_ERR_BAD_SYNTAX, 0); + } + + if (!state->s_prev_node) { + /* empty expression. */ + return PARSE_RESULT(IVY_ERR_BAD_SYNTAX, 0); + } + + state->s_prev = IVY_KW_IF; + state->s_source = state->s_prev_node; + state->s_prev_node = NULL; + + struct expr_parser_state *expr = (struct expr_parser_state *)parser_push_state(ctx, IVY_AST_EXPR, 0); + expr->s_terminator = IVY_SYM_RIGHT_BRACE; + expr->s_subexpr_depth = 1; + + return PARSE_RESULT(IVY_OK, 0); +} + +static enum ivy_status add_child( + struct parser_state *parent, struct ivy_ast_node *child) +{ + struct package_parser_state *state + = (struct package_parser_state *)parent; + + if (state->s_prev_node) { + return IVY_ERR_BAD_SYNTAX; + } + + state->s_prev_node = child; + return IVY_OK; +} + +static void pkg_static_collect_children( + struct ivy_ast_node *node, struct ivy_ast_node_iterator *iterator) +{ + struct ivy_ast_pkg_static_node *pkg = (struct ivy_ast_pkg_static_node *)node; + + b_queue_iterator it = {0}; + b_queue_foreach (&it, &pkg->n_items) { + struct ivy_ast_node *item = b_unbox(struct ivy_ast_node, it.entry, n_entry); + ast_node_iterator_enqueue_node(iterator, node, item); + } +} + + +static void pkg_static_item_to_string(struct ivy_ast_node *node, b_string *str) +{ + struct ivy_ast_pkg_static_item_node *item = (struct ivy_ast_pkg_static_item_node *)node; + + b_string_append_cstr(str, ivy_ast_node_type_to_string(node->n_type)); + + if (!item->n_index) { + b_string_append_cstrf(str, " (%u)", item->n_implicit_index); + } +} + +static void pkg_static_item_collect_children( + struct ivy_ast_node *node, struct ivy_ast_node_iterator *iterator) +{ + struct ivy_ast_pkg_static_item_node *item = (struct ivy_ast_pkg_static_item_node *)node; + + if (item->n_index) { + ast_node_iterator_enqueue_node(iterator, node, item->n_index); + } + + if (item->n_value) { + ast_node_iterator_enqueue_node(iterator, node, item->n_value); + } +} + +static void pkg_dynamic_collect_children( + struct ivy_ast_node *node, struct ivy_ast_node_iterator *iterator) +{ + struct ivy_ast_pkg_dynamic_node *pkg = (struct ivy_ast_pkg_dynamic_node *)node; + + if (pkg->n_transform) { + ast_node_iterator_enqueue_node(iterator, node, pkg->n_transform); + } + + if (pkg->n_item) { + ast_node_iterator_enqueue_node(iterator, node, pkg->n_item); + } + + if (pkg->n_source) { + ast_node_iterator_enqueue_node(iterator, node, pkg->n_source); + } + + if (pkg->n_cond) { + ast_node_iterator_enqueue_node(iterator, node, pkg->n_cond); + } +} + +static void init_state(struct ivy_parser *ctx, struct parser_state *sp, uintptr_t arg) +{ + struct package_parser_state *state = (struct package_parser_state *)sp; + state->s_type = PACKAGE_NONE; + state->s_prev = IVY_SYM_LEFT_BRACE; + state->s_next_implicit_index = 0; + + struct expr_parser_state *expr = (struct expr_parser_state *)parser_push_state(ctx, IVY_AST_EXPR, 0); + expr->s_terminator = IVY_KW_FOR; + expr->s_subexpr_depth = 1; +} + +struct ast_node_type pkg_node_ops = { + .n_init_state = init_state, + .n_add_child = add_child, + .n_state_size = sizeof(struct package_parser_state), + /* placeholder node, will be replaced with either ivy_ast_pkg_static_node + * or ivy_ast_pkg_dynamic_node when finished. */ + .n_node_size = sizeof(struct ivy_ast_node), + .n_keyword_parsers = { + KW_PARSER(FOR, parse_for), + KW_PARSER(IN, parse_in), + KW_PARSER(IF, parse_if), + }, + .n_symbol_parsers = { + SYM_PARSER(COMMA, parse_comma), + SYM_PARSER(EQUAL_RIGHT_ANGLE, parse_equal_right_angle), + SYM_PARSER(RIGHT_BRACE, parse_right_brace), + }, +}; + +struct ast_node_type pkg_static_node_ops = { + .n_collect_children = pkg_static_collect_children, + .n_state_size = sizeof(struct parser_state), + .n_node_size = sizeof(struct ivy_ast_pkg_static_node), +}; + +struct ast_node_type pkg_static_item_node_ops = { + .n_to_string = pkg_static_item_to_string, + .n_collect_children = pkg_static_item_collect_children, + .n_state_size = sizeof(struct parser_state), + .n_node_size = sizeof(struct ivy_ast_pkg_static_item_node), +}; + +struct ast_node_type pkg_dynamic_node_ops = { + .n_collect_children = pkg_dynamic_collect_children, + .n_state_size = sizeof(struct parser_state), + .n_node_size = sizeof(struct ivy_ast_pkg_dynamic_node), +}; diff --git a/lang/include/ivy/lang/ast.h b/lang/include/ivy/lang/ast.h index 363fa47..33e4f3f 100644 --- a/lang/include/ivy/lang/ast.h +++ b/lang/include/ivy/lang/ast.h @@ -40,8 +40,10 @@ enum ivy_ast_node_type { IVY_AST_COND_GROUP, IVY_AST_COND, IVY_AST_TUPLE, - IVY_AST_PKG_INIT, - IVY_AST_PKG_COMPREHENSION, + IVY_AST_PKG, + IVY_AST_PKG_STATIC, + IVY_AST_PKG_STATIC_ITEM, + IVY_AST_PKG_DYNAMIC, IVY_AST_TYPE_COUNT, }; @@ -259,12 +261,23 @@ struct ivy_ast_tuple_node { b_queue n_members; }; -struct ivy_ast_pkg_init_node { +struct ivy_ast_pkg_static_item_node { struct ivy_ast_node n_base; + struct ivy_ast_node *n_value; + + struct ivy_ast_node *n_index; + /* if n_index is null (no index was specified) this contained the index + * implicitly assigned by the compiler. */ + unsigned int n_implicit_index; +}; + +struct ivy_ast_pkg_static_node { + struct ivy_ast_node n_base; + /* queue of struct ivy_ast_pkg_static_item_node */ b_queue n_items; }; -struct ivy_ast_pkg_comprehension_node { +struct ivy_ast_pkg_dynamic_node { struct ivy_ast_node n_base; struct ivy_ast_node *n_transform; struct ivy_ast_node *n_item;