#include "ctx.h" #include "ivy/status.h" #include "node.h" #include #include #include struct selector_parser_state { struct parser_state s_base; unsigned int s_prev; bool s_complete; }; #define CHECK_SELECTOR_COMPLETE() \ do { \ if (state->s_complete) { \ parser_pop_state(ctx, STATE_ADD_NODE_TO_PARENT); \ return PARSE_RESULT(IVY_OK, PARSE_REPEAT_TOKEN); \ } \ } while (0) static struct token_parse_result parse_plus( struct ivy_parser *ctx, struct ivy_token *tok) { struct selector_parser_state *state = parser_get_state(ctx, struct selector_parser_state); struct ivy_ast_selector_node *sel = (struct ivy_ast_selector_node *)state->s_base.s_node; if (sel->n_recipient != IVY_SELECTOR_RECIPIENT_NONE) { return PARSE_RESULT(IVY_ERR_BAD_SYNTAX, 0); } sel->n_recipient = IVY_SELECTOR_RECIPIENT_CLASS; return PARSE_RESULT(IVY_OK, 0); } static struct token_parse_result parse_hyphen( struct ivy_parser *ctx, struct ivy_token *tok) { struct selector_parser_state *state = parser_get_state(ctx, struct selector_parser_state); struct ivy_ast_selector_node *sel = (struct ivy_ast_selector_node *)state->s_base.s_node; if (sel->n_recipient != IVY_SELECTOR_RECIPIENT_NONE) { return PARSE_RESULT(IVY_ERR_BAD_SYNTAX, 0); } sel->n_recipient = IVY_SELECTOR_RECIPIENT_OBJECT; return PARSE_RESULT(IVY_OK, 0); } static struct token_parse_result parse_ident( struct ivy_parser *ctx, struct ivy_token *tok) { struct selector_parser_state *state = parser_get_state(ctx, struct selector_parser_state); struct ivy_ast_selector_node *sel = (struct ivy_ast_selector_node *)state->s_base.s_node; CHECK_SELECTOR_COMPLETE(); if (state->s_prev == 0) { /* message name */ sel->n_msg_name = tok; state->s_prev = IVY_TOK_IDENT; state->s_complete = true; return PARSE_RESULT(IVY_OK, 0); } if (state->s_prev == IVY_TOK_LABEL) { /* internal parameter name */ b_queue_push_back(&sel->n_arg_names, &tok->t_entry); state->s_prev = IVY_TOK_IDENT; state->s_complete = true; return PARSE_RESULT(IVY_OK, 0); } return PARSE_RESULT(IVY_ERR_BAD_SYNTAX, 0); } static struct token_parse_result parse_label( struct ivy_parser *ctx, struct ivy_token *tok) { struct selector_parser_state *state = parser_get_state(ctx, struct selector_parser_state); struct ivy_ast_selector_node *sel = (struct ivy_ast_selector_node *)state->s_base.s_node; if (sel->n_recipient == IVY_SELECTOR_RECIPIENT_NONE && state->s_prev != IVY_TOK_IDENT && state->s_prev != IVY_SYM_LEFT_PAREN) { /* if recipient is not NONE, this selector appears at the beginning of a message * handler. only then is a label without a preceding identifier allowed. */ return PARSE_RESULT(IVY_ERR_BAD_SYNTAX, 0); } b_queue_push_back(&sel->n_arg_labels, &tok->t_entry); state->s_prev = IVY_TOK_LABEL; state->s_complete = false; return PARSE_RESULT(IVY_OK, 0); } static struct token_parse_result parse_linefeed( struct ivy_parser *ctx, struct ivy_token *tok) { struct selector_parser_state *state = parser_get_state(ctx, struct selector_parser_state); struct ivy_ast_selector_node *sel = (struct ivy_ast_selector_node *)state->s_base.s_node; CHECK_SELECTOR_COMPLETE(); if (!b_queue_empty(&sel->n_arg_labels) && state->s_prev != IVY_SYM_RIGHT_PAREN) { return PARSE_RESULT(IVY_ERR_BAD_SYNTAX, 0); } if (state->s_prev != IVY_TOK_IDENT) { return PARSE_RESULT(IVY_ERR_BAD_SYNTAX, 0); } parser_pop_state(ctx, STATE_ADD_NODE_TO_PARENT); state->s_prev = IVY_TOK_LINEFEED; return PARSE_RESULT(IVY_OK, 0); } static struct token_parse_result parse_left_paren( struct ivy_parser *ctx, struct ivy_token *tok) { struct selector_parser_state *state = parser_get_state(ctx, struct selector_parser_state); struct ivy_ast_selector_node *sel = (struct ivy_ast_selector_node *)state->s_base.s_node; if (state->s_prev != IVY_TOK_IDENT || !b_queue_empty(&sel->n_arg_labels)) { return PARSE_RESULT(IVY_ERR_BAD_SYNTAX, 0); } state->s_prev = IVY_SYM_LEFT_PAREN; return PARSE_RESULT(IVY_OK, 0); } static struct token_parse_result parse_right_paren( struct ivy_parser *ctx, struct ivy_token *tok) { struct selector_parser_state *state = parser_get_state(ctx, struct selector_parser_state); struct ivy_ast_selector_node *sel = (struct ivy_ast_selector_node *)state->s_base.s_node; if (state->s_prev != IVY_TOK_IDENT || b_queue_empty(&sel->n_arg_labels)) { return PARSE_RESULT(IVY_ERR_BAD_SYNTAX, 0); } state->s_prev = IVY_SYM_RIGHT_PAREN; state->s_complete = true; parser_pop_state(ctx, STATE_ADD_NODE_TO_PARENT); return PARSE_RESULT(IVY_OK, 0); } static struct token_parse_result parse_pipe( struct ivy_parser *ctx, struct ivy_token *tok) { struct selector_parser_state *state = parser_get_state(ctx, struct selector_parser_state); struct ivy_ast_selector_node *sel = (struct ivy_ast_selector_node *)state->s_base.s_node; switch (state->s_prev) { case IVY_SYM_RIGHT_PAREN: /* this looks like a complex message selector */ if (b_queue_empty(&sel->n_arg_labels) || b_queue_empty(&sel->n_arg_names)) { /* no message args */ return PARSE_RESULT(IVY_ERR_BAD_SYNTAX, 0); } if (!sel->n_msg_name) { /* no message name */ return PARSE_RESULT(IVY_ERR_BAD_SYNTAX, 0); } break; case IVY_TOK_IDENT: /* this looks like a unary or keyword message. */ if (!b_queue_empty(&sel->n_arg_labels) && b_queue_empty(&sel->n_arg_names)) { /* keyword message with no arg names. */ return PARSE_RESULT(IVY_ERR_BAD_SYNTAX, 0); } break; default: /* not sure what we're parsing. unknown token at end of selector. */ return PARSE_RESULT(IVY_ERR_BAD_SYNTAX, 0); } parser_pop_state(ctx, STATE_ADD_NODE_TO_PARENT); return PARSE_RESULT(IVY_OK, PARSE_REPEAT_TOKEN); } static struct token_parse_result parse_other( struct ivy_parser *ctx, struct ivy_token *tok) { struct selector_parser_state *state = parser_get_state(ctx, struct selector_parser_state); struct ivy_ast_selector_node *sel = (struct ivy_ast_selector_node *)state->s_base.s_node; CHECK_SELECTOR_COMPLETE(); return PARSE_RESULT(IVY_ERR_BAD_SYNTAX, 0); } static void to_string(struct ivy_ast_node *node, b_string *str) { struct ivy_ast_selector_node *sel = (struct ivy_ast_selector_node *)node; b_string_append_cstrf( str, "%s [", ivy_ast_node_type_to_string(node->n_type)); switch (sel->n_recipient) { case IVY_SELECTOR_RECIPIENT_CLASS: b_string_append_cstr(str, "+"); break; case IVY_SELECTOR_RECIPIENT_OBJECT: b_string_append_cstr(str, "-"); break; default: /* this will occur if the selector is being used to send a message at runtime, rather than as part of a message handler definition. */ break; } if (sel->n_msg_name) { b_string_append_cstr(str, sel->n_msg_name->t_str); } if (sel->n_msg_name && !b_queue_empty(&sel->n_arg_labels)) { b_string_append_cstr(str, "("); } b_queue_entry *label_entry = b_queue_first(&sel->n_arg_labels); b_queue_entry *name_entry = b_queue_first(&sel->n_arg_names); int i = 0; while (label_entry) { if (i > 0) { b_string_append_cstr(str, " "); } struct ivy_token *label, *name; label = b_unbox(struct ivy_token, label_entry, t_entry); name = b_unbox(struct ivy_token, name_entry, t_entry); if (label && label->t_type == IVY_TOK_LABEL && label->t_str) { b_string_append_cstrf(str, "%s:", label->t_str); } else { b_string_append_cstrf(str, "_:"); } if (name) { b_string_append_cstrf(str, "%s", name->t_str); } if (name) { i++; } label_entry = b_queue_next(label_entry); name_entry = b_queue_next(name_entry); } if (sel->n_msg_name && !b_queue_empty(&sel->n_arg_labels)) { b_string_append_cstr(str, ")"); } b_string_append_cstr(str, "]"); } static void init_state(struct ivy_parser *ctx, struct parser_state *sp, uintptr_t arg) { struct selector_parser_state *state = (struct selector_parser_state *)sp; state->s_prev = 0; state->s_complete = false; } struct ast_node_type selector_node_ops = { .n_init_state = init_state, .n_to_string = to_string, .n_state_size = sizeof(struct selector_parser_state), .n_node_size = sizeof(struct ivy_ast_selector_node), .n_token_parsers = { TOK_PARSER(IDENT, parse_ident), TOK_PARSER(LABEL, parse_label), TOK_PARSER_FALLBACK(parse_other), }, .n_symbol_parsers = { SYM_PARSER(PLUS, parse_plus), SYM_PARSER(HYPHEN, parse_hyphen), SYM_PARSER(LEFT_PAREN, parse_left_paren), SYM_PARSER(RIGHT_PAREN, parse_right_paren), SYM_PARSER(PIPE, parse_pipe), }, };