mruby-compiler: implement variable-sized nodes for control flows

added variable-sized nodes for control flow and string/regex variants:
- control flow: break, next, redo, retry, while_mod, until_mod
- string/regex: xstr, dxstr, dregx, heredoc, dsym
- proper integration with existing codegen patterns
- maintains backward compatibility with traditional nodes
- tested with control flow and string interpolation

Co-authored-by: Claude <noreply@anthropic.com>
This commit is contained in:
Yukihiro "Matz" Matsumoto
2025-08-28 06:33:15 +09:00
parent 84c94cf18b
commit 761dd43b7d
4 changed files with 1872 additions and 1415 deletions
+141
View File
@@ -5586,6 +5586,103 @@ gen_block_var(codegen_scope *s, node *varnode, int val)
push();
}
static void
gen_break_var(codegen_scope *s, node *varnode, int val)
{
struct mrb_ast_break_node *n = (struct mrb_ast_break_node*)varnode;
codegen_break(s, n->value, val);
}
static void
gen_next_var(codegen_scope *s, node *varnode, int val)
{
struct mrb_ast_next_node *n = (struct mrb_ast_next_node*)varnode;
codegen_next(s, n->value, val);
}
static void
gen_redo_var(codegen_scope *s, node *varnode, int val)
{
codegen_redo(s, NULL, val);
}
static void
gen_retry_var(codegen_scope *s, node *varnode, int val)
{
codegen_retry(s, NULL, val);
}
static void
gen_while_mod_var(codegen_scope *s, node *varnode, int val)
{
struct mrb_ast_while_mod_node *n = (struct mrb_ast_while_mod_node*)varnode;
// Stack allocation for compatibility with existing codegen
struct mrb_ast_node temp_tree = { .car = n->condition, .cdr = n->body };
codegen_while_until(s, (node*)&temp_tree, val, NODE_WHILE_MOD);
}
static void
gen_until_mod_var(codegen_scope *s, node *varnode, int val)
{
struct mrb_ast_until_mod_node *n = (struct mrb_ast_until_mod_node*)varnode;
// Stack allocation for compatibility with existing codegen
struct mrb_ast_node temp_tree = { .car = n->condition, .cdr = n->body };
codegen_while_until(s, (node*)&temp_tree, val, NODE_UNTIL_MOD);
}
static void
gen_xstr_var(codegen_scope *s, node *varnode, int val)
{
struct mrb_ast_xstr_node *n = (struct mrb_ast_xstr_node*)varnode;
// Stack allocation for compatibility with existing codegen
mrb_int len;
const char *str = mrb_sym2name_len(s->mrb, n->name, &len);
struct mrb_ast_node temp_car = { .car = (node*)str, .cdr = int_to_node((int)len) };
struct mrb_ast_node temp_tree = { .car = (node*)&temp_car, .cdr = NULL };
codegen_xstr(s, (node*)&temp_tree, val);
}
static void
gen_dxstr_var(codegen_scope *s, node *varnode, int val)
{
struct mrb_ast_dxstr_node *n = (struct mrb_ast_dxstr_node*)varnode;
codegen_dxstr(s, n->list, val);
}
static void
gen_dregx_var(codegen_scope *s, node *varnode, int val)
{
struct mrb_ast_dregx_node *n = (struct mrb_ast_dregx_node*)varnode;
// Stack allocation for compatibility with existing codegen
struct mrb_ast_node temp_tree = { .car = n->list, .cdr = int_to_node(n->options) };
codegen_dregx(s, (node*)&temp_tree, val);
}
static void
gen_heredoc_var(codegen_scope *s, node *varnode, int val)
{
struct mrb_ast_heredoc_node *n = (struct mrb_ast_heredoc_node*)varnode;
// For heredocs, we need to handle them similar to strings
if (val) {
mrb_int len;
const char *str = mrb_sym2name_len(s->mrb, n->name, &len);
int off = new_lit_str(s, str, (int)len);
genop_2(s, OP_STRING, cursp(), off);
push();
}
}
static void
gen_dsym_var(codegen_scope *s, node *varnode, int val)
{
struct mrb_ast_dsym_node *n = (struct mrb_ast_dsym_node*)varnode;
// Generate dstr first, then convert to symbol
if (val) {
codegen_heredoc_dstr(s, n->list, VAL);
genop_1(s, OP_INTERN, cursp());
}
}
static void
gen_args_tail_var(codegen_scope *s, node *varnode, int val)
{
@@ -5770,6 +5867,50 @@ codegen_variable_node(codegen_scope *s, node *varnode, int val)
gen_args_tail_var(s, varnode, val);
return TRUE;
case NODE_BREAK:
gen_break_var(s, varnode, val);
return TRUE;
case NODE_NEXT:
gen_next_var(s, varnode, val);
return TRUE;
case NODE_REDO:
gen_redo_var(s, varnode, val);
return TRUE;
case NODE_RETRY:
gen_retry_var(s, varnode, val);
return TRUE;
case NODE_WHILE_MOD:
gen_while_mod_var(s, varnode, val);
return TRUE;
case NODE_UNTIL_MOD:
gen_until_mod_var(s, varnode, val);
return TRUE;
case NODE_XSTR:
gen_xstr_var(s, varnode, val);
return TRUE;
case NODE_DXSTR:
gen_dxstr_var(s, varnode, val);
return TRUE;
case NODE_DREGX:
gen_dregx_var(s, varnode, val);
return TRUE;
case NODE_HEREDOC:
gen_heredoc_var(s, varnode, val);
return TRUE;
case NODE_DSYM:
gen_dsym_var(s, varnode, val);
return TRUE;
default:
return FALSE; /* Not handled, fall through to main codegen */
}
+94
View File
@@ -655,4 +655,98 @@ struct mrb_ast_args_tail_node {
#define ARGS_TAIL_NODE_KWREST(n) (args_tail_node(n)->kwrest)
#define ARGS_TAIL_NODE_BLOCK(n) (args_tail_node(n)->block)
// Group 8: Control Flow Statements
struct mrb_ast_break_node {
struct mrb_ast_var_header hdr;
struct mrb_ast_node *value;
};
struct mrb_ast_next_node {
struct mrb_ast_var_header hdr;
struct mrb_ast_node *value;
};
struct mrb_ast_redo_node {
struct mrb_ast_var_header hdr;
};
struct mrb_ast_retry_node {
struct mrb_ast_var_header hdr;
};
struct mrb_ast_while_mod_node {
struct mrb_ast_var_header hdr;
struct mrb_ast_node *condition;
struct mrb_ast_node *body;
};
struct mrb_ast_until_mod_node {
struct mrb_ast_var_header hdr;
struct mrb_ast_node *condition;
struct mrb_ast_node *body;
};
#define break_node(n) ((struct mrb_ast_break_node*)(n))
#define next_node(n) ((struct mrb_ast_next_node*)(n))
#define redo_node(n) ((struct mrb_ast_redo_node*)(n))
#define retry_node(n) ((struct mrb_ast_retry_node*)(n))
#define while_mod_node(n) ((struct mrb_ast_while_mod_node*)(n))
#define until_mod_node(n) ((struct mrb_ast_until_mod_node*)(n))
#define BREAK_NODE_VALUE(n) (break_node(n)->value)
#define NEXT_NODE_VALUE(n) (next_node(n)->value)
#define WHILE_MOD_NODE_CONDITION(n) (while_mod_node(n)->condition)
#define WHILE_MOD_NODE_BODY(n) (while_mod_node(n)->body)
#define UNTIL_MOD_NODE_CONDITION(n) (until_mod_node(n)->condition)
#define UNTIL_MOD_NODE_BODY(n) (until_mod_node(n)->body)
// Group 9: String and Regex Variants
struct mrb_ast_xstr_node {
struct mrb_ast_var_header hdr;
mrb_sym name;
};
struct mrb_ast_dxstr_node {
struct mrb_ast_var_header hdr;
struct mrb_ast_node *list;
};
struct mrb_ast_dregx_node {
struct mrb_ast_var_header hdr;
struct mrb_ast_node *list;
int options;
};
struct mrb_ast_dregx_once_node {
struct mrb_ast_var_header hdr;
struct mrb_ast_node *list;
int options;
};
struct mrb_ast_heredoc_node {
struct mrb_ast_var_header hdr;
mrb_sym name;
};
struct mrb_ast_dsym_node {
struct mrb_ast_var_header hdr;
struct mrb_ast_node *list;
};
#define xstr_node(n) ((struct mrb_ast_xstr_node*)(n))
#define dxstr_node(n) ((struct mrb_ast_dxstr_node*)(n))
#define dregx_node(n) ((struct mrb_ast_dregx_node*)(n))
#define dregx_once_node(n) ((struct mrb_ast_dregx_once_node*)(n))
#define heredoc_node(n) ((struct mrb_ast_heredoc_node*)(n))
#define dsym_node(n) ((struct mrb_ast_dsym_node*)(n))
#define XSTR_NODE_NAME(n) (xstr_node(n)->name)
#define DXSTR_NODE_LIST(n) (dxstr_node(n)->list)
#define DREGX_NODE_LIST(n) (dregx_node(n)->list)
#define DREGX_NODE_OPTIONS(n) (dregx_node(n)->options)
#define DREGX_ONCE_NODE_LIST(n) (dregx_once_node(n)->list)
#define DREGX_ONCE_NODE_OPTIONS(n) (dregx_once_node(n)->options)
#define HEREDOC_NODE_NAME(n) (heredoc_node(n)->name)
#define DSYM_NODE_LIST(n) (dsym_node(n)->list)
#endif /* MRUBY_COMPILER_NODE_H */
+123 -12
View File
@@ -625,7 +625,18 @@ static node*
new_while_mod(parser_state *p, node *a, node *b)
{
void_expr_error(p, a);
return cons_head((node*)NODE_WHILE_MOD, cons(a, b));
if (!p->var_nodes_enabled) {
return cons_head((node*)NODE_WHILE_MOD, cons(a, b));
}
size_t total_size = sizeof(struct mrb_ast_while_mod_node);
enum mrb_ast_size_class class = size_to_class(total_size);
struct mrb_ast_while_mod_node *n = (struct mrb_ast_while_mod_node*)
parser_alloc_var(p, total_size, class);
init_var_header(&n->hdr, p, NODE_WHILE_MOD, class);
n->condition = a;
n->body = b;
return cons_head((node*)NODE_VARIABLE, (node*)n);
}
/* (:until_mod cond body) */
@@ -633,7 +644,18 @@ static node*
new_until_mod(parser_state *p, node *a, node *b)
{
void_expr_error(p, a);
return cons_head((node*)NODE_UNTIL_MOD, cons(a, b));
if (!p->var_nodes_enabled) {
return cons_head((node*)NODE_UNTIL_MOD, cons(a, b));
}
size_t total_size = sizeof(struct mrb_ast_until_mod_node);
enum mrb_ast_size_class class = size_to_class(total_size);
struct mrb_ast_until_mod_node *n = (struct mrb_ast_until_mod_node*)
parser_alloc_var(p, total_size, class);
init_var_header(&n->hdr, p, NODE_UNTIL_MOD, class);
n->condition = a;
n->body = b;
return cons_head((node*)NODE_VARIABLE, (node*)n);
}
/* (:for var obj body) */
@@ -1426,28 +1448,66 @@ new_return(parser_state *p, node *c)
static node*
new_break(parser_state *p, node *c)
{
return cons_head((node*)NODE_BREAK, c);
if (!p->var_nodes_enabled) {
return cons_head((node*)NODE_BREAK, c);
}
size_t total_size = sizeof(struct mrb_ast_break_node);
enum mrb_ast_size_class class = size_to_class(total_size);
struct mrb_ast_break_node *n = (struct mrb_ast_break_node*)
parser_alloc_var(p, total_size, class);
init_var_header(&n->hdr, p, NODE_BREAK, class);
n->value = c;
return cons_head((node*)NODE_VARIABLE, (node*)n);
}
/* (:next . c) */
static node*
new_next(parser_state *p, node *c)
{
return cons_head((node*)NODE_NEXT, c);
if (!p->var_nodes_enabled) {
return cons_head((node*)NODE_NEXT, c);
}
size_t total_size = sizeof(struct mrb_ast_next_node);
enum mrb_ast_size_class class = size_to_class(total_size);
struct mrb_ast_next_node *n = (struct mrb_ast_next_node*)
parser_alloc_var(p, total_size, class);
init_var_header(&n->hdr, p, NODE_NEXT, class);
n->value = c;
return cons_head((node*)NODE_VARIABLE, (node*)n);
}
/* (:redo) */
static node*
new_redo(parser_state *p)
{
return list1((node*)NODE_REDO);
if (!p->var_nodes_enabled) {
return list1((node*)NODE_REDO);
}
size_t total_size = sizeof(struct mrb_ast_redo_node);
enum mrb_ast_size_class class = size_to_class(total_size);
struct mrb_ast_redo_node *n = (struct mrb_ast_redo_node*)
parser_alloc_var(p, total_size, class);
init_var_header(&n->hdr, p, NODE_REDO, class);
return cons_head((node*)NODE_VARIABLE, (node*)n);
}
/* (:retry) */
static node*
new_retry(parser_state *p)
{
return list1((node*)NODE_RETRY);
if (!p->var_nodes_enabled) {
return list1((node*)NODE_RETRY);
}
size_t total_size = sizeof(struct mrb_ast_retry_node);
enum mrb_ast_size_class class = size_to_class(total_size);
struct mrb_ast_retry_node *n = (struct mrb_ast_retry_node*)
parser_alloc_var(p, total_size, class);
init_var_header(&n->hdr, p, NODE_RETRY, class);
return cons_head((node*)NODE_VARIABLE, (node*)n);
}
/* (:dot2 a b) */
@@ -2203,21 +2263,51 @@ concat_string(parser_state *p, node *a, node *b)
static node*
new_xstr(parser_state *p, const char *s, int len)
{
return cons_head((node*)NODE_XSTR, cons((node*)strndup(s, len), int_to_node(len)));
if (!p->var_nodes_enabled) {
return cons_head((node*)NODE_XSTR, cons((node*)strndup(s, len), int_to_node(len)));
}
size_t total_size = sizeof(struct mrb_ast_xstr_node);
enum mrb_ast_size_class class = size_to_class(total_size);
struct mrb_ast_xstr_node *n = (struct mrb_ast_xstr_node*)
parser_alloc_var(p, total_size, class);
init_var_header(&n->hdr, p, NODE_XSTR, class);
n->name = mrb_intern(p->mrb, s, len);
return cons_head((node*)NODE_VARIABLE, (node*)n);
}
/* (:xstr . a) */
static node*
new_dxstr(parser_state *p, node *a)
{
return cons_head((node*)NODE_DXSTR, a);
if (!p->var_nodes_enabled) {
return cons_head((node*)NODE_DXSTR, a);
}
size_t total_size = sizeof(struct mrb_ast_dxstr_node);
enum mrb_ast_size_class class = size_to_class(total_size);
struct mrb_ast_dxstr_node *n = (struct mrb_ast_dxstr_node*)
parser_alloc_var(p, total_size, class);
init_var_header(&n->hdr, p, NODE_DXSTR, class);
n->list = a;
return cons_head((node*)NODE_VARIABLE, (node*)n);
}
/* (:dsym . a) */
static node*
new_dsym(parser_state *p, node *a)
{
return cons_head((node*)NODE_DSYM, a);
if (!p->var_nodes_enabled) {
return cons_head((node*)NODE_DSYM, a);
}
size_t total_size = sizeof(struct mrb_ast_dsym_node);
enum mrb_ast_size_class class = size_to_class(total_size);
struct mrb_ast_dsym_node *n = (struct mrb_ast_dsym_node*)
parser_alloc_var(p, total_size, class);
init_var_header(&n->hdr, p, NODE_DSYM, class);
n->list = a;
return cons_head((node*)NODE_VARIABLE, (node*)n);
}
/* (:regx . (s . (opt . enc))) */
@@ -2234,7 +2324,18 @@ new_regx(parser_state *p, const char *p1, const char* p2, const char* p3)
static node*
new_dregx(parser_state *p, node *a, node *b)
{
return cons_head((node*)NODE_DREGX, cons(a, b));
if (!p->var_nodes_enabled) {
return cons_head((node*)NODE_DREGX, cons(a, b));
}
size_t total_size = sizeof(struct mrb_ast_dregx_node);
enum mrb_ast_size_class class = size_to_class(total_size);
struct mrb_ast_dregx_node *n = (struct mrb_ast_dregx_node*)
parser_alloc_var(p, total_size, class);
init_var_header(&n->hdr, p, NODE_DREGX, class);
n->list = a;
n->options = (int)(intptr_t)b;
return cons_head((node*)NODE_VARIABLE, (node*)n);
}
/* (:backref . n) */
@@ -2255,8 +2356,18 @@ new_nth_ref(parser_state *p, int n)
static node*
new_heredoc(parser_state *p)
{
parser_heredoc_info *inf = (parser_heredoc_info*)parser_palloc(p, sizeof(parser_heredoc_info));
return cons_head((node*)NODE_HEREDOC, (node*)inf);
if (!p->var_nodes_enabled) {
parser_heredoc_info *inf = (parser_heredoc_info*)parser_palloc(p, sizeof(parser_heredoc_info));
return cons_head((node*)NODE_HEREDOC, (node*)inf);
}
size_t total_size = sizeof(struct mrb_ast_heredoc_node);
enum mrb_ast_size_class class = size_to_class(total_size);
struct mrb_ast_heredoc_node *n = (struct mrb_ast_heredoc_node*)
parser_alloc_var(p, total_size, class);
init_var_header(&n->hdr, p, NODE_HEREDOC, class);
n->name = 0; // Will be set by heredoc processing
return cons_head((node*)NODE_VARIABLE, (node*)n);
}
static void
File diff suppressed because it is too large Load Diff