fix(dx): Quick Win 1-3 for better error messages and API simplification

Quick Win 1: Show available boxes on "Unknown Box type" error
- vm.rs, vm_fallback.rs: Display sorted list of available user-defined boxes
- Before: "Unknown Box type: Foo"
- After:  "Unknown Box type: Foo. Available: Bar, Baz, Main"

Quick Win 2: Show stderr on child process timeout
- child.rs, selfhost_exe.rs: Capture and display stderr (up to 500 chars)
- Helps diagnose what went wrong in selfhost compiler child process

Quick Win 3: Simplify Stage-B compiler API (SSOT)
- compiler_stageb.hako: Add StageBDriverBox.compile() as single entry point
- compiler_stageb.hako: Remove StageBMain compatibility wrapper
- compiler.hako: Change from `using ... as StageBMain` to direct import
- compiler.hako: Call StageBDriverBox.compile() directly

Also includes child_env.rs NYASH_MODULES env var for module mapping.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
nyash-codex
2025-11-25 08:44:31 +09:00
parent 6726ee246d
commit c479e5f527
7 changed files with 190 additions and 15 deletions

View File

@ -232,13 +232,32 @@ impl NyashRunner {
args: &[Box<dyn crate::box_trait::NyashBox>],
) -> Result<Box<dyn crate::box_trait::NyashBox>, RuntimeError>
{
let opt = { self.decls.read().unwrap().get(name).cloned() };
let guard = self.decls.read().unwrap();
let opt = guard.get(name).cloned();
let decl = match opt {
Some(d) => d,
Some(d) => {
drop(guard);
d
}
None => {
// Quick Win 1: Show available boxes for easier debugging
let mut available: Vec<_> = guard.keys().cloned().collect();
available.sort();
drop(guard);
let hint = if available.is_empty() {
"No user-defined boxes available".to_string()
} else if available.len() <= 10 {
format!("Available: {}", available.join(", "))
} else {
format!(
"Available ({} boxes): {}, ...",
available.len(),
available[..10].join(", ")
)
};
return Err(RuntimeError::InvalidOperation {
message: format!("Unknown Box type: {}", name),
})
message: format!("Unknown Box type: {}. {}", name, hint),
});
}
};
let mut inst = InstanceBox::from_declaration(
@ -446,13 +465,32 @@ impl NyashRunner {
args: &[Box<dyn crate::box_trait::NyashBox>],
) -> Result<Box<dyn crate::box_trait::NyashBox>, RuntimeError>
{
let opt = { self.decls.read().unwrap().get(name).cloned() };
let guard = self.decls.read().unwrap();
let opt = guard.get(name).cloned();
let decl = match opt {
Some(d) => d,
Some(d) => {
drop(guard);
d
}
None => {
// Quick Win 1: Show available boxes for easier debugging
let mut available: Vec<_> = guard.keys().cloned().collect();
available.sort();
drop(guard);
let hint = if available.is_empty() {
"No user-defined boxes available".to_string()
} else if available.len() <= 10 {
format!("Available: {}", available.join(", "))
} else {
format!(
"Available ({} boxes): {}, ...",
available.len(),
available[..10].join(", ")
)
};
return Err(RuntimeError::InvalidOperation {
message: format!("Unknown Box type: {}", name),
})
message: format!("Unknown Box type: {}. {}", name, hint),
});
}
};
let mut inst = InstanceBox::from_declaration(