chore: Phase 25.1 完了 - LoopForm v2/Stage1 CLI/環境変数削減 + Phase 26-D からの変更
Phase 25.1 完了成果: - ✅ LoopForm v2 テスト・ドキュメント・コメント完備 - 4ケース(A/B/C/D)完全テストカバレッジ - 最小再現ケース作成(SSAバグ調査用) - SSOT文書作成(loopform_ssot.md) - 全ソースに [LoopForm] コメントタグ追加 - ✅ Stage-1 CLI デバッグ環境構築 - stage1_cli.hako 実装 - stage1_bridge.rs ブリッジ実装 - デバッグツール作成(stage1_debug.sh/stage1_minimal.sh) - アーキテクチャ改善提案文書 - ✅ 環境変数削減計画策定 - 25変数の完全調査・分類 - 6段階削減ロードマップ(25→5、80%削減) - 即時削除可能変数特定(NYASH_CONFIG/NYASH_DEBUG) Phase 26-D からの累積変更: - PHI実装改善(ExitPhiBuilder/HeaderPhiBuilder等) - MIRビルダーリファクタリング - 型伝播・最適化パス改善 - その他約300ファイルの累積変更 🎯 技術的成果: - SSAバグ根本原因特定(条件分岐内loop変数変更) - Region+next_iパターン適用完了(UsingCollectorBox等) - LoopFormパターン文書化・テスト化完了 - セルフホスティング基盤強化 Co-Authored-By: Claude <noreply@anthropic.com> Co-Authored-By: ChatGPT <noreply@openai.com> Co-Authored-By: Task Assistant <task@anthropic.com>
This commit is contained in:
@ -4,8 +4,8 @@ mod tests {
|
||||
use std::sync::{Arc, RwLock};
|
||||
|
||||
use crate::backend::VM;
|
||||
use crate::box_trait::NyashBox;
|
||||
use crate::box_factory::RuntimeError;
|
||||
use crate::box_trait::NyashBox;
|
||||
use crate::mir::{
|
||||
BasicBlockId, ConstValue, EffectMask, FunctionSignature, MirFunction, MirInstruction,
|
||||
MirModule, MirType,
|
||||
|
||||
@ -147,10 +147,7 @@ fn mir_compiler_stageb_breakfinder_ssa_debug() {
|
||||
let cr = mc.compile(ast).expect("compile compiler_stageb.hako ok");
|
||||
|
||||
// 必要に応じて StageBBodyExtractorBox.build_body_src/2 だけの MIR をダンプする。
|
||||
if std::env::var("NYASH_MIR_TEST_DUMP")
|
||||
.ok()
|
||||
.as_deref() == Some("1")
|
||||
{
|
||||
if std::env::var("NYASH_MIR_TEST_DUMP").ok().as_deref() == Some("1") {
|
||||
if let Some(func) = cr
|
||||
.module
|
||||
.functions
|
||||
|
||||
@ -39,10 +39,17 @@ fn mir_funcscanner_skip_ws_direct_vm() {
|
||||
match mc.compile(ast) {
|
||||
Ok(compiled) => {
|
||||
eprintln!("[test] Compilation successful");
|
||||
eprintln!("[test] Module has {} functions", compiled.module.functions.len());
|
||||
eprintln!(
|
||||
"[test] Module has {} functions",
|
||||
compiled.module.functions.len()
|
||||
);
|
||||
|
||||
// Check if FuncScannerBox.skip_whitespace/2 exists
|
||||
if let Some(func) = compiled.module.functions.get("FuncScannerBox.skip_whitespace/2") {
|
||||
if let Some(func) = compiled
|
||||
.module
|
||||
.functions
|
||||
.get("FuncScannerBox.skip_whitespace/2")
|
||||
{
|
||||
eprintln!("[test] Found FuncScannerBox.skip_whitespace/2");
|
||||
eprintln!("[test] Function has {} blocks", func.blocks.len());
|
||||
|
||||
@ -50,7 +57,10 @@ fn mir_funcscanner_skip_ws_direct_vm() {
|
||||
if std::env::var("NYASH_MIR_TEST_DUMP").ok().as_deref() == Some("1") {
|
||||
use crate::mir::MirPrinter;
|
||||
let dump = MirPrinter::new().print_function(func);
|
||||
eprintln!("----- MIR DUMP: FuncScannerBox.skip_whitespace/2 -----\n{}", dump);
|
||||
eprintln!(
|
||||
"----- MIR DUMP: FuncScannerBox.skip_whitespace/2 -----\n{}",
|
||||
dump
|
||||
);
|
||||
}
|
||||
} else {
|
||||
eprintln!("[test] WARNING: FuncScannerBox.skip_whitespace/2 not found in module");
|
||||
|
||||
@ -37,19 +37,18 @@ fn mir_funcscanner_fib_min_ssa_debug() {
|
||||
|
||||
// funcscanner_fib_min.hako と同じソースをそのまま使う
|
||||
let src = include_str!("../../lang/src/compiler/tests/funcscanner_fib_min.hako");
|
||||
let ast: ASTNode = NyashParser::parse_from_string(src).expect("parse funcscanner_fib_min.hako ok");
|
||||
let ast: ASTNode =
|
||||
NyashParser::parse_from_string(src).expect("parse funcscanner_fib_min.hako ok");
|
||||
|
||||
let mut mc = MirCompiler::with_options(false);
|
||||
let cr = mc.compile(ast).expect("compile funcscanner_fib_min.hako ok");
|
||||
let cr = mc
|
||||
.compile(ast)
|
||||
.expect("compile funcscanner_fib_min.hako ok");
|
||||
|
||||
let mut verifier = MirVerifier::new();
|
||||
if let Err(errors) = verifier.verify_module(&cr.module) {
|
||||
// デバッグ時に MIR 全体を見たい場合は NYASH_MIR_TEST_DUMP=1 で有効化
|
||||
if std::env::var("NYASH_MIR_TEST_DUMP")
|
||||
.ok()
|
||||
.as_deref()
|
||||
== Some("1")
|
||||
{
|
||||
if std::env::var("NYASH_MIR_TEST_DUMP").ok().as_deref() == Some("1") {
|
||||
let dump = MirPrinter::new().print_module(&cr.module);
|
||||
eprintln!("----- MIR DUMP (FuncScanner.fib_min) -----\n{}", dump);
|
||||
}
|
||||
@ -83,13 +82,12 @@ fn mir_funcscanner_scan_methods_ssa_debug() {
|
||||
|
||||
let mut verifier = MirVerifier::new();
|
||||
if let Err(errors) = verifier.verify_module(&cr.module) {
|
||||
if std::env::var("NYASH_MIR_TEST_DUMP")
|
||||
.ok()
|
||||
.as_deref()
|
||||
== Some("1")
|
||||
{
|
||||
if std::env::var("NYASH_MIR_TEST_DUMP").ok().as_deref() == Some("1") {
|
||||
let dump = MirPrinter::new().print_module(&cr.module);
|
||||
eprintln!("----- MIR DUMP (FuncScanner.scan_methods_min) -----\n{}", dump);
|
||||
eprintln!(
|
||||
"----- MIR DUMP (FuncScanner.scan_methods_min) -----\n{}",
|
||||
dump
|
||||
);
|
||||
}
|
||||
for e in &errors {
|
||||
eprintln!("[rust-mir-verify] {}", e);
|
||||
|
||||
@ -6,8 +6,8 @@
|
||||
*/
|
||||
|
||||
use crate::ast::ASTNode;
|
||||
use crate::mir::{MirCompiler, MirVerifier};
|
||||
use crate::mir::printer::MirPrinter;
|
||||
use crate::mir::{MirCompiler, MirVerifier};
|
||||
use crate::parser::NyashParser;
|
||||
|
||||
#[test]
|
||||
@ -80,13 +80,24 @@ static box TestLocals {
|
||||
}
|
||||
|
||||
// Assert Copy instructions are present
|
||||
assert!(has_copy, "MIR should contain Copy instructions for local variable initializations");
|
||||
assert!(
|
||||
has_copy,
|
||||
"MIR should contain Copy instructions for local variable initializations"
|
||||
);
|
||||
|
||||
// Assert no SSA violations
|
||||
assert!(violations.is_empty(), "MIR should not have SSA violations: {:?}", violations);
|
||||
assert!(
|
||||
violations.is_empty(),
|
||||
"MIR should not have SSA violations: {:?}",
|
||||
violations
|
||||
);
|
||||
|
||||
// We expect at least 3 copy instructions (for a, b, c)
|
||||
assert!(copy_count >= 3, "Expected at least 3 copy instructions, found {}", copy_count);
|
||||
assert!(
|
||||
copy_count >= 3,
|
||||
"Expected at least 3 copy instructions, found {}",
|
||||
copy_count
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
@ -115,5 +126,8 @@ static box TestUninit {
|
||||
eprintln!("{}", mir_output);
|
||||
|
||||
// Uninitialized locals should have void constants, not copy
|
||||
assert!(mir_output.contains("const void"), "Uninitialized locals should have void constants");
|
||||
assert!(
|
||||
mir_output.contains("const void"),
|
||||
"Uninitialized locals should have void constants"
|
||||
);
|
||||
}
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
use crate::ast::ASTNode;
|
||||
use crate::mir::{MirCompiler, MirVerifier};
|
||||
use crate::mir::printer::MirPrinter;
|
||||
use crate::mir::{MirCompiler, MirVerifier};
|
||||
use crate::parser::NyashParser;
|
||||
|
||||
fn ensure_stage3_env() {
|
||||
@ -188,6 +188,219 @@ static box Stage1UsingResolverFull {
|
||||
}
|
||||
}
|
||||
|
||||
/// Region+next_i パターンのループが SSA 崩れなく MIR 化できることを固定する軽量テスト。
|
||||
#[test]
|
||||
fn mir_stage1_using_resolver_region_loop_verifies() {
|
||||
ensure_stage3_env();
|
||||
let src = r#"
|
||||
static box Stage1UsingResolverRegionLoop {
|
||||
process(entries) {
|
||||
local prefix = ""
|
||||
local i = 0
|
||||
local n = entries.length()
|
||||
loop(i < n) {
|
||||
local next_i = i + 1
|
||||
local entry = entries.get(i)
|
||||
local name = "" + entry.get("name")
|
||||
local ok = 1
|
||||
if name == "" { ok = 0 }
|
||||
if ok == 1 { prefix = prefix + name }
|
||||
i = next_i
|
||||
}
|
||||
return prefix
|
||||
}
|
||||
|
||||
main() {
|
||||
local arr = new ArrayBox()
|
||||
local m1 = new MapBox(); m1.set("name", "A"); arr.push(m1)
|
||||
local m2 = new MapBox(); m2.set("name", ""); arr.push(m2)
|
||||
local m3 = new MapBox(); m3.set("name", "B"); arr.push(m3)
|
||||
return Stage1UsingResolverRegionLoop.process(arr)
|
||||
}
|
||||
}
|
||||
"#;
|
||||
|
||||
let ast: ASTNode = NyashParser::parse_from_string(src).expect("parse ok");
|
||||
let mut mc = MirCompiler::with_options(false);
|
||||
let cr = mc.compile(ast).expect("compile");
|
||||
|
||||
let mut verifier = MirVerifier::new();
|
||||
if let Err(errors) = verifier.verify_module(&cr.module) {
|
||||
for e in &errors {
|
||||
eprintln!("[rust-mir-verify] {}", e);
|
||||
}
|
||||
panic!("MIR verification failed for Stage1UsingResolverRegionLoop");
|
||||
}
|
||||
}
|
||||
|
||||
/// JSON スキャン + early-exit パターンでも PHI/SSA が崩れないことを確認する。
|
||||
#[test]
|
||||
fn mir_stage1_using_resolver_collect_entries_early_exit_verifies() {
|
||||
ensure_stage3_env();
|
||||
let src = r#"
|
||||
static box Stage1UsingResolverEarlyExit {
|
||||
// Minimal substring finder (exclusive start/end) for JSON scanning
|
||||
_find(text, pattern, start_pos) {
|
||||
local tl = text.length()
|
||||
local pl = pattern.length()
|
||||
local i = start_pos
|
||||
loop(i < tl) {
|
||||
if i + pl > tl { return -1 }
|
||||
local j = 0
|
||||
local ok = 1
|
||||
loop(j < pl) {
|
||||
if text.substring(i + j, i + j + 1) != pattern.substring(j, j + 1) {
|
||||
ok = 0
|
||||
break
|
||||
}
|
||||
j = j + 1
|
||||
}
|
||||
if ok == 1 { return i }
|
||||
i = i + 1
|
||||
}
|
||||
return -1
|
||||
}
|
||||
|
||||
_read_until_quote(text, start_pos) {
|
||||
local tl = text.length()
|
||||
local i = start_pos
|
||||
local out = ""
|
||||
loop(i < tl) {
|
||||
local ch = text.substring(i, i + 1)
|
||||
if ch == "\"" { break }
|
||||
out = out + ch
|
||||
i = i + 1
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
collect_entries(stop_name) {
|
||||
// Two objects + one early-exit sentinel; loop uses Region+next_pos 形。
|
||||
local json = "[{\"name\":\"A\"},{\"name\":\"stop\"},{\"name\":\"C\"}]"
|
||||
local pos = 0
|
||||
local n = json.length()
|
||||
local count = 0
|
||||
loop(pos < n) {
|
||||
local next_pos = n
|
||||
local name_idx = me._find(json, "\"name\":\"", pos)
|
||||
if name_idx < 0 {
|
||||
next_pos = n
|
||||
} else {
|
||||
local name = me._read_until_quote(json, name_idx + 8)
|
||||
local obj_end = me._find(json, "}", name_idx)
|
||||
if obj_end < 0 { obj_end = n }
|
||||
count = count + 1
|
||||
if name == stop_name {
|
||||
next_pos = n
|
||||
} else {
|
||||
next_pos = obj_end + 1
|
||||
}
|
||||
}
|
||||
pos = next_pos
|
||||
}
|
||||
return count
|
||||
}
|
||||
|
||||
main() {
|
||||
return me.collect_entries("stop")
|
||||
}
|
||||
}
|
||||
"#;
|
||||
|
||||
let ast: ASTNode = NyashParser::parse_from_string(src).expect("parse ok");
|
||||
let mut mc = MirCompiler::with_options(false);
|
||||
let cr = mc.compile(ast).expect("compile");
|
||||
|
||||
let mut verifier = MirVerifier::new();
|
||||
if let Err(errors) = verifier.verify_module(&cr.module) {
|
||||
for e in &errors {
|
||||
eprintln!("[rust-mir-verify] {}", e);
|
||||
}
|
||||
panic!("MIR verification failed for Stage1UsingResolverEarlyExit");
|
||||
}
|
||||
}
|
||||
|
||||
/// modules_list 分割ループを Region+next_start 形で書いた場合でも SSA が崩れないことを確認する。
|
||||
#[test]
|
||||
fn mir_stage1_using_resolver_module_map_regionized_verifies() {
|
||||
ensure_stage3_env();
|
||||
let src = r#"
|
||||
static box Stage1UsingResolverModuleMap {
|
||||
_find(text, pat, start_pos) {
|
||||
local tl = text.length()
|
||||
local pl = pat.length()
|
||||
local i = start_pos
|
||||
loop(i < tl) {
|
||||
if i + pl > tl { return -1 }
|
||||
local ok = 1
|
||||
local j = 0
|
||||
loop(j < pl) {
|
||||
if text.substring(i + j, i + j + 1) != pat.substring(j, j + 1) {
|
||||
ok = 0
|
||||
break
|
||||
}
|
||||
j = j + 1
|
||||
}
|
||||
if ok == 1 { return i }
|
||||
i = i + 1
|
||||
}
|
||||
return -1
|
||||
}
|
||||
|
||||
build_map(raw) {
|
||||
local map = new MapBox()
|
||||
if raw == null { return map }
|
||||
local delim = "|||"
|
||||
local start = 0
|
||||
local cont = 1
|
||||
loop(cont == 1) {
|
||||
local next_start = raw.length()
|
||||
local next = me._find(raw, delim, start)
|
||||
local seg = ""
|
||||
if next >= 0 {
|
||||
seg = raw.substring(start, next)
|
||||
next_start = next + delim.length()
|
||||
} else {
|
||||
seg = raw.substring(start, raw.length())
|
||||
cont = 0
|
||||
}
|
||||
if seg.length() > 0 {
|
||||
local eq_idx = me._find(seg, "=", 0)
|
||||
if eq_idx >= 0 {
|
||||
local key = seg.substring(0, eq_idx)
|
||||
local val = seg.substring(eq_idx + 1, seg.length())
|
||||
if key != "" && val != "" {
|
||||
map.set(key, val)
|
||||
}
|
||||
}
|
||||
}
|
||||
start = next_start
|
||||
}
|
||||
return map
|
||||
}
|
||||
|
||||
main() {
|
||||
// 2 entries + 空 + 終端
|
||||
local raw = "A=path_a|||B=path_b|||"
|
||||
local m = me.build_map(raw)
|
||||
return m.size()
|
||||
}
|
||||
}
|
||||
"#;
|
||||
|
||||
let ast: ASTNode = NyashParser::parse_from_string(src).expect("parse ok");
|
||||
let mut mc = MirCompiler::with_options(false);
|
||||
let cr = mc.compile(ast).expect("compile");
|
||||
|
||||
let mut verifier = MirVerifier::new();
|
||||
if let Err(errors) = verifier.verify_module(&cr.module) {
|
||||
for e in &errors {
|
||||
eprintln!("[rust-mir-verify] {}", e);
|
||||
}
|
||||
panic!("MIR verification failed for Stage1UsingResolverModuleMap");
|
||||
}
|
||||
}
|
||||
|
||||
/// Verify MIR/SSA for ParserBox.parse_program2 in isolation by compiling a small wrapper.
|
||||
#[test]
|
||||
fn mir_parserbox_parse_program2_harness_parses_minimal_source() {
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
use crate::parser::NyashParser;
|
||||
use crate::mir::MirPrinter;
|
||||
use crate::parser::NyashParser;
|
||||
|
||||
fn ensure_stage3_env() {
|
||||
std::env::set_var("NYASH_PARSER_STAGE3", "1");
|
||||
@ -98,7 +98,10 @@ static box StageBArgsBox {
|
||||
if let Err(errors) = verifier.verify_module(&cr.module) {
|
||||
if std::env::var("NYASH_MIR_TEST_DUMP").ok().as_deref() == Some("1") {
|
||||
let dump = MirPrinter::new().print_module(&cr.module);
|
||||
eprintln!("----- MIR DUMP (StageBArgsBox.process if+loop) -----\n{}", dump);
|
||||
eprintln!(
|
||||
"----- MIR DUMP (StageBArgsBox.process if+loop) -----\n{}",
|
||||
dump
|
||||
);
|
||||
}
|
||||
for e in &errors {
|
||||
eprintln!("[rust-mir-verify] {}", e);
|
||||
@ -154,7 +157,10 @@ static box TestNested {
|
||||
if let Err(errors) = verifier.verify_module(&cr.module) {
|
||||
if std::env::var("NYASH_MIR_TEST_DUMP").ok().as_deref() == Some("1") {
|
||||
let dump = MirPrinter::new().print_module(&cr.module);
|
||||
eprintln!("----- MIR DUMP (TestNested.complex nested if+loop) -----\n{}", dump);
|
||||
eprintln!(
|
||||
"----- MIR DUMP (TestNested.complex nested if+loop) -----\n{}",
|
||||
dump
|
||||
);
|
||||
}
|
||||
for e in &errors {
|
||||
eprintln!("[rust-mir-verify] {}", e);
|
||||
@ -196,7 +202,10 @@ static box StageBArgsBox {
|
||||
if let Err(errors) = verifier.verify_module(&cr.module) {
|
||||
if std::env::var("NYASH_MIR_TEST_DUMP").ok().as_deref() == Some("1") {
|
||||
let dump = MirPrinter::new().print_module(&cr.module);
|
||||
eprintln!("----- MIR DUMP (StageBArgsBox.process loop cond uses length) -----\n{}", dump);
|
||||
eprintln!(
|
||||
"----- MIR DUMP (StageBArgsBox.process loop cond uses length) -----\n{}",
|
||||
dump
|
||||
);
|
||||
}
|
||||
for e in &errors {
|
||||
eprintln!("[rust-mir-verify] {}", e);
|
||||
@ -238,7 +247,10 @@ static box TestNested2 {
|
||||
if let Err(errors) = verifier.verify_module(&cr.module) {
|
||||
if std::env::var("NYASH_MIR_TEST_DUMP").ok().as_deref() == Some("1") {
|
||||
let dump = MirPrinter::new().print_module(&cr.module);
|
||||
eprintln!("----- MIR DUMP (TestNested2.walk conditional+loop length) -----\n{}", dump);
|
||||
eprintln!(
|
||||
"----- MIR DUMP (TestNested2.walk conditional+loop length) -----\n{}",
|
||||
dump
|
||||
);
|
||||
}
|
||||
for e in &errors {
|
||||
eprintln!("[rust-mir-verify] {}", e);
|
||||
@ -286,7 +298,10 @@ static box JsonScanBoxMini {
|
||||
if let Err(errors) = verifier.verify_module(&cr.module) {
|
||||
if std::env::var("NYASH_MIR_TEST_DUMP").ok().as_deref() == Some("1") {
|
||||
let dump = MirPrinter::new().print_module(&cr.module);
|
||||
eprintln!("----- MIR DUMP (JsonScanBoxMini.seek_array_end) -----\n{}", dump);
|
||||
eprintln!(
|
||||
"----- MIR DUMP (JsonScanBoxMini.seek_array_end) -----\n{}",
|
||||
dump
|
||||
);
|
||||
}
|
||||
for e in &errors {
|
||||
eprintln!("[rust-mir-verify] {}", e);
|
||||
|
||||
@ -59,7 +59,10 @@ static box LoopBreakContinueBox {
|
||||
if let Err(errors) = verifier.verify_module(&cr.module) {
|
||||
if std::env::var("NYASH_MIR_TEST_DUMP").ok().as_deref() == Some("1") {
|
||||
let dump = MirPrinter::new().print_module(&cr.module);
|
||||
eprintln!("----- MIR DUMP (LoopBreakContinueBox.sum_positive_until_null) -----\n{}", dump);
|
||||
eprintln!(
|
||||
"----- MIR DUMP (LoopBreakContinueBox.sum_positive_until_null) -----\n{}",
|
||||
dump
|
||||
);
|
||||
}
|
||||
for e in &errors {
|
||||
eprintln!("[rust-mir-verify] {}", e);
|
||||
@ -130,7 +133,10 @@ static box LoopNestedBreakBox {
|
||||
if let Err(errors) = verifier.verify_module(&cr.module) {
|
||||
if std::env::var("NYASH_MIR_TEST_DUMP").ok().as_deref() == Some("1") {
|
||||
let dump = MirPrinter::new().print_module(&cr.module);
|
||||
eprintln!("----- MIR DUMP (LoopNestedBreakBox.nested_walk) -----\n{}", dump);
|
||||
eprintln!(
|
||||
"----- MIR DUMP (LoopNestedBreakBox.nested_walk) -----\n{}",
|
||||
dump
|
||||
);
|
||||
}
|
||||
for e in &errors {
|
||||
eprintln!("[rust-mir-verify] {}", e);
|
||||
|
||||
@ -4,7 +4,6 @@
|
||||
/// - パラメータ型自動登録(Phase 26-A-3)
|
||||
/// - is_parameter型安全判定(Phase 26-A-4)
|
||||
/// - 実際のMIRビルド環境での動作確認
|
||||
|
||||
use crate::mir::{MirBuilder, MirValueKind, ValueId};
|
||||
|
||||
/// GUARD checkバグ完全再現防止テスト
|
||||
@ -94,9 +93,18 @@ fn test_instance_method_parameters() {
|
||||
assert!(!builder.is_value_parameter(ValueId(3)), "not parameter");
|
||||
|
||||
// 型情報確認
|
||||
assert_eq!(builder.get_value_kind(ValueId(0)), Some(MirValueKind::Parameter(0)));
|
||||
assert_eq!(builder.get_value_kind(ValueId(1)), Some(MirValueKind::Parameter(1)));
|
||||
assert_eq!(builder.get_value_kind(ValueId(2)), Some(MirValueKind::Parameter(2)));
|
||||
assert_eq!(
|
||||
builder.get_value_kind(ValueId(0)),
|
||||
Some(MirValueKind::Parameter(0))
|
||||
);
|
||||
assert_eq!(
|
||||
builder.get_value_kind(ValueId(1)),
|
||||
Some(MirValueKind::Parameter(1))
|
||||
);
|
||||
assert_eq!(
|
||||
builder.get_value_kind(ValueId(2)),
|
||||
Some(MirValueKind::Parameter(2))
|
||||
);
|
||||
}
|
||||
|
||||
/// ループ内でのパラメータ/ローカル変数の区別テスト
|
||||
@ -108,20 +116,38 @@ fn test_loop_parameter_vs_local_distinction() {
|
||||
|
||||
// Phase 26-A-2: new_typed_value() で各種ValueId作成
|
||||
let limit = builder.new_typed_value(MirValueKind::Parameter(0)); // パラメータ
|
||||
let i = builder.new_typed_value(MirValueKind::Local(0)); // ローカル変数
|
||||
let sum = builder.new_typed_value(MirValueKind::Local(1)); // ローカル変数
|
||||
let i = builder.new_typed_value(MirValueKind::Local(0)); // ローカル変数
|
||||
let sum = builder.new_typed_value(MirValueKind::Local(1)); // ローカル変数
|
||||
let carrier = builder.new_typed_value(MirValueKind::LoopCarrier); // ループキャリア
|
||||
|
||||
// パラメータ判定
|
||||
assert!(builder.is_value_parameter(limit.value_id()), "limit はパラメータ");
|
||||
assert!(!builder.is_value_parameter(i.value_id()), "i はローカル変数");
|
||||
assert!(!builder.is_value_parameter(sum.value_id()), "sum はローカル変数");
|
||||
assert!(!builder.is_value_parameter(carrier.value_id()), "carrier はループキャリア");
|
||||
assert!(
|
||||
builder.is_value_parameter(limit.value_id()),
|
||||
"limit はパラメータ"
|
||||
);
|
||||
assert!(
|
||||
!builder.is_value_parameter(i.value_id()),
|
||||
"i はローカル変数"
|
||||
);
|
||||
assert!(
|
||||
!builder.is_value_parameter(sum.value_id()),
|
||||
"sum はローカル変数"
|
||||
);
|
||||
assert!(
|
||||
!builder.is_value_parameter(carrier.value_id()),
|
||||
"carrier はループキャリア"
|
||||
);
|
||||
|
||||
// 型情報確認
|
||||
assert_eq!(builder.get_value_kind(limit.value_id()), Some(MirValueKind::Parameter(0)));
|
||||
assert_eq!(
|
||||
builder.get_value_kind(limit.value_id()),
|
||||
Some(MirValueKind::Parameter(0))
|
||||
);
|
||||
assert!(builder.get_value_kind(i.value_id()).unwrap().is_local());
|
||||
assert!(builder.get_value_kind(carrier.value_id()).unwrap().is_loop_carrier());
|
||||
assert!(builder
|
||||
.get_value_kind(carrier.value_id())
|
||||
.unwrap()
|
||||
.is_loop_carrier());
|
||||
}
|
||||
|
||||
/// パラメータなし関数のテスト
|
||||
|
||||
@ -6,14 +6,16 @@ pub mod identical_exec;
|
||||
pub mod identical_exec_collections;
|
||||
pub mod identical_exec_instance;
|
||||
pub mod identical_exec_string;
|
||||
pub mod mir_breakfinder_ssa;
|
||||
pub mod mir_funcscanner_skip_ws;
|
||||
pub mod mir_funcscanner_ssa;
|
||||
pub mod mir_locals_ssa;
|
||||
pub mod mir_loopform_conditional_reassign;
|
||||
pub mod mir_loopform_exit_phi;
|
||||
pub mod mir_loopform_complex;
|
||||
pub mod mir_stage1_using_resolver_verify;
|
||||
pub mod mir_stageb_like_args_length;
|
||||
pub mod mir_stageb_loop_break_continue;
|
||||
pub mod mir_stage1_using_resolver_verify;
|
||||
pub mod mir_locals_ssa;
|
||||
pub mod mir_loopform_exit_phi;
|
||||
pub mod mir_breakfinder_ssa;
|
||||
pub mod mir_funcscanner_ssa;
|
||||
pub mod mir_funcscanner_skip_ws;
|
||||
pub mod mir_value_kind; // Phase 26-A-5: ValueId型安全化統合テスト
|
||||
pub mod nyash_abi_basic;
|
||||
pub mod parser_static_box_members;
|
||||
|
||||
@ -1,19 +1,27 @@
|
||||
use crate::parser::NyashParser;
|
||||
|
||||
fn parse(src: &str) -> crate::ast::ASTNode { NyashParser::parse_from_string(src).expect("parse ok") }
|
||||
fn parse(src: &str) -> crate::ast::ASTNode {
|
||||
NyashParser::parse_from_string(src).expect("parse ok")
|
||||
}
|
||||
|
||||
fn no_toplevel_funccall(ast: &crate::ast::ASTNode) -> bool {
|
||||
match ast {
|
||||
crate::ast::ASTNode::Program { statements, .. } => {
|
||||
!statements.iter().any(|n| matches!(n, crate::ast::ASTNode::FunctionCall { .. }))
|
||||
}
|
||||
crate::ast::ASTNode::Program { statements, .. } => !statements
|
||||
.iter()
|
||||
.any(|n| matches!(n, crate::ast::ASTNode::FunctionCall { .. })),
|
||||
_ => true,
|
||||
}
|
||||
}
|
||||
|
||||
fn box_has_methods(ast: &crate::ast::ASTNode, box_name: &str, methods: &[&str]) -> bool {
|
||||
fn check_box(b: &crate::ast::ASTNode, box_name: &str, methods: &[&str]) -> bool {
|
||||
if let crate::ast::ASTNode::BoxDeclaration { name, methods: m, is_static, .. } = b {
|
||||
if let crate::ast::ASTNode::BoxDeclaration {
|
||||
name,
|
||||
methods: m,
|
||||
is_static,
|
||||
..
|
||||
} = b
|
||||
{
|
||||
if name == box_name && *is_static {
|
||||
return methods.iter().all(|k| {
|
||||
if let Some(node) = m.get(*k) {
|
||||
@ -25,7 +33,9 @@ fn box_has_methods(ast: &crate::ast::ASTNode, box_name: &str, methods: &[&str])
|
||||
false
|
||||
}
|
||||
match ast {
|
||||
crate::ast::ASTNode::Program { statements, .. } => statements.iter().any(|n| check_box(n, box_name, methods)),
|
||||
crate::ast::ASTNode::Program { statements, .. } => {
|
||||
statements.iter().any(|n| check_box(n, box_name, methods))
|
||||
}
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
@ -39,8 +49,14 @@ static box S {
|
||||
}
|
||||
"#;
|
||||
let ast = parse(src);
|
||||
assert!(no_toplevel_funccall(&ast), "no top-level FunctionCall expected");
|
||||
assert!(box_has_methods(&ast, "S", &["f", "g"]), "static box S should have f and g methods");
|
||||
assert!(
|
||||
no_toplevel_funccall(&ast),
|
||||
"no top-level FunctionCall expected"
|
||||
);
|
||||
assert!(
|
||||
box_has_methods(&ast, "S", &["f", "g"]),
|
||||
"static box S should have f and g methods"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
@ -59,7 +75,13 @@ static box S {
|
||||
}
|
||||
"#;
|
||||
let ast = parse(src);
|
||||
assert!(no_toplevel_funccall(&ast), "no top-level FunctionCall expected at seams");
|
||||
assert!(box_has_methods(&ast, "S", &["parse_float", "is_empty_or_whitespace"]));
|
||||
assert!(
|
||||
no_toplevel_funccall(&ast),
|
||||
"no top-level FunctionCall expected at seams"
|
||||
);
|
||||
assert!(box_has_methods(
|
||||
&ast,
|
||||
"S",
|
||||
&["parse_float", "is_empty_or_whitespace"]
|
||||
));
|
||||
}
|
||||
|
||||
|
||||
@ -67,13 +67,7 @@ mod tests {
|
||||
.unwrap_or_else(|| panic!("{}::{} returned None", box_ty, method))
|
||||
}
|
||||
|
||||
fn inv_void(
|
||||
h: &PluginHost,
|
||||
box_ty: &str,
|
||||
method: &str,
|
||||
id: u32,
|
||||
args: &[Box<dyn NyashBox>],
|
||||
) {
|
||||
fn inv_void(h: &PluginHost, box_ty: &str, method: &str, id: u32, args: &[Box<dyn NyashBox>]) {
|
||||
let _ = h
|
||||
.invoke_instance_method(box_ty, method, id, args)
|
||||
.expect(&format!("invoke {}::{}", box_ty, method));
|
||||
@ -104,7 +98,13 @@ mod tests {
|
||||
let _g = EnvGuard::set("NYASH_DISABLE_TYPEBOX", "1");
|
||||
let (bt1, id1, _hold1) = create_plugin_instance("MapBox");
|
||||
let out_tlv = with_host(|h| {
|
||||
inv_void(h, &bt1, "set", id1, &[Box::new(StringBox::new("k")), Box::new(IntegerBox::new(42))]);
|
||||
inv_void(
|
||||
h,
|
||||
&bt1,
|
||||
"set",
|
||||
id1,
|
||||
&[Box::new(StringBox::new("k")), Box::new(IntegerBox::new(42))],
|
||||
);
|
||||
let sz = inv_some(h, &bt1, "size", id1, &[]);
|
||||
let gv = inv_some(h, &bt1, "get", id1, &[Box::new(StringBox::new("k"))]);
|
||||
(sz.to_string_box().value, gv.to_string_box().value)
|
||||
@ -114,7 +114,13 @@ mod tests {
|
||||
let _g2 = EnvGuard::remove("NYASH_DISABLE_TYPEBOX");
|
||||
let (bt2, id2, _hold2) = create_plugin_instance("MapBox");
|
||||
let out_tb = with_host(|h| {
|
||||
inv_void(h, &bt2, "set", id2, &[Box::new(StringBox::new("k")), Box::new(IntegerBox::new(42))]);
|
||||
inv_void(
|
||||
h,
|
||||
&bt2,
|
||||
"set",
|
||||
id2,
|
||||
&[Box::new(StringBox::new("k")), Box::new(IntegerBox::new(42))],
|
||||
);
|
||||
let sz = inv_some(h, &bt2, "size", id2, &[]);
|
||||
let gv = inv_some(h, &bt2, "get", id2, &[Box::new(StringBox::new("k"))]);
|
||||
(sz.to_string_box().value, gv.to_string_box().value)
|
||||
@ -131,7 +137,13 @@ mod tests {
|
||||
let _g = EnvGuard::set("NYASH_DISABLE_TYPEBOX", "1");
|
||||
let (bt1, id1, _hold1) = create_plugin_instance("ArrayBox");
|
||||
let out_tlv = with_host(|h| {
|
||||
inv_void(h, &bt1, "set", id1, &[Box::new(IntegerBox::new(0)), Box::new(IntegerBox::new(7))]);
|
||||
inv_void(
|
||||
h,
|
||||
&bt1,
|
||||
"set",
|
||||
id1,
|
||||
&[Box::new(IntegerBox::new(0)), Box::new(IntegerBox::new(7))],
|
||||
);
|
||||
let ln = inv_some(h, &bt1, "len", id1, &[]);
|
||||
let gv = inv_some(h, &bt1, "get", id1, &[Box::new(IntegerBox::new(0))]);
|
||||
(ln.to_string_box().value, gv.to_string_box().value)
|
||||
@ -140,7 +152,13 @@ mod tests {
|
||||
let _g2 = EnvGuard::remove("NYASH_DISABLE_TYPEBOX");
|
||||
let (bt2, id2, _hold2) = create_plugin_instance("ArrayBox");
|
||||
let out_tb = with_host(|h| {
|
||||
inv_void(h, &bt2, "set", id2, &[Box::new(IntegerBox::new(0)), Box::new(IntegerBox::new(7))]);
|
||||
inv_void(
|
||||
h,
|
||||
&bt2,
|
||||
"set",
|
||||
id2,
|
||||
&[Box::new(IntegerBox::new(0)), Box::new(IntegerBox::new(7))],
|
||||
);
|
||||
let ln = inv_some(h, &bt2, "length", id2, &[]);
|
||||
let gv = inv_some(h, &bt2, "get", id2, &[Box::new(IntegerBox::new(0))]);
|
||||
(ln.to_string_box().value, gv.to_string_box().value)
|
||||
@ -338,7 +356,13 @@ mod tests {
|
||||
let (bt1, id1, _hold1) = create_plugin_instance("RegexBox");
|
||||
let out_tlv = with_host(|h| {
|
||||
inv_void(h, &bt1, "compile", id1, &[Box::new(StringBox::new("h.+o"))]);
|
||||
let m = inv_some(h, &bt1, "isMatch", id1, &[Box::new(StringBox::new("hello"))]);
|
||||
let m = inv_some(
|
||||
h,
|
||||
&bt1,
|
||||
"isMatch",
|
||||
id1,
|
||||
&[Box::new(StringBox::new("hello"))],
|
||||
);
|
||||
let f = inv_some(h, &bt1, "find", id1, &[Box::new(StringBox::new("hello"))]);
|
||||
(m.to_string_box().value, f.to_string_box().value)
|
||||
});
|
||||
@ -348,7 +372,13 @@ mod tests {
|
||||
let (bt2, id2, _hold2) = create_plugin_instance("RegexBox");
|
||||
let out_tb = with_host(|h| {
|
||||
inv_void(h, &bt2, "compile", id2, &[Box::new(StringBox::new("h.+o"))]);
|
||||
let m = inv_some(h, &bt2, "isMatch", id2, &[Box::new(StringBox::new("hello"))]);
|
||||
let m = inv_some(
|
||||
h,
|
||||
&bt2,
|
||||
"isMatch",
|
||||
id2,
|
||||
&[Box::new(StringBox::new("hello"))],
|
||||
);
|
||||
let f = inv_some(h, &bt2, "find", id2, &[Box::new(StringBox::new("hello"))]);
|
||||
(m.to_string_box().value, f.to_string_box().value)
|
||||
});
|
||||
@ -378,8 +408,20 @@ mod tests {
|
||||
Box::new(StringBox::new("c.txt")),
|
||||
],
|
||||
);
|
||||
let d = inv_some(h, &bt1, "dirname", id1, &[Box::new(StringBox::new("/a/b/c.txt"))]);
|
||||
let b = inv_some(h, &bt1, "basename", id1, &[Box::new(StringBox::new("/a/b/c.txt"))]);
|
||||
let d = inv_some(
|
||||
h,
|
||||
&bt1,
|
||||
"dirname",
|
||||
id1,
|
||||
&[Box::new(StringBox::new("/a/b/c.txt"))],
|
||||
);
|
||||
let b = inv_some(
|
||||
h,
|
||||
&bt1,
|
||||
"basename",
|
||||
id1,
|
||||
&[Box::new(StringBox::new("/a/b/c.txt"))],
|
||||
);
|
||||
let n = inv_some(
|
||||
h,
|
||||
&bt1,
|
||||
@ -409,8 +451,20 @@ mod tests {
|
||||
Box::new(StringBox::new("c.txt")),
|
||||
],
|
||||
);
|
||||
let d = inv_some(h, &bt2, "dirname", id2, &[Box::new(StringBox::new("/a/b/c.txt"))]);
|
||||
let b = inv_some(h, &bt2, "basename", id2, &[Box::new(StringBox::new("/a/b/c.txt"))]);
|
||||
let d = inv_some(
|
||||
h,
|
||||
&bt2,
|
||||
"dirname",
|
||||
id2,
|
||||
&[Box::new(StringBox::new("/a/b/c.txt"))],
|
||||
);
|
||||
let b = inv_some(
|
||||
h,
|
||||
&bt2,
|
||||
"basename",
|
||||
id2,
|
||||
&[Box::new(StringBox::new("/a/b/c.txt"))],
|
||||
);
|
||||
let n = inv_some(
|
||||
h,
|
||||
&bt2,
|
||||
|
||||
Reference in New Issue
Block a user