feat(control_tree): Phase 136/137 - return literal and add expression (dev-only)

Phase 136 P0: Return literal (Integer) support
- Extend loop(true) break-once to support `return 7`
- Fixtures: phase136_loop_true_break_once_return_literal_min.hako (exit code 7)
- VM/LLVM EXE parity achieved

Phase 137 P0: Return add expression support
- Extend to support `return x + 2` and `return 5 + 3`
- LHS: Variable or Integer literal
- RHS: Integer literal only
- Fixtures:
  - phase137_loop_true_break_once_return_add_min.hako (exit code 3)
  - phase137_loop_true_break_once_return_add_const_min.hako (exit code 8)
  - phase137_loop_true_break_once_post_return_add_min.hako (exit code 13)
- VM/LLVM EXE parity achieved

Implementation:
- Added lower_return_value_to_vid() method in loop_true_break_once.rs
- Replaced extract_variable_name() with unified return value lowering
- Supported patterns: Variable, Integer literal, BinaryOp Add
- Out-of-scope patterns return Ok(None) for fallback
- SSOT documentation added (lines 29-46)

Tests: 5 fixtures + 10 smoke tests (5 VM + 5 LLVM EXE), all PASS

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
This commit is contained in:
nyash-codex
2025-12-19 00:15:32 +09:00
parent 91c7dfbf0b
commit ff09adebe0
16 changed files with 703 additions and 23 deletions

View File

@ -0,0 +1,26 @@
// Phase 136 P0: loop(true) break-once + post assign + return literal fixture
//
// Purpose: Test loop(true) { <assign>* ; break }; <assign>; return <int_literal>
// Expected exit code: 7
//
// Structure:
// x = 0 // pre-loop init
// loop(true) { // condition is Bool literal true
// x = 1 // body assignment
// break // break at end
// }
// x = x + 2 // post-loop computation (this IS executed)
// return 7 // return integer literal (not x, so result is 7 not 3)
static box Main {
main() {
local x
x = 0
loop(true) {
x = 1
break
}
x = x + 2
return 7
}
}

View File

@ -0,0 +1,24 @@
// Phase 136 P0: loop(true) break-once + return literal minimal fixture
//
// Purpose: Test loop(true) { <assign>* ; break }; return <int_literal>
// Expected exit code: 7
//
// Structure:
// x = 0 // pre-loop init
// loop(true) { // condition is Bool literal true
// x = 1 // body assignment
// break // break at end
// }
// return 7 // return integer literal (not variable)
static box Main {
main() {
local x
x = 0
loop(true) {
x = 1
break
}
return 7
}
}

View File

@ -0,0 +1,26 @@
// Phase 137 P0: loop(true) break-once with post-loop assigns + return add minimal fixture
//
// Purpose: Test post-loop assigns + return add expression
// Expected output: 13 (0 → 1 → 11 → 13)
//
// Structure:
// x = 0 // pre-loop init
// loop(true) { // condition is Bool literal true
// x = 1 // body assignment
// break // break at end
// }
// x = x + 10 // post-loop assignment
// return x + 2 // return add expression
static box Main {
main() {
local x
x = 0
loop(true) {
x = 1
break
}
x = x + 10
return x + 2
}
}

View File

@ -0,0 +1,19 @@
// Phase 137 P0: loop(true) break-once with return add (const + const) minimal fixture
//
// Purpose: Test return 5 + 3 (constant + constant)
// Expected output: 8
//
// Structure:
// loop(true) { // condition is Bool literal true
// break // break at end
// }
// return 5 + 3 // return add expression (const + const)
static box Main {
main() {
loop(true) {
break
}
return 5 + 3
}
}

View File

@ -0,0 +1,24 @@
// Phase 137 P0: loop(true) break-once with return add minimal fixture
//
// Purpose: Test return x + 2 (where x is env variable)
// Expected output: 3 (x=0, x=1 in loop, return 1+2)
//
// Structure:
// x = 0 // pre-loop init
// loop(true) { // condition is Bool literal true
// x = 1 // body assignment (to make x part of env)
// break // break at end
// }
// return x + 2 // return add expression
static box Main {
main() {
local x
x = 0
loop(true) {
x = 1
break
}
return x + 2
}
}

View File

@ -26,6 +26,22 @@
//! - Post-loop (Phase 132-P4): One assignment + return (reuse Phase 130's lower_assign_stmt) //! - Post-loop (Phase 132-P4): One assignment + return (reuse Phase 130's lower_assign_stmt)
//! - Post-loop (Phase 133-P0): Multiple assignments + return (extend Phase 132-P4) //! - Post-loop (Phase 133-P0): Multiple assignments + return (extend Phase 132-P4)
//! //!
//! ## Return Value Lowering SSOT (Phase 137+)
//!
//! - Function: `lower_return_value_to_vid()`
//! - Responsibility: Lower return values (variable, literal, expr) to ValueId
//! - Supported patterns:
//! - Variable: env lookup
//! - Integer literal: Const generation
//! - Add expr (Phase 137): x + 2 → BinOp(Add, env[x], Const(2))
//! - Fallback: Out-of-scope patterns return `Ok(None)` for legacy routing
//!
//! ### Boxification Trigger
//!
//! If 2+ files need identical return lowering logic, promote to:
//! - `normalized_shadow/common/return_value_lowerer_box.rs`
//! - Single responsibility: return value → ValueId conversion
//!
//! ## Fail-Fast //! ## Fail-Fast
//! //!
//! - Out of scope → Ok(None) (fallback to legacy) //! - Out of scope → Ok(None) (fallback to legacy)
@ -33,11 +49,11 @@
use super::env_layout::EnvLayout; use super::env_layout::EnvLayout;
use super::legacy::LegacyLowerer; use super::legacy::LegacyLowerer;
use crate::ast::{ASTNode, LiteralValue}; use crate::ast::{ASTNode, BinaryOperator, LiteralValue};
use crate::mir::control_tree::step_tree::{StepNode, StepStmtKind, StepTree}; use crate::mir::control_tree::step_tree::{AstNodeHandle, StepNode, StepStmtKind, StepTree};
use crate::mir::join_ir::lowering::carrier_info::JoinFragmentMeta; use crate::mir::join_ir::lowering::carrier_info::JoinFragmentMeta;
use crate::mir::join_ir::lowering::error_tags; use crate::mir::join_ir::lowering::error_tags;
use crate::mir::join_ir::{ConstValue, JoinFunction, JoinFuncId, JoinInst, JoinModule, MirLikeInst}; use crate::mir::join_ir::{BinOpKind, ConstValue, JoinFunction, JoinFuncId, JoinInst, JoinModule, MirLikeInst};
use crate::mir::join_ir_vm_bridge::join_func_name; use crate::mir::join_ir_vm_bridge::join_func_name;
use crate::mir::ValueId; use crate::mir::ValueId;
use std::collections::BTreeMap; use std::collections::BTreeMap;
@ -393,17 +409,21 @@ impl LoopTrueBreakOnceBuilderBox {
let StepNode::Stmt { kind: StepStmtKind::Return { value_ast }, .. } = return_node else { let StepNode::Stmt { kind: StepStmtKind::Return { value_ast }, .. } = return_node else {
return Ok(None); return Ok(None);
}; };
if let Some(ast_handle) = value_ast {
if let Some(var_name) = Self::extract_variable_name(&ast_handle.0) { // Lower post-loop return (Phase 136: support variable + integer literal)
if let Some(vid) = env_post_k.get(&var_name).copied() { if let Some(_ast_handle) = value_ast {
// Return with value
match Self::lower_return_value_to_vid(value_ast, &mut post_k_func.body, &mut next_value_id, &env_post_k)? {
Some(vid) => {
post_k_func.body.push(JoinInst::Ret { value: Some(vid) }); post_k_func.body.push(JoinInst::Ret { value: Some(vid) });
} else { }
return Ok(None); // Variable not in env None => {
} // Out of scope (unsupported return value type)
} else { return Ok(None);
return Ok(None); // Return value is not a variable }
} }
} else { } else {
// Return void
post_k_func.body.push(JoinInst::Ret { value: None }); post_k_func.body.push(JoinInst::Ret { value: None });
} }
@ -455,16 +475,16 @@ impl LoopTrueBreakOnceBuilderBox {
match &post_nodes[0] { match &post_nodes[0] {
StepNode::Stmt { kind, .. } => match kind { StepNode::Stmt { kind, .. } => match kind {
StepStmtKind::Return { value_ast } => { StepStmtKind::Return { value_ast } => {
if let Some(ast_handle) = value_ast { if let Some(_ast_handle) = value_ast {
// Return variable from env // Return with value (Phase 136: variable + integer literal)
if let Some(var_name) = Self::extract_variable_name(&ast_handle.0) { match Self::lower_return_value_to_vid(value_ast, &mut k_exit_func.body, &mut next_value_id, &env_k_exit)? {
if let Some(vid) = env_k_exit.get(&var_name).copied() { Some(vid) => {
k_exit_func.body.push(JoinInst::Ret { value: Some(vid) }); k_exit_func.body.push(JoinInst::Ret { value: Some(vid) });
} else {
return Ok(None); // Variable not in env
} }
} else { None => {
return Ok(None); // Return value is not a variable // Out of scope
return Ok(None);
}
} }
} else { } else {
// Return void // Return void
@ -606,11 +626,119 @@ impl LoopTrueBreakOnceBuilderBox {
} }
} }
/// Extract variable name from AST node /// Lower return value (variable or integer literal) to ValueId
fn extract_variable_name(ast: &ASTNode) -> Option<String> { ///
match ast { /// Phase 136 P0: Support return variable and return integer literal
ASTNode::Variable { name, .. } => Some(name.clone()), ///
_ => None, /// Returns:
/// - Ok(Some(vid)): variable from env or newly generated const
/// - Ok(None): out of scope (fallback to default behavior)
///
/// Note: Does NOT return Err - unsupported patterns return Ok(None) for fallback
fn lower_return_value_to_vid(
value_ast: &Option<AstNodeHandle>,
body: &mut Vec<JoinInst>,
next_value_id: &mut u32,
env: &BTreeMap<String, ValueId>,
) -> Result<Option<ValueId>, String> {
match value_ast {
None => {
// void return
Ok(Some(ValueId(0))) // Dummy - caller handles void separately
}
Some(ast_handle) => {
match ast_handle.0.as_ref() {
// Variable: lookup in env
ASTNode::Variable { name, .. } => {
if let Some(&vid) = env.get(name) {
Ok(Some(vid))
} else {
// Variable not in env - out of scope
Ok(None)
}
}
// Integer literal: generate Const instruction (Phase 123 pattern)
ASTNode::Literal { value: LiteralValue::Integer(i), .. } => {
let const_vid = ValueId(*next_value_id);
*next_value_id += 1;
body.push(JoinInst::Compute(MirLikeInst::Const {
dst: const_vid,
value: ConstValue::Integer(*i),
}));
Ok(Some(const_vid))
}
// Phase 137 P0: BinaryOp (x + 2) support
ASTNode::BinaryOp { operator, left, right, .. } => {
// Phase 137 contract: Add only
if !matches!(operator, BinaryOperator::Add) {
return Ok(None); // out of scope
}
// Lower LHS (Variable or Integer literal)
let lhs_vid = match left.as_ref() {
ASTNode::Variable { name, .. } => {
// Get from env
if let Some(&vid) = env.get(name) {
vid
} else {
// Variable not in env - out of scope
return Ok(None);
}
}
ASTNode::Literal { value: LiteralValue::Integer(i), .. } => {
// Generate Const for LHS integer literal
let vid = ValueId(*next_value_id);
*next_value_id += 1;
body.push(JoinInst::Compute(MirLikeInst::Const {
dst: vid,
value: ConstValue::Integer(*i),
}));
vid
}
_ => {
// Other LHS types - out of scope
return Ok(None);
}
};
// Lower RHS (Integer literal only)
let rhs_vid = match right.as_ref() {
ASTNode::Literal { value: LiteralValue::Integer(i), .. } => {
// Generate Const for RHS integer literal
let vid = ValueId(*next_value_id);
*next_value_id += 1;
body.push(JoinInst::Compute(MirLikeInst::Const {
dst: vid,
value: ConstValue::Integer(*i),
}));
vid
}
_ => {
// Other RHS types - out of scope (e.g., return x + y)
return Ok(None);
}
};
// Generate BinOp Add
let result_vid = ValueId(*next_value_id);
*next_value_id += 1;
body.push(JoinInst::Compute(MirLikeInst::BinOp {
dst: result_vid,
op: BinOpKind::Add,
lhs: lhs_vid,
rhs: rhs_vid,
}));
Ok(Some(result_vid))
}
_ => {
// Other return value types - out of scope
Ok(None)
}
}
}
} }
} }
} }

View File

@ -0,0 +1,38 @@
#!/bin/bash
# Phase 136 P0: loop(true) break-once + post assign + return literal (LLVM EXE parity)
# Pattern: loop(true) { x = 1; break } → x = x + 2 → return 7 (exit code 7)
source "$(dirname "$0")/../../../lib/test_runner.sh"
source "$(dirname "$0")/../../../lib/llvm_exe_runner.sh"
export SMOKES_USE_PYVM=0
require_env || exit 2
llvm_exe_preflight_or_skip || exit 0
# Phase 136 is a dev-only Normalized shadow loop case.
# LLVM EXE emission must run with JoinIR dev/strict enabled, otherwise it will freeze.
require_joinir_dev
# Phase 136: minimal plugin set (StringBox, ConsoleBox, IntegerBox only)
STRINGBOX_SO="$NYASH_ROOT/plugins/nyash-string-plugin/libnyash_string_plugin.so"
CONSOLEBOX_SO="$NYASH_ROOT/plugins/nyash-console-plugin/libnyash_console_plugin.so"
INTEGERBOX_SO="$NYASH_ROOT/plugins/nyash-integer-plugin/libnyash_integer_plugin.so"
LLVM_REQUIRED_PLUGINS=(
"StringBox|$STRINGBOX_SO|nyash-string-plugin"
"ConsoleBox|$CONSOLEBOX_SO|nyash-console-plugin"
"IntegerBox|$INTEGERBOX_SO|nyash-integer-plugin"
)
LLVM_PLUGIN_BUILD_LOG="/tmp/phase136_loop_true_break_once_post_return_literal_plugin_build.log"
llvm_exe_ensure_plugins_or_fail || exit 1
INPUT_HAKO="$NYASH_ROOT/apps/tests/phase136_loop_true_break_once_post_return_literal_min.hako"
OUTPUT_EXE="$NYASH_ROOT/tmp/phase136_loop_true_break_once_post_return_literal_llvm_exe"
EXPECTED_EXIT_CODE=7
LLVM_BUILD_LOG="/tmp/phase136_loop_true_break_once_post_return_literal_build.log"
if llvm_exe_build_and_run_expect_exit_code; then
test_pass "phase136_loop_true_break_once_post_return_literal_llvm_exe: exit code matches expected (7)"
else
exit 1
fi

View File

@ -0,0 +1,54 @@
#!/bin/bash
# Phase 136 P0: loop(true) break-once + post assign + return literal (VM)
#
# Verifies that loop(true) { <assign>* ; break }; <assign>; return <int_literal> works:
# - x = 0 → loop(true) { x = 1; break } → x = x + 2 → return 7 → exit code 7
# - Dev-only: NYASH_JOINIR_DEV=1 HAKO_JOINIR_STRICT=1
source "$(dirname "$0")/../../../lib/test_runner.sh"
export SMOKES_USE_PYVM=0
require_env || exit 2
# Phase 136 is a dev-only Normalized shadow loop case.
require_joinir_dev
PASS_COUNT=0
FAIL_COUNT=0
RUN_TIMEOUT_SECS=${RUN_TIMEOUT_SECS:-10}
echo "[INFO] Phase 136 P0: loop(true) break-once + post assign + return literal (VM)"
# Test: phase136_loop_true_break_once_post_return_literal_min.hako
echo "[INFO] Test: phase136_loop_true_break_once_post_return_literal_min.hako"
INPUT="$NYASH_ROOT/apps/tests/phase136_loop_true_break_once_post_return_literal_min.hako"
set +e
OUTPUT=$(timeout "$RUN_TIMEOUT_SECS" env \
NYASH_DISABLE_PLUGINS=1 \
"$NYASH_BIN" --backend vm "$INPUT" 2>&1)
EXIT_CODE=$?
set -e
if [ "$EXIT_CODE" -eq 124 ]; then
echo "[FAIL] hakorune timed out (>${RUN_TIMEOUT_SECS}s)"
FAIL_COUNT=$((FAIL_COUNT + 1))
elif [ "$EXIT_CODE" -eq 7 ]; then
# Phase 136: expected output is exit code 7 (return literal)
echo "[PASS] exit code verified: 7"
PASS_COUNT=$((PASS_COUNT + 1))
else
echo "[FAIL] hakorune failed with exit code $EXIT_CODE (expected 7)"
echo "[INFO] output (tail):"
echo "$OUTPUT" | tail -n 50 || true
FAIL_COUNT=$((FAIL_COUNT + 1))
fi
echo "[INFO] PASS: $PASS_COUNT, FAIL: $FAIL_COUNT"
if [ "$FAIL_COUNT" -eq 0 ]; then
test_pass "phase136_loop_true_break_once_post_return_literal_vm: All tests passed"
exit 0
else
test_fail "phase136_loop_true_break_once_post_return_literal_vm: $FAIL_COUNT test(s) failed"
exit 1
fi

View File

@ -0,0 +1,38 @@
#!/bin/bash
# Phase 136 P0: loop(true) break-once + return literal (LLVM EXE parity)
# Pattern: loop(true) { x = 1; break } → return 7 (exit code 7)
source "$(dirname "$0")/../../../lib/test_runner.sh"
source "$(dirname "$0")/../../../lib/llvm_exe_runner.sh"
export SMOKES_USE_PYVM=0
require_env || exit 2
llvm_exe_preflight_or_skip || exit 0
# Phase 136 is a dev-only Normalized shadow loop case.
# LLVM EXE emission must run with JoinIR dev/strict enabled, otherwise it will freeze.
require_joinir_dev
# Phase 136: minimal plugin set (StringBox, ConsoleBox, IntegerBox only)
STRINGBOX_SO="$NYASH_ROOT/plugins/nyash-string-plugin/libnyash_string_plugin.so"
CONSOLEBOX_SO="$NYASH_ROOT/plugins/nyash-console-plugin/libnyash_console_plugin.so"
INTEGERBOX_SO="$NYASH_ROOT/plugins/nyash-integer-plugin/libnyash_integer_plugin.so"
LLVM_REQUIRED_PLUGINS=(
"StringBox|$STRINGBOX_SO|nyash-string-plugin"
"ConsoleBox|$CONSOLEBOX_SO|nyash-console-plugin"
"IntegerBox|$INTEGERBOX_SO|nyash-integer-plugin"
)
LLVM_PLUGIN_BUILD_LOG="/tmp/phase136_loop_true_break_once_return_literal_plugin_build.log"
llvm_exe_ensure_plugins_or_fail || exit 1
INPUT_HAKO="$NYASH_ROOT/apps/tests/phase136_loop_true_break_once_return_literal_min.hako"
OUTPUT_EXE="$NYASH_ROOT/tmp/phase136_loop_true_break_once_return_literal_llvm_exe"
EXPECTED_EXIT_CODE=7
LLVM_BUILD_LOG="/tmp/phase136_loop_true_break_once_return_literal_build.log"
if llvm_exe_build_and_run_expect_exit_code; then
test_pass "phase136_loop_true_break_once_return_literal_llvm_exe: exit code matches expected (7)"
else
exit 1
fi

View File

@ -0,0 +1,54 @@
#!/bin/bash
# Phase 136 P0: loop(true) break-once + return literal (VM)
#
# Verifies that loop(true) { <assign>* ; break }; return <int_literal> works:
# - x = 0 → loop(true) { x = 1; break } → return 7 → exit code 7
# - Dev-only: NYASH_JOINIR_DEV=1 HAKO_JOINIR_STRICT=1
source "$(dirname "$0")/../../../lib/test_runner.sh"
export SMOKES_USE_PYVM=0
require_env || exit 2
# Phase 136 is a dev-only Normalized shadow loop case.
require_joinir_dev
PASS_COUNT=0
FAIL_COUNT=0
RUN_TIMEOUT_SECS=${RUN_TIMEOUT_SECS:-10}
echo "[INFO] Phase 136 P0: loop(true) break-once + return literal (VM)"
# Test: phase136_loop_true_break_once_return_literal_min.hako
echo "[INFO] Test: phase136_loop_true_break_once_return_literal_min.hako"
INPUT="$NYASH_ROOT/apps/tests/phase136_loop_true_break_once_return_literal_min.hako"
set +e
OUTPUT=$(timeout "$RUN_TIMEOUT_SECS" env \
NYASH_DISABLE_PLUGINS=1 \
"$NYASH_BIN" --backend vm "$INPUT" 2>&1)
EXIT_CODE=$?
set -e
if [ "$EXIT_CODE" -eq 124 ]; then
echo "[FAIL] hakorune timed out (>${RUN_TIMEOUT_SECS}s)"
FAIL_COUNT=$((FAIL_COUNT + 1))
elif [ "$EXIT_CODE" -eq 7 ]; then
# Phase 136: expected output is exit code 7 (return literal)
echo "[PASS] exit code verified: 7"
PASS_COUNT=$((PASS_COUNT + 1))
else
echo "[FAIL] hakorune failed with exit code $EXIT_CODE (expected 7)"
echo "[INFO] output (tail):"
echo "$OUTPUT" | tail -n 50 || true
FAIL_COUNT=$((FAIL_COUNT + 1))
fi
echo "[INFO] PASS: $PASS_COUNT, FAIL: $FAIL_COUNT"
if [ "$FAIL_COUNT" -eq 0 ]; then
test_pass "phase136_loop_true_break_once_return_literal_vm: All tests passed"
exit 0
else
test_fail "phase136_loop_true_break_once_return_literal_vm: $FAIL_COUNT test(s) failed"
exit 1
fi

View File

@ -0,0 +1,35 @@
#!/bin/bash
# Phase 137 P0: loop(true) break-once with post-loop assigns + return add (LLVM EXE parity)
# Pattern: x = 0 → loop(true) { x = 1; break } → x = x + 10 → return x + 2 (should be 13)
source "$(dirname "$0")/../../../lib/test_runner.sh"
source "$(dirname "$0")/../../../lib/llvm_exe_runner.sh"
export SMOKES_USE_PYVM=0
require_env || exit 2
llvm_exe_preflight_or_skip || exit 0
require_joinir_dev
# Phase 137: minimal plugin set (StringBox, ConsoleBox, IntegerBox only)
STRINGBOX_SO="$NYASH_ROOT/plugins/nyash-string-plugin/libnyash_string_plugin.so"
CONSOLEBOX_SO="$NYASH_ROOT/plugins/nyash-console-plugin/libnyash_console_plugin.so"
INTEGERBOX_SO="$NYASH_ROOT/plugins/nyash-integer-plugin/libnyash_integer_plugin.so"
LLVM_REQUIRED_PLUGINS=(
"StringBox|$STRINGBOX_SO|nyash-string-plugin"
"ConsoleBox|$CONSOLEBOX_SO|nyash-console-plugin"
"IntegerBox|$INTEGERBOX_SO|nyash-integer-plugin"
)
LLVM_PLUGIN_BUILD_LOG="/tmp/phase137_loop_true_break_once_post_return_add_plugin_build.log"
llvm_exe_ensure_plugins_or_fail || exit 1
INPUT_HAKO="$NYASH_ROOT/apps/tests/phase137_loop_true_break_once_post_return_add_min.hako"
OUTPUT_EXE="$NYASH_ROOT/tmp/phase137_loop_true_break_once_post_return_add_llvm_exe"
EXPECTED_EXIT_CODE=13
LLVM_BUILD_LOG="/tmp/phase137_loop_true_break_once_post_return_add_build.log"
if llvm_exe_build_and_run_expect_exit_code; then
test_pass "phase137_loop_true_break_once_post_return_add_llvm_exe: exit code matches expected (13)"
else
exit 1
fi

View File

@ -0,0 +1,48 @@
#!/bin/bash
# Phase 137 P0: loop(true) break-once with post-loop assigns + return add (VM)
#
# Verifies: x = 0 → loop(true) { x = 1; break } → x = x + 10 → return x + 2
# Expected: exit code 13 (0 → 1 → 11 → 13)
source "$(dirname "$0")/../../../lib/test_runner.sh"
export SMOKES_USE_PYVM=0
require_env || exit 2
require_joinir_dev
PASS_COUNT=0
FAIL_COUNT=0
RUN_TIMEOUT_SECS=${RUN_TIMEOUT_SECS:-10}
echo "[INFO] Phase 137 P0: loop(true) break-once with post assigns + return x + 2 (VM)"
INPUT="$NYASH_ROOT/apps/tests/phase137_loop_true_break_once_post_return_add_min.hako"
set +e
OUTPUT=$(timeout "$RUN_TIMEOUT_SECS" env \
NYASH_DISABLE_PLUGINS=1 \
"$NYASH_BIN" --backend vm "$INPUT" 2>&1)
EXIT_CODE=$?
set -e
if [ "$EXIT_CODE" -eq 124 ]; then
echo "[FAIL] hakorune timed out (>${RUN_TIMEOUT_SECS}s)"
FAIL_COUNT=$((FAIL_COUNT + 1))
elif [ "$EXIT_CODE" -eq 13 ]; then
echo "[PASS] exit code verified: 13"
PASS_COUNT=$((PASS_COUNT + 1))
else
echo "[FAIL] hakorune failed with exit code $EXIT_CODE (expected 13)"
echo "[INFO] output (tail):"
echo "$OUTPUT" | tail -n 50 || true
FAIL_COUNT=$((FAIL_COUNT + 1))
fi
echo "[INFO] PASS: $PASS_COUNT, FAIL: $FAIL_COUNT"
if [ "$FAIL_COUNT" -eq 0 ]; then
test_pass "phase137_loop_true_break_once_post_return_add_vm: All tests passed"
exit 0
else
test_fail "phase137_loop_true_break_once_post_return_add_vm: $FAIL_COUNT test(s) failed"
exit 1
fi

View File

@ -0,0 +1,35 @@
#!/bin/bash
# Phase 137 P0: loop(true) break-once with return add (const + const) (LLVM EXE parity)
# Pattern: loop(true) { break } → return 5 + 3 (should be 8)
source "$(dirname "$0")/../../../lib/test_runner.sh"
source "$(dirname "$0")/../../../lib/llvm_exe_runner.sh"
export SMOKES_USE_PYVM=0
require_env || exit 2
llvm_exe_preflight_or_skip || exit 0
require_joinir_dev
# Phase 137: minimal plugin set (StringBox, ConsoleBox, IntegerBox only)
STRINGBOX_SO="$NYASH_ROOT/plugins/nyash-string-plugin/libnyash_string_plugin.so"
CONSOLEBOX_SO="$NYASH_ROOT/plugins/nyash-console-plugin/libnyash_console_plugin.so"
INTEGERBOX_SO="$NYASH_ROOT/plugins/nyash-integer-plugin/libnyash_integer_plugin.so"
LLVM_REQUIRED_PLUGINS=(
"StringBox|$STRINGBOX_SO|nyash-string-plugin"
"ConsoleBox|$CONSOLEBOX_SO|nyash-console-plugin"
"IntegerBox|$INTEGERBOX_SO|nyash-integer-plugin"
)
LLVM_PLUGIN_BUILD_LOG="/tmp/phase137_loop_true_break_once_return_add_const_plugin_build.log"
llvm_exe_ensure_plugins_or_fail || exit 1
INPUT_HAKO="$NYASH_ROOT/apps/tests/phase137_loop_true_break_once_return_add_const_min.hako"
OUTPUT_EXE="$NYASH_ROOT/tmp/phase137_loop_true_break_once_return_add_const_llvm_exe"
EXPECTED_EXIT_CODE=8
LLVM_BUILD_LOG="/tmp/phase137_loop_true_break_once_return_add_const_build.log"
if llvm_exe_build_and_run_expect_exit_code; then
test_pass "phase137_loop_true_break_once_return_add_const_llvm_exe: exit code matches expected (8)"
else
exit 1
fi

View File

@ -0,0 +1,48 @@
#!/bin/bash
# Phase 137 P0: loop(true) break-once with return add (const + const) (VM)
#
# Verifies: return 5 + 3
# Expected: exit code 8
source "$(dirname "$0")/../../../lib/test_runner.sh"
export SMOKES_USE_PYVM=0
require_env || exit 2
require_joinir_dev
PASS_COUNT=0
FAIL_COUNT=0
RUN_TIMEOUT_SECS=${RUN_TIMEOUT_SECS:-10}
echo "[INFO] Phase 137 P0: loop(true) break-once with return 5 + 3 (VM)"
INPUT="$NYASH_ROOT/apps/tests/phase137_loop_true_break_once_return_add_const_min.hako"
set +e
OUTPUT=$(timeout "$RUN_TIMEOUT_SECS" env \
NYASH_DISABLE_PLUGINS=1 \
"$NYASH_BIN" --backend vm "$INPUT" 2>&1)
EXIT_CODE=$?
set -e
if [ "$EXIT_CODE" -eq 124 ]; then
echo "[FAIL] hakorune timed out (>${RUN_TIMEOUT_SECS}s)"
FAIL_COUNT=$((FAIL_COUNT + 1))
elif [ "$EXIT_CODE" -eq 8 ]; then
echo "[PASS] exit code verified: 8"
PASS_COUNT=$((PASS_COUNT + 1))
else
echo "[FAIL] hakorune failed with exit code $EXIT_CODE (expected 8)"
echo "[INFO] output (tail):"
echo "$OUTPUT" | tail -n 50 || true
FAIL_COUNT=$((FAIL_COUNT + 1))
fi
echo "[INFO] PASS: $PASS_COUNT, FAIL: $FAIL_COUNT"
if [ "$FAIL_COUNT" -eq 0 ]; then
test_pass "phase137_loop_true_break_once_return_add_const_vm: All tests passed"
exit 0
else
test_fail "phase137_loop_true_break_once_return_add_const_vm: $FAIL_COUNT test(s) failed"
exit 1
fi

View File

@ -0,0 +1,35 @@
#!/bin/bash
# Phase 137 P0: loop(true) break-once with return add (LLVM EXE parity)
# Pattern: x = 1 → loop(true) { break } → return x + 2 (should be 3)
source "$(dirname "$0")/../../../lib/test_runner.sh"
source "$(dirname "$0")/../../../lib/llvm_exe_runner.sh"
export SMOKES_USE_PYVM=0
require_env || exit 2
llvm_exe_preflight_or_skip || exit 0
require_joinir_dev
# Phase 137: minimal plugin set (StringBox, ConsoleBox, IntegerBox only)
STRINGBOX_SO="$NYASH_ROOT/plugins/nyash-string-plugin/libnyash_string_plugin.so"
CONSOLEBOX_SO="$NYASH_ROOT/plugins/nyash-console-plugin/libnyash_console_plugin.so"
INTEGERBOX_SO="$NYASH_ROOT/plugins/nyash-integer-plugin/libnyash_integer_plugin.so"
LLVM_REQUIRED_PLUGINS=(
"StringBox|$STRINGBOX_SO|nyash-string-plugin"
"ConsoleBox|$CONSOLEBOX_SO|nyash-console-plugin"
"IntegerBox|$INTEGERBOX_SO|nyash-integer-plugin"
)
LLVM_PLUGIN_BUILD_LOG="/tmp/phase137_loop_true_break_once_return_add_plugin_build.log"
llvm_exe_ensure_plugins_or_fail || exit 1
INPUT_HAKO="$NYASH_ROOT/apps/tests/phase137_loop_true_break_once_return_add_min.hako"
OUTPUT_EXE="$NYASH_ROOT/tmp/phase137_loop_true_break_once_return_add_llvm_exe"
EXPECTED_EXIT_CODE=3
LLVM_BUILD_LOG="/tmp/phase137_loop_true_break_once_return_add_build.log"
if llvm_exe_build_and_run_expect_exit_code; then
test_pass "phase137_loop_true_break_once_return_add_llvm_exe: exit code matches expected (3)"
else
exit 1
fi

View File

@ -0,0 +1,48 @@
#!/bin/bash
# Phase 137 P0: loop(true) break-once with return add (VM)
#
# Verifies: return x + 2 (where x is env variable)
# Expected: x = 1 → return x + 2 → exit code 3
source "$(dirname "$0")/../../../lib/test_runner.sh"
export SMOKES_USE_PYVM=0
require_env || exit 2
require_joinir_dev
PASS_COUNT=0
FAIL_COUNT=0
RUN_TIMEOUT_SECS=${RUN_TIMEOUT_SECS:-10}
echo "[INFO] Phase 137 P0: loop(true) break-once with return x + 2 (VM)"
INPUT="$NYASH_ROOT/apps/tests/phase137_loop_true_break_once_return_add_min.hako"
set +e
OUTPUT=$(timeout "$RUN_TIMEOUT_SECS" env \
NYASH_DISABLE_PLUGINS=1 \
"$NYASH_BIN" --backend vm "$INPUT" 2>&1)
EXIT_CODE=$?
set -e
if [ "$EXIT_CODE" -eq 124 ]; then
echo "[FAIL] hakorune timed out (>${RUN_TIMEOUT_SECS}s)"
FAIL_COUNT=$((FAIL_COUNT + 1))
elif [ "$EXIT_CODE" -eq 3 ]; then
echo "[PASS] exit code verified: 3"
PASS_COUNT=$((PASS_COUNT + 1))
else
echo "[FAIL] hakorune failed with exit code $EXIT_CODE (expected 3)"
echo "[INFO] output (tail):"
echo "$OUTPUT" | tail -n 50 || true
FAIL_COUNT=$((FAIL_COUNT + 1))
fi
echo "[INFO] PASS: $PASS_COUNT, FAIL: $FAIL_COUNT"
if [ "$FAIL_COUNT" -eq 0 ]; then
test_pass "phase137_loop_true_break_once_return_add_vm: All tests passed"
exit 0
else
test_fail "phase137_loop_true_break_once_return_add_vm: $FAIL_COUNT test(s) failed"
exit 1
fi