mirror of
https://github.com/mruby/mruby
synced 2026-06-08 16:11:16 +00:00
mruby-compiler: migrate NODE_FOR to variable-sized nodes exclusively
Remove conditional logic and consolidate NODE_FOR implementation to use variable-sized nodes exclusively. This eliminates dual code paths and completes the NODE_FOR migration. Changes: - inline new_for_var into new_for, remove p->var_nodes_enabled condition - remove new_for_var function and forward declaration - enhance gen_for_var with complete for-loop implementation from for_body - remove codegen_for and for_body functions - remove NODE_FOR case from main codegen switch (traditional cons-list path) The for-loop implementation preserves Ruby's each-based semantics with proper block scoping, argument handling, and loop control (break/next/redo) while providing better memory efficiency through variable-sized nodes. Co-authored-by: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -2290,79 +2290,6 @@ search_upvar(codegen_scope *s, mrb_sym id, int *idx)
|
||||
return -1; /* not reached */
|
||||
}
|
||||
|
||||
/*
|
||||
* Generates the bytecode for a `for` loop.
|
||||
*
|
||||
* A `for` loop in mruby, like `for x in collection`, is typically syntactic sugar for
|
||||
* `collection.each { |x| ... }`. This function implements that transformation.
|
||||
*
|
||||
* The process involves:
|
||||
* 1. Generating code for the `collection` (the receiver of the `each` call).
|
||||
* 2. Creating a new scope for the block that will be passed to `each`.
|
||||
* 3. Inside this new block scope:
|
||||
* a. Emitting `OP_ENTER` to set up the block's argument handling.
|
||||
* The argument specification `0x40000` likely indicates a block that
|
||||
* takes one mandatory argument.
|
||||
* b. Generating code to assign the iterated item (passed as a block argument)
|
||||
* to the loop variable(s) specified in `tree->car`. This can be a simple
|
||||
* assignment or a multiple assignment (destructuring).
|
||||
* c. Setting up a `LOOP_FOR` context for handling `break`/`next`/`redo` within the loop.
|
||||
* d. Generating code for the actual body of the `for` loop (`tree->cdr->cdr->car`).
|
||||
* e. Emitting `OP_RETURN` for the block's implicit return.
|
||||
* 4. Finalizing the block scope and obtaining its `mrb_irep`.
|
||||
* 5. Back in the original scope, generating `OP_BLOCK` to create a closure from the
|
||||
* block's `mrb_irep`.
|
||||
* 6. Generating `OP_SENDB` to call the `each` method (by symbol) on the collection,
|
||||
* passing the newly created block.
|
||||
*
|
||||
* @param s The current code generation scope.
|
||||
* @param tree The AST node representing the `for` loop.
|
||||
* `tree->car` contains the loop variable(s).
|
||||
* `tree->cdr->car` is the collection being iterated over.
|
||||
* `tree->cdr->cdr->car` is the body of the loop.
|
||||
*/
|
||||
static void
|
||||
for_body(codegen_scope *s, node *tree)
|
||||
{
|
||||
codegen_scope *prev = s;
|
||||
int idx;
|
||||
struct loopinfo *lp;
|
||||
node *n2;
|
||||
|
||||
/* generate receiver */
|
||||
codegen(s, tree->cdr->car, VAL);
|
||||
/* generate loop-block */
|
||||
s = scope_new(s->mrb, s, NULL);
|
||||
|
||||
push(); /* push for a block parameter */
|
||||
|
||||
/* generate loop variable */
|
||||
n2 = tree->car;
|
||||
genop_W(s, OP_ENTER, 0x40000);
|
||||
if (n2->car && !n2->car->cdr && !n2->cdr) {
|
||||
gen_assignment(s, n2->car->car, NULL, 1, NOVAL);
|
||||
}
|
||||
else {
|
||||
gen_massignment(s, n2, 1, VAL);
|
||||
}
|
||||
/* construct loop */
|
||||
lp = loop_push(s, LOOP_FOR);
|
||||
lp->pc1 = new_label(s);
|
||||
genop_0(s, OP_NOP); /* for redo */
|
||||
|
||||
/* loop body */
|
||||
codegen(s, tree->cdr->cdr->car, VAL);
|
||||
pop();
|
||||
gen_return(s, OP_RETURN, cursp());
|
||||
loop_pop(s, NOVAL);
|
||||
scope_finish(s);
|
||||
s = prev;
|
||||
genop_2(s, OP_BLOCK, cursp(), s->irep->rlen-1);
|
||||
push();pop(); /* space for a block */
|
||||
pop();
|
||||
idx = new_sym(s, MRB_SYM_2(s->mrb, each));
|
||||
genop_3(s, OP_SENDB, cursp(), idx, 0);
|
||||
}
|
||||
|
||||
/*
|
||||
* Generates the bytecode for the body of a lambda or a block.
|
||||
@@ -3980,14 +3907,6 @@ codegen_case(codegen_scope *s, node *tree, int val)
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
codegen_for(codegen_scope *s, node *tree, int val)
|
||||
{
|
||||
for_body(s, tree);
|
||||
if (val) push();
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
codegen_negate(codegen_scope *s, node *tree, int val)
|
||||
{
|
||||
@@ -5041,17 +4960,47 @@ static void
|
||||
gen_for_var(codegen_scope *s, node *varnode, int val)
|
||||
{
|
||||
struct mrb_ast_for_node *for_n = for_node(varnode);
|
||||
node *var = FOR_NODE_VAR(for_n);
|
||||
node *iterable = FOR_NODE_ITERABLE(for_n);
|
||||
node *body = FOR_NODE_BODY(for_n);
|
||||
|
||||
/* Generate iterable */
|
||||
codegen_scope *prev = s;
|
||||
int idx;
|
||||
struct loopinfo *lp;
|
||||
|
||||
/* generate receiver */
|
||||
codegen(s, iterable, VAL);
|
||||
pop(); /* Remove iterable value */
|
||||
/* generate loop-block */
|
||||
s = scope_new(s->mrb, s, NULL);
|
||||
|
||||
/* For now, use a simple iteration approach - this can be optimized later */
|
||||
if (val) {
|
||||
genop_1(s, OP_LOADNIL, cursp());
|
||||
push();
|
||||
push(); /* push for a block parameter */
|
||||
|
||||
/* generate loop variable */
|
||||
genop_W(s, OP_ENTER, 0x40000);
|
||||
if (var->car && !var->car->cdr && !var->cdr) {
|
||||
gen_assignment(s, var->car->car, NULL, 1, NOVAL);
|
||||
}
|
||||
else {
|
||||
gen_massignment(s, var, 1, VAL);
|
||||
}
|
||||
/* construct loop */
|
||||
lp = loop_push(s, LOOP_FOR);
|
||||
lp->pc1 = new_label(s);
|
||||
genop_0(s, OP_NOP); /* for redo */
|
||||
|
||||
/* loop body */
|
||||
codegen(s, body, VAL);
|
||||
pop();
|
||||
gen_return(s, OP_RETURN, cursp());
|
||||
loop_pop(s, NOVAL);
|
||||
scope_finish(s);
|
||||
s = prev;
|
||||
genop_2(s, OP_BLOCK, cursp(), s->irep->rlen-1);
|
||||
push();pop(); /* space for a block */
|
||||
pop();
|
||||
idx = new_sym(s, MRB_SYM_2(s->mrb, each));
|
||||
genop_3(s, OP_SENDB, cursp(), idx, 0);
|
||||
if (val) push();
|
||||
}
|
||||
|
||||
static void
|
||||
@@ -6375,9 +6324,6 @@ codegen(codegen_scope *s, node *tree, int val)
|
||||
codegen_block(s, tree, val);
|
||||
break;
|
||||
|
||||
case NODE_FOR:
|
||||
codegen_for(s, tree, val);
|
||||
break;
|
||||
|
||||
case NODE_CASE:
|
||||
codegen_case(s, tree, val);
|
||||
|
||||
@@ -599,7 +599,6 @@ new_alias(parser_state *p, mrb_sym a, mrb_sym b)
|
||||
static node* new_array_var(parser_state *p, node *a);
|
||||
static node* new_hash_var(parser_state *p, node *a);
|
||||
static node* new_case_var(parser_state *p, node *value, node *when_list);
|
||||
static node* new_for_var(parser_state *p, node *var, node *iterable, node *body);
|
||||
static node* new_def_var(parser_state *p, mrb_sym name, node *args, node *body);
|
||||
static node* new_class_var(parser_state *p, node *name, node *superclass, node *body);
|
||||
static node* new_module_var(parser_state *p, node *name, node *body);
|
||||
@@ -688,11 +687,18 @@ static node*
|
||||
new_for(parser_state *p, node *v, node *o, node *b)
|
||||
{
|
||||
void_expr_error(p, o);
|
||||
// If variable-sized nodes are enabled, use the specialized creation function
|
||||
if (p->var_nodes_enabled) {
|
||||
return new_for_var(p, v, o, b);
|
||||
}
|
||||
return list4((node*)NODE_FOR, v, o, b);
|
||||
|
||||
size_t total_size = sizeof(struct mrb_ast_for_node);
|
||||
enum mrb_ast_size_class class = size_to_class(total_size);
|
||||
|
||||
struct mrb_ast_for_node *n = (struct mrb_ast_for_node*)parser_alloc_var(p, total_size, class);
|
||||
|
||||
init_var_header(&n->header, p, NODE_FOR, class);
|
||||
n->var = v;
|
||||
n->iterable = o;
|
||||
n->body = b;
|
||||
|
||||
return cons_head((node*)NODE_VARIABLE, (node*)n);
|
||||
}
|
||||
|
||||
/* (:case a ((when ...) body) ((when...) body)) */
|
||||
@@ -896,22 +902,6 @@ new_hash_var(parser_state *p, node *a)
|
||||
}
|
||||
|
||||
|
||||
/* Variable-sized for node creation */
|
||||
static node*
|
||||
new_for_var(parser_state *p, node *var, node *iterable, node *body)
|
||||
{
|
||||
size_t total_size = sizeof(struct mrb_ast_for_node);
|
||||
enum mrb_ast_size_class class = size_to_class(total_size);
|
||||
|
||||
struct mrb_ast_for_node *n = (struct mrb_ast_for_node*)parser_alloc_var(p, total_size, class);
|
||||
|
||||
init_var_header(&n->header, p, NODE_FOR, class);
|
||||
n->var = var;
|
||||
n->iterable = iterable;
|
||||
n->body = body;
|
||||
|
||||
return cons_head((node*)NODE_VARIABLE, (node*)n);
|
||||
}
|
||||
|
||||
/* Variable-sized method definition node creation */
|
||||
static node*
|
||||
|
||||
+1117
-1127
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user