#include "ctx.h" #include static bool parse_named_selector( struct ivy_parser *parser, struct ivy_ast_selector_node *sel) { struct ivy_token *tok = NULL; if (!parse_word(parser, &tok)) { parse_trace(NULL, "expected selector name"); return false; } sel->n_msg_name = tok; if (!parse_symbol(parser, IVY_SYM_LEFT_PAREN)) { return true; } bool ok = true; while (1) { if (parse_symbol(parser, IVY_SYM_RIGHT_PAREN)) { break; } struct ivy_token *label = NULL, *arg = NULL; if (!fx_queue_empty(&sel->n_arg_labels) && !parse_symbol(parser, IVY_SYM_COMMA)) { parse_trace(NULL, "expected comma after selector arg"); ok = false; break; } if (!parse_label(parser, &label)) { parse_trace(NULL, "expected selector arg label"); ok = false; break; } if (!parse_word(parser, &arg)) { parse_trace(NULL, "expected selector arg name"); ok = false; break; } fx_queue_push_back(&sel->n_arg_labels, &label->t_entry); fx_queue_push_back(&sel->n_arg_names, &arg->t_entry); } return true; } static bool parse_unnamed_selector( struct ivy_parser *parser, struct ivy_ast_selector_node *sel) { bool ok = true; while (1) { struct ivy_token *label = NULL, *arg = NULL; if (!parse_label(parser, &label)) { break; } if (!parse_word(parser, &arg)) { parse_trace(NULL, "expected selector arg name"); ok = false; break; } fx_queue_push_back(&sel->n_arg_labels, &label->t_entry); fx_queue_push_back(&sel->n_arg_names, &arg->t_entry); } return true; } static bool parse_selector(struct ivy_parser *parser, struct ivy_ast_node **out) { struct ivy_ast_selector_node *sel = create_node(IVY_AST_SELECTOR); if (parse_symbol(parser, IVY_SYM_PLUS)) { sel->n_recipient = IVY_SELECTOR_RECIPIENT_CLASS; } else if (parse_symbol(parser, IVY_SYM_HYPHEN)) { sel->n_recipient = IVY_SELECTOR_RECIPIENT_OBJECT; } else { parse_trace(NULL, "invalid selector prefix"); return false; } bool ok = true; if (peek_word(parser)) { ok = parse_named_selector(parser, sel); } else { ok = parse_unnamed_selector(parser, sel); } if (!ok) { return false; } *out = generic_node(sel); return true; } static bool parse_msg(struct ivy_parser *parser, struct ivy_ast_node **out) { struct ivy_ast_node *sel = NULL; if (!parse_selector(parser, &sel)) { parse_trace(NULL, "invalid message handler selector"); return false; } struct ivy_ast_msgh_node *msgh = create_node(IVY_AST_MSGH); msgh->n_sel = (struct ivy_ast_selector_node *)sel; bool ok = true; struct ivy_ast_node *body = NULL; if (peek_symbol(parser, IVY_SYM_LEFT_BRACKET)) { ok = parse_block(parser, &body); } else if (parse_symbol(parser, IVY_SYM_PIPE)) { ok = parse_expr(parser, EXPR_PARSE_NORMAL, &body); ok = ok && parse_symbol(parser, IVY_SYM_DOT); } else { parse_trace(NULL, "expected message handler body"); ok = false; } if (!ok) { return false; } msgh->n_body = body; *out = generic_node(msgh); return true; } static bool parse_default_property( struct ivy_parser *parser, struct ivy_ast_property_node *prop) { if (!parse_symbol(parser, IVY_SYM_LEFT_PAREN)) { parse_trace(parser, "expected '('"); return false; } bool ok = true; while (ok) { parse_linefeed(parser); if (parse_symbol(parser, IVY_SYM_RIGHT_PAREN)) { break; } int access_types_set = prop->n_flags & (IVY_AST_PROPERTY_SET | IVY_AST_PROPERTY_GET); if (access_types_set != 0 && !parse_symbol(parser, IVY_SYM_COMMA)) { parse_trace( NULL, "expected `,` or `)` after property access " "type"); ok = false; break; } parse_linefeed(parser); int access_type = 0; if (parse_keyword(parser, IVY_KW_GET)) { access_type = IVY_AST_PROPERTY_GET; } else if (parse_keyword(parser, IVY_KW_SET)) { access_type = IVY_AST_PROPERTY_SET; } else { parse_trace(parser, "unknown property access type"); ok = false; break; } parse_linefeed(parser); if (prop->n_flags & access_type) { parse_trace(parser, "property access type already set"); ok = false; break; } prop->n_flags |= access_type; } return true; } static bool parse_property(struct ivy_parser *parser, struct ivy_ast_node **out) { struct ivy_token *tok = NULL; if (!parse_word(parser, &tok)) { parse_trace(NULL, "expected property name"); return false; } struct ivy_ast_property_node *prop = create_node(IVY_AST_PROPERTY); prop->n_ident = tok; if (peek_symbol(parser, IVY_SYM_LEFT_PAREN)) { return parse_default_property(parser, prop); } if (!parse_symbol(parser, IVY_SYM_PIPE)) { parse_trace(NULL, "expected `|` after property name"); return false; } bool ok = true; while (ok) { if (parse_symbol(parser, IVY_SYM_DOT)) { break; } parse_linefeed(parser); if ((prop->n_get || prop->n_set) && !parse_symbol(parser, IVY_SYM_COMMA)) { parse_trace( NULL, "expected `,` or `.` after property " "branch"); ok = false; break; } parse_linefeed(parser); int access_type = 0; if (parse_keyword(parser, IVY_KW_GET)) { access_type = IVY_AST_PROPERTY_GET; } else if (parse_keyword(parser, IVY_KW_SET)) { access_type = IVY_AST_PROPERTY_SET; } else { parse_trace(NULL, "unknown property access type"); ok = false; break; } parse_linefeed(parser); if (!parse_symbol(parser, IVY_SYM_EQUAL_RIGHT_ANGLE)) { parse_trace( NULL, "expected `=>` after property branch " "type"); ok = false; break; } parse_linefeed(parser); struct ivy_ast_node *body = NULL; if (peek_symbol(parser, IVY_SYM_LEFT_BRACKET)) { ok = parse_block(parser, &body); } else { ok = parse_expr(parser, EXPR_PARSE_STOP_COMMA, &body); } if (!ok) { parse_trace(parser, "invalid property value"); break; } prop->n_flags |= access_type; switch (access_type) { case IVY_AST_PROPERTY_GET: if (prop->n_get) { parse_trace(NULL, "property getter already set"); ok = false; break; } prop->n_get = body; break; case IVY_AST_PROPERTY_SET: if (prop->n_set) { parse_trace(NULL, "property setter already set"); ok = false; break; } prop->n_set = body; break; default: ok = false; break; } } if (!ok) { return false; } *out = generic_node(prop); return true; } bool parse_class(struct ivy_parser *parser, struct ivy_ast_node **out) { if (!parse_keyword(parser, IVY_KW_CLASS)) { return false; } struct ivy_token *ident = NULL; if (!parse_word(parser, &ident)) { parse_trace(NULL, "expected class identifier"); return false; } if (!parse_symbol(parser, IVY_SYM_LEFT_BRACKET)) { parse_trace(NULL, "expected `[` after class identifier"); return false; } struct ivy_ast_class_node *class = create_node(IVY_AST_CLASS); if (!class) { parser->p_status = IVY_ERR_NO_MEMORY; return false; } class->n_ident = ident; bool result = true; while (1) { struct ivy_ast_node *msg = NULL, *property = NULL; if (parse_linefeed(parser)) { continue; } if (parse_symbol(parser, IVY_SYM_RIGHT_BRACKET)) { break; } if (peek_symbol(parser, IVY_SYM_HYPHEN)) { result = parse_msg(parser, &msg); } else if (peek_symbol(parser, IVY_SYM_PLUS)) { result = parse_msg(parser, &msg); } else if (peek_word(parser)) { result = parse_property(parser, &property); } else { parse_trace( parser, "unexpected token %s in class", ivy_token_to_string(peek_token(parser))); result = false; } if (!result) { break; } if (msg) { fx_queue_push_back(&class->n_msg_handlers, &msg->n_entry); } else if (property) { fx_queue_push_back( &class->n_msg_handlers, &property->n_entry); } } *out = generic_node(class); return result; }