# 機能設計書 11-Rewriter

## 概要

本ドキュメントは、CoffeeScriptコンパイラにおけるRewriter機能の設計を記述する。RewriterはLexer（字句解析器）が生成したトークン列を受け取り、CoffeeScript特有の暗黙的な構文を明示的な形式に変換する中間処理層である。

### 本機能の処理概要

Rewriterは、CoffeeScriptの省略記法や暗黙的な構文を正規化し、パーサーが処理しやすい形式にトークン列を変換する機能である。

**業務上の目的・背景**：CoffeeScriptは、括弧やブレースの省略、インデントベースのブロック構造など、多くの暗黙的な構文を持つ。これらの構文を直接パーサーで処理すると、文法規則が複雑化し、パーステーブルが肥大化する。Rewriterは、これらの複雑さをパーサーから分離し、トークン列の前処理として暗黙的な構文を明示的な形式に変換することで、文法定義をシンプルに保つことを目的としている。

**機能の利用シーン**：CoffeeScriptソースコードのコンパイル時、Lexerがトークン列を生成した直後にRewriterが呼び出される。具体的には以下のような場面で使用される。
- 関数呼び出しの括弧が省略されている場合（例：`console.log "hello"`）
- オブジェクトリテラルのブレースが省略されている場合（例：`a: 1, b: 2`）
- インデントによるブロック構造の解析

**主要な処理内容**：
1. 先頭の改行トークンの除去（removeLeadingNewlines）
2. 関数呼び出しの開始括弧と閉じ括弧の対応付け（closeOpenCalls）
3. インデックスアクセスの開始ブラケットと閉じブラケットの対応付け（closeOpenIndexes）
4. 行の正規化とシングルライナーの処理（normalizeLines）
5. 後置条件文のタグ付け（tagPostfixConditionals）
6. 暗黙の括弧とブレースの追加（addImplicitBracesAndParens）
7. コメントの救出処理（rescueStowawayComments）
8. 生成されたトークンへの位置情報の付加（addLocationDataToGeneratedTokens）
9. JSX属性の検証（enforceValidJSXAttributes）
10. インデント位置情報の修正（fixIndentationLocationData）
11. トークンデータの文法への公開（exposeTokenDataToGrammar）

**関連システム・外部連携**：Rewriterは、Lexerから出力されたトークン列を入力として受け取り、パーサー（grammar.coffee）に渡すトークン列を出力する。helpersモジュールからthrowSyntaxError、extractAllCommentTokensをインポートして使用する。

**権限による制御**：特になし。コンパイラ内部の処理として、すべてのコンパイル処理に対して一律に適用される。

## 関連画面

| 画面No | 画面名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| 13 | Annotated Source - Rewriter | 主画面 | トークン書き換えrewriter.coffeeのソースコード解説表示 |
| 28 | Annotated Source - Rewriter | 主画面 | トークン書き換えrewriter.coffeeのソースコード解説表示（v2） |

## 機能種別

データ変換処理 / トークン列書き換え

## 入力仕様

### 入力パラメータ

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| tokens | Array | Yes | Lexerが生成したトークンの配列 | 有効なトークン配列であること |

### 入力データソース

Lexer（src/lexer.coffee）が生成したトークン配列を入力として受け取る。

## 出力仕様

### 出力データ

| 項目名 | 型 | 説明 |
|--------|-----|------|
| tokens | Array | 暗黙の構文が明示化されたトークン配列 |

### 出力先

パーサー（src/grammar.coffee）に渡される。

## 処理フロー

### 処理シーケンス

```
1. removeLeadingNewlines()
   └─ 先頭のTERMINATORトークンを削除し、コメントを後続トークンに移動

2. closeOpenCalls()
   └─ CALL_STARTトークンに対応するCALL_ENDトークンを設定

3. closeOpenIndexes()
   └─ INDEX_STARTトークンに対応するINDEX_ENDトークンを設定

4. normalizeLines()
   └─ 単一行の制御構造にINDENT/OUTDENTを追加

5. tagPostfixConditionals()
   └─ 後置IF文をPOST_IFとしてタグ付け

6. addImplicitBracesAndParens()
   └─ 暗黙の括弧とブレースを挿入

7. rescueStowawayComments()
   └─ 削除予定トークンからコメントを救出

8. addLocationDataToGeneratedTokens()
   └─ 生成されたトークンに位置情報を付加

9. enforceValidJSXAttributes()
   └─ JSX属性が有効な形式であることを検証

10. fixIndentationLocationData()
    └─ INDENT/OUTDENTの位置情報を修正

11. exposeTokenDataToGrammar()
    └─ トークンデータを文法に公開
```

### フローチャート

```mermaid
flowchart TD
    A[開始: rewrite] --> B[removeLeadingNewlines]
    B --> C[closeOpenCalls]
    C --> D[closeOpenIndexes]
    D --> E[normalizeLines]
    E --> F[tagPostfixConditionals]
    F --> G[addImplicitBracesAndParens]
    G --> H[rescueStowawayComments]
    H --> I[addLocationDataToGeneratedTokens]
    I --> J[enforceValidJSXAttributes]
    J --> K[fixIndentationLocationData]
    K --> L[exposeTokenDataToGrammar]
    L --> M[終了: トークン配列を返却]
```

## ビジネスルール

### 業務ルール

| ルールNo | ルール名 | 内容 | 適用条件 |
|---------|---------|------|---------|
| BR-11-01 | 暗黙の関数呼び出し | スペースで区切られた識別子の後にIMPLICIT_CALLトークンが続く場合、CALL_START/CALL_ENDを挿入 | IMPLICIT_FUNCトークンがスペース付きで、次トークンがIMPLICIT_CALLの場合 |
| BR-11-02 | 暗黙のオブジェクトリテラル | コロン（:）の前の識別子をキーとして、暗黙のブレースを挿入 | コロントークンが検出され、明示的なブレースがない場合 |
| BR-11-03 | 後置条件文 | 式の後にIF/UNLESSが続く場合、POST_IF/POST_UNLESSとしてタグ付け | IFトークンの後にTERMINATORまたはINDENTが続かない場合 |
| BR-11-04 | バランスペア | 括弧、ブラケット、ブレース、INDENT/OUTDENTは対応する閉じトークンが必要 | 開始トークンが検出された場合 |

### 計算ロジック

特になし。

## データベース操作仕様

### 操作別データベース影響一覧

データベースは使用しない。

### テーブル別操作詳細

該当なし。

## エラー処理

### エラーケース一覧

| エラーコード | エラー種別 | 発生条件 | 対処方法 |
|------------|----------|---------|---------|
| JSX_ATTRIBUTE_ERROR | SyntaxError | JSX属性に不正な値が指定された場合 | throwSyntaxErrorでエラーメッセージを出力 |

### リトライ仕様

リトライは行わない。構文エラーはコンパイル失敗として処理される。

## トランザクション仕様

トランザクションは使用しない。

## パフォーマンス要件

特に定められていないが、トークン列のスキャンは各処理パスで1回のみ行われ、O(n)の計算量となる。

## セキュリティ考慮事項

特になし。コンパイラ内部の処理であり、外部からの入力を直接扱わない。

## 備考

- 環境変数 `DEBUG_TOKEN_STREAM` を設定することで、初期トークン列をデバッグ出力可能
- 環境変数 `DEBUG_REWRITTEN_TOKEN_STREAM` を設定することで、書き換え後のトークン列をデバッグ出力可能

---

## コードリーディングガイド

本機能を理解するために参照すべきファイルと、推奨する読み解き順序を以下に示す。

### 推奨読解順序

#### Step 1: 定数定義を理解する

Rewriterで使用される定数配列を理解することが重要。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | rewriter.coffee | `src/rewriter.coffee` | 789-867行目の定数定義（BALANCED_PAIRS, EXPRESSION_START, EXPRESSION_END, IMPLICIT_FUNC, IMPLICIT_CALL等）を理解する |

**読解のコツ**: BALANCED_PAIRSはトークンの開始/終了ペアを定義し、IMPLICIT_FUNCとIMPLICIT_CALLは暗黙の関数呼び出しを判定するためのトークンリストである。

#### Step 2: Rewriterクラスのエントリーポイントを理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | rewriter.coffee | `src/rewriter.coffee` | 36-64行目のRewriterクラス定義とrewriteメソッドを理解する |

**主要処理フロー**:
1. **43行目**: rewriteメソッドがトークン配列を受け取る
2. **50-60行目**: 各変換パスを順次呼び出し
3. **64行目**: 変換後のトークン配列を返却

#### Step 3: ヘルパー関数を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | rewriter.coffee | `src/rewriter.coffee` | 11-33行目のmoveComments、generate関数を理解する |

**主要処理フロー**:
- **11-23行目**: moveCommentsはコメントをトークン間で移動する
- **27-32行目**: generateは暗黙的な構文のためのトークンを生成する

#### Step 4: 主要な変換処理を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 4-1 | rewriter.coffee | `src/rewriter.coffee` | 176-438行目のaddImplicitBracesAndParensメソッドを理解する |

**主要処理フロー**:
- **178行目**: スタックを使用してネストレベルを追跡
- **203-210行目**: startImplicitCallとendImplicitCallで暗黙の関数呼び出しを処理
- **212-222行目**: startImplicitObjectとendImplicitObjectで暗黙のオブジェクトを処理
- **290-299行目**: IMPLICIT_FUNCとIMPLICIT_CALLの組み合わせで暗黙の関数呼び出しを検出

### プログラム呼び出し階層図

```
CoffeeScript.compile() (coffeescript.coffee)
    │
    └─ Lexer.tokenize() (lexer.coffee)
           │
           └─ Rewriter.rewrite() (rewriter.coffee:43)
                  │
                  ├─ removeLeadingNewlines() (rewriter.coffee:94)
                  ├─ closeOpenCalls() (rewriter.coffee:107)
                  ├─ closeOpenIndexes() (rewriter.coffee:120)
                  ├─ normalizeLines() (rewriter.coffee:654)
                  ├─ tagPostfixConditionals() (rewriter.coffee:738)
                  ├─ addImplicitBracesAndParens() (rewriter.coffee:176)
                  ├─ rescueStowawayComments() (rewriter.coffee:452)
                  ├─ addLocationDataToGeneratedTokens() (rewriter.coffee:538)
                  ├─ enforceValidJSXAttributes() (rewriter.coffee:441)
                  ├─ fixIndentationLocationData() (rewriter.coffee:567)
                  └─ exposeTokenDataToGrammar() (rewriter.coffee:761)
```

### データフロー図

```
[入力]                    [処理]                           [出力]

Lexerからの          Rewriter.rewrite()                Parserへの
トークン配列 ───▶    ├─ 先頭改行削除                   トークン配列
                     ├─ 呼び出し対応付け
[token, token,       ├─ インデックス対応付け      ───▶ [token, CALL_START,
 token, ...]         ├─ 行正規化                         token, CALL_END,
                     ├─ 後置条件タグ付け                 '{', token, '}', ...]
                     ├─ 暗黙の括弧/ブレース追加
                     └─ 位置情報付加
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| rewriter.coffee | `src/rewriter.coffee` | ソース | Rewriterクラスの実装本体 |
| lexer.coffee | `src/lexer.coffee` | ソース | トークン生成元のLexer |
| grammar.coffee | `src/grammar.coffee` | ソース | Rewriter出力を消費するパーサー文法定義 |
| helpers.coffee | `src/helpers.coffee` | ソース | throwSyntaxError等のヘルパー関数 |
| coffeescript.coffee | `src/coffeescript.coffee` | ソース | コンパイルパイプラインの統合 |
