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:
nyash-codex
2025-11-21 06:25:17 +09:00
parent baf028a94f
commit f9d100ce01
366 changed files with 14322 additions and 5236 deletions

View File

@ -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,

View File

@ -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

View File

@ -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");

View File

@ -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);

View File

@ -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"
);
}

View File

@ -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() {

View File

@ -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);

View File

@ -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);

View File

@ -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());
}
/// パラメータなし関数のテスト

View File

@ -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;

View File

@ -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"]
));
}

View File

@ -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,