Files
hakorune/docs/development/roadmap/phases/phase-11.9/advanced-designs/box-first-grammar-architecture.md

9.3 KiB

Box-First統一文法アーキテクチャ再設計

🚨 現在の設計の問題点

1. 密結合の罠

// ❌ 現在の設計: 各層がUnifiedGrammarEngineに直接依存
impl Tokenizer {
    fn tokenize(&mut self) {
        self.engine.is_keyword() // 直接参照!
    }
}

2. 根が這う実装

// ❌ UnifiedKeyword構造体が全層の情報を持つ
struct UnifiedKeyword {
    token_type: TokenType,      // Tokenizer層
    semantic_action: Action,    // Parser層
    mir_instruction: MirOp,     // MIR層
    vm_opcode: VmOp,           // VM層
    jit_pattern: JitPattern,    // JIT層
    // すべてが絡み合っている!
}

3. 巨大な神オブジェクト

// ❌ UnifiedGrammarEngineが全てを知っている
struct UnifiedGrammarEngine {
    keywords: KeywordRegistry,
    syntax: SyntaxRules,
    semantics: SemanticRules,
    execution: ExecutionSemantics,
    // 責任が多すぎる!
}

🎯 Box-First再設計

核心思想: 「箱に入れて、箱同士をつなぐ」

┌─────────────┐     ┌─────────────┐     ┌─────────────┐
│ GrammarBox  │     │ TokenBox    │     │ ParserBox   │
│ (定義のみ)  │ --> │ (Token化)   │ --> │ (構文解析)  │
└─────────────┘     └─────────────┘     └─────────────┘
       |                                         |
       v                                         v
┌─────────────┐                         ┌─────────────┐
│ SemanticBox │ <---------------------- │   ASTBox    │
│ (意味解釈)  │                         │ (構文木)    │
└─────────────┘                         └─────────────┘
       |
       v
┌─────────────┐     ┌─────────────┐     ┌─────────────┐
│   MIRBox    │ --> │    VMBox    │     │   JITBox    │
│ (中間表現)  │     │ (実行)      │     │ (コンパイル) │
└─────────────┘     └─────────────┘     └─────────────┘

📦 各箱の責任と境界

1. GrammarBox - 純粋な定義の箱

// 定義だけを持つ、実装を持たない
box GrammarBox {
    init { definitions }
    
    // キーワード定義を返すだけ
    getKeywordDef(word) {
        return me.definitions.keywords.get(word)
    }
    
    // 演算子定義を返すだけ
    getOperatorDef(symbol) {
        return me.definitions.operators.get(symbol)
    }
}

// キーワード定義は純粋なデータ
box KeywordDef {
    init { literal, category, aliases }
    // 実装なし、データのみ
}

2. TokenBox - トークン化だけの責任

box TokenBox {
    init { grammarBox }  // 定義への参照のみ
    
    tokenize(text) {
        local tokens = []
        // GrammarBoxに聞くだけ、自分では判断しない
        loop(text.hasMore()) {
            local word = text.readWord()
            local def = me.grammarBox.getKeywordDef(word)
            if def {
                tokens.push(new Token(def.category, word))
            } else {
                tokens.push(new Token("IDENTIFIER", word))
            }
        }
        return tokens
    }
}

3. SemanticBox - 意味解釈の箱

box SemanticBox {
    init { }  // 他の箱に依存しない!
    
    // 純粋関数として実装
    add(left, right) {
        // String + String
        if left.isString() and right.isString() {
            return new StringBox(left.value + right.value)
        }
        // Number + Number
        if left.isNumber() and right.isNumber() {
            return new IntegerBox(left.value + right.value)
        }
        // エラー
        return new ErrorBox("Type mismatch")
    }
    
    coerceToString(value) {
        // 各型の変換ロジック
        if value.isString() { return value }
        if value.isNumber() { return new StringBox(value.toString()) }
        // ...
    }
}

4. MIRBuilderBox - AST→MIR変換の箱

box MIRBuilderBox {
    init { semanticBox }  // セマンティクスへの参照のみ
    
    buildFromAST(ast) {
        // ASTの種類に応じてMIRを生成
        if ast.type == "BinaryOp" {
            return me.buildBinaryOp(ast)
        }
        // ...
    }
    
    buildBinaryOp(ast) {
        local left = me.buildFromAST(ast.left)
        local right = me.buildFromAST(ast.right)
        
        // セマンティクスに聞いて、適切なMIR命令を選択
        if ast.op == "+" {
            // SemanticBoxに型情報を聞く
            local mirOp = me.selectAddInstruction(left.type, right.type)
            return new MIRNode(mirOp, left, right)
        }
    }
}

🔄 疎結合の実現方法

1. インターフェース(契約)による結合

// 各箱は最小限のインターフェースだけを公開
trait TokenProvider {
    fn next_token(&mut self) -> Option<Token>;
}

trait SemanticProvider {
    fn apply_operator(&self, op: &str, args: &[Value]) -> Result<Value>;
}

trait MIRProvider {
    fn get_instruction(&self, index: usize) -> &MIRInstruction;
}

2. メッセージパッシング

// 箱同士は直接呼び出さず、メッセージで通信
box ParserBox {
    parseExpression() {
        // TokenBoxにメッセージを送る
        local token = me.sendMessage(me.tokenBox, "nextToken")
        
        // 結果を処理
        if token.type == "NUMBER" {
            return new NumberNode(token.value)
        }
    }
}

3. イベント駆動

// 文法変更時の通知システム
box GrammarBox {
    updateKeyword(word, newDef) {
        me.definitions.keywords.set(word, newDef)
        // 変更を通知(購読者に伝える)
        me.notify("keyword_changed", word)
    }
}

box TokenBox {
    init { grammarBox }
    
    constructor() {
        // 文法変更を購読
        me.grammarBox.subscribe("keyword_changed", me.onKeywordChanged)
    }
    
    onKeywordChanged(word) {
        // キャッシュをクリア
        me.clearCache()
    }
}

📐 ビルド時生成の箱化

GeneratorBox - コード生成も箱

box GeneratorBox {
    init { grammarBox, outputPath }
    
    generate() {
        local grammar = me.grammarBox.getDefinitions()
        
        // 各層向けのコードを生成
        me.generateTokens(grammar.keywords)
        me.generateParseTables(grammar.syntax)
        me.generateSemanticTables(grammar.operators)
    }
    
    generateTokens(keywords) {
        local code = "pub enum Token {\n"
        keywords.forEach((name, def) => {
            code += "    " + name + ",\n"
        })
        code += "}\n"
        
        me.writeFile("generated/tokens.rs", code)
    }
}

🎯 密結合を避ける設計原則

1. 単一責任の原則

  • GrammarBox: 定義の管理のみ
  • TokenBox: トークン化のみ
  • ParserBox: 構文解析のみ
  • SemanticBox: 意味解釈のみ

2. 依存関係の逆転

// ❌ 悪い例: 具象に依存
box VMBox {
    init { mirBuilder: MIRBuilderBox }  // 具象型に依存
}

// ✅ 良い例: 抽象に依存
box VMBox {
    init { mirProvider: MIRProvider }  // インターフェースに依存
}

3. Open/Closed原則

// 新しい演算子の追加が既存コードを変更しない
box OperatorRegistry {
    init { operators }
    
    register(symbol, handler) {
        me.operators.set(symbol, handler)
    }
    
    apply(symbol, args) {
        local handler = me.operators.get(symbol)
        if handler {
            return handler.apply(args)
        }
        return new ErrorBox("Unknown operator")
    }
}

🔧 段階的移行(箱単位)

Phase 1: GrammarBox導入

  • grammar.yamlをGrammarBoxでラップ
  • 既存コードはGrammarBox経由でアクセス

Phase 2: TokenBox分離

  • Tokenizerの機能をTokenBoxに移動
  • GrammarBoxへの依存を最小化

Phase 3: SemanticBox独立

  • 演算子実装をSemanticBoxに集約
  • 純粋関数として実装

Phase 4: 箱間通信の確立

  • メッセージパッシング導入
  • イベントシステム構築

📊 疎結合度の測定

1. 依存関係グラフ

GrammarBox (依存なし)
    ↓
TokenBox → GrammarBox (1依存)
ParserBox → TokenBox (1依存)
SemanticBox (依存なし)
MIRBox → SemanticBox (1依存)
VMBox → MIRBox (1依存)
JITBox → MIRBox (1依存)

2. 変更影響範囲

  • 新キーワード追加: GrammarBoxのみ
  • 新演算子追加: GrammarBox + SemanticBoxのみ
  • 新バックエンド追加: 既存箱への変更なし

🚀 期待される効果

  1. 真の疎結合: 各箱が独立して開発・テスト可能
  2. 容易な拡張: 新しい箱の追加が既存を壊さない
  3. 明確な境界: 責任の所在が明確
  4. 並行開発: チームが独立して各箱を開発可能

これで「Everything is Box」哲学に忠実な、真に疎結合な統一文法アーキテクチャが実現されます。