#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_flags 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_state); 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_state); 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_state); 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_state); 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); } finalise_property(state); 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_state); 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_add_terminator(expr, 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_state); 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_state); 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_state); 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_state); 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 to_string(struct ivy_ast_node *node, b_string *str) { struct ivy_ast_property_node *prop = (struct ivy_ast_property_node *)node; b_string_append_cstrf( str, "%s (%s) [", ivy_ast_node_type_to_string(node->n_type), prop->n_ident->t_str); if (prop->n_flags & IVY_AST_PROPERTY_GET) { b_string_append_cstr(str, "get"); } if ((prop->n_flags & IVY_AST_PROPERTY_GET) && prop->n_flags & IVY_AST_PROPERTY_SET) { b_string_append_cstr(str, ", "); } if (prop->n_flags & IVY_AST_PROPERTY_SET) { b_string_append_cstr(str, "set"); } b_string_append_cstr(str, "]"); } struct ast_node_type property_node_ops = { .n_to_string = to_string, .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), }, };