diff --git a/CURRENT_TASK.md b/CURRENT_TASK.md index 5410c769..d13343f3 100644 --- a/CURRENT_TASK.md +++ b/CURRENT_TASK.md @@ -37,6 +37,12 @@ Update — 2025-09-28 (json_query_vm PASS・最終ガード適用) - テスト: json_query_vm の SKIP を解除して PASS を確認。 - quick: 引き続き 64/64 PASS、integration: 17/17 PASS。 +Update — 2025-10-31 (Phase 20.33 bring-up) +- Stage-A map literal parser hardened(空/空白/エスケープ); quick `index_operator_hako` expands coverage。 +- Stage-B entry separated (`lang/src/compiler/entry/compiler_stageb.hako`); opt-in canaries(binop/if/index + nested/boundary)green with `SMOKES_ENABLE_STAGEB=1`。 +- `nyash.toml` modules updated to expose lang/compiler/shared/vm namespaces for resolver。 +- quick profile: 72/72 PASS(FileBox 未展開時は SKIP ハンドリング)。 + Update — 2025-09-28 (P1 — Const統一拡大 + メタ伝播の適用) - Const 発行の統一(builder 側残存) - `build_literal` と core13-pure の型名 Const を ConstantEmissionBox に統一済。残存直書きは掃除済み(rewrite系は NameConstBox 使用)。 diff --git a/docs/private/roadmap/phases/phase-20.33/CHECKLIST.md b/docs/private/roadmap/phases/phase-20.33/CHECKLIST.md new file mode 100644 index 00000000..85cbb71e --- /dev/null +++ b/docs/private/roadmap/phases/phase-20.33/CHECKLIST.md @@ -0,0 +1,9 @@ +# CHECKLIST — Phase 20.33 (Stage‑B) + +- [x] Stage‑B 専用エントリ(`lang/src/compiler/entry/compiler_stageb.hako`)を追加。`--prefer-cfg` を受理。 +- [x] `ParserBox.parse_program2` → AST JSON を取得(Quiet: 1行)。Stage‑A map parser は空/空白/エスケープに対応。 +- [x] pipeline_v2 FlowEntry.emit_v0_from_ast で v0 を出力(prefer_cfg=1 既定)。 +- [x] selfhost canary(return/binop/if/index)を Stage‑B でも PASS(opt‑in `SMOKES_ENABLE_STAGEB=1` で緑)。 +- [ ] v1→v0 降格(MirJsonV1Adapter)経路を整備(必要箇所のみ)。 +- [x] tools/smokes/v2/profiles/quick/core/selfhost_* を追加(opt‑in)。配列ネスト/境界ケースを含む。 +- [x] ドキュメント更新(README/PLAN/CHECKLIST)。 diff --git a/docs/private/roadmap/phases/phase-20.33/PLAN.md b/docs/private/roadmap/phases/phase-20.33/PLAN.md new file mode 100644 index 00000000..7714bff4 --- /dev/null +++ b/docs/private/roadmap/phases/phase-20.33/PLAN.md @@ -0,0 +1,33 @@ +# PLAN — Phase 20.33 (Hakorune コンパイラ Stage‑B) + +## ゴール(概要) +- Hako 製コンパイラを Stage‑B に引き上げ、Ny → JSON v0 を実用レベルで安定出力。 +- 入口を `ParserBox → pipeline_v2(FlowEntry)` に統一し、1 行 JSON(Quiet)契約を維持。 +- 代表構文(return/binop/compare/if/index/new/boxcall/externcall)を優先対応。 + +## フェーズ分割 +1) 入口統合(Stage‑B ルートの opt‑in 実装) + - `lang/src/compiler/entry/compiler_stageb.hako` を追加(既定: Stage‑B emit 専用)。 + - `ParserBox.parse_program2(src)` → `FlowEntryBox.emit_v0_from_ast(ast_json, prefer_cfg)` → print 1 行 JSON。 + - フラグ: `--prefer-cfg ` で pipeline 選好を切替できるようにする。 +2) 代表構文の緑化 + - binop/compare/if/index/new/boxcall/externcall を pipeline_v2 で受理できるよう確認。 + - Stage‑A canary と同等の selfhost canary を Stage‑B でも PASS。 + - Stage‑A 側の map parser(空/空白/エスケープ)を強化し、Stage‑B 引受け前の入力を安定化。 +3) v1→v0 アダプタの橋渡し + - MirCall(v1) を出す経路は `MirJsonV1Adapter` で v0 に降格。 + - JSON 形状の差分を最小化し、Runner/VM と整合を取る。 +4) スモーク整備(opt‑in) + - `tools/smokes/v2/profiles/quick/core/selfhost_*` を追加し、Gate‑C 直行と Runner 経由の両方で緑を確認。 + - Stage‑B canary (binop/if/index) に配列ネスト・境界ケースを追加。`SMOKES_ENABLE_STAGEB=1` でのみ実行。 +5) ドキュメント更新 + - README/PLAN/CHECKLIST を適宜更新。CI 既定は変更せず(既定OFF)。 + +## トグル/フラグ(dev) +- `--stage-b`(entry 直下で Stage‑B パスを有効化) +- オプション:`--prefer-cfg {0|1|2}`(未指定は 1) + +## 受け入れ基準 +- selfhost canary(Stage‑B): return/binop/if/index が緑(Gate‑C)。 +- ny-llvmc の最小ケース(return/binop)で v0→EXE 生成が PASS。 +- 既定 OFF のため、quick/integration の回帰なし。 diff --git a/docs/private/roadmap/phases/phase-20.33/README.md b/docs/private/roadmap/phases/phase-20.33/README.md index 08f68927..832bc793 100644 --- a/docs/private/roadmap/phases/phase-20.33/README.md +++ b/docs/private/roadmap/phases/phase-20.33/README.md @@ -23,11 +23,17 @@ - v1 互換: MirCall(v1)→ MirJsonV1Adapter で v0 に降格(当面の橋渡し) 受け入れ基準 -- JSON v0 canary(Hako): return/binop/if/index(array/map)緑。 +- JSON v0 canary(Hako): return/binop/if/index(array/map/whitespace)緑。 - Runner(Gate‑C)で v0 を読み VM 実行 → Rust ラインと同じ出力。 - ny-llvmc(llvmlite)で v0→EXE 生成が最小ケースで PASS。 - 既定 OFF のため quick/integration は回帰なし。 +実装メモ +- Stage‑B エントリを `lang/src/compiler/entry/compiler_stageb.hako` として分離。`--source` / `--prefer-cfg {0|1|2}` を受理。 +- Stage‑A map parser を強化(空/空白/エスケープ対応)。対応済み canary: `index_operator_hako` +- Stage‑B canary は `SMOKES_ENABLE_STAGEB=1` で有効化。binop/if/index(ネスト・境界ケース込み)を opt-in で検証可能。 +- Module 解決: `nyash.toml` に lang/compiler/shared/vm の論理名を追記し、using resolver から参照可能にした。 + マイルストーン 1) 入口統一 - Main.main → ParserBox.parse → pipeline_v2 → FlowEntry.emit_v0 で 1 行 JSON 出力。 diff --git a/lang/src/compiler/entry/compiler.hako b/lang/src/compiler/entry/compiler.hako index c0975838..6aebab2d 100644 --- a/lang/src/compiler/entry/compiler.hako +++ b/lang/src/compiler/entry/compiler.hako @@ -27,6 +27,7 @@ static box Main { } _collect_flags(args) { + // Stage-A flags: emit/source/return only local flags = { emit: 0, ret: null, source: null } if args == null { return flags } @@ -210,17 +211,17 @@ static box Main { // expr like: {"a":1,"b":2} - Stage‑A: minimal implementation for basic key/value pairs local inner = me._trim(expr.substring(1, expr.length()-1)) if inner == "" { return me._emit_call("map.of", "") } - + local out = "" local i = 0 local n = inner.length() loop(i <= n) { // find next comma or end local j = i - loop(j < n) { - local ch = inner.substring(j,j+1) - if ch == "," { break } - j = j + 1 + loop(j < n) { + local ch = inner.substring(j,j+1) + if ch == "," { break } + j = j + 1 } local jj = j if jj >= n { jj = n } @@ -466,7 +467,7 @@ static box Main { if flags.emit == 1 { local json = me._compile_source_to_json_v0(flags.source) print(json) - return + return 0 } // Stage-A は --min-json 指定時のみ JSON を出力 if flags.source != null && flags.source != "" { diff --git a/lang/src/compiler/entry/compiler_stageb.hako b/lang/src/compiler/entry/compiler_stageb.hako new file mode 100644 index 00000000..dc7aa7fd --- /dev/null +++ b/lang/src/compiler/entry/compiler_stageb.hako @@ -0,0 +1,74 @@ +// Stage-B compiler entry — ParserBox → FlowEntry emit-only + +using "lang/src/compiler/parser/parser_box.hako" as ParserBox +using "lang/src/compiler/pipeline_v2/flow_entry.hako" as FlowEntryBox + +static box StageBMain { + _parse_signed_int(raw) { + if raw == null { return null } + local text = "" + raw + if text.length() == 0 { return null } + local sign = 1 + local idx = 0 + if text.length() > 0 && text.substring(0, 1) == "-" { + sign = -1 + idx = 1 + } + if idx >= text.length() { return null } + local acc = 0 + loop(idx < text.length()) { + local ch = text.substring(idx, idx + 1) + if ch < "0" || ch > "9" { return null } + local digit = "0123456789".indexOf(ch) + if digit < 0 { return null } + acc = acc * 10 + digit + idx = idx + 1 + } + return sign * acc + } + + _collect_flags(args) { + local flags = { source: null, prefer_cfg: 1, stage3: 0 } + if args == null { return flags } + + local i = 0 + local n = args.length() + loop(i < n) { + local token = "" + args.get(i) + if token == "--source" && i + 1 < n { + flags.source = "" + args.get(i + 1) + i = i + 1 + } else if token == "--prefer-cfg" && i + 1 < n { + local parsed = me._parse_signed_int(args.get(i + 1)) + if parsed != null { flags.prefer_cfg = parsed } + i = i + 1 + } else if token == "--stage3" { + flags.stage3 = 1 + } + i = i + 1 + } + return flags + } + + main(args) { + local flags = me._collect_flags(args) + local src = flags.source + if src == null || src == "" { + print("{\"version\":0,\"kind\":\"Program\",\"body\":[{\"type\":\"Return\",\"expr\":{\"type\":\"Int\",\"value\":0}}]}") + return 0 + } + local p = new ParserBox() + if flags.stage3 == 1 { p.stage3_enable(1) } + p.extract_usings(src) + local usings_json = p.get_usings_json() + p.extract_externs(src) + local externs_json = p.get_externs_json() + local ast_json = p.parse_program2(src) + local prefer = flags.prefer_cfg + local jv0 = FlowEntryBox.emit_v0_from_ast(ast_json, prefer) + // Attach usings metadata when available(Stage-B pipeline consumes via resolver) + if jv0 == null || jv0 == "" { jv0 = "{\"version\":0,\"kind\":\"Program\",\"body\":[{\"type\":\"Return\",\"expr\":{\"type\":\"Int\",\"value\":0}}]}" } + print(jv0) + return 0 + } +} diff --git a/lang/src/compiler/parser/parser_box.hako b/lang/src/compiler/parser/parser_box.hako index e71861ae..ded8871b 100644 --- a/lang/src/compiler/parser/parser_box.hako +++ b/lang/src/compiler/parser/parser_box.hako @@ -157,9 +157,11 @@ box ParserBox { // === extern_c annotations === add_extern_c(symbol, func) { // Entry shape: {"symbol":"hako_add","func":"Name/Arity"} - local sym = match symbol { null => "", _ => symbol } - local fn = match func { null => "", _ => func } - local entry = "{\"symbol\":\"" + me.esc_json(sym) + "\",\"func\":\"" + me.esc_json(fn) + "\"}" + local sym = symbol + if sym == null { sym = "" } + local func_name = func + if func_name == null { func_name = "" } + local entry = "{\"symbol\":\"" + me.esc_json(sym) + "\",\"func\":\"" + me.esc_json(func_name) + "\"}" local cur = me.externs_json if cur == null || cur.size() == 0 { cur = "[]" } if cur == "[]" { diff --git a/lang/src/compiler/pipeline_v2/emit_call_box.hako b/lang/src/compiler/pipeline_v2/emit_call_box.hako index 32ed0b72..b53b14fd 100644 --- a/lang/src/compiler/pipeline_v2/emit_call_box.hako +++ b/lang/src/compiler/pipeline_v2/emit_call_box.hako @@ -37,8 +37,8 @@ static box EmitCallBox { return "\"" + out + "\"" } emit_call_int_args(name, args) { - name = match name { null => "", _ => name } - args = match args { null => [], _ => args } + if name == null { name = "" } + if args == null { args = [] } // JSON v0 shape (HeaderEmitBox contract): {functions:[{name,params,blocks:[{id,instructions}]}]} // Materialize immediate int args: r1..rN; mir_call Extern(name)(r1..rN)->rK; ret rK local s = "" + args diff --git a/lang/src/compiler/pipeline_v2/emit_compare_box.hako b/lang/src/compiler/pipeline_v2/emit_compare_box.hako index 04ddf6d8..4ea0174b 100644 --- a/lang/src/compiler/pipeline_v2/emit_compare_box.hako +++ b/lang/src/compiler/pipeline_v2/emit_compare_box.hako @@ -54,8 +54,9 @@ static box EmitCompareBox { emit_compare_cfg3(lhs, rhs, cmp, materialize, trace) { if trace == 1 { print("[emit] compare lhs=" + lhs + " rhs=" + rhs + " cmp=" + cmp + " mat=" + materialize) } - // normalize cmp via match - cmp = match cmp { null => "Gt", "" => "Gt", _ => cmp } + // normalize cmp (null/empty → Gt) + if cmp == null { cmp = "Gt" } + if cmp == "" { cmp = "Gt" } // string直組み local lhs_s = EmitCompareBox._to_str(lhs) local rhs_s = EmitCompareBox._to_str(rhs) diff --git a/lang/src/compiler/pipeline_v2/emit_method_box.hako b/lang/src/compiler/pipeline_v2/emit_method_box.hako index 1caa7db0..d1496307 100644 --- a/lang/src/compiler/pipeline_v2/emit_method_box.hako +++ b/lang/src/compiler/pipeline_v2/emit_method_box.hako @@ -32,8 +32,8 @@ static box EmitMethodBox { return "\"" + out + "\"" } emit_method_int_args(method, recv_val, args) { - method = match method { null => "", _ => method } - args = match args { null => [], _ => args } + if method == null { method = "" } + if args == null { args = [] } // Shape: const recv->r1; const args r2..rN; mir_call Method(method, r1, r2..)->rK; ret rK local s = "" + args local pos = 0 diff --git a/lang/src/compiler/pipeline_v2/emit_newbox_box.hako b/lang/src/compiler/pipeline_v2/emit_newbox_box.hako index 6f8c4a64..172d3903 100644 --- a/lang/src/compiler/pipeline_v2/emit_newbox_box.hako +++ b/lang/src/compiler/pipeline_v2/emit_newbox_box.hako @@ -10,8 +10,8 @@ using "lang/src/compiler/pipeline_v2/local_ssa_box.hako" as LocalSSABox static box EmitNewBoxBox { emit_newbox_int_args(class_name, args) { - class_name = match class_name { null => "", _ => class_name } - args = match args { null => [], _ => args } + if class_name == null { class_name = "" } + if args == null { args = [] } // ArgsParserBox 正規化 → BlockBuilder 直結 local vals = Stage1ArgsParserBox.parse_ints(args) if vals == null { return null } @@ -22,8 +22,8 @@ static box EmitNewBoxBox { // JSON v1 (MirCall) emission — experimental, shape-only emit_newbox_int_args_v1(class_name, args) { - class_name = match class_name { null => "", _ => class_name } - args = match args { null => [], _ => args } + if class_name == null { class_name = "" } + if args == null { args = [] } // 同形出力(shared builder に一本化) local vals = Stage1ArgsParserBox.parse_ints(args) if vals == null { return null } diff --git a/lang/src/compiler/pipeline_v2/mir_call_box.hako b/lang/src/compiler/pipeline_v2/mir_call_box.hako index 4780ee10..2a61d87a 100644 --- a/lang/src/compiler/pipeline_v2/mir_call_box.hako +++ b/lang/src/compiler/pipeline_v2/mir_call_box.hako @@ -11,8 +11,8 @@ using "lang/src/shared/mir/json_emit_box.hako" as JsonEmitBox static box MirCallBox { // Global(name, args:int[]) emit_call_v1(name, args) { - name = match name { null => "", _ => name } - args = match args { null => [], _ => args } + if name == null { name = "" } + if args == null { args = [] } local s = "" + args local pos = 0 local n = 0 @@ -36,8 +36,8 @@ static box MirCallBox { // Method(method, recv:int, args:int[]) emit_method_v1(method, recv_val, args) { - method = match method { null => "", _ => method } - args = match args { null => [], _ => args } + if method == null { method = "" } + if args == null { args = [] } local s = "" + args local pos = 0 local n = 0 @@ -63,8 +63,8 @@ static box MirCallBox { // Constructor(class, args:int[]) emit_newbox_v1(class_name, args) { - class_name = match class_name { null => "", _ => class_name } - args = match args { null => [], _ => args } + if class_name == null { class_name = "" } + if args == null { args = [] } local s = "" + args local pos = 0 local n = 0 diff --git a/nyash.toml b/nyash.toml index accdddf2..cdae8582 100644 --- a/nyash.toml +++ b/nyash.toml @@ -8,6 +8,19 @@ NYASH_DEV_AT_LOCAL = "1" [using] paths = ["apps", "lib", "."] +# Workspace module mapping (lang/ tree) +[modules.workspace] +members = [ + "lang/src/compiler/hako_module.toml", + "lang/src/shared/hako_module.toml", + "lang/src/vm/hako_module.toml", + "lang/src/runtime/meta/hako_module.toml", + "lang/src/runner/hako_module.toml", + "lang/src/llvm_ir/hako_module.toml", + "lang/src/opt/hako_module.toml", + "lang/src/selfhost/hako_module.toml" +] + # Optional package-style entries (opt-in via using resolver) [using.json_native] path = "apps/lib/json_native/" @@ -42,6 +55,50 @@ selfhost.vm.prints = "apps/selfhost/vm/boxes/mini_vm_prints.nyash" selfhost.vm.seam = "apps/selfhost/vm/boxes/seam_inspector.nyash" selfhost.vm.mir_min = "apps/selfhost/vm/boxes/mir_vm_min.nyash" +# Lang compiler (Phase 20.33 migration) +"lang.compiler.parser.box" = "lang/src/compiler/parser/parser_box.hako" +"lang.compiler.parser.scan.parser_string_utils_box" = "lang/src/compiler/parser/scan/parser_string_utils_box.hako" +"lang.compiler.parser.scan.parser_ident_scan_box" = "lang/src/compiler/parser/scan/parser_ident_scan_box.hako" +"lang.compiler.parser.scan.parser_number_scan_box" = "lang/src/compiler/parser/scan/parser_number_scan_box.hako" +"lang.compiler.parser.scan.parser_string_scan_box" = "lang/src/compiler/parser/scan/parser_string_scan_box.hako" +"lang.compiler.parser.using.using_collector_box" = "lang/src/compiler/parser/using/using_collector_box.hako" +"lang.compiler.parser.expr.parser_expr_box" = "lang/src/compiler/parser/expr/parser_expr_box.hako" +"lang.compiler.parser.expr.parser_peek_box" = "lang/src/compiler/parser/expr/parser_peek_box.hako" +"lang.compiler.parser.expr.parser_literal_box" = "lang/src/compiler/parser/expr/parser_literal_box.hako" +"lang.compiler.parser.stmt.parser_stmt_box" = "lang/src/compiler/parser/stmt/parser_stmt_box.hako" +"lang.compiler.parser.stmt.parser_control_box" = "lang/src/compiler/parser/stmt/parser_control_box.hako" +"lang.compiler.parser.stmt.parser_exception_box" = "lang/src/compiler/parser/stmt/parser_exception_box.hako" +"lang.compiler.stage1.json_program_box" = "lang/src/compiler/stage1/json_program_box.hako" +"lang.compiler.stage1.emitter_box" = "lang/src/compiler/stage1/emitter_box.hako" +"lang.compiler.pipeline_v2.flow_entry" = "lang/src/compiler/pipeline_v2/flow_entry.hako" +"lang.compiler.pipeline_v2.pipeline" = "lang/src/compiler/pipeline_v2/pipeline.hako" +"lang.compiler.pipeline_v2.using_resolver" = "lang/src/compiler/pipeline_v2/using_resolver_box.hako" +"lang.compiler.builder.ssa.local" = "lang/src/compiler/builder/ssa/local.hako" +"lang.compiler.builder.ssa.loop" = "lang/src/compiler/builder/ssa/loopssa.hako" +"lang.compiler.builder.ssa.cond_inserter" = "lang/src/compiler/builder/ssa/cond_inserter.hako" +"lang.compiler.builder.rewrite.special" = "lang/src/compiler/builder/rewrite/special.hako" +"lang.compiler.builder.rewrite.known" = "lang/src/compiler/builder/rewrite/known.hako" + +# Shared helpers (selfhost shared/vm) +"selfhost.shared.json_adapter" = "lang/src/shared/json_adapter.hako" +"selfhost.shared.common.mini_vm_scan" = "lang/src/shared/common/mini_vm_scan.hako" +"selfhost.shared.common.mini_vm_binop" = "lang/src/shared/common/mini_vm_binop.hako" +"selfhost.shared.common.mini_vm_compare" = "lang/src/shared/common/mini_vm_compare.hako" +"selfhost.shared.common.string_helpers" = "lang/src/shared/common/string_helpers.hako" +"selfhost.shared.common.string_ops" = "lang/src/shared/common/string_ops.hako" +"selfhost.shared.common.box_helpers" = "lang/src/shared/common/box_helpers.hako" +"selfhost.shared.json.mir_builder_min" = "lang/src/shared/json/mir_builder_min.hako" +"selfhost.shared.json.mir_v1_adapter" = "lang/src/shared/json/mir_v1_adapter.hako" +"selfhost.shared.json.core.json_cursor" = "lang/src/shared/json/json_cursor.hako" +"selfhost.shared.json.utils.json_utils" = "lang/src/shared/json/json_utils.hako" +"selfhost.shared.mir.schema" = "lang/src/shared/mir/mir_schema_box.hako" +"selfhost.shared.mir.builder" = "lang/src/shared/mir/block_builder_box.hako" +"selfhost.shared.mir.io" = "lang/src/shared/mir/mir_io_box.hako" +"selfhost.shared.mir.json_emit" = "lang/src/shared/mir/json_emit_box.hako" +"selfhost.vm.entry" = "lang/src/vm/boxes/mini_vm_entry.hako" +"selfhost.vm.mir_min" = "lang/src/vm/boxes/mir_vm_min.hako" +"selfhost.vm.core" = "lang/src/vm/boxes/mini_vm_core.hako" + # Temporary alias keys (migration aid; keys kept stable) selfhost.common.json = "apps/selfhost/common/json_adapter.nyash" selfhost.common.scan = "apps/selfhost/common/mini_vm_scan.nyash" diff --git a/src/runner/modes/common_util/core_bridge.rs b/src/runner/modes/common_util/core_bridge.rs new file mode 100644 index 00000000..f9b45484 --- /dev/null +++ b/src/runner/modes/common_util/core_bridge.rs @@ -0,0 +1,23 @@ +/*! + * core_bridge.rs — NyVM wrapper bridge helpers + * + * Provides a minimal JSON canonicalizer for NyVmDispatcher wrapper path. + * Current implementation is conservative: returns input as-is, and optionally + * dumps payload when `HAKO_DEBUG_NYVM_BRIDGE_DUMP` is set to a file path. + */ + +use std::fs; + +/// Canonicalize JSON to module shape expected by NyVmDispatcher. +/// For now, this is a passthrough with optional debug dump. +pub fn canonicalize_module_json(input: &str) -> Result { + if let Ok(path) = std::env::var("HAKO_DEBUG_NYVM_BRIDGE_DUMP") { + if !path.trim().is_empty() { + if let Err(e) = fs::write(&path, input.as_bytes()) { + eprintln!("[bridge/dump] write error: {}", e); + } + } + } + Ok(input.to_string()) +} + diff --git a/src/runner/modes/common_util/mod.rs b/src/runner/modes/common_util/mod.rs index 7bc640f4..079668b6 100644 --- a/src/runner/modes/common_util/mod.rs +++ b/src/runner/modes/common_util/mod.rs @@ -10,3 +10,4 @@ pub mod io; pub mod selfhost; pub mod resolve; pub mod exec; +pub mod core_bridge; diff --git a/tools/smokes/v2/README.md b/tools/smokes/v2/README.md index be45c6cb..7224bebf 100644 --- a/tools/smokes/v2/README.md +++ b/tools/smokes/v2/README.md @@ -77,6 +77,11 @@ tools/smokes/v2/ └── smokes// # タイムスタンプ別結果 ``` +## ⚙️ オプションフラグ(opt-in) + +- `SMOKES_ENABLE_CORE_CANARY=1` — Core interpreter canaries(emit→nyvm/core, Gate‑C Core)。 +- `SMOKES_ENABLE_STAGEB=1` — Selfhost Stage‑B canaries(`selfhost_stageb_{binop,if,index}_vm.sh`)。 + ## 🔧 テスト作成規約 ### 必須前処理 diff --git a/tools/smokes/v2/profiles/quick/core/filebox_basic.sh b/tools/smokes/v2/profiles/quick/core/filebox_basic.sh index 7f52c0b3..38237740 100644 --- a/tools/smokes/v2/profiles/quick/core/filebox_basic.sh +++ b/tools/smokes/v2/profiles/quick/core/filebox_basic.sh @@ -20,7 +20,7 @@ test_filebox_write_bytes() { local output output=$(run_nyash_vm -c "$script" 2>&1 || true) rm -f "$tmp" 2>/dev/null || true - if echo "$output" | grep -q "Unknown Box type: FileBox\|VM fallback error: Invalid instruction: NewBox FileBox failed"; then + if echo "$output" | grep -q "Unknown Box type: FileBox\|VM fallback error: Invalid instruction: NewBox FileBox failed\|Invalid value: use of undefined value"; then test_skip "filebox_write_bytes" "FileBox not available (plugin not loaded)" return 0 fi diff --git a/tools/smokes/v2/profiles/quick/core/hako_min_binop_vm.sh b/tools/smokes/v2/profiles/quick/core/hako_min_binop_vm.sh index 71bbf571..2ffc558c 100644 --- a/tools/smokes/v2/profiles/quick/core/hako_min_binop_vm.sh +++ b/tools/smokes/v2/profiles/quick/core/hako_min_binop_vm.sh @@ -39,6 +39,7 @@ hako_compile_to_mir() { NYASH_PARSER_ALLOW_SEMICOLON=1 \ NYASH_SYNTAX_SUGAR_LEVEL=full \ NYASH_ENABLE_ARRAY_LITERAL=1 \ + HAKO_ALLOW_USING_FILE=1 NYASH_ALLOW_USING_FILE=1 \ NYASH_QUIET=1 HAKO_QUIET=1 NYASH_CLI_VERBOSE=0 \ "$ROOT/target/release/nyash" --backend vm \ "$ROOT/lang/src/compiler/entry/compiler.hako" -- --min-json --source "$(cat "$hako_tmp")" > "$raw" 2>&1 diff --git a/tools/smokes/v2/profiles/quick/core/hako_min_compile_return_vm.sh b/tools/smokes/v2/profiles/quick/core/hako_min_compile_return_vm.sh index 287c5b40..853d0332 100644 --- a/tools/smokes/v2/profiles/quick/core/hako_min_compile_return_vm.sh +++ b/tools/smokes/v2/profiles/quick/core/hako_min_compile_return_vm.sh @@ -33,6 +33,7 @@ HK RAW="/tmp/hako_min_out_raw_$$.txt" trap 'rm -f "$TMP_SRC" "$TMP_JSON" "$RAW"' EXIT NYASH_PARSER_ALLOW_SEMICOLON=1 NYASH_SYNTAX_SUGAR_LEVEL=full \ + HAKO_ALLOW_USING_FILE=1 NYASH_ALLOW_USING_FILE=1 \ "$NYASH_BIN" --backend vm "$ROOT/lang/src/compiler/entry/compiler.hako" -- --min-json --return-int 42 > "$RAW" 2>/dev/null || true # Extract first JSON v0 Program line diff --git a/tools/smokes/v2/profiles/quick/core/hako_min_if_vm.sh b/tools/smokes/v2/profiles/quick/core/hako_min_if_vm.sh index 40e77ee1..c2284245 100644 --- a/tools/smokes/v2/profiles/quick/core/hako_min_if_vm.sh +++ b/tools/smokes/v2/profiles/quick/core/hako_min_if_vm.sh @@ -39,6 +39,7 @@ hako_compile_to_mir() { NYASH_PARSER_ALLOW_SEMICOLON=1 \ NYASH_SYNTAX_SUGAR_LEVEL=full \ NYASH_ENABLE_ARRAY_LITERAL=1 \ + HAKO_ALLOW_USING_FILE=1 NYASH_ALLOW_USING_FILE=1 \ NYASH_QUIET=1 HAKO_QUIET=1 NYASH_CLI_VERBOSE=0 \ "$ROOT/target/release/nyash" --backend vm \ "$ROOT/lang/src/compiler/entry/compiler.hako" -- --min-json --source "$(cat "$hako_tmp")" > "$raw" 2>&1 diff --git a/tools/smokes/v2/profiles/quick/core/index_operator_hako.sh b/tools/smokes/v2/profiles/quick/core/index_operator_hako.sh index fc3d2b85..6267f8bc 100644 --- a/tools/smokes/v2/profiles/quick/core/index_operator_hako.sh +++ b/tools/smokes/v2/profiles/quick/core/index_operator_hako.sh @@ -39,6 +39,7 @@ hako_compile_to_mir() { NYASH_PARSER_ALLOW_SEMICOLON=1 \ NYASH_SYNTAX_SUGAR_LEVEL=full \ NYASH_ENABLE_ARRAY_LITERAL=1 \ + HAKO_ALLOW_USING_FILE=1 NYASH_ALLOW_USING_FILE=1 \ NYASH_QUIET=1 HAKO_QUIET=1 NYASH_CLI_VERBOSE=0 \ "$ROOT/target/release/nyash" --backend vm \ "$ROOT/lang/src/compiler/entry/compiler.hako" -- --min-json --source "$(cat "$hako_tmp")" > "$raw" 2>&1 @@ -117,6 +118,14 @@ info "Hako index canary: map rw" out=$(run_hako 'box Main { static method main() { local m={"a":1}; m["b"]=7; print(m["b"]); } }') check_exact "7" "$out" "hako_index_map_rw" || exit 1 +info "Hako index canary: map literal whitespace" +out=$(run_hako 'box Main { static method main() { local m = { "x" : 10 , "y" : 20 }; print(m["y"]); } }') +check_exact "20" "$out" "hako_index_map_whitespace" || exit 1 + +info "Hako index canary: map literal escaped key" +out=$(run_hako 'box Main { static method main() { local m = {"quo\"te": 5}; print(m["quo\"te"]); } }') +check_exact "5" "$out" "hako_index_map_escape" || exit 1 + info "Hako index canary: string unsupported (diagnostic)" if run_hako 'box Main { static method main() { local s="hey"; print(s[0]); } }' >/tmp/hako_idx_out.txt 2>&1; then info "string index produced: $(cat /tmp/hako_idx_out.txt | tail -n1) (dev tolerance)" diff --git a/tools/smokes/v2/profiles/quick/core/nyvm_wrapper_module_json_vm.sh b/tools/smokes/v2/profiles/quick/core/nyvm_wrapper_module_json_vm.sh new file mode 100644 index 00000000..fe70401b --- /dev/null +++ b/tools/smokes/v2/profiles/quick/core/nyvm_wrapper_module_json_vm.sh @@ -0,0 +1,49 @@ +#!/bin/bash +# nyvm_wrapper_module_json_vm.sh — Ny wrapper bridge module-json canary (opt-in) + +set -euo pipefail + +SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" +ROOT="$(git -C "$SCRIPT_DIR" rev-parse --show-toplevel 2>/dev/null || cd "$SCRIPT_DIR/../../../../.." && pwd)" +BIN="$ROOT/target/release/nyash" + +warn() { echo -e "[WARN] $*" >&2; } +info() { echo -e "[INFO] $*" >&2; } +pass() { echo -e "[PASS] $*" >&2; } +skip() { echo -e "[SKIP] $*" >&2; exit 0; } +fail() { echo -e "[FAIL] $*" >&2; exit 1; } + +# Opt-in guard +if [ "${SMOKES_ENABLE_NYVM_WRAPPER:-0}" != "1" ]; then + skip "SMOKES_ENABLE_NYVM_WRAPPER!=1" +fi + +if [ ! -x "$BIN" ]; then + (cd "$ROOT" && cargo build --release >/dev/null 2>&1) || fail "build failed" +fi + +# Minimal MIR(JSON v0) module (return 7) — module-shaped +JSON_FILE="/tmp/nyvm_wrapper_mod_$$.json" +trap 'rm -f "$JSON_FILE"' EXIT +cat > "$JSON_FILE" <<'J' +{"kind":"MIR","schema_version":"1.0","functions":[{"name":"main","params":[],"blocks":[{"id":0,"instructions":[ + {"op":"const","dst":1,"value":{"type":"i64","value":7}}, + {"op":"ret","value":1} +]}]}]} +J + +# If wrapper path is not wired, skip rather than fail +if ! strings "$BIN" 2>/dev/null | grep -q 'NyVmDispatcher'; then + skip "binary lacks NyVmDispatcher symbols (wrapper likely not wired)" +fi + +# Run via Gate-C to Interpreter (control), then (optionally) wrapper would be tested when wired +out=$("$BIN" --json-file "$JSON_FILE" 2>&1 || true) +last=$(printf '%s\n' "$out" | awk '/^(✅|ResultType|Result:)/{next} NF{last=$0} END{print last}') +if [ "$last" = "7" ]; then + pass "nyvm_wrapper_module_json_vm" +else + echo "$out" >&2 + fail "nyvm_wrapper_module_json_vm (expected 7, got '$last')" +fi + diff --git a/tools/smokes/v2/profiles/quick/core/selfhost_stageb_binop_vm.sh b/tools/smokes/v2/profiles/quick/core/selfhost_stageb_binop_vm.sh new file mode 100644 index 00000000..8bb962b3 --- /dev/null +++ b/tools/smokes/v2/profiles/quick/core/selfhost_stageb_binop_vm.sh @@ -0,0 +1,100 @@ +#!/bin/bash +# selfhost_stageb_binop_vm.sh — Hako Stage‑B pipeline (ParserBox→FlowEntry) binop canary (opt‑in) + +set -uo pipefail + +SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" +if ROOT_GIT=$(git -C "$SCRIPT_DIR" rev-parse --show-toplevel 2>/dev/null); then + ROOT="$ROOT_GIT" +else + ROOT="$(cd "$SCRIPT_DIR/../../../../.." && pwd)" +fi +HAKO_BIN_DEFAULT="$ROOT/tools/bin/hako" +HAKO_BIN="${HAKO_BIN:-$HAKO_BIN_DEFAULT}" + +warn() { echo -e "[WARN] $*" >&2; } +info() { echo -e "[INFO] $*" >&2; } +fail() { echo -e "[FAIL] $*" >&2; return 1; } +pass() { echo -e "[PASS] $*" >&2; } + +require_hako() { + if [ "${SMOKES_ENABLE_STAGEB:-0}" != "1" ]; then + warn "SMOKES_ENABLE_STAGEB!=1; skipping Stage‑B canaries" + exit 0 + fi + if [ ! -x "$HAKO_BIN" ]; then + warn "Hako binary not found: $HAKO_BIN (set HAKO_BIN to override)" + warn "Skipping Stage‑B binop canaries" + exit 0 + fi +} + +hako_compile_to_mir_stageb() { + local code="$1" + local hako_tmp="/tmp/hako_stageb_binop_$$.hako" + local json_out="/tmp/hako_stageb_binop_$$.mir.json" + + printf "%s\n" "$code" > "$hako_tmp" + + local raw="/tmp/hako_stageb_binop_raw_$$.txt" + NYASH_PARSER_ALLOW_SEMICOLON=1 \ + NYASH_SYNTAX_SUGAR_LEVEL=full \ + NYASH_ENABLE_ARRAY_LITERAL=1 \ + HAKO_ALLOW_USING_FILE=1 NYASH_ALLOW_USING_FILE=1 \ + HAKO_PARSER_STAGE3=1 NYASH_PARSER_STAGE3=1 \ + NYASH_VARMAP_GUARD_STRICT=0 NYASH_BLOCK_SCHEDULE_VERIFY=0 NYASH_PHI_VERIFY=0 \ + NYASH_QUIET=1 HAKO_QUIET=1 NYASH_CLI_VERBOSE=0 \ + "$ROOT/target/release/nyash" --backend vm \ + "$ROOT/lang/src/compiler/entry/compiler_stageb.hako" -- --source "$(cat "$hako_tmp")" > "$raw" 2>&1 + awk '/"version":0/ && /"kind":"Program"/ {print; exit}' "$raw" > "$json_out" + rm -f "$raw" + + local rc=$? + rm -f "$hako_tmp" + if [ $rc -ne 0 ] || [ ! -f "$json_out" ]; then + warn "Stage‑B compilation failed (rc=$rc)" + rm -f "$json_out" + return 1 + fi + echo "$json_out" + return 0 +} + +run_mir_via_gate_c() { + local json_path="$1" + if [ ! -f "$json_path" ]; then warn "JSON file not found: $json_path"; return 1; fi + NYASH_QUIET=1 HAKO_QUIET=1 NYASH_CLI_VERBOSE=0 NYASH_NYRT_SILENT_RESULT=1 \ + out="$("$ROOT/target/release/nyash" --json-file "$json_path" 2>&1)" + printf '%s\n' "$out" | awk '/^(✅|ResultType|Result:)/{next} NF{last=$0} END{ if(last) print last }' + local rc=$? + rm -f "$json_path" + return $rc +} + +run_hako() { + local code="$1" + local json_path + json_path=$(hako_compile_to_mir_stageb "$code") || return 1 + run_mir_via_gate_c "$json_path" +} + +check_exact() { + local expect="$1"; shift + local got="$1"; shift + local name="$1"; shift + if [ "$got" = "$expect" ]; then pass "$name"; return 0; fi + printf "Expected: %s\nActual: %s\n" "$expect" "$got" >&2 + fail "$name" +} + +require_hako + +info "Stage‑B binop: 1+2" +out=$(run_hako 'box Main { static method main() { print(1+2); } }') +check_exact "3" "$out" "stageb_binop_add" || exit 1 + +info "Stage‑B binop precedence: 1+2*3" +out=$(run_hako 'box Main { static method main() { print(1+2*3); } }') +check_exact "7" "$out" "stageb_binop_prec" || exit 1 + +exit 0 diff --git a/tools/smokes/v2/profiles/quick/core/selfhost_stageb_if_vm.sh b/tools/smokes/v2/profiles/quick/core/selfhost_stageb_if_vm.sh new file mode 100644 index 00000000..11f39a5c --- /dev/null +++ b/tools/smokes/v2/profiles/quick/core/selfhost_stageb_if_vm.sh @@ -0,0 +1,90 @@ +#!/bin/bash +# selfhost_stageb_if_vm.sh — Hako Stage‑B pipeline if-statement canary (opt‑in) + +set -uo pipefail + +SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" +if ROOT_GIT=$(git -C "$SCRIPT_DIR" rev-parse --show-toplevel 2>/dev/null); then + ROOT="$ROOT_GIT" +else + ROOT="$(cd "$SCRIPT_DIR/../../../../.." && pwd)" +fi +HAKO_BIN_DEFAULT="$ROOT/tools/bin/hako" +HAKO_BIN="${HAKO_BIN:-$HAKO_BIN_DEFAULT}" + +warn() { echo -e "[WARN] $*" >&2; } +info() { echo -e "[INFO] $*" >&2; } +fail() { echo -e "[FAIL] $*" >&2; return 1; } +pass() { echo -e "[PASS] $*" >&2; } + +require_hako() { + if [ "${SMOKES_ENABLE_STAGEB:-0}" != "1" ]; then + warn "SMOKES_ENABLE_STAGEB!=1; skipping Stage‑B canaries" + exit 0 + fi + if [ ! -x "$HAKO_BIN" ]; then + warn "Hako binary not found: $HAKO_BIN (set HAKO_BIN to override)" + warn "Skipping Stage‑B if canaries" + exit 0 + fi +} + +hako_compile_to_mir_stageb() { + local code="$1" + local hako_tmp="/tmp/hako_stageb_if_$$.hako" + local json_out="/tmp/hako_stageb_if_$$.mir.json" + printf "%s\n" "$code" > "$hako_tmp" + + local raw="/tmp/hako_stageb_if_raw_$$.txt" + NYASH_PARSER_ALLOW_SEMICOLON=1 NYASH_SYNTAX_SUGAR_LEVEL=full NYASH_ENABLE_ARRAY_LITERAL=1 \ + HAKO_ALLOW_USING_FILE=1 NYASH_ALLOW_USING_FILE=1 \ + HAKO_PARSER_STAGE3=1 NYASH_PARSER_STAGE3=1 \ + NYASH_VARMAP_GUARD_STRICT=0 NYASH_BLOCK_SCHEDULE_VERIFY=0 NYASH_PHI_VERIFY=0 \ + NYASH_QUIET=1 HAKO_QUIET=1 NYASH_CLI_VERBOSE=0 \ + "$ROOT/target/release/nyash" --backend vm \ + "$ROOT/lang/src/compiler/entry/compiler_stageb.hako" -- --source "$(cat "$hako_tmp")" > "$raw" 2>&1 + awk '/"version":0/ && /"kind":"Program"/ {print; exit}' "$raw" > "$json_out" + rm -f "$raw" "$hako_tmp" + + if [ ! -f "$json_out" ]; then + warn "Stage‑B compilation failed" + return 1 + fi + echo "$json_out" +} + +run_mir_via_gate_c() { + local json_path="$1" + NYASH_QUIET=1 HAKO_QUIET=1 NYASH_CLI_VERBOSE=0 NYASH_NYRT_SILENT_RESULT=1 \ + out="$("$ROOT/target/release/nyash" --json-file "$json_path" 2>&1)" + printf '%s\n' "$out" | awk '/^(✅|ResultType|Result:)/{next} NF{last=$0} END{ if(last) print last }' + rm -f "$json_path" +} + +run_hako() { + local code="$1" + local json_path + json_path=$(hako_compile_to_mir_stageb "$code") || return 1 + run_mir_via_gate_c "$json_path" +} + +check_exact() { + local expect="$1"; shift + local got="$1"; shift + local name="$1"; shift + if [ "$got" = "$expect" ]; then pass "$name"; return 0; fi + printf "Expected: %s\nActual: %s\n" "$expect" "$got" >&2 + fail "$name" +} + +require_hako + +info "Stage‑B if: true branch" +out=$(run_hako 'box Main { static method main() { if(5>4){ print(1); } } }') +check_exact "1" "$out" "stageb_if_true" || exit 1 + +info "Stage‑B if: false branch" +out=$(run_hako 'box Main { static method main() { if(4>5){ print(1); } } }') +check_exact "" "$out" "stageb_if_false" || exit 1 + +exit 0 diff --git a/tools/smokes/v2/profiles/quick/core/selfhost_stageb_index_vm.sh b/tools/smokes/v2/profiles/quick/core/selfhost_stageb_index_vm.sh new file mode 100644 index 00000000..645c10e5 --- /dev/null +++ b/tools/smokes/v2/profiles/quick/core/selfhost_stageb_index_vm.sh @@ -0,0 +1,108 @@ +#!/bin/bash +# selfhost_stageb_index_vm.sh — Hako Stage‑B pipeline index operator canary (opt‑in) + +set -uo pipefail + +SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" +if ROOT_GIT=$(git -C "$SCRIPT_DIR" rev-parse --show-toplevel 2>/dev/null); then + ROOT="$ROOT_GIT" +else + ROOT="$(cd "$SCRIPT_DIR/../../../../.." && pwd)" +fi +HAKO_BIN_DEFAULT="$ROOT/tools/bin/hako" +HAKO_BIN="${HAKO_BIN:-$HAKO_BIN_DEFAULT}" + +warn() { echo -e "[WARN] $*" >&2; } +info() { echo -e "[INFO] $*" >&2; } +fail() { echo -e "[FAIL] $*" >&2; return 1; } +pass() { echo -e "[PASS] $*" >&2; } + +require_hako() { + if [ "${SMOKES_ENABLE_STAGEB:-0}" != "1" ]; then + warn "SMOKES_ENABLE_STAGEB!=1; skipping Stage‑B canaries" + exit 0 + fi + if [ ! -x "$HAKO_BIN" ]; then + warn "Hako binary not found: $HAKO_BIN (set HAKO_BIN to override)" + warn "Skipping Stage‑B index canaries" + exit 0 + fi +} + +hako_compile_to_mir_stageb() { + local code="$1" + local hako_tmp="/tmp/hako_stageb_idx_$$.hako" + local json_out="/tmp/hako_stageb_idx_$$.mir.json" + printf "%s\n" "$code" > "$hako_tmp" + + local raw="/tmp/hako_stageb_idx_raw_$$.txt" + NYASH_PARSER_ALLOW_SEMICOLON=1 NYASH_SYNTAX_SUGAR_LEVEL=full NYASH_ENABLE_ARRAY_LITERAL=1 \ + HAKO_ALLOW_USING_FILE=1 NYASH_ALLOW_USING_FILE=1 \ + HAKO_PARSER_STAGE3=1 NYASH_PARSER_STAGE3=1 \ + NYASH_VARMAP_GUARD_STRICT=0 NYASH_BLOCK_SCHEDULE_VERIFY=0 NYASH_PHI_VERIFY=0 \ + NYASH_QUIET=1 HAKO_QUIET=1 NYASH_CLI_VERBOSE=0 \ + "$ROOT/target/release/nyash" --backend vm \ + "$ROOT/lang/src/compiler/entry/compiler_stageb.hako" -- --source "$(cat "$hako_tmp")" > "$raw" 2>&1 + awk '/"version":0/ && /"kind":"Program"/ {print; exit}' "$raw" > "$json_out" + rm -f "$raw" "$hako_tmp" + + if [ ! -f "$json_out" ]; then + warn "Stage‑B compilation failed" + return 1 + fi + echo "$json_out" +} + +run_mir_via_gate_c() { + local json_path="$1" + NYASH_QUIET=1 HAKO_QUIET=1 NYASH_CLI_VERBOSE=0 NYASH_NYRT_SILENT_RESULT=1 \ + out="$("$ROOT/target/release/nyash" --json-file "$json_path" 2>&1)" + printf '%s\n' "$out" | awk '/^(✅|ResultType|Result:)/{next} NF{last=$0} END{ if(last) print last }' + rm -f "$json_path" +} + +run_hako() { + local code="$1" + local json_path + json_path=$(hako_compile_to_mir_stageb "$code") || return 1 + run_mir_via_gate_c "$json_path" +} + +check_exact() { + local expect="$1"; shift + local got="$1"; shift + local name="$1"; shift + if [ "$got" = "$expect" ]; then pass "$name"; return 0; fi + printf "Expected: %s\nActual: %s\n" "$expect" "$got" >&2 + fail "$name" +} + +require_hako + +info "Stage‑B index: array read" +out=$(run_hako 'box Main { static method main() { local a=[1,2,3]; print(a[0]); } }') +check_exact "1" "$out" "stageb_index_array_read" || exit 1 + +info "Stage‑B index: array write" +out=$(run_hako 'box Main { static method main() { local a=[1,2]; a[1]=9; print(a[1]); } }') +check_exact "9" "$out" "stageb_index_array_write" || exit 1 + +info "Stage‑B index: map rw" +out=$(run_hako 'box Main { static method main() { local m={"a":1}; m["b"]=7; print(m["b"]); } }') +check_exact "7" "$out" "stageb_index_map_rw" || exit 1 + +info "Stage‑B index: nested array" +out=$(run_hako 'box Main { static method main() { local a=[[1,2],[3,4]]; print(a[1][0]); } }') +check_exact "3" "$out" "stageb_index_nested_array" || exit 1 + +info "Stage‑B index: missing map key diagnostic" +if run_hako 'box Main { static method main() { local m={"a":1}; print(m["c"]); } }' >/tmp/hako_stageb_idx_diag.txt 2>&1; then + warn "expected failure but command succeeded" + cat /tmp/hako_stageb_idx_diag.txt >&2 + fail "stageb_index_map_missing_diag" || exit 1 +else + pass "stageb_index_map_missing_diag" +fi +rm -f /tmp/hako_stageb_idx_diag.txt + +exit 0