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,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