lang: replace stack-based parser with a recursive parser

This commit is contained in:
2026-04-06 18:25:43 +01:00
parent 3a549aa6df
commit b904813cef
77 changed files with 2984 additions and 6499 deletions
+365
View File
@@ -0,0 +1,365 @@
#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;
}