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:
@ -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
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -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
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -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
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -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
|
||||||
|
}
|
||||||
|
}
|
||||||
24
apps/tests/phase137_loop_true_break_once_return_add_min.hako
Normal file
24
apps/tests/phase137_loop_true_break_once_return_add_min.hako
Normal 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
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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
|
||||||
@ -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
|
||||||
@ -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
|
||||||
@ -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
|
||||||
@ -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
|
||||||
@ -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
|
||||||
@ -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
|
||||||
@ -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
|
||||||
@ -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
|
||||||
@ -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
|
||||||
Reference in New Issue
Block a user