From ff09adebe0b6f64820d4696b6c92f450e2bf590d Mon Sep 17 00:00:00 2001 From: nyash-codex Date: Fri, 19 Dec 2025 00:15:32 +0900 Subject: [PATCH] feat(control_tree): Phase 136/137 - return literal and add expression (dev-only) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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 --- ...ue_break_once_post_return_literal_min.hako | 26 +++ ...op_true_break_once_return_literal_min.hako | 24 +++ ...p_true_break_once_post_return_add_min.hako | 26 +++ ..._true_break_once_return_add_const_min.hako | 19 ++ ...7_loop_true_break_once_return_add_min.hako | 24 +++ .../normalized_shadow/loop_true_break_once.rs | 174 +++++++++++++++--- ...break_once_post_return_literal_llvm_exe.sh | 38 ++++ ..._true_break_once_post_return_literal_vm.sh | 54 ++++++ ...true_break_once_return_literal_llvm_exe.sh | 38 ++++ ..._loop_true_break_once_return_literal_vm.sh | 54 ++++++ ...rue_break_once_post_return_add_llvm_exe.sh | 35 ++++ ...loop_true_break_once_post_return_add_vm.sh | 48 +++++ ...ue_break_once_return_add_const_llvm_exe.sh | 35 ++++ ...oop_true_break_once_return_add_const_vm.sh | 48 +++++ ...oop_true_break_once_return_add_llvm_exe.sh | 35 ++++ ...e137_loop_true_break_once_return_add_vm.sh | 48 +++++ 16 files changed, 703 insertions(+), 23 deletions(-) create mode 100644 apps/tests/phase136_loop_true_break_once_post_return_literal_min.hako create mode 100644 apps/tests/phase136_loop_true_break_once_return_literal_min.hako create mode 100644 apps/tests/phase137_loop_true_break_once_post_return_add_min.hako create mode 100644 apps/tests/phase137_loop_true_break_once_return_add_const_min.hako create mode 100644 apps/tests/phase137_loop_true_break_once_return_add_min.hako create mode 100644 tools/smokes/v2/profiles/integration/apps/phase136_loop_true_break_once_post_return_literal_llvm_exe.sh create mode 100644 tools/smokes/v2/profiles/integration/apps/phase136_loop_true_break_once_post_return_literal_vm.sh create mode 100644 tools/smokes/v2/profiles/integration/apps/phase136_loop_true_break_once_return_literal_llvm_exe.sh create mode 100644 tools/smokes/v2/profiles/integration/apps/phase136_loop_true_break_once_return_literal_vm.sh create mode 100644 tools/smokes/v2/profiles/integration/apps/phase137_loop_true_break_once_post_return_add_llvm_exe.sh create mode 100644 tools/smokes/v2/profiles/integration/apps/phase137_loop_true_break_once_post_return_add_vm.sh create mode 100644 tools/smokes/v2/profiles/integration/apps/phase137_loop_true_break_once_return_add_const_llvm_exe.sh create mode 100644 tools/smokes/v2/profiles/integration/apps/phase137_loop_true_break_once_return_add_const_vm.sh create mode 100644 tools/smokes/v2/profiles/integration/apps/phase137_loop_true_break_once_return_add_llvm_exe.sh create mode 100644 tools/smokes/v2/profiles/integration/apps/phase137_loop_true_break_once_return_add_vm.sh diff --git a/apps/tests/phase136_loop_true_break_once_post_return_literal_min.hako b/apps/tests/phase136_loop_true_break_once_post_return_literal_min.hako new file mode 100644 index 00000000..6b3ee6db --- /dev/null +++ b/apps/tests/phase136_loop_true_break_once_post_return_literal_min.hako @@ -0,0 +1,26 @@ +// Phase 136 P0: loop(true) break-once + post assign + return literal fixture +// +// Purpose: Test loop(true) { * ; break }; ; return +// 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 + } +} diff --git a/apps/tests/phase136_loop_true_break_once_return_literal_min.hako b/apps/tests/phase136_loop_true_break_once_return_literal_min.hako new file mode 100644 index 00000000..f714a862 --- /dev/null +++ b/apps/tests/phase136_loop_true_break_once_return_literal_min.hako @@ -0,0 +1,24 @@ +// Phase 136 P0: loop(true) break-once + return literal minimal fixture +// +// Purpose: Test loop(true) { * ; break }; return +// 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 + } +} diff --git a/apps/tests/phase137_loop_true_break_once_post_return_add_min.hako b/apps/tests/phase137_loop_true_break_once_post_return_add_min.hako new file mode 100644 index 00000000..d4f10466 --- /dev/null +++ b/apps/tests/phase137_loop_true_break_once_post_return_add_min.hako @@ -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 + } +} diff --git a/apps/tests/phase137_loop_true_break_once_return_add_const_min.hako b/apps/tests/phase137_loop_true_break_once_return_add_const_min.hako new file mode 100644 index 00000000..d0dbb5da --- /dev/null +++ b/apps/tests/phase137_loop_true_break_once_return_add_const_min.hako @@ -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 + } +} diff --git a/apps/tests/phase137_loop_true_break_once_return_add_min.hako b/apps/tests/phase137_loop_true_break_once_return_add_min.hako new file mode 100644 index 00000000..11cc9ce8 --- /dev/null +++ b/apps/tests/phase137_loop_true_break_once_return_add_min.hako @@ -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 + } +} diff --git a/src/mir/control_tree/normalized_shadow/loop_true_break_once.rs b/src/mir/control_tree/normalized_shadow/loop_true_break_once.rs index f3d11736..6d25a737 100644 --- a/src/mir/control_tree/normalized_shadow/loop_true_break_once.rs +++ b/src/mir/control_tree/normalized_shadow/loop_true_break_once.rs @@ -26,6 +26,22 @@ //! - 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) //! +//! ## 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 //! //! - Out of scope → Ok(None) (fallback to legacy) @@ -33,11 +49,11 @@ use super::env_layout::EnvLayout; use super::legacy::LegacyLowerer; -use crate::ast::{ASTNode, LiteralValue}; -use crate::mir::control_tree::step_tree::{StepNode, StepStmtKind, StepTree}; +use crate::ast::{ASTNode, BinaryOperator, LiteralValue}; +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::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::ValueId; use std::collections::BTreeMap; @@ -393,17 +409,21 @@ impl LoopTrueBreakOnceBuilderBox { let StepNode::Stmt { kind: StepStmtKind::Return { value_ast }, .. } = return_node else { return Ok(None); }; - if let Some(ast_handle) = value_ast { - if let Some(var_name) = Self::extract_variable_name(&ast_handle.0) { - if let Some(vid) = env_post_k.get(&var_name).copied() { + + // Lower post-loop return (Phase 136: support variable + integer literal) + 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) }); - } else { - return Ok(None); // Variable not in env } - } else { - return Ok(None); // Return value is not a variable + None => { + // Out of scope (unsupported return value type) + return Ok(None); + } } } else { + // Return void post_k_func.body.push(JoinInst::Ret { value: None }); } @@ -455,16 +475,16 @@ impl LoopTrueBreakOnceBuilderBox { match &post_nodes[0] { StepNode::Stmt { kind, .. } => match kind { StepStmtKind::Return { value_ast } => { - if let Some(ast_handle) = value_ast { - // Return variable from env - if let Some(var_name) = Self::extract_variable_name(&ast_handle.0) { - if let Some(vid) = env_k_exit.get(&var_name).copied() { + if let Some(_ast_handle) = value_ast { + // Return with value (Phase 136: variable + integer literal) + match Self::lower_return_value_to_vid(value_ast, &mut k_exit_func.body, &mut next_value_id, &env_k_exit)? { + Some(vid) => { k_exit_func.body.push(JoinInst::Ret { value: Some(vid) }); - } else { - return Ok(None); // Variable not in env } - } else { - return Ok(None); // Return value is not a variable + None => { + // Out of scope + return Ok(None); + } } } else { // Return void @@ -606,11 +626,119 @@ impl LoopTrueBreakOnceBuilderBox { } } - /// Extract variable name from AST node - fn extract_variable_name(ast: &ASTNode) -> Option { - match ast { - ASTNode::Variable { name, .. } => Some(name.clone()), - _ => None, + /// Lower return value (variable or integer literal) to ValueId + /// + /// Phase 136 P0: Support return variable and return integer literal + /// + /// 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, + body: &mut Vec, + next_value_id: &mut u32, + env: &BTreeMap, + ) -> Result, 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) + } + } + } } } } diff --git a/tools/smokes/v2/profiles/integration/apps/phase136_loop_true_break_once_post_return_literal_llvm_exe.sh b/tools/smokes/v2/profiles/integration/apps/phase136_loop_true_break_once_post_return_literal_llvm_exe.sh new file mode 100644 index 00000000..77065209 --- /dev/null +++ b/tools/smokes/v2/profiles/integration/apps/phase136_loop_true_break_once_post_return_literal_llvm_exe.sh @@ -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 diff --git a/tools/smokes/v2/profiles/integration/apps/phase136_loop_true_break_once_post_return_literal_vm.sh b/tools/smokes/v2/profiles/integration/apps/phase136_loop_true_break_once_post_return_literal_vm.sh new file mode 100644 index 00000000..6e52dc0a --- /dev/null +++ b/tools/smokes/v2/profiles/integration/apps/phase136_loop_true_break_once_post_return_literal_vm.sh @@ -0,0 +1,54 @@ +#!/bin/bash +# Phase 136 P0: loop(true) break-once + post assign + return literal (VM) +# +# Verifies that loop(true) { * ; break }; ; return 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 diff --git a/tools/smokes/v2/profiles/integration/apps/phase136_loop_true_break_once_return_literal_llvm_exe.sh b/tools/smokes/v2/profiles/integration/apps/phase136_loop_true_break_once_return_literal_llvm_exe.sh new file mode 100644 index 00000000..4d43a2c3 --- /dev/null +++ b/tools/smokes/v2/profiles/integration/apps/phase136_loop_true_break_once_return_literal_llvm_exe.sh @@ -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 diff --git a/tools/smokes/v2/profiles/integration/apps/phase136_loop_true_break_once_return_literal_vm.sh b/tools/smokes/v2/profiles/integration/apps/phase136_loop_true_break_once_return_literal_vm.sh new file mode 100644 index 00000000..d7804e36 --- /dev/null +++ b/tools/smokes/v2/profiles/integration/apps/phase136_loop_true_break_once_return_literal_vm.sh @@ -0,0 +1,54 @@ +#!/bin/bash +# Phase 136 P0: loop(true) break-once + return literal (VM) +# +# Verifies that loop(true) { * ; break }; return 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 diff --git a/tools/smokes/v2/profiles/integration/apps/phase137_loop_true_break_once_post_return_add_llvm_exe.sh b/tools/smokes/v2/profiles/integration/apps/phase137_loop_true_break_once_post_return_add_llvm_exe.sh new file mode 100644 index 00000000..98efc09d --- /dev/null +++ b/tools/smokes/v2/profiles/integration/apps/phase137_loop_true_break_once_post_return_add_llvm_exe.sh @@ -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 diff --git a/tools/smokes/v2/profiles/integration/apps/phase137_loop_true_break_once_post_return_add_vm.sh b/tools/smokes/v2/profiles/integration/apps/phase137_loop_true_break_once_post_return_add_vm.sh new file mode 100644 index 00000000..b69e801d --- /dev/null +++ b/tools/smokes/v2/profiles/integration/apps/phase137_loop_true_break_once_post_return_add_vm.sh @@ -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 diff --git a/tools/smokes/v2/profiles/integration/apps/phase137_loop_true_break_once_return_add_const_llvm_exe.sh b/tools/smokes/v2/profiles/integration/apps/phase137_loop_true_break_once_return_add_const_llvm_exe.sh new file mode 100644 index 00000000..bd1cea7d --- /dev/null +++ b/tools/smokes/v2/profiles/integration/apps/phase137_loop_true_break_once_return_add_const_llvm_exe.sh @@ -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 diff --git a/tools/smokes/v2/profiles/integration/apps/phase137_loop_true_break_once_return_add_const_vm.sh b/tools/smokes/v2/profiles/integration/apps/phase137_loop_true_break_once_return_add_const_vm.sh new file mode 100644 index 00000000..27f19840 --- /dev/null +++ b/tools/smokes/v2/profiles/integration/apps/phase137_loop_true_break_once_return_add_const_vm.sh @@ -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 diff --git a/tools/smokes/v2/profiles/integration/apps/phase137_loop_true_break_once_return_add_llvm_exe.sh b/tools/smokes/v2/profiles/integration/apps/phase137_loop_true_break_once_return_add_llvm_exe.sh new file mode 100644 index 00000000..5966678b --- /dev/null +++ b/tools/smokes/v2/profiles/integration/apps/phase137_loop_true_break_once_return_add_llvm_exe.sh @@ -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 diff --git a/tools/smokes/v2/profiles/integration/apps/phase137_loop_true_break_once_return_add_vm.sh b/tools/smokes/v2/profiles/integration/apps/phase137_loop_true_break_once_return_add_vm.sh new file mode 100644 index 00000000..9ebc2d30 --- /dev/null +++ b/tools/smokes/v2/profiles/integration/apps/phase137_loop_true_break_once_return_add_vm.sh @@ -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