Files
hakorune/docs/development/roadmap/phases/phase-16-macro-revolution/TECHNICAL_SPEC.md

17 KiB
Raw Blame History

Phase 16 Technical Specification: Box-Based Macro System

Date: 2025-09-19
Version: 0.1.0
Status: DRAFT - 実装前仕様

🏗️ アーキテクチャ概要

システム構成

Nyash Source Code
        ↓
   Lexer/Parser  
        ↓
    Raw AST
        ↓
┌─────────────────────┐
│  Macro Expansion    │ ← 新規実装部分
│  ┌───────────────┐  │
│  │ AST Pattern   │  │ ← Phase 1
│  │ Matching      │  │  
│  └───────────────┘  │
│  ┌───────────────┐  │  
│  │ Quote/Unquote │  │ ← Phase 2
│  │ System        │  │
│  └───────────────┘  │
│  ┌───────────────┐  │
│  │ HIR Patch     │  │ ← Phase 3  
│  │ Engine        │  │
│  └───────────────┘  │
└─────────────────────┘
        ↓
   Expanded AST
        ↓
   MIR Lowering  ← 既存システム(無変更)
        ↓
   MIR14 Instructions
        ↓
   VM/JIT/AOT Execution

🎯 Phase 1: AST Pattern Matching

新規AST構造体

PatternAst

#[derive(Debug, Clone)]
pub enum PatternAst {
    // 基本パターン
    Wildcard { span: Span },                                    // _
    Identifier { name: String, span: Span },                   // variable
    Literal { value: LiteralValue, span: Span },               // 42, "hello"
    
    // 構造パターン  
    BoxPattern {
        name: String,                                           // BoxDeclaration
        fields: Vec<FieldPattern>,                              // { field1, field2, .. }
        span: Span,
    },
    
    // 配列パターン
    ArrayPattern {
        elements: Vec<PatternAst>,                              // [first, second, ...]
        rest: Option<String>,                                   // ...rest
        span: Span,
    },
    
    // OR パターン
    OrPattern {
        patterns: Vec<PatternAst>,                              // pattern1 | pattern2
        span: Span,
    },
    
    // バインドパターン
    BindPattern {
        name: String,                                           // @variable
        pattern: Box<PatternAst>,                               // @var pattern
        span: Span,
    },
}

#[derive(Debug, Clone)]
pub struct FieldPattern {
    pub name: String,
    pub pattern: PatternAst,
    pub span: Span,
}

MatchExpression

#[derive(Debug, Clone)]
pub struct MatchExpression {
    pub target: Box<ASTNode>,                                  // match対象
    pub arms: Vec<MatchArm>,                                   // マッチアーム
    pub span: Span,
}

#[derive(Debug, Clone)]  
pub struct MatchArm {
    pub pattern: PatternAst,                                   // パターン
    pub guard: Option<Box<ASTNode>>,                           // if guard
    pub body: Vec<ASTNode>,                                    // 実行文
    pub span: Span,
}

パターンマッチング構文

基本構文

match ast_node {
    BoxDeclaration { name, fields, .. } => {
        // Box宣言の処理
    }
    FunctionDeclaration { name: "main", .. } => {
        // main関数の特別処理
    }
    _ => {
        // その他
    }
}

高度なパターン

match ast_node {
    // 束縛パターン
    BoxDeclaration { name: @box_name, fields: [first, ...rest] } => {
        // box_nameにnameをバインド
        // firstに最初のフィールド、restに残り
    }
    
    // ORパターン
    Literal { value: IntegerValue | StringValue } => {
        // 整数または文字列リテラル
    }
    
    // ガード
    BoxDeclaration { fields } if fields.length > 5 => {
        // フィールドが5個より多いBox
    }
}

実装詳細

Parser拡張

impl NyashParser {
    /// match式のパース
    pub fn parse_match_expression(&mut self) -> Result<MatchExpression, ParseError> {
        self.consume(TokenType::MATCH)?;
        let target = self.parse_expression()?;
        self.consume(TokenType::LBRACE)?;
        
        let mut arms = Vec::new();
        while !self.match_token(&TokenType::RBRACE) {
            arms.push(self.parse_match_arm()?);
        }
        
        self.consume(TokenType::RBRACE)?;
        Ok(MatchExpression { target: Box::new(target), arms, span: Span::unknown() })
    }
    
    /// パターンのパース
    pub fn parse_pattern(&mut self) -> Result<PatternAst, ParseError> {
        // パターン実装...
    }
}

PatternMatcher

pub struct PatternMatcher {
    bindings: HashMap<String, ASTNode>,
}

impl PatternMatcher {
    /// パターンマッチング実行
    pub fn match_pattern(&mut self, pattern: &PatternAst, value: &ASTNode) -> bool {
        match (pattern, value) {
            (PatternAst::Wildcard { .. }, _) => true,
            
            (PatternAst::Identifier { name, .. }, value) => {
                self.bindings.insert(name.clone(), value.clone());
                true
            }
            
            (PatternAst::BoxPattern { name, fields, .. }, 
             ASTNode::BoxDeclaration { name: box_name, fields: box_fields, .. }) => {
                if name == box_name {
                    self.match_fields(fields, box_fields)
                } else {
                    false
                }
            }
            
            // その他のパターン...
            _ => false,
        }
    }
}

🎯 Phase 2: Quote/Unquote System

新規AST構造体

QuoteExpression

#[derive(Debug, Clone)]
pub struct QuoteExpression {
    pub template: Vec<ASTNode>,                                // テンプレート
    pub span: Span,
}

#[derive(Debug, Clone)]
pub struct UnquoteExpression {
    pub template: Box<QuoteExpression>,                        // 展開するテンプレート
    pub substitutions: HashMap<String, ASTNode>,               // 置換マップ
    pub span: Span,
}

// テンプレート内の変数展開
#[derive(Debug, Clone)]
pub struct TemplateVariable {
    pub name: String,                                          // $(variable_name)
    pub span: Span,
}

Quote/Unquote構文

基本構文

// コードテンプレート作成
let template = quote! {
    $(method_name)(other) {
        return me.$(field_name).equals(other.$(field_name))
    }
}

// テンプレート展開
let generated = unquote! {
    template with {
        method_name: "equals",
        field_name: "name"
    }
}

高度な展開

// リスト展開
let field_comparisons = quote! {
    $(for field in fields) {
        me.$(field.name).equals(other.$(field.name))
    }
}

// 条件展開
let method_body = quote! {
    $(if has_fields) {
        return $(field_comparisons)
    } else {
        return true
    }
}

実装詳細

TemplateEngine

pub struct TemplateEngine {
    substitutions: HashMap<String, ASTNode>,
}

impl TemplateEngine {
    /// テンプレート展開
    pub fn expand_template(&self, template: &QuoteExpression) -> Result<Vec<ASTNode>, MacroError> {
        let mut result = Vec::new();
        
        for node in &template.template {
            match self.expand_node(node)? {
                ExpandResult::Single(n) => result.push(n),
                ExpandResult::Multiple(nodes) => result.extend(nodes),
            }
        }
        
        Ok(result)
    }
    
    /// 単一ノード展開
    fn expand_node(&self, node: &ASTNode) -> Result<ExpandResult, MacroError> {
        match node {
            ASTNode::TemplateVariable { name, .. } => {
                if let Some(substitution) = self.substitutions.get(name) {
                    Ok(ExpandResult::Single(substitution.clone()))
                } else {
                    Err(MacroError::UnboundVariable(name.clone()))
                }
            }
            
            // 再帰的展開
            _ => self.expand_node_recursive(node),
        }
    }
}

🎯 Phase 3: HIRパッチ式マクロエンジン

MacroEngine Core

MacroRegistry

pub struct MacroRegistry {
    derive_macros: HashMap<String, Box<dyn DeriveMacro>>,
    attribute_macros: HashMap<String, Box<dyn AttributeMacro>>,
    function_macros: HashMap<String, Box<dyn FunctionMacro>>,
}

impl MacroRegistry {
    pub fn register_derive<T: DeriveMacro + 'static>(&mut self, name: &str, macro_impl: T) {
        self.derive_macros.insert(name.to_string(), Box::new(macro_impl));
    }
    
    pub fn expand_derive(&self, name: &str, input: &BoxDeclaration) -> Result<Vec<ASTNode>, MacroError> {
        if let Some(macro_impl) = self.derive_macros.get(name) {
            macro_impl.expand(input)
        } else {
            Err(MacroError::UnknownDeriveMacro(name.to_string()))
        }
    }
}

DeriveMacro Trait

pub trait DeriveMacro {
    /// derive マクロ展開
    fn expand(&self, input: &BoxDeclaration) -> Result<Vec<ASTNode>, MacroError>;
    
    /// サポートする型チェック
    fn supports_box(&self, box_decl: &BoxDeclaration) -> bool {
        true  // デフォルトすべてのBoxをサポート
    }
}

@derive実装例

EqualsDeriveMacro

pub struct EqualsDeriveMacro;

impl DeriveMacro for EqualsDeriveMacro {
    fn expand(&self, input: &BoxDeclaration) -> Result<Vec<ASTNode>, MacroError> {
        let method_name = "equals";
        let param_name = "other";
        
        // フィールド比較の生成
        let field_comparisons = self.generate_field_comparisons(&input.fields)?;
        
        // equals メソッドの生成
        let equals_method = ASTNode::FunctionDeclaration {
            name: method_name.to_string(),
            params: vec![param_name.to_string()],
            body: vec![
                ASTNode::Return {
                    value: Some(Box::new(field_comparisons)),
                    span: Span::unknown(),
                }
            ],
            is_static: false,
            is_override: false,
            span: Span::unknown(),
        };
        
        Ok(vec![equals_method])
    }
    
    fn generate_field_comparisons(&self, fields: &[String]) -> Result<ASTNode, MacroError> {
        if fields.is_empty() {
            // フィールドなし常にtrue
            return Ok(ASTNode::Literal {
                value: LiteralValue::Bool(true),
                span: Span::unknown(),
            });
        }
        
        // フィールド比較の連鎖
        let mut comparison = self.generate_single_field_comparison(&fields[0])?;
        
        for field in &fields[1..] {
            let field_comp = self.generate_single_field_comparison(field)?;
            comparison = ASTNode::BinaryOp {
                operator: BinaryOperator::And,
                left: Box::new(comparison),
                right: Box::new(field_comp),
                span: Span::unknown(),
            };
        }
        
        Ok(comparison)
    }
    
    fn generate_single_field_comparison(&self, field: &str) -> Result<ASTNode, MacroError> {
        // me.field.equals(other.field)
        Ok(ASTNode::MethodCall {
            object: Box::new(ASTNode::FieldAccess {
                object: Box::new(ASTNode::Me { span: Span::unknown() }),
                field: field.to_string(),
                span: Span::unknown(),
            }),
            method: "equals".to_string(),
            arguments: vec![
                ASTNode::FieldAccess {
                    object: Box::new(ASTNode::Variable {
                        name: "other".to_string(),
                        span: Span::unknown(),
                    }),
                    field: field.to_string(),
                    span: Span::unknown(),
                }
            ],
            span: Span::unknown(),
        })
    }
}

TestMacro

pub struct TestMacro;

impl AttributeMacro for TestMacro {
    fn expand(&self, input: &FunctionDeclaration) -> Result<Vec<ASTNode>, MacroError> {
        // テスト関数をテストレジストリに登録
        let register_call = ASTNode::MethodCall {
            object: Box::new(ASTNode::Variable {
                name: "TestRegistry".to_string(),
                span: Span::unknown(),
            }),
            method: "register".to_string(),
            arguments: vec![
                ASTNode::Literal {
                    value: LiteralValue::String(input.name.clone()),
                    span: Span::unknown(),
                },
                ASTNode::Variable {
                    name: input.name.clone(),
                    span: Span::unknown(),
                }
            ],
            span: Span::unknown(),
        };
        
        Ok(vec![
            register_call,
            ASTNode::FunctionDeclaration {
                name: input.name.clone(),
                params: input.params.clone(),
                body: input.body.clone(),
                is_static: input.is_static,
                is_override: input.is_override,
                span: input.span.clone(),
            }
        ])
    }
}

🛡️ エラーハンドリング

MacroError定義

#[derive(Debug, Clone)]
pub enum MacroError {
    // パターンマッチングエラー
    PatternMismatch { expected: String, found: String },
    UnboundVariable(String),
    
    // Quote/Unquoteエラー  
    TemplateExpansionFailed(String),
    InvalidSubstitution { variable: String, reason: String },
    
    // Deriveマクロエラー
    UnknownDeriveMacro(String),
    UnsupportedBoxType { derive_name: String, box_name: String },
    
    // 一般エラー
    RecursionLimitExceeded,
    CircularDependency(Vec<String>),
}

エラーメッセージ

impl MacroError {
    pub fn user_message(&self) -> String {
        match self {
            MacroError::UnknownDeriveMacro(name) => {
                format!("Unknown derive trait '{}'
Available traits: Equals, ToString, Clone, Debug
Did you mean 'ToString'?", name)
            }
            
            MacroError::PatternMismatch { expected, found } => {
                format!("Pattern mismatch: expected {}, found {}", expected, found)
            }
            
            _ => format!("Macro error: {:?}", self),
        }
    }
}

🎨 CLI統合

新規コマンドオプション

既に実装済み:

// src/cli.rs で確認済み
.arg(
    Arg::new("expand")
        .long("expand")  
        .help("Macro: enable macro engine and dump expansion traces")
        .action(clap::ArgAction::SetTrue)
)
.arg(
    Arg::new("run-tests")
        .long("run-tests")
        .help("Run tests: enable macro engine and inject test harness")  
        .action(clap::ArgAction::SetTrue)
)
.arg(
    Arg::new("test-filter")
        .long("test-filter")
        .value_name("SUBSTR")
        .help("Only run tests whose name contains SUBSTR")
)

環境変数

// マクロエンジン有効化
NYASH_MACRO_ENABLE=1

// マクロ展開トレース
NYASH_MACRO_TRACE=1  

// テスト実行
NYASH_TEST_RUN=1

// テストフィルタ
NYASH_TEST_FILTER="substring"

📊 パフォーマンス要件

目標指標

  • 展開時間: < 100ms中規模プロジェクト
  • メモリ使用量: < 20% 増加(ベースコンパイラ比)
  • 型チェック: 100% コンパイル時
  • エラー検出: 100% コンパイル時

最適化戦略

  1. 遅延展開: 必要な時のみマクロ展開
  2. キャッシュ: 同一パターンの結果をキャッシュ
  3. 並列処理: 独立なマクロの並列展開
  4. メモリプール: AST ノードの効率的割り当て

🚀 実装順序

Week 1-2: AST Pattern Matching

  1. PatternAst構造体定義
  2. Parser拡張match式
  3. PatternMatcher実装
  4. 基本テストケース

Week 3-4: Quote/Unquote

  1. QuoteExpression定義
  2. TemplateEngine実装
  3. 変数置換システム
  4. エラーハンドリング

Week 5-6: HIRパッチエンジン

  1. MacroRegistry実装
  2. DeriveMacro trait定義
  3. EqualsDeriveMacro実装
  4. TestMacro実装

Week 7-8: 統合・最適化

  1. CLI統合
  2. エラーメッセージ改善
  3. パフォーマンス最適化
  4. ドキュメント作成

この仕様に基づいて、世界最強のBox-Basedマクロシステムを実装する 🌟