refactor(mir): Phase 137-6-S2 - dev-only で canonicalizer decision を提案として受け取る

## 目的
Canonicalizer の decision を router に差し込む(既定挙動不変)

## 変更内容

### choose_pattern_kind() に parity check 統合
- dev-only 時に Canonicalizer を呼び出し
- router_choice と canonical_choice を比較
- 不一致時の動作:
  - strict mode (`HAKO_JOINIR_STRICT=1`): panic (Fail-Fast)
  - debug mode (`NYASH_JOINIR_DEV=1`): ログのみ
- 既定挙動: router_choice を維持(Canonicalizer は提案のみ)

### ログ出力
```
[choose_pattern_kind/PARITY] OK: canonical and actual agree on Pattern2Break
```

## 効果
-  Canonicalizer → Router の parity check 統合
-  SSOT 入口での一致性検証
-  既定挙動完全不変(フラグOFF時)
-  新 env 追加なし(既存の `joinir_dev_enabled()` と `strict_enabled()` を使用)

## テスト結果
-  `cargo build --release`: 成功
-  skip_whitespace: parity green
  ```
  NYASH_JOINIR_DEV=1 HAKO_JOINIR_STRICT=1 ./target/release/hakorune \
    tools/selfhost/test_pattern3_skip_whitespace.hako
  → [choose_pattern_kind/PARITY] OK
  ```
-  スモークテスト(simple_*): 5/5 PASS
-  退行なし

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

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
nyash-codex
2025-12-16 07:42:35 +09:00
parent fc4c343d88
commit 91d7607682
2 changed files with 52 additions and 30 deletions

View File

@ -36,37 +36,15 @@ pub static OPERATORS_DIV_RULES: &[(&str, &str, &str, &str)] = &[
]; ];
pub fn lookup_keyword(word: &str) -> Option<&'static str> { pub fn lookup_keyword(word: &str) -> Option<&'static str> {
for (k, t) in KEYWORDS { for (k, t) in KEYWORDS {
if *k == word { return Some(*t); } if *k == word {
return Some(*t);
}
} }
None None
} }
pub static SYNTAX_ALLOWED_STATEMENTS: &[&str] = &[ pub static SYNTAX_ALLOWED_STATEMENTS: &[&str] = &[
"box", "box", "global", "function", "static", "if", "loop", "break", "return", "print", "nowait",
"global", "include", "local", "outbox", "try", "throw", "using", "from",
"function",
"static",
"if",
"loop",
"break",
"return",
"print",
"nowait",
"include",
"local",
"outbox",
"try",
"throw",
"using",
"from",
]; ];
pub static SYNTAX_ALLOWED_BINOPS: &[&str] = &[ pub static SYNTAX_ALLOWED_BINOPS: &[&str] = &["add", "sub", "mul", "div", "and", "or", "eq", "ne"];
"add",
"sub",
"mul",
"div",
"and",
"or",
"eq",
"ne",
];

View File

@ -11,6 +11,7 @@ use crate::mir::ValueId;
/// 将来的には Canonicalizer decision に委譲する。 /// 将来的には Canonicalizer decision に委譲する。
/// ///
/// Phase 137-6-S1: 現時点では既存の router ロジックLoopFeatures ベース)を使用 /// Phase 137-6-S1: 現時点では既存の router ロジックLoopFeatures ベース)を使用
/// Phase 137-6-S2: dev-only で canonicalizer decision を提案として受け取る
pub(in crate::mir::builder) fn choose_pattern_kind( pub(in crate::mir::builder) fn choose_pattern_kind(
condition: &ASTNode, condition: &ASTNode,
body: &[ASTNode], body: &[ASTNode],
@ -25,8 +26,51 @@ pub(in crate::mir::builder) fn choose_pattern_kind(
// Phase 193: Extract features using modularized extractor // Phase 193: Extract features using modularized extractor
let features = ast_features::extract_features(condition, body, has_continue, has_break); let features = ast_features::extract_features(condition, body, has_continue, has_break);
// Phase 192: Classify pattern based on features // Phase 192: Classify pattern based on features (既存の router 結果)
loop_pattern_detection::classify(&features) let router_choice = loop_pattern_detection::classify(&features);
// Phase 137-6-S2: dev-only で Canonicalizer の提案を取得
if crate::config::env::joinir_dev_enabled() {
use crate::ast::Span;
use crate::mir::loop_canonicalizer::canonicalize_loop_expr;
let loop_ast = ASTNode::Loop {
condition: Box::new(condition.clone()),
body: body.to_vec(),
span: Span::unknown(),
};
if let Ok((_skeleton, decision)) = canonicalize_loop_expr(&loop_ast) {
if let Some(canonical_choice) = decision.chosen {
// parity check
if canonical_choice != router_choice {
let msg = format!(
"[choose_pattern_kind/PARITY] router={:?}, canonicalizer={:?}",
router_choice, canonical_choice
);
if crate::config::env::joinir_dev::strict_enabled() {
// strict mode: 不一致は Fail-Fast
panic!("{}", msg);
} else {
// debug mode: ログのみ
eprintln!("{}", msg);
}
} else {
// Patterns match - success!
eprintln!(
"[choose_pattern_kind/PARITY] OK: canonical and actual agree on {:?}",
canonical_choice
);
}
// TODO (Phase 137-6-S3): ここで canonical_choice を返す
// 現時点では router_choice を維持(既定挙動不変)
}
}
}
router_choice
} }
impl MirBuilder { impl MirBuilder {