Phase 21.3 WIP: Hako Source Checker improvements

## 🎯 Checker/Analyzer拡張

###  実装追加
- テストフレームワーク追加(tools/hako_check/tests/)
- ルール改善(HC003グローバルassign、HC040静的箱トップレベルassign)
- テストランナー(run_tests.sh)

### 🔧 Rust側修正
- AST utilities拡張(src/ast/utils.rs)
- MIR lowerers新設(src/mir/lowerers/)
- Parser制御フロー改善(src/parser/statements/control_flow.rs)
- Tokenizer識別子処理改善(src/tokenizer/lex_ident.rs)

### 📁 主要変更
- tools/hako_check/cli.hako - CLI改善
- tools/hako_check/hako_source_checker.hako - Checker core更新
- tools/hako_check/tests/ - NEW (テストケース追加)
- tools/hako_check/run_tests.sh - NEW (テストランナー)
- src/mir/lowerers/ - NEW (MIR lowering utilities)

🤖 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-07 21:04:01 +09:00
parent b8fbbafc0c
commit 86489ffe43
20 changed files with 547 additions and 165 deletions

View File

@ -26,71 +26,47 @@ impl NyashRunner {
process::exit(1);
}
};
// Using preprocessing: AST prelude merge.hako/Hakoライクは強制AST
let mut code2 = code.clone();
if crate::config::env::enable_using() {
let mut use_ast = crate::config::env::using_ast_enabled();
let is_hako = filename.ends_with(".hako")
|| crate::runner::modes::common_util::hako::looks_like_hako_code(&code2);
if is_hako { use_ast = true; }
if use_ast {
match crate::runner::modes::common_util::resolve::resolve_prelude_paths_profiled(self, &code2, filename) {
Ok((clean, paths)) => {
// If any prelude is .hako, prefer text-merge (Hakorune surface is not Nyash AST)
let has_hako = paths.iter().any(|p| p.ends_with(".hako"));
if has_hako {
match crate::runner::modes::common_util::resolve::merge_prelude_text(self, &code2, filename) {
Ok(merged) => {
if std::env::var("NYASH_RESOLVE_TRACE").ok().as_deref() == Some("1") {
eprintln!("[using/text-merge] preludes={} (vm-fallback)", paths.len());
}
code2 = merged;
let trace = crate::config::env::cli_verbose()
|| crate::config::env::env_bool("NYASH_RESOLVE_TRACE");
// Unified using/prelude handling (SSOT, parity with vm.rs):
// - resolve_prelude_paths_profiled で preludes を発見
// - merge_prelude_text で text-merge.hako は AST parse しない)
let mut code2 = if crate::config::env::enable_using() {
match crate::runner::modes::common_util::resolve::resolve_prelude_paths_profiled(
self,
&code,
filename,
) {
Ok((_, prelude_paths)) => {
if !prelude_paths.is_empty() {
match crate::runner::modes::common_util::resolve::merge_prelude_text(
self,
&code,
filename,
) {
Ok(merged) => {
if trace {
eprintln!(
"[using/text-merge] preludes={} (vm-fallback)",
prelude_paths.len()
);
}
Err(e) => { eprintln!("{}", e); process::exit(1); }
merged
}
// Fall through to normal parse of merged text below
} else {
// AST prelude merge path
code2 = clean;
let preexpanded = crate::runner::modes::common_util::resolve::preexpand_at_local(&code2);
code2 = preexpanded;
if crate::runner::modes::common_util::hako::looks_like_hako_code(&code2) {
code2 = crate::runner::modes::common_util::hako::strip_local_decl(&code2);
}
let main_ast = match NyashParser::parse_from_string(&code2) {
Ok(ast) => ast,
Err(e) => { eprintln!("❌ Parse error in {}: {}", filename, e); process::exit(1); }
};
if !paths.is_empty() {
match crate::runner::modes::common_util::resolve::parse_preludes_to_asts(self, &paths) {
Ok(v) => {
if std::env::var("NYASH_RESOLVE_TRACE").ok().as_deref() == Some("1") {
eprintln!("[using/ast-merge] preludes={} (vm-fallback)", v.len());
}
let ast = crate::runner::modes::common_util::resolve::merge_prelude_asts_with_main(v, &main_ast);
self.execute_vm_fallback_from_ast(filename, ast);
return; // done
}
Err(e) => { eprintln!("{}", e); process::exit(1); }
}
} else {
self.execute_vm_fallback_from_ast(filename, main_ast);
return;
Err(e) => {
eprintln!("{}", e);
process::exit(1);
}
}
} else {
code.clone()
}
Err(e) => { eprintln!("{}", e); process::exit(1); }
}
} else {
// Fallback: text-prelude merge言語非依存
match crate::runner::modes::common_util::resolve::merge_prelude_text(self, &code2, filename) {
Ok(merged) => {
if std::env::var("NYASH_RESOLVE_TRACE").ok().as_deref() == Some("1") {
eprintln!("[using/text-merge] applied (vm-fallback): {} bytes", merged.len());
}
code2 = merged;
}
Err(e) => { eprintln!("❌ using text merge error: {}", e); process::exit(1); }
Err(e) => {
eprintln!("{}", e);
process::exit(1);
}
}
} else {
@ -101,14 +77,25 @@ impl NyashRunner {
);
process::exit(1);
}
}
code
};
// Dev sugar pre-expand: @name = expr → local name = expr
code2 = crate::runner::modes::common_util::resolve::preexpand_at_local(&code2);
// Hako-friendly normalize: strip leading `local ` at line head for Nyash parser compatibility.
if crate::runner::modes::common_util::hako::looks_like_hako_code(&code2) {
// Hako-friendly normalize
if crate::runner::modes::common_util::hako::looks_like_hako_code(&code2)
|| filename.ends_with(".hako")
{
code2 = crate::runner::modes::common_util::hako::strip_local_decl(&code2);
}
if trace && (std::env::var("NYASH_PARSER_STAGE3").ok() == Some("1".into())
|| std::env::var("HAKO_PARSER_STAGE3").ok() == Some("1".into()))
{
eprintln!("[vm-fallback] Stage-3: enabled (env) for {}", filename);
}
// FailFast (optin): Hako 構文を Nyash VM 経路で実行しない
// 目的: .hako は Hakorune VM、MIR は Core/LLVM に役割分離するためのガード
{
@ -436,7 +423,6 @@ impl NyashRunner {
let mut interp = MirInterpreter::new();
match interp.execute_module(&module) {
Ok(result) => {
// Normalize display (avoid nonexistent coerce_to_exit_code here)
use nyash_rust::box_trait::{BoolBox, IntegerBox};
let rc = if let Some(ib) = result.as_any().downcast_ref::<IntegerBox>() {
ib.value as i32
@ -448,13 +434,15 @@ impl NyashRunner {
// For CAPI pure pipeline, suppress "RC:" text to keep last line = exe path
let capi = std::env::var("NYASH_LLVM_USE_CAPI").ok().as_deref() == Some("1");
let pure = std::env::var("HAKO_CAPI_PURE").ok().as_deref() == Some("1");
if capi && pure {
process::exit(rc);
} else {
if !(capi && pure) {
println!("RC: {}", rc);
}
process::exit(rc);
}
Err(e) => {
eprintln!("❌ VM fallback runtime error: {}", e);
process::exit(1);
}
Err(e) => { eprintln!("❌ VM fallback runtime error: {}", e); process::exit(1); }
}
}
}