366 lines
7.6 KiB
C
366 lines
7.6 KiB
C
#include "ctx.h"
|
|
|
|
#include <ivy/lang/ast.h>
|
|
|
|
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;
|
|
}
|