Add NODE_BEGIN for explicit begin...end blocks

This commit introduces NODE_BEGIN as a distinct AST node type for
explicit begin...end blocks, separate from NODE_STMTS which represents
general statement sequences. This distinction will be essential for
implementing CRuby-compatible begin...end while/until constructs.

Key changes:
- Added NODE_BEGIN enum in node.h
- Added new_begin() function in parse.y using optimized cons() structure
- Modified begin...end grammar rule to generate NODE_BEGIN nodes
- Added NODE_BEGIN codegen support in codegen.c
- Added NODE_BEGIN to parser dump functionality

NODE_BEGIN uses a simpler cons() structure instead of list2() for
better memory efficiency, as it only contains a single body node.

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
Yukihiro "Matz" Matsumoto
2025-07-05 21:06:29 +09:00
parent dc0a5f433f
commit ebc10f3c57
4 changed files with 1162 additions and 1074 deletions
+58 -1
View File
@@ -3140,6 +3140,7 @@ gen_literal_array(codegen_scope *s, node *tree, mrb_bool sym, int val)
break;
/* fall through */
case NODE_STMTS:
case NODE_BEGIN:
codegen(s, tree->car, VAL);
j++;
break;
@@ -3192,7 +3193,7 @@ gen_literal_array(codegen_scope *s, node *tree, mrb_bool sym, int val)
else {
while (tree) {
switch (nint(tree->car->car)) {
case NODE_STMTS: case NODE_BLOCK:
case NODE_STMTS: case NODE_BEGIN: case NODE_BLOCK:
codegen(s, tree->car, NOVAL);
}
tree = tree->cdr;
@@ -3355,6 +3356,11 @@ codegen(codegen_scope *s, node *tree, int val)
}
break;
case NODE_BEGIN:
/* NODE_BEGIN contains a single body node directly in cdr */
codegen(s, tree, val);
break;
case NODE_RESCUE:
{
int noexc;
@@ -3647,6 +3653,57 @@ codegen(codegen_scope *s, node *tree, int val)
}
break;
case NODE_WHILE_MOD:
case NODE_UNTIL_MOD:
{
/* Post-tested loops: execute body first, then check condition */
if (false_always(tree->car)) {
if (nt == NODE_WHILE_MOD) {
/* begin...end while false - execute once then exit */
codegen(s, tree->cdr, val);
if (val) push();
goto exit;
}
}
else if (true_always(tree->car)) {
if (nt == NODE_UNTIL_MOD) {
/* begin...end until true - execute once then exit */
codegen(s, tree->cdr, val);
if (val) push();
goto exit;
}
}
/* Post-tested loops: execute body first, then condition */
struct loopinfo *lp = loop_push(s, LOOP_NORMAL);
if (!val) lp->reg = -1;
/* Jump to body start on first iteration */
uint32_t body_start = genjmp2_0(s, OP_JMP, cursp(), NOVAL);
/* pc0 is next target - condition check */
lp->pc0 = new_label(s);
codegen(s, tree->car, VAL);
pop();
if (nt == NODE_WHILE_MOD) {
pos = genjmp2_0(s, OP_JMPNOT, cursp(), NOVAL);
}
else {
pos = genjmp2_0(s, OP_JMPIF, cursp(), NOVAL);
}
/* pc1 is redo target - body start */
lp->pc1 = new_label(s);
dispatch(s, body_start); /* patch initial jump to come here */
codegen(s, tree->cdr, NOVAL);
genjmp(s, OP_JMP, lp->pc0);
dispatch(s, pos);
loop_pop(s, val);
}
break;
case NODE_FOR:
for_body(s, tree);
if (val) push();
+1
View File
@@ -23,6 +23,7 @@ enum node_type {
NODE_REDO,
NODE_RETRY,
NODE_STMTS,
NODE_BEGIN,
NODE_RESCUE,
NODE_ENSURE,
NODE_AND,
+14 -1
View File
@@ -394,6 +394,13 @@ new_stmts(parser_state *p, node *body)
return cons((node*)NODE_STMTS, 0);
}
/* (:begin body) */
static node*
new_begin(parser_state *p, node *body)
{
return cons((node*)NODE_BEGIN, body);
}
#define newline_node(n) (n)
/* (:rescue body rescue else) */
@@ -2740,7 +2747,7 @@ primary : literal
keyword_end
{
p->cmdarg_stack = $<stack>2;
$$ = $3;
$$ = new_begin(p, $3);
}
| tLPAREN_ARG
{
@@ -4314,6 +4321,7 @@ void_expr_error(parser_state *p, node *n)
}
break;
case NODE_STMTS:
case NODE_BEGIN:
if (n->cdr) {
while (n->cdr) {
n = n->cdr;
@@ -7138,6 +7146,11 @@ mrb_parser_dump(mrb_state *mrb, node *tree, int offset)
dump_recur(mrb, tree, offset+1);
break;
case NODE_BEGIN:
printf("NODE_BEGIN:\n");
mrb_parser_dump(mrb, tree, offset+1);
break;
case NODE_RESCUE:
printf("NODE_RESCUE:\n");
if (tree->car) {
File diff suppressed because it is too large Load Diff