# 機能設計書 7-Lexer

## 概要

本ドキュメントは、CoffeeScriptソースコードを字句解析してトークン列を生成するLexer機能について、その処理内容、入出力仕様、およびコードリーディングガイドを記載したものである。

### 本機能の処理概要

Lexer機能は、CoffeeScriptソースコードの文字ストリームを読み取り、識別子、数値、文字列、正規表現、JSX等の構文要素を認識してトークン列に変換する字句解析器である。

**業務上の目的・背景**：コンパイラの第一段階として、ソースコードをパーサーが処理可能な形式（トークン列）に変換する必要がある。Lexerは言語の字句規則を実装し、各種リテラル、キーワード、演算子を正確に認識する責務を持つ。

**機能の利用シーン**：コンパイルパイプラインの第一段階として使用、エディタでの構文ハイライト実装時、静的解析ツール開発時、言語サーバー実装時に利用される。

**主要な処理内容**：
1. 識別子・キーワードの認識（identifierToken）
2. 数値リテラルの認識（numberToken）
3. 文字列リテラルの認識（stringToken）- 補間対応
4. 正規表現リテラルの認識（regexToken）
5. コメントの抽出（commentToken）
6. JSX構文の認識（jsxToken）
7. インデント/アウトデントの追跡（lineToken）
8. 演算子・記号の認識（literalToken）
9. Rewriterによるトークン正規化

**関連システム・外部連携**：Rewriterモジュール、helpersモジュール

**権限による制御**：特になし（すべてのユーザーが利用可能）

## 関連画面

| 画面No | 画面名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| 10 | Annotated Source - Lexer | 主機能 | 字句解析器lexer.coffeeのソースコード解説表示 |
| 25 | Annotated Source - Lexer | 主機能 | 字句解析器lexer.coffeeのソースコード解説表示（v2） |

## 機能種別

計算処理 / テキスト解析

## 入力仕様

### 入力パラメータ

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| code | string | Yes | CoffeeScriptソースコード | 文字列であること |
| opts.literate | boolean | No | Literate CoffeeScriptとして処理 | - |
| opts.line | number | No | 開始行番号（デフォルト: 0） | 0以上 |
| opts.column | number | No | 開始列番号（デフォルト: 0） | 0以上 |
| opts.offset | number | No | 開始オフセット（デフォルト: 0） | 0以上 |
| opts.rewrite | boolean | No | Rewriter適用（デフォルト: true） | - |
| opts.untilBalanced | boolean | No | バランス達成で終了 | - |

### 入力データソース

- 直接のソースコード文字列

## 出力仕様

### 出力データ

| 項目名 | 型 | 説明 |
|--------|-----|------|
| tokens | array | トークン配列 |
| tokens[n] | array | [tag, value, locationData] |

### 出力先

- 戻り値として返却

## 処理フロー

### 処理シーケンス

```
1. 状態変数初期化
   └─ indent, ends, tokens等をリセット

2. ソースコード前処理（clean）
   └─ BOM除去、改行正規化、Literate変換

3. メインループ
   └─ チャンクごとにトークンマッチャーを実行
       ├─ identifierToken
       ├─ commentToken
       ├─ whitespaceToken
       ├─ lineToken
       ├─ stringToken
       ├─ numberToken
       ├─ jsxToken
       ├─ regexToken
       ├─ jsToken
       └─ literalToken

4. 位置情報更新
   └─ 消費文字数に基づき行・列を更新

5. インデントクローズ
   └─ 残りのインデントを閉じる

6. 未閉じチェック
   └─ 括弧等の未閉じエラー

7. Rewriter適用
   └─ 暗黙の括弧・ブレース補完

8. トークン列返却
```

### フローチャート

```mermaid
flowchart TD
    A[開始: tokenize] --> B[状態変数初期化]
    B --> C[clean: 前処理]
    C --> D{chunk残り?}
    D -->|No| H[closeIndentation]
    D -->|Yes| E[トークンマッチャー実行]
    E --> F[consumed = マッチ文字数]
    F --> G[位置情報更新]
    G --> D
    H --> I{ends.pop?}
    I -->|Yes| J[未閉じエラー]
    I -->|No| K{rewrite=off?}
    K -->|Yes| L[tokens返却]
    K -->|No| M[Rewriter.rewrite]
    M --> L
    L --> N[終了]
    J --> O[エラースロー]
```

## ビジネスルール

### 業務ルール

| ルールNo | ルール名 | 内容 | 適用条件 |
|---------|---------|------|---------|
| BR-01 | マッチャー優先順位 | 識別子→コメント→空白→改行→文字列→数値→JSX→正規表現→JS→リテラル | メインループ |
| BR-02 | 予約語エラー | JavaScript予約語の識別子使用はエラー | 識別子マッチ時 |
| BR-03 | インデント一貫性 | タブとスペースの混在はエラー | lineToken |
| BR-04 | 文字列補間 | "#{...}"形式で式展開 | stringToken |

### 計算ロジック

**インデントサイズ計算**:
```coffeescript
size = indent.length - 1 - indent.lastIndexOf '\n'
```

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

本機能はデータベース操作を行わない。

## エラー処理

### エラーケース一覧

| エラーコード | エラー種別 | 発生条件 | 対処方法 |
|------------|----------|---------|---------|
| SyntaxError | 予約語使用 | 予約語を識別子として使用 | 別名を使用 |
| SyntaxError | 混在インデント | タブとスペースの混在 | 統一 |
| SyntaxError | 未閉じ | 括弧/文字列が閉じられていない | 閉じ記号追加 |
| SyntaxError | 不正数値 | 不正な数値形式 | 正しい形式 |
| SyntaxError | 不正エスケープ | 不正なエスケープシーケンス | 修正 |

### リトライ仕様

リトライは行わない

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

本機能はトランザクション管理を行わない

## パフォーマンス要件

- O(n)の時間計算量
- 正規表現マッチングによる効率的な処理

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

- 入力コードの長さ制限なし

## 備考

- Lexerクラスはシングルトンとしてインスタンス化される
- 各トークンマッチャーは消費文字数を返す

---

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

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

### 推奨読解順序

#### Step 1: データ構造を理解する

まず、トークンの構造とLexerの状態変数を理解する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | lexer.coffee | `src/lexer.coffee` | 1-10行目のコメントでトークン形式を確認 |
| 1-2 | lexer.coffee | `src/lexer.coffee` | 38-53行目で状態変数（indent, ends, tokens等）を確認 |
| 1-3 | lexer.coffee | `src/lexer.coffee` | 1226-1268行目でキーワード定義を確認 |

**読解のコツ**: トークンは `[tag, value, locationData]` の3要素配列。追加プロパティ（spaced, comments等）はオブジェクトプロパティとして付与される。

#### Step 2: エントリーポイントを理解する

tokenizeメソッドの全体フローを理解する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | lexer.coffee | `src/lexer.coffee` | tokenizeメソッド（37-92行目） |

**主要処理フロー**:
1. **38-53行目**: 状態変数初期化
2. **55-62行目**: 位置情報初期化
3. **63行目**: clean呼び出し
4. **68-85行目**: メインループ
5. **89行目**: closeIndentation
6. **90行目**: 未閉じチェック
7. **92行目**: Rewriter適用

#### Step 3: 前処理（clean）を理解する

ソースコード正規化を理解する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | lexer.coffee | `src/lexer.coffee` | cleanメソッド（97-114行目） |

**主要処理フロー**:
- **99-102行目**: BOM除去
- **103-107行目**: 先頭空白処理
- **108-112行目**: キャリッジリターン除去、末尾空白除去
- **113行目**: Literate変換

#### Step 4: 各トークンマッチャーを理解する

主要なトークンマッチャーを理解する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 4-1 | lexer.coffee | `src/lexer.coffee` | identifierToken（125-260行目）- 最も複雑なマッチャー |
| 4-2 | lexer.coffee | `src/lexer.coffee` | numberToken（264-287行目） |
| 4-3 | lexer.coffee | `src/lexer.coffee` | stringToken（291-325行目） |
| 4-4 | lexer.coffee | `src/lexer.coffee` | commentToken（330-428行目） |
| 4-5 | lexer.coffee | `src/lexer.coffee` | regexToken（444-502行目） |
| 4-6 | lexer.coffee | `src/lexer.coffee` | lineToken（514-565行目） |
| 4-7 | lexer.coffee | `src/lexer.coffee` | jsxToken（626-741行目） |
| 4-8 | lexer.coffee | `src/lexer.coffee` | literalToken（755-823行目） |

**読解のコツ**:
- 各マッチャーは `@chunk` に対してマッチングを行う
- マッチ成功時は消費した文字数を返す
- マッチ失敗時は0を返す
- `@token` メソッドでトークンを配列に追加

#### Step 5: 文字列補間処理を理解する

文字列内の式展開処理を理解する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 5-1 | lexer.coffee | `src/lexer.coffee` | matchWithInterpolations（883-947行目） |
| 5-2 | lexer.coffee | `src/lexer.coffee` | mergeInterpolationTokens（953-1020行目） |

**主要処理フロー**:
- **883行目**: 補間検出ループ開始
- **906-907行目**: 再帰的Lexer呼び出し
- **916-924行目**: 補間トークンの変換
- **953行目**: 補間トークンのマージ

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

```
Lexer.tokenize (lexer.coffee:37)
    │
    ├─ clean (lexer.coffee:97)
    │      └─ invertLiterate (helpers.coffee:67)
    │
    ├─ メインループ (lexer.coffee:68-85)
    │      ├─ identifierToken (lexer.coffee:125)
    │      ├─ commentToken (lexer.coffee:330)
    │      ├─ whitespaceToken (lexer.coffee:600)
    │      ├─ lineToken (lexer.coffee:514)
    │      │      └─ outdentToken (lexer.coffee:569)
    │      ├─ stringToken (lexer.coffee:291)
    │      │      ├─ matchWithInterpolations (lexer.coffee:883)
    │      │      │      └─ new Lexer().tokenize (再帰)
    │      │      └─ mergeInterpolationTokens (lexer.coffee:953)
    │      ├─ numberToken (lexer.coffee:264)
    │      ├─ jsxToken (lexer.coffee:626)
    │      │      └─ matchWithInterpolations (JSX内テキスト)
    │      ├─ regexToken (lexer.coffee:444)
    │      │      └─ matchWithInterpolations (ヒアドキュメント正規表現)
    │      ├─ jsToken (lexer.coffee:431)
    │      └─ literalToken (lexer.coffee:755)
    │
    ├─ closeIndentation (lexer.coffee:861)
    │
    └─ Rewriter.rewrite (rewriter.coffee)
```

### データフロー図

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

ソースコード       ──▶  clean               ──▶  正規化コード
"x = 1 + 2"              │
                        ▼
                  identifierToken      ──▶  ['IDENTIFIER', 'x', loc]
                        │
                        ▼
                  literalToken         ──▶  ['=', '=', loc]
                        │
                        ▼
                  numberToken          ──▶  ['NUMBER', '1', loc]
                        │
                        ▼
                  literalToken         ──▶  ['+', '+', loc]
                        │
                        ▼
                  numberToken          ──▶  ['NUMBER', '2', loc]
                        │
                        ▼
                  Rewriter             ──▶  正規化トークン列
                                           (暗黙の括弧補完等)
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| lexer.coffee | `src/lexer.coffee` | ソース | Lexerクラス本体 |
| rewriter.coffee | `src/rewriter.coffee` | ソース | トークン正規化 |
| helpers.coffee | `src/helpers.coffee` | ソース | invertLiterate等 |
| coffeescript.coffee | `src/coffeescript.coffee` | ソース | Lexerインスタンス化 |
