# 機能設計書 93-レキサーフィルタ

## 概要

本ドキュメントは、F#コンパイラにおけるレキサーフィルタ（LexFilter）機能について記述する。レキサーフィルタは、インデントベースの「軽量構文（Lightweight Syntax）」をサポートするために、レキサーとパーサーの間に位置するトークンストリーム変換機能である。

### 本機能の処理概要

**業務上の目的・背景**：F#は「軽量構文」と呼ばれるインデントベースの構文をサポートしている。これにより、明示的なbegin/endやin等のキーワードを省略できる。レキサーフィルタは、レキサーが生成したトークンストリームを解析し、インデント情報に基づいて暗黙的なトークン（OBLOCKBEGIN, OBLOCKEND, ODECLEND等）を挿入する。

**機能の利用シーン**：
- 通常のF#ソースコードのコンパイル時（軽量構文が有効な場合）
- IDE連携でのシンタックスハイライトやエラー報告時
- FSIインタラクティブセッションでの入力解析時

**主要な処理内容**：
1. インデントスタックの管理 - 現在のインデントレベルを追跡
2. コンテキストスタックの管理 - let, match, if等の構文コンテキストを追跡
3. 暗黙トークンの挿入 - OBLOCKBEGIN, OBLOCKEND, ODECLEND等を適切な位置に挿入
4. オフサイドルールの適用 - インデントレベルの変化に基づくブロック終了の検出

**関連システム・外部連携**：レキサー（lex.fsl）からトークンを受け取り、パーサー（pars.fsy）にトークンストリームを提供する。

**権限による制御**：該当なし（コンパイラ内部機能）

## 関連画面

| 画面No | 画面名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| - | IDE統合 | 参照画面 | インデントベースのエラー表示 |

## 機能種別

トークンストリーム変換

## 入力仕様

### 入力パラメータ

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| lightSyntaxStatus | LightSyntaxStatus | Yes | 軽量構文の有効/無効状態 | - |
| lexbuf | UnicodeLexing.Lexbuf | Yes | レキサーバッファ | - |
| getToken | unit -> token | Yes | レキサーからトークンを取得する関数 | - |

### 入力データソース

レキサー（lex.fsl）からのトークンストリーム

## 出力仕様

### 出力データ

| 項目名 | 型 | 説明 |
|--------|-----|------|
| token | token | 変換後のトークン（暗黙トークン含む） |

### 出力先

パーサー（pars.fsy）

## 処理フロー

### 処理シーケンス

```
1. トークン取得
   └─ レキサーから次のトークンを取得

2. コンテキスト判定
   ├─ 現在のコンテキストスタックを確認
   └─ トークン種別に応じた処理を選択

3. インデント処理
   ├─ トークンの開始位置（列）を取得
   ├─ 現在のオフサイドコンテキストと比較
   └─ 必要に応じてOBLOCKEND等を挿入

4. コンテキスト更新
   ├─ 新しいブロック開始時: OBLOCKBEGIN挿入
   ├─ ブロック終了時: OBLOCKEND挿入
   └─ 宣言終了時: ODECLEND挿入

5. トークン返却
   └─ 変換後のトークンをパーサーに返却
```

### フローチャート

```mermaid
flowchart TD
    A[レキサーからトークン取得] --> B{軽量構文有効?}
    B -->|No| Z[そのまま返却]
    B -->|Yes| C{コンテキスト判定}
    C -->|ブロック開始| D[OBLOCKBEGIN挿入]
    C -->|インデント減少| E[OBLOCKEND挿入]
    C -->|宣言終了| F[ODECLEND挿入]
    C -->|その他| G[トークン処理]
    D --> H[コンテキストスタック更新]
    E --> H
    F --> H
    G --> H
    H --> I[変換後トークン返却]
```

## ビジネスルール

### 業務ルール

| ルールNo | ルール名 | 内容 | 適用条件 |
|---------|---------|------|---------|
| BR-93-1 | オフサイドルール | 行の開始位置が現在のオフサイドより左なら終了 | 軽量構文有効時 |
| BR-93-2 | letバインディング | let/let recの後にOBLOCKBEGIN挿入 | 軽量構文有効時 |
| BR-93-3 | ifブロック | then/elseの後にOBLOCKBEGIN挿入 | 軽量構文有効時 |
| BR-93-4 | matchブロック | withの後にOBLOCKBEGIN挿入 | 軽量構文有効時 |

### 計算ロジック

**オフサイドルールの判定**：
```
現在の行の開始列 < オフサイドスタックのトップの列
=> ブロック終了（OBLOCKEND挿入）
```

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

該当なし

## エラー処理

### エラーケース一覧

| エラーコード | エラー種別 | 発生条件 | 対処方法 |
|------------|----------|---------|---------|
| FS0058 | IndentationProblem | インデントが不正 | 正しいインデントに修正 |
| FS0510 | UnexpectedEndOfFile | ファイル終端でブロック未閉 | 必要なトークンを追加 |

### リトライ仕様

該当なし

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

該当なし

## パフォーマンス要件

トークンストリーム変換のオーバーヘッドは最小限であること

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

該当なし

## 備考

軽量構文は`#light "off"`ディレクティブで無効化できる。詳細構文（Verbose Syntax）を使用する場合、レキサーフィルタはパススルーとなる。

---

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

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

### 推奨読解順序

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

レキサーフィルタで使用される型と状態を理解する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | LexFilter.fs | `src/Compiler/SyntaxTree/LexFilter.fs` | Context型、Position型、OffsideStack型の定義 |

**主要な型定義**:
- **Context**: 現在の構文コンテキスト（CtxtLetDecl, CtxtIf, CtxtMatch等）
- **Position**: トークンの位置情報（行番号、列番号）
- **LexFilterImpl**: レキサーフィルタの実装クラス

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

レキサーフィルタの主要な処理を確認。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | LexFilter.fs | `src/Compiler/SyntaxTree/LexFilter.fs` | GetToken関数 - メインのトークン取得ロジック |

**主要処理フロー**:
- **150-200行目**: コンテキストスタックの定義と操作
- **300-400行目**: オフサイドルールの実装
- **500-600行目**: 各種トークンに対する処理分岐

#### Step 3: 呼び出し元を理解する

レキサーフィルタがどのように使われるかを確認。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | ParseAndCheckInputs.fs | `src/Compiler/Driver/ParseAndCheckInputs.fs` | パーサー呼び出し時のLexFilter使用 |

**読解のコツ**:
- F#のコンピュテーション式（match式のネスト）に慣れていると読みやすい
- コンテキストスタックとオフサイドスタックの2つのスタックを追跡することが重要
- 暗黙トークン（OBLOCKBEGIN, OBLOCKEND等）はpars.fsyで定義されている

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

```
ParseAndCheckInputs.fs
    │
    └─ LexFilter.fs (LexFilterImpl.GetToken)
           │
           ├─ コンテキスト判定
           │      ├─ CtxtLetDecl: let/let rec
           │      ├─ CtxtIf: if/then/else
           │      ├─ CtxtMatch: match/with
           │      ├─ CtxtTry: try/with/finally
           │      └─ CtxtFun: fun/function
           │
           ├─ オフサイド処理
           │      ├─ PushOffsideContext: オフサイドコンテキスト追加
           │      ├─ PopOffsideContext: オフサイドコンテキスト削除
           │      └─ CheckOffsideRule: オフサイドルール判定
           │
           └─ 暗黙トークン挿入
                  ├─ OBLOCKBEGIN: ブロック開始
                  ├─ OBLOCKEND: ブロック終了
                  └─ ODECLEND: 宣言終了
```

### データフロー図

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

lex.fsl ───▶ トークン ───▶ LexFilter ───▶ 変換後トークン ───▶ pars.fsy
                              │
                              ├─ コンテキストスタック
                              │      [CtxtMatch, CtxtIf, CtxtLetDecl]
                              │
                              └─ オフサイドスタック
                                     [(4, "let"), (8, "if"), ...]
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| LexFilter.fs | `src/Compiler/SyntaxTree/LexFilter.fs` | ソース | レキサーフィルタ実装 |
| LexFilter.fsi | `src/Compiler/SyntaxTree/LexFilter.fsi` | ソース | 公開シグネチャ |
| lex.fsl | `src/Compiler/lex.fsl` | ソース | レキサー定義（入力元） |
| pars.fsy | `src/Compiler/pars.fsy` | ソース | パーサー定義（出力先） |
| ParseAndCheckInputs.fs | `src/Compiler/Driver/ParseAndCheckInputs.fs` | ソース | LexFilterの呼び出し元 |
