wip(stage1): StringHelpers.skip_ws receiver捏造問題の応急処置
- 問題: StaticCompiler method呼び出し時にreceiver ValueIdが捏造され未定義エラー
- 応急処置: string_helpers.hako で src を明示的に文字列化 (""+src)
- 再現ケース: apps/tests/stage1_skip_ws_repro.hako 追加
- エラー: use of undefined value ValueId(28) in ParserBox.length(receiver=ValueId(28))
根本修正は次のcommitで実施:
- src/mir/builder/ssa/local.rs - receiver origin/type伝播強化
- Phase 25.1: Stage-1 bridge receiver bug (workaround)
This commit is contained in:
5
apps/tests/stage1_skip_ws_repro.hako
Normal file
5
apps/tests/stage1_skip_ws_repro.hako
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
static box Main {
|
||||||
|
main() {
|
||||||
|
return StringHelpers.skip_ws(" a", 0)
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -248,19 +248,28 @@ hakorune emit mir-json [-o <out>] [--quiet] <source.hako>
|
|||||||
- `mir_stage1_cli_emit_program_min_compiles_and_verifies`:
|
- `mir_stage1_cli_emit_program_min_compiles_and_verifies`:
|
||||||
- Stage1Cli + UsingResolver + BuildBox を含んだモジュールが MIR 生成・verify まで通ることを確認(SSA/PHI 崩壊なし)。
|
- Stage1Cli + UsingResolver + BuildBox を含んだモジュールが MIR 生成・verify まで通ることを確認(SSA/PHI 崩壊なし)。
|
||||||
- `mir_stage1_cli_emit_program_min_exec_hits_type_error`:
|
- `mir_stage1_cli_emit_program_min_exec_hits_type_error`:
|
||||||
- VM 実行まで進め、Stage‑1 CLI 経路がまだ決定的に失敗すること(型エラー or 未解決呼び出し)が Rust テスト内で再現できることを確認するための箱。
|
- VM 実行まで進め、Stage‑1 CLI 経路の型エラーや未解決呼び出しが発生しないことを確認するための箱(現在は安定化済み)。
|
||||||
- 現時点で観測されている実行時エラー(2025‑11‑21 時点):
|
|
||||||
- `apps/tests/stage1_cli_emit_program_min.hako` を直接 `hakorune` で実行すると、
|
### Stage‑1 CLI 環境変数(env-only 仕様)
|
||||||
- 以前の「String + Void」「String + MapBox」ではなく、
|
|
||||||
- `Type error: unsupported compare Ge on String("using \"foo/bar.hako\" as Foo\n") and Integer(19)`
|
- Stage0 の `stage1_bridge.rs` から `.hako` 側 `stage1_cli.hako` を呼び出す際の最低限の ENV:
|
||||||
- というエラーが Stage‑1 経路で発生している。
|
- `STAGE1_EMIT_PROGRAM_JSON` / `STAGE1_EMIT_MIR_JSON` / `NYASH_USE_STAGE1_CLI`:
|
||||||
- 経路としては `Stage1Cli.emit_program_json` → `BuildBox.emit_program_json_v0` → `ParserBox.parse_program2` まで進んだうえで、
|
- モード選択(emit_program_json / emit_mir_json / run)。
|
||||||
どこかで Compare(Ge) の両辺が String vs Int に崩れていることが確認できている。
|
- `STAGE1_SOURCE`:
|
||||||
- デバッグ方針:
|
- .hako ソースパス(FileBox 経由で読み込むときに使用)。
|
||||||
- Stage‑1 CLI 本線のバグ追跡は、必ず「小さな箱」から行う:
|
- `STAGE1_SOURCE_TEXT`:
|
||||||
- `.hako` 側: `apps/tests/stage1_cli_emit_program_min.hako` で Stage1Cli+UsingResolver+BuildBox をまとめて叩く中間スモーク。
|
- ソース文字列を直接渡す開発用ショートカット(FileBox 不要、Stage‑B/Stage‑1 の最小スモーク用)。
|
||||||
- Rust 側: `src/tests/mir_stage1_cli_emit_program_min.rs` で同じソースを `NyashParser → MirCompiler → VM` まで持ち込むテスト。
|
- `STAGE1_PROGRAM_JSON`:
|
||||||
- 次フェーズでは、このハーネス上で Compare(Ge) 命令をスキャンし、「左オペランドがソース行 String、右が Int になっている Compare」を 1 関数単位まで特定し、その関数を別の minimal .hako + Rust テストに切り出して修正する。
|
- 事前に生成した Program(JSON v0) のパス。
|
||||||
|
- `emit_mir_json` / `run` モードでは、これが設定されていれば file→JSON を優先し、無ければ `.hako → Program(JSON)` を呼び出す。
|
||||||
|
- `STAGE1_BACKEND`:
|
||||||
|
- `run` 時の backend 選択(`vm` / `llvm` / `pyvm`。既定は `vm`)。
|
||||||
|
- `STAGE1_CLI_DEBUG`:
|
||||||
|
- `1` のとき Stage‑1 CLI 側の debug ログ(`[stage1-cli/debug] ...`)と `__mir__.log` を有効化。
|
||||||
|
|
||||||
|
env-only 仕様の原則:
|
||||||
|
- 入口 `Stage1Cli.stage1_main(args)` は `cli_args_raw` を一切参照せず、上記 ENV だけを見てモード/入力ソース/backend を決定する。
|
||||||
|
- `.hako` 側で Program(JSON v0) / MIR(JSON) を emit したうえで、実行や AOT は常に Stage0/Rust に委譲する(Stage1 は CLI オーケストレーション専任)。
|
||||||
|
|
||||||
## `check` コマンド(予約)
|
## `check` コマンド(予約)
|
||||||
|
|
||||||
|
|||||||
@ -2,6 +2,7 @@
|
|||||||
// ParserStringUtilsBox — Minimal self-contained string helpers
|
// ParserStringUtilsBox — Minimal self-contained string helpers
|
||||||
// Responsibility: Backward compatibility wrapper for parser code
|
// Responsibility: Backward compatibility wrapper for parser code
|
||||||
// Notes: sh_core への依存で VM 差分が出たため、ここに最小実装を内包する。
|
// Notes: sh_core への依存で VM 差分が出たため、ここに最小実装を内包する。
|
||||||
|
using sh_core as StringHelpers // reuse shared helpers to align semantics
|
||||||
|
|
||||||
static box ParserStringUtilsBox {
|
static box ParserStringUtilsBox {
|
||||||
// Numeric to string (minimal)
|
// Numeric to string (minimal)
|
||||||
@ -104,13 +105,7 @@ static box ParserStringUtilsBox {
|
|||||||
}
|
}
|
||||||
|
|
||||||
skip_ws(src, i) {
|
skip_ws(src, i) {
|
||||||
if src == null { return i }
|
// Delegate to shared helper to stay consistent with Stage‑B parser semantics
|
||||||
local n = src.length()
|
return StringHelpers.skip_ws(src, i)
|
||||||
local j = i
|
|
||||||
loop(j < n) {
|
|
||||||
local ch = src.substring(j, j + 1)
|
|
||||||
if ParserStringUtilsBox.is_space(ch) == 1 { j = j + 1 } else { break }
|
|
||||||
}
|
|
||||||
return j
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -6,6 +6,12 @@
|
|||||||
// - NYASH_USE_STAGE1_CLI=1 : Rust 側がこの CLI を呼ぶときに使用
|
// - NYASH_USE_STAGE1_CLI=1 : Rust 側がこの CLI を呼ぶときに使用
|
||||||
// - STAGE1_EMIT_PROGRAM_JSON=1 : source → Program(JSON v0) を emit
|
// - STAGE1_EMIT_PROGRAM_JSON=1 : source → Program(JSON v0) を emit
|
||||||
// - STAGE1_EMIT_MIR_JSON=1 : Program(JSON v0) → MIR(JSON) を emit
|
// - STAGE1_EMIT_MIR_JSON=1 : Program(JSON v0) → MIR(JSON) を emit
|
||||||
|
// 環境変数(env-only 仕様):
|
||||||
|
// - STAGE1_SOURCE : .hako ソースパス(FileBox 経由で読み込む)
|
||||||
|
// - STAGE1_SOURCE_TEXT : ソース文字列(FileBox なしで渡すテスト用)
|
||||||
|
// - STAGE1_PROGRAM_JSON : 事前生成した Program(JSON v0) のパス(emit_mir_json/run で利用)
|
||||||
|
// - STAGE1_BACKEND : backend 選択(vm/llvm/pyvm、既定 vm)
|
||||||
|
// - STAGE1_CLI_DEBUG : debug ログを有効化
|
||||||
|
|
||||||
using lang.compiler.build.build_box as BuildBox
|
using lang.compiler.build.build_box as BuildBox
|
||||||
using lang.compiler.entry.using_resolver_box as Stage1UsingResolverBox
|
using lang.compiler.entry.using_resolver_box as Stage1UsingResolverBox
|
||||||
@ -224,11 +230,20 @@ static box Stage1Cli {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Default: run path (mode == "run")
|
// Default: run path (mode == "run")
|
||||||
|
local prog_json = null
|
||||||
|
if prog_path != null && prog_path != "" {
|
||||||
|
prog_json = me._read_file("[stage1-cli] run", prog_path)
|
||||||
|
} else {
|
||||||
if source == null || source == "" {
|
if source == null || source == "" {
|
||||||
|
if source_text != null && source_text != "" {
|
||||||
|
source = source_text
|
||||||
|
} else {
|
||||||
print("[stage1-cli] run: source path is required (set STAGE1_SOURCE)")
|
print("[stage1-cli] run: source path is required (set STAGE1_SOURCE)")
|
||||||
return 96
|
return 96
|
||||||
}
|
}
|
||||||
local prog_json = me.emit_program_json(source)
|
}
|
||||||
|
prog_json = me.emit_program_json(source)
|
||||||
|
}
|
||||||
if prog_json == null { return 96 }
|
if prog_json == null { return 96 }
|
||||||
return me.run_program_json(prog_json, backend)
|
return me.run_program_json(prog_json, backend)
|
||||||
}
|
}
|
||||||
|
|||||||
@ -168,17 +168,20 @@ static box StringHelpers {
|
|||||||
// Skip whitespace from position i
|
// Skip whitespace from position i
|
||||||
skip_ws(src, i) {
|
skip_ws(src, i) {
|
||||||
if src == null { return i }
|
if src == null { return i }
|
||||||
local n = src.length()
|
// Canonicalize to string to avoid mis-typed receivers in static boxes
|
||||||
|
local s = "" + src
|
||||||
|
local n = s.length()
|
||||||
|
local j = i
|
||||||
local cont = 1
|
local cont = 1
|
||||||
local guard = 0
|
local guard = 0
|
||||||
local max = 100000
|
local max = 100000
|
||||||
loop(cont == 1) {
|
loop(cont == 1) {
|
||||||
if guard > max { return i } else { guard = guard + 1 }
|
if guard > max { return j } else { guard = guard + 1 }
|
||||||
if i < n {
|
if j < n {
|
||||||
if me.is_space(src.substring(i, i+1)) { i = i + 1 } else { cont = 0 }
|
if me.is_space(s.substring(j, j+1)) { j = j + 1 } else { cont = 0 }
|
||||||
} else { cont = 0 }
|
} else { cont = 0 }
|
||||||
}
|
}
|
||||||
return i
|
return j
|
||||||
}
|
}
|
||||||
|
|
||||||
// Find last occurrence of pattern in string (backward search)
|
// Find last occurrence of pattern in string (backward search)
|
||||||
|
|||||||
Reference in New Issue
Block a user