From 1f7319458e42ad930800561fd13f4e18777de7d9 Mon Sep 17 00:00:00 2001 From: Max Wash Date: Fri, 6 Dec 2024 19:47:27 +0000 Subject: [PATCH] lang: ast: implement parsing of properties --- lang/ast/expr/arith.c | 6 + lang/ast/node.c | 2 + lang/ast/property.c | 394 ++++++++++++++++++++++++++++++++++++ lang/include/ivy/lang/ast.h | 8 + 4 files changed, 410 insertions(+) diff --git a/lang/ast/expr/arith.c b/lang/ast/expr/arith.c index cac9ac9..6ee8068 100644 --- a/lang/ast/expr/arith.c +++ b/lang/ast/expr/arith.c @@ -1087,6 +1087,12 @@ struct token_parse_result arith_parse_comma( struct expr_parser_state *state = parser_get_state(ctx, struct expr_parser_state); + if (state->s_terminator == IVY_SYM_COMMA) { + struct token_parse_result result = expr_finalise_and_return(ctx, state); + result.r_flags = PARSE_REPEAT_TOKEN; + return result; + } + if (state->s_type != EXPR_TYPE_ARITH) { return PARSE_RESULT(IVY_ERR_BAD_SYNTAX, 0); } diff --git a/lang/ast/node.c b/lang/ast/node.c index b266e8d..b908349 100644 --- a/lang/ast/node.c +++ b/lang/ast/node.c @@ -28,6 +28,7 @@ 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 property_node_ops; extern struct ast_node_type discard_node_ops; static const struct ast_node_type *node_ops[] = { @@ -54,6 +55,7 @@ static const struct ast_node_type *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_PROPERTY] = &property_node_ops, [IVY_AST_DISCARD] = &discard_node_ops, }; static const size_t nr_node_ops = sizeof node_ops / sizeof node_ops[0]; diff --git a/lang/ast/property.c b/lang/ast/property.c index e69de29..107d2fe 100644 --- a/lang/ast/property.c +++ b/lang/ast/property.c @@ -0,0 +1,394 @@ +#include "block.h" +#include "ctx.h" +#include "expr/expr.h" +#include "iterate.h" +#include "ivy/status.h" +#include "node.h" + +#include +#include +#include + +#define PROPERTY_TYPE(flags) (flags & (PROPERTY_FULL | PROPERTY_AUTO)) + +enum property_flags { + PROPERTY_NONE = 0x00u, + /* a property defined across multiple lines using get/set => syntax */ + PROPERTY_FULL = 0x01u, + /* a property defined across a single line lines (get, set) syntax */ + PROPERTY_AUTO = 0x02u, + /* property has getter defined. */ + PROPERTY_GET = 0x04u, + /* property has setter defined. */ + PROPERTY_SET = 0x08u, +}; + +struct property_parser_state { + struct parser_state s_base; + enum property_type s_flags; + + struct ivy_ast_node *s_prev_node; + + struct ivy_token *s_ident; + struct ivy_ast_node *s_get, *s_set; + + /* one of either 0, PROPERTY_GET, or PROPERTY_SET, depending on which part of the + * property is currently being parsed. */ + unsigned int s_cur_component; + unsigned int s_prev; +}; + +static void finalise_property(struct property_parser_state *state) +{ + struct ivy_ast_property_node *prop = (struct ivy_ast_property_node *)state->s_base.s_node; + + if (state->s_flags & PROPERTY_GET) { + prop->n_flags |= IVY_AST_PROPERTY_GET; + } + + if (state->s_flags & PROPERTY_SET) { + prop->n_flags |= IVY_AST_PROPERTY_SET; + } + + prop->n_ident = state->s_ident; + prop->n_get = state->s_get; + prop->n_set = state->s_set; + + state->s_ident = NULL; + state->s_get = NULL; + state->s_set = NULL; +} + +static struct token_parse_result parse_ident( + struct ivy_parser *ctx, struct ivy_token *tok) +{ + struct property_parser_state *state = parser_get_state(ctx, struct property_parser_type); + + if (state->s_prev != IVY_SYM_DOLLAR) { + /* the only ident we're looking for is the property name, which + * can only occur after the $ symbol. */ + return PARSE_RESULT(IVY_ERR_BAD_SYNTAX, 0); + } + + state->s_ident = tok; + state->s_prev = IVY_TOK_IDENT; + return PARSE_RESULT(IVY_OK, 0); +} + +static struct token_parse_result parse_pipe( + struct ivy_parser *ctx, struct ivy_token *tok) +{ + struct property_parser_state *state = parser_get_state(ctx, struct property_parser_type); + + if (state->s_prev != IVY_TOK_IDENT) { + /* the only pipe we're looking for is the property + * name/implementation separator, which can only occur after + * the property name. */ + return PARSE_RESULT(IVY_ERR_BAD_SYNTAX, 0); + } + + if (PROPERTY_TYPE(state->s_flags) != PROPERTY_NONE) { + /* we've already determined what kind of syntax is being used + * to define this property (which is what the pipe symbol is + * for). */ + return PARSE_RESULT(IVY_ERR_BAD_SYNTAX, 0); + } + + state->s_prev = IVY_SYM_PIPE; + state->s_flags |= PROPERTY_FULL; + + return PARSE_RESULT(IVY_OK, 0); +} + +static struct token_parse_result parse_left_paren( + struct ivy_parser *ctx, struct ivy_token *tok) +{ + struct property_parser_state *state = parser_get_state(ctx, struct property_parser_type); + + if (state->s_prev != IVY_TOK_IDENT) { + /* the only left paren we're looking for comes after the + * property name */ + return PARSE_RESULT(IVY_ERR_BAD_SYNTAX, 0); + } + + if (PROPERTY_TYPE(state->s_flags) != PROPERTY_NONE) { + /* we've already determined what kind of syntax is being used + * to define this property (which is what this symbol is + * for). */ + return PARSE_RESULT(IVY_ERR_BAD_SYNTAX, 0); + } + + state->s_prev = IVY_SYM_LEFT_PAREN; + state->s_flags |= PROPERTY_AUTO; + + return PARSE_RESULT(IVY_OK, 0); +} + +static struct token_parse_result parse_right_paren( + struct ivy_parser *ctx, struct ivy_token *tok) +{ + struct property_parser_state *state = parser_get_state(ctx, struct property_parser_type); + + if (PROPERTY_TYPE(state->s_flags) != PROPERTY_AUTO) { + /* can only use parenthesis in auto-properties. */ + return PARSE_RESULT(IVY_ERR_BAD_SYNTAX, 0); + } + + if (state->s_prev != IVY_KW_GET && state->s_prev != IVY_KW_SET) { + /* the only left paren we're looking for comes after an expression */ + return PARSE_RESULT(IVY_ERR_BAD_SYNTAX, 0); + } + + if ((state->s_flags & (PROPERTY_GET | PROPERTY_SET)) == 0) { + /* auto-property with neither a getter nor a setter. */ + return PARSE_RESULT(IVY_ERR_BAD_SYNTAX, 0); + } + + parser_pop_state(ctx, STATE_ADD_NODE_TO_PARENT); + return PARSE_RESULT(IVY_OK, 0); +} + +static struct token_parse_result parse_equal_right_arrow( + struct ivy_parser *ctx, struct ivy_token *tok) +{ + struct property_parser_state *state = parser_get_state(ctx, struct property_parser_type); + + if (PROPERTY_TYPE(state->s_flags) != PROPERTY_FULL) { + /* this symbol cannot be used in auto-properties. */ + return PARSE_RESULT(IVY_ERR_BAD_SYNTAX, 0); + } + + if (state->s_prev != IVY_KW_GET && state->s_prev != IVY_KW_SET) { + /* this symbol can only be used after get or set */ + return PARSE_RESULT(IVY_ERR_BAD_SYNTAX, 0); + } + + state->s_prev = IVY_SYM_EQUAL_RIGHT_ANGLE; + + 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 property_parser_state *state = parser_get_state(ctx, struct property_parser_type); + + if (PROPERTY_TYPE(state->s_flags) == PROPERTY_FULL && state->s_prev != IVY_SYM_EQUAL_RIGHT_ANGLE) { + /* this symbol can only be used after get or set */ + return PARSE_RESULT(IVY_ERR_BAD_SYNTAX, 0); + } + + if (state->s_cur_component != PROPERTY_GET && state->s_cur_component != PROPERTY_SET) { + return PARSE_RESULT(IVY_ERR_BAD_SYNTAX, 0); + } + + state->s_prev = IVY_SYM_COMMA; + + if (PROPERTY_TYPE(state->s_flags) == PROPERTY_AUTO) { + state->s_cur_component = 0; + return PARSE_RESULT(IVY_OK, 0); + } + + if (!state->s_prev_node) { + /* empty getter/setter expression. */ + return PARSE_RESULT(IVY_ERR_BAD_SYNTAX, 0); + } + + switch (state->s_cur_component) { + case PROPERTY_GET: + state->s_get = state->s_prev_node; + break; + case PROPERTY_SET: + state->s_set = state->s_prev_node; + break; + default: + /* this case is handled at the start of this function. */ + break; + } + + state->s_cur_component = 0; + state->s_prev_node = NULL; + + return PARSE_RESULT(IVY_OK, 0); +} + +static struct token_parse_result parse_dot( + struct ivy_parser *ctx, struct ivy_token *tok) +{ + struct property_parser_state *state = parser_get_state(ctx, struct property_parser_type); + + if (PROPERTY_TYPE(state->s_flags) != PROPERTY_FULL) { + /* this symbol cannot be used in auto-properties. */ + return PARSE_RESULT(IVY_ERR_BAD_SYNTAX, 0); + } + + if (state->s_cur_component != PROPERTY_GET && state->s_cur_component != PROPERTY_SET) { + /* not actually parsing a getter or setter */ + return PARSE_RESULT(IVY_ERR_BAD_SYNTAX, 0); + } + + if (state->s_prev != IVY_SYM_EQUAL_RIGHT_ANGLE) { + /* this symbol can only be used after => and an expression */ + return PARSE_RESULT(IVY_ERR_BAD_SYNTAX, 0); + } + + if (!state->s_prev_node) { + /* empty expression. */ + return PARSE_RESULT(IVY_ERR_BAD_SYNTAX, 0); + } + + switch (state->s_cur_component) { + case PROPERTY_GET: + state->s_get = state->s_prev_node; + break; + case PROPERTY_SET: + state->s_set = state->s_prev_node; + break; + default: + /* this case is handled at the start of this function. */ + break; + } + + state->s_cur_component = 0; + + finalise_property(state); + parser_pop_state(ctx, STATE_ADD_NODE_TO_PARENT); + return PARSE_RESULT(IVY_OK, 0); +} + +static struct token_parse_result parse_get( + struct ivy_parser *ctx, struct ivy_token *tok) +{ + struct property_parser_state *state = parser_get_state(ctx, struct property_parser_type); + + if (state->s_flags & PROPERTY_GET) { + /* property already has getter defined */ + return PARSE_RESULT(IVY_ERR_BAD_SYNTAX, 0); + } + + if (PROPERTY_TYPE(state->s_flags) == PROPERTY_FULL && state->s_prev != IVY_SYM_PIPE && state->s_prev != IVY_SYM_COMMA) { + /* getter is not expected here. */ + return PARSE_RESULT(IVY_ERR_BAD_SYNTAX, 0); + } + + if (PROPERTY_TYPE(state->s_flags) == PROPERTY_AUTO && state->s_prev != IVY_SYM_LEFT_PAREN && state->s_prev != IVY_SYM_COMMA) { + /* getter is not expected here. */ + return PARSE_RESULT(IVY_ERR_BAD_SYNTAX, 0); + } + + state->s_flags |= PROPERTY_GET; + state->s_cur_component = PROPERTY_GET; + state->s_prev = IVY_KW_GET; + + return PARSE_RESULT(IVY_OK, 0); +} + +static struct token_parse_result parse_set( + struct ivy_parser *ctx, struct ivy_token *tok) +{ + struct property_parser_state *state = parser_get_state(ctx, struct property_parser_type); + + if (state->s_flags & PROPERTY_SET) { + /* property already has setter defined */ + return PARSE_RESULT(IVY_ERR_BAD_SYNTAX, 0); + } + + if (PROPERTY_TYPE(state->s_flags) == PROPERTY_FULL && state->s_prev != IVY_SYM_PIPE && state->s_prev != IVY_SYM_COMMA) { + /* setter is not expected here. */ + return PARSE_RESULT(IVY_ERR_BAD_SYNTAX, 0); + } + + if (PROPERTY_TYPE(state->s_flags) == PROPERTY_AUTO && state->s_prev != IVY_SYM_LEFT_PAREN && state->s_prev != IVY_SYM_COMMA) { + /* setter is not expected here. */ + return PARSE_RESULT(IVY_ERR_BAD_SYNTAX, 0); + } + + state->s_flags |= PROPERTY_SET; + state->s_cur_component = PROPERTY_SET; + state->s_prev = IVY_KW_SET; + + return PARSE_RESULT(IVY_OK, 0); +} + +static enum ivy_status add_child( + struct parser_state *parent, struct ivy_ast_node *child) +{ + struct property_parser_state *state = (struct property_parser_state *)parent; + + if (state->s_prev_node) { + return IVY_ERR_BAD_SYNTAX; + } + + state->s_prev_node = child; + return IVY_OK; +} + +static void init_state(struct ivy_parser *ctx, struct parser_state *sp, uintptr_t arg) +{ + struct property_parser_state *state = (struct property_parser_state *)sp; + state->s_flags = PROPERTY_NONE; + state->s_prev = IVY_SYM_DOLLAR; +} + +static void collect_children( + struct ivy_ast_node *node, struct ivy_ast_node_iterator *iterator) +{ + struct ivy_ast_property_node *prop = (struct ivy_ast_property_node *)node; + + if (prop->n_get) { + ast_node_iterator_enqueue_node(iterator, node, prop->n_get); + } + + if (prop->n_set) { + ast_node_iterator_enqueue_node(iterator, node, prop->n_set); + } +} + +static void print(struct ivy_ast_node *node) +{ + struct ivy_ast_property_node *prop = (struct ivy_ast_property_node *)node; + + printf("%s (%s) [", ivy_ast_node_type_to_string(node->n_type), prop->n_ident->t_str); + + if (prop->n_flags & IVY_AST_PROPERTY_GET) { + puts("get"); + } + + if ((prop->n_flags & IVY_AST_PROPERTY_GET) && prop->n_flags & IVY_AST_PROPERTY_SET) { + puts(", "); + } + + if (prop->n_flags & IVY_AST_PROPERTY_SET) { + puts("set"); + } + + puts("]\n"); +} + +struct ast_node_type property_node_ops = { + .n_print = print, + .n_add_child = add_child, + .n_init_state = init_state, + .n_collect_children = collect_children, + .n_state_size = sizeof(struct property_parser_state), + .n_node_size = sizeof(struct ivy_ast_property_node), + .n_token_parsers = { + TOK_PARSER(IDENT, parse_ident), + }, + .n_keyword_parsers = { + KW_PARSER(GET, parse_get), + KW_PARSER(SET, parse_set), + }, + .n_symbol_parsers = { + SYM_PARSER(PIPE, parse_pipe), + SYM_PARSER(EQUAL_RIGHT_ANGLE, parse_equal_right_arrow), + SYM_PARSER(COMMA, parse_comma), + SYM_PARSER(DOT, parse_dot), + SYM_PARSER(LEFT_PAREN, parse_left_paren), + SYM_PARSER(RIGHT_PAREN, parse_right_paren), + }, +}; diff --git a/lang/include/ivy/lang/ast.h b/lang/include/ivy/lang/ast.h index 49890f5..4904d59 100644 --- a/lang/include/ivy/lang/ast.h +++ b/lang/include/ivy/lang/ast.h @@ -117,6 +117,14 @@ struct ivy_ast_msgh_node { struct ivy_ast_property_node { struct ivy_ast_node n_base; struct ivy_token *n_ident; + + /* if get/set is set in these flags but the corresponding child node + * is NULL, the getter/setter should be synthesised by the compiler. */ + enum { + IVY_AST_PROPERTY_GET = 0x01u, + IVY_AST_PROPERTY_SET = 0x02u, + } n_flags; + /* one of either: * a) a lambda. the lambda is executed to get the property value; or, * b) a constant value. the constant is returned as the property