diff --git a/lang/src/compiler/entry/compiler_stageb.hako b/lang/src/compiler/entry/compiler_stageb.hako index 64de6256..54e2f1e8 100644 --- a/lang/src/compiler/entry/compiler_stageb.hako +++ b/lang/src/compiler/entry/compiler_stageb.hako @@ -9,7 +9,7 @@ // - Toggles around: HAKO_MIR_BUILDER_{INTERNAL,REGISTRY,DELEGATE}(MirBuilder側)。ここでは JSON 生成に専念する。 // - Recommended: Dev/CI は wrapper(tools/hakorune_emit_mir.sh)経由で Program→MIR を行い、失敗時は Gate‑C に自動委譲する。 -using sh_core as StringHelpers // Required: ParserStringUtilsBox depends on this (using chain unresolved) +// Note: sh_core must be in [modules] for parser dependencies to resolve using "hako.compiler.entry.bundle_resolver" as BundleResolver using lang.compiler.parser.box as ParserBox using lang.compiler.entry.func_scanner as FuncScannerBox @@ -46,9 +46,17 @@ static box Main { // local externs_json = p.get_externs_json() // 4) If wrapped in `box Main { method main() { ... } }` or `static box Main { method main() { ... } }`, - // extract the method body text + // extract the method body text. Allow disabling via env HAKO_STAGEB_BODY_EXTRACT=0. local body_src = null { + local do_extract = 1 + { + local ex = env.get("HAKO_STAGEB_BODY_EXTRACT") + if ex != null && ("" + ex) == "0" { do_extract = 0 } + } + if do_extract == 0 { + body_src = src + } else { // naive search for "method main" → '(' → ')' → '{' ... balanced '}' local s = src // naive substring search for "method main"; fallback to "main(" inside box Main @@ -194,10 +202,11 @@ static box Main { } } } + } + // Fallback: if extraction failed or produced empty, use full src + if body_src == null || ("" + body_src).length() == 0 { body_src = src } } - if body_src == null { body_src = src } - // 4.7) Strip comments from body_src to avoid stray tokens in Program(JSON) { local s = body_src diff --git a/lang/src/compiler/pipeline_v2/using_resolver_box.hako b/lang/src/compiler/pipeline_v2/using_resolver_box.hako index de069d9f..8c073616 100644 --- a/lang/src/compiler/pipeline_v2/using_resolver_box.hako +++ b/lang/src/compiler/pipeline_v2/using_resolver_box.hako @@ -10,17 +10,55 @@ using lang.compiler.pipeline_v2.regex_flow as RegexFlow static box UsingResolverBox { // Lightweight state as String: holds modules_json only + // Format: modules_json (raw JSON string from nyash.toml [modules]) state_new() { return "" } + // Load using.aliases JSON (not used in minimal impl, kept for compatibility) load_usings_json(state, usings_json) { return state } - load_modules_json(state, mod_json) { return ("" + mod_json) } + // Load modules JSON from nyash.toml [modules] section + // Expected format: {"module.path": "file.hako", ...} + load_modules_json(state, mod_json) { + if mod_json == null { return "" } + return ("" + mod_json) + } - resolve_path_alias(state, alias) { return null } + // Resolve path from alias using modules map + // Returns the file path if alias matches a module key, null otherwise + resolve_path_alias(state, alias) { + if alias == null { return null } + local s = "" + state + if s.length() == 0 { return null } + // Search for exact match in modules JSON + // Format: "alias":"path" + local search_key = "\"" + alias + "\"" + local pos = RegexFlow.find_from(s, search_key, 0) + if pos < 0 { return null } + + // Find the colon after the key + local colon_pos = RegexFlow.find_from(s, ":", pos) + if colon_pos < 0 { return null } + + // Find the opening quote of the value + local val_start = RegexFlow.find_from(s, "\"", colon_pos) + if val_start < 0 { return null } + + // Find the closing quote of the value + local val_end = RegexFlow.find_from(s, "\"", val_start + 1) + if val_end < 0 { return null } + + // Extract and return the path value + return s.substring(val_start + 1, val_end) + } + + // Resolve namespace alias by tail matching + // Returns the full module path if unique match found, null if ambiguous or not found resolve_namespace_alias(state, alias) { if alias == null { return null } local s = "" + state + if s.length() == 0 { return null } + // Prefer unique tail match by last segment local i = 0 local start = 0 @@ -57,13 +95,31 @@ static box UsingResolverBox { return found } - resolve_module_path_from_alias(state, alias) { return null } + // Resolve module path from alias (delegates to resolve_path_alias) + resolve_module_path_from_alias(state, alias) { + return me.resolve_path_alias(state, alias) + } - guess_namespace_from_tail(state, tail) { return me.resolve_namespace_alias(state, tail) } + // Guess namespace from tail segment (delegates to resolve_namespace_alias) + guess_namespace_from_tail(state, tail) { + return me.resolve_namespace_alias(state, tail) + } + // No-op for minimal implementation upgrade_aliases(state) { return 0 } - to_context_json(state) { return "{}" } + // Convert state to context JSON for ParserBox + // Format: {"modules": {...}, "aliases": {}} + to_context_json(state) { + if state == null { return "{\"modules\":{},\"aliases\":{}}" } + local s = "" + state + if s.length() == 0 { return "{\"modules\":{},\"aliases\":{}}" } + + // Wrap modules_json in context structure + return "{\"modules\":" + s + ",\"aliases\":{}}" + } + + // Helper to convert map to JSON (minimal stub) map_to_json(m) { return "{}" } } diff --git a/lang/src/llvm_ir/boxes/aot_prep/helpers/common.hako b/lang/src/llvm_ir/boxes/aot_prep/helpers/common.hako index 9ff2eae9..18ed03a3 100644 --- a/lang/src/llvm_ir/boxes/aot_prep/helpers/common.hako +++ b/lang/src/llvm_ir/boxes/aot_prep/helpers/common.hako @@ -9,6 +9,31 @@ static box AotPrepHelpers { return me._linear_expr(json, vid, 0) } + // Public: fold integer binops on constants (returns string i64 or "") + evaluate_binop_constant(operation, lhs_val, rhs_val) { + if operation == "" { return "" } + local li = StringHelpers.to_i64(lhs_val) + local ri = StringHelpers.to_i64(rhs_val) + if li == null || ri == null { return "" } + local res = null + if operation == "add" || operation == "+" { + res = li + ri + } else if operation == "sub" || operation == "-" { + res = li - ri + } else if operation == "mul" || operation == "*" { + res = li * ri + } else if operation == "sdiv" || operation == "div" || operation == "/" { + if ri == 0 { return "" } + res = li / ri + } else if operation == "srem" || operation == "rem" || operation == "%" { + if ri == 0 { return "" } + res = li % ri + } else { + return "" + } + return StringHelpers.int_to_str(res) + } + // Public: Is `vid` defined by a const (following copy chains) is_const_vid(json, vid) { if vid == "" { return false } @@ -111,4 +136,3 @@ static box AotPrepHelpers { return -1 } } - diff --git a/lang/src/llvm_ir/boxes/aot_prep/passes/loop_hoist.hako b/lang/src/llvm_ir/boxes/aot_prep/passes/loop_hoist.hako index 0463be79..9fc0fc66 100644 --- a/lang/src/llvm_ir/boxes/aot_prep/passes/loop_hoist.hako +++ b/lang/src/llvm_ir/boxes/aot_prep/passes/loop_hoist.hako @@ -1,7 +1,7 @@ // AotPrepLoopHoistBox — Hoist loop-local consts (binop/div/rem/compare) to block head using selfhost.shared.json.utils.json_frag as JsonFragBox using selfhost.shared.common.string_helpers as StringHelpers -using selfhost.llvm.ir.aot_prep as AotPrepBox // for _evaluate_binop_constant +using selfhost.llvm.ir.aot_prep.helpers.common as AotPrepHelpers // for evaluate_binop_constant static box AotPrepLoopHoistBox { run(json) { @@ -75,7 +75,7 @@ static box AotPrepLoopHoistBox { local lhs_val = const_vals.contains(lhs) ? const_vals[lhs] : "" local rhs_val = const_vals.contains(rhs) ? const_vals[rhs] : "" if lhs_val == "" || rhs_val == "" { continue } - local computed = AotPrepBox._evaluate_binop_constant(operation, lhs_val, rhs_val) + local computed = AotPrepHelpers.evaluate_binop_constant(operation, lhs_val, rhs_val) if computed == "" { continue } const_defs[dst] = inst const_vals[dst] = computed diff --git a/nyash.toml b/nyash.toml index 5087ef2c..63349f08 100644 --- a/nyash.toml +++ b/nyash.toml @@ -50,6 +50,8 @@ path = "lang/src/shared/json/stringify.hako" path = "lang/src/shared/common/string_helpers.hako" [modules] +# Core shared helpers (needed by parser/compiler in Stage-B) +"sh_core" = "lang/src/shared/common/string_helpers.hako" # Map logical namespaces to Nyash source paths (consumed by runner) # NOTE (Phase‑20.33): legacy `apps/selfhost/*` modules have been retired. # If you still rely on `selfhost.*` keys, migrate to the `lang.*`/`hakorune.*` diff --git a/tools/test_stageb_using.sh b/tools/test_stageb_using.sh new file mode 100644 index 00000000..ea18e313 --- /dev/null +++ b/tools/test_stageb_using.sh @@ -0,0 +1,81 @@ +#!/usr/bin/env bash +# Test Stage-B using resolution functionality +set -euo pipefail + +SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" +ROOT="$(cd "$SCRIPT_DIR/.." && pwd)" + +# Build if needed +if [ ! -f "$ROOT/target/release/nyash" ]; then + echo "Building nyash..." + cd "$ROOT" + cargo build --release +fi + +NYASH_BIN="$ROOT/target/release/nyash" + +# Test 1: Basic using resolution (currently should work without HAKO_STAGEB_USING_RESOLVE) +echo "=== Test 1: Basic Stage-B compilation (existing path) ===" +cat > /tmp/test_stageb_basic.hako <<'EOF' +local x = 42 +return x +EOF + +if "$NYASH_BIN" --backend vm "$ROOT/lang/src/compiler/entry/compiler_stageb.hako" -- --source "local x = 42; return x" 2>&1 | grep -q '"kind":"Program"'; then + echo "✓ Test 1 PASSED: Basic Stage-B works" +else + echo "✗ Test 1 FAILED: Basic Stage-B broken" + exit 1 +fi + +# Test 2: Using resolution enabled (opt-in) +echo "" +echo "=== Test 2: Using resolution with opt-in ===" +cat > /tmp/test_stageb_using.hako <<'EOF' +using selfhost.shared.common.string_helpers as StringHelpers +local result = StringHelpers.skip_ws(" hello") +return result +EOF + +# First test: with using resolution disabled (default) +echo "Testing with using resolution DISABLED (should fail or ignore)..." +if HAKO_STAGEB_USING_RESOLVE=0 "$NYASH_BIN" --backend vm "$ROOT/lang/src/compiler/entry/compiler_stageb.hako" -- --source "$(cat /tmp/test_stageb_using.hako)" 2>&1 | grep -q '"kind":"Program"'; then + echo "✓ Test 2a PASSED: Stage-B works without using resolution" +else + echo "Note: Stage-B without using resolution may fail (expected for using statements)" +fi + +# Second test: with using resolution enabled +echo "" +echo "Testing with using resolution ENABLED..." + +# Create modules JSON from nyash.toml +MODULES_JSON='{"selfhost.shared.common.string_helpers":"lang/src/shared/common/string_helpers.hako"}' + +if HAKO_STAGEB_USING_RESOLVE=1 HAKO_MODULES_JSON="$MODULES_JSON" "$NYASH_BIN" --backend vm "$ROOT/lang/src/compiler/entry/compiler_stageb.hako" -- --source "$(cat /tmp/test_stageb_using.hako)" 2>&1 | grep -q '"kind":"Program"'; then + echo "✓ Test 2b PASSED: Stage-B with using resolution works" +else + echo "✗ Test 2b FAILED: Stage-B with using resolution broken" + echo "Debugging output:" + HAKO_STAGEB_USING_RESOLVE=1 HAKO_MODULES_JSON="$MODULES_JSON" "$NYASH_BIN" --backend vm "$ROOT/lang/src/compiler/entry/compiler_stageb.hako" -- --source "$(cat /tmp/test_stageb_using.hako)" 2>&1 | tail -20 +fi + +# Test 3: Verify existing emit_mir.sh path still works +echo "" +echo "=== Test 3: Verify hakorune_emit_mir.sh compatibility ===" +cat > /tmp/test_emit_simple.hako <<'EOF' +local i = 0 +loop(i < 10) { + i = i + 1 +} +return i +EOF + +if bash "$ROOT/tools/hakorune_emit_mir.sh" /tmp/test_emit_simple.hako /tmp/test_emit_output.json 2>&1 | grep -q "OK"; then + echo "✓ Test 3 PASSED: emit_mir.sh still works" +else + echo "✗ Test 3 FAILED: emit_mir.sh broken" +fi + +echo "" +echo "=== All tests completed ==="