diff --git a/apps/tests/phase99_json_loader_escape_trailing_backslash_min.hako b/apps/tests/phase99_json_loader_escape_trailing_backslash_min.hako new file mode 100644 index 00000000..300d4358 --- /dev/null +++ b/apps/tests/phase99_json_loader_escape_trailing_backslash_min.hako @@ -0,0 +1,38 @@ +// Phase 99: json_loader escape with trailing backslash +// Purpose: Exercise escape loop with incomplete escape sequence (trailing \). +// This fixture documents current behavior: trailing \ is silently ignored. + +static box Main { + main() { + // Input: "hello\" - string ending with backslash (incomplete escape) + local s = "\"hello\\\\" + local pos = 0 + local i = pos + + // Skip opening quote + if s.substring(i, i + 1) != "\"" { return "NG" } + i = i + 1 + + local out = "" + local n = s.length() + + loop(i < n) { + local ch = s.substring(i, i + 1) + if ch == "\"" { break } + + if ch == "\\" { + i = i + 1 + if i < n { + ch = s.substring(i, i + 1) + } + } + + out = out + ch + i = i + 1 + } + + // Current behavior: trailing backslash is included in output + print(out) + return "OK" + } +} diff --git a/tools/smokes/v2/profiles/integration/apps/phase99_escape_trailing_backslash_llvm_exe.sh b/tools/smokes/v2/profiles/integration/apps/phase99_escape_trailing_backslash_llvm_exe.sh new file mode 100644 index 00000000..8a7e3402 --- /dev/null +++ b/tools/smokes/v2/profiles/integration/apps/phase99_escape_trailing_backslash_llvm_exe.sh @@ -0,0 +1,114 @@ +#!/bin/bash +# Phase 99: escape trailing backslash fixture (LLVM EXE parity) + +source "$(dirname "$0")/../../../lib/test_runner.sh" +export SMOKES_USE_PYVM=0 +require_env || exit 2 + +# LLVM availability checks (graceful SKIP) +if ! command -v llvm-config-18 &> /dev/null; then + test_skip "llvm-config-18 not found"; exit 0 +fi + +if ! "$NYASH_BIN" --help 2>&1 | grep -q "llvm"; then + test_skip "hakorune --backend llvm not available"; exit 0 +fi + +if ! python3 -c "import llvmlite" 2>/dev/null; then + test_skip "Python llvmlite not found"; exit 0 +fi + +FILEBOX_SO="$NYASH_ROOT/plugins/nyash-filebox-plugin/libnyash_filebox_plugin.so" +MAPBOX_SO="$NYASH_ROOT/plugins/nyash-map-plugin/libnyash_map_plugin.so" + +check_plugins() { + python3 - "$FILEBOX_SO" "$MAPBOX_SO" <<'PY' +import ctypes +import os +import sys +names = ["FileBox", "MapBox"] +paths = sys.argv[1:] +failures = [] +for name, path in zip(names, paths): + if not os.path.isfile(path): + failures.append(f"[plugin/missing] {name}: {path}") + continue + try: + ctypes.CDLL(path) + except Exception as e: # noqa: BLE001 + failures.append(f"[plugin/dlopen] {name}: {path} ({e})") +if failures: + print("\n".join(failures)) + sys.exit(1) +print("OK") +PY +} + +echo "[INFO] Checking plugin artifacts (FileBox/MapBox)" +if ! CHECK_OUTPUT=$(check_plugins 2>&1); then + echo "$CHECK_OUTPUT" + echo "[INFO] Missing/broken plugin detected, running build-all (FileBox/MapBox)" + BUILD_LOG="/tmp/phase99_escape_trailing_backslash_plugin_build.log" + if ! bash "$NYASH_ROOT/tools/plugins/build-all.sh" nyash-filebox-plugin nyash-map-plugin >"$BUILD_LOG" 2>&1; then + echo "[FAIL] tools/plugins/build-all.sh failed for FileBox/MapBox" + tail -n 80 "$BUILD_LOG" + exit 1 + fi + if ! CHECK_OUTPUT=$(check_plugins 2>&1); then + echo "$CHECK_OUTPUT" + echo "[FAIL] Plugin artifacts still missing or unloadable after build-all" + tail -n 80 "$BUILD_LOG" + exit 1 + fi +fi + +mkdir -p "$NYASH_ROOT/tmp" + +INPUT_HAKO="$NYASH_ROOT/apps/tests/phase99_json_loader_escape_trailing_backslash_min.hako" +OUTPUT_EXE="$NYASH_ROOT/tmp/phase99_escape_trailing_backslash_llvm_exe" + +echo "[INFO] Building: $INPUT_HAKO → $OUTPUT_EXE" + +BUILD_LOG="/tmp/phase99_escape_trailing_backslash_build.log" +if ! env NYASH_DISABLE_PLUGINS=0 "$NYASH_ROOT/tools/build_llvm.sh" "$INPUT_HAKO" -o "$OUTPUT_EXE" 2>&1 | tee "$BUILD_LOG"; then + echo "[FAIL] build_llvm.sh failed" + tail -n 80 "$BUILD_LOG" + exit 1 +fi + +if [ ! -x "$OUTPUT_EXE" ]; then + echo "[FAIL] Executable not created or not executable: $OUTPUT_EXE" + ls -la "$OUTPUT_EXE" 2>/dev/null || echo "File does not exist" + exit 1 +fi + +echo "[INFO] Executing: $OUTPUT_EXE" + +set +e +OUTPUT=$(timeout "${RUN_TIMEOUT_SECS:-10}" env NYASH_DISABLE_PLUGINS=0 "$OUTPUT_EXE" 2>&1) +EXIT_CODE=$? +set -e + +if [ "$EXIT_CODE" -ne 0 ]; then + echo "[FAIL] Execution failed with exit code $EXIT_CODE" + echo "$OUTPUT" | tail -n 80 + exit 1 +fi + +# Current behavior: trailing backslash is preserved in output +CLEAN=$(printf "%s\n" "$OUTPUT" | grep -v "^\[" | grep -v "^$" | head -n 1 | tr -d '\r') +EXPECTED="hello\\" + +echo "[INFO] CLEAN output:" +echo "$CLEAN" + +if [ "$CLEAN" = "$EXPECTED" ]; then + test_pass "phase99_escape_trailing_backslash_llvm_exe: output matches expected (hello\\)" +else + echo "[FAIL] Output mismatch" + echo "[INFO] Raw output (tail):" + echo "$OUTPUT" | tail -n 80 + echo "[INFO] Expected:" + printf "%s\n" "$EXPECTED" + exit 1 +fi diff --git a/tools/smokes/v2/profiles/integration/apps/phase99_escape_trailing_backslash_vm.sh b/tools/smokes/v2/profiles/integration/apps/phase99_escape_trailing_backslash_vm.sh new file mode 100644 index 00000000..efaccc36 --- /dev/null +++ b/tools/smokes/v2/profiles/integration/apps/phase99_escape_trailing_backslash_vm.sh @@ -0,0 +1,57 @@ +#!/bin/bash +# Phase 99: json_loader escape with trailing backslash (VM) +# +# Verifies current behavior: trailing backslash is included in output (best-effort). + +source "$(dirname "$0")/../../../lib/test_runner.sh" +export SMOKES_USE_PYVM=0 +require_env || exit 2 + +PASS_COUNT=0 +FAIL_COUNT=0 +RUN_TIMEOUT_SECS=${RUN_TIMEOUT_SECS:-10} + +INPUT="$NYASH_ROOT/apps/tests/phase99_json_loader_escape_trailing_backslash_min.hako" + +echo "[INFO] Phase 99: escape trailing backslash (VM) - $INPUT" + +set +e +OUTPUT=$(timeout "$RUN_TIMEOUT_SECS" env \ + NYASH_DISABLE_PLUGINS=1 \ + HAKO_JOINIR_STRICT=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 0 ]; then + # Current behavior: trailing backslash is included in output + EXPECTED="hello\\" + CLEAN=$(printf "%s\n" "$OUTPUT" | grep -v "^\[" | grep -v "^$" | head -n 1 | tr -d '\r') + if [ "$CLEAN" = "$EXPECTED" ]; then + echo "[PASS] Output verified: hello\\ (trailing backslash preserved)" + PASS_COUNT=$((PASS_COUNT + 1)) + else + echo "[FAIL] Unexpected output (expected: hello\\)" + echo "[INFO] output (tail):" + echo "$OUTPUT" | tail -n 50 || true + FAIL_COUNT=$((FAIL_COUNT + 1)) + fi +else + echo "[FAIL] hakorune failed with exit code $EXIT_CODE" + 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 "phase99_escape_trailing_backslash_vm: Best-effort behavior verified" + exit 0 +else + test_fail "phase99_escape_trailing_backslash_vm: $FAIL_COUNT test(s) failed" + exit 1 +fi