# 機能設計書 2-レキサー（字句解析）

## 概要

本ドキュメントは、Nimコンパイラのレキサー（字句解析器）の機能設計について記載する。レキサーはNimソースコードをトークンに分割する字句解析機能を提供する。

### 本機能の処理概要

レキサーは、Nimソースコードの文字列を読み取り、意味のある最小単位であるトークン（字句）に分解する処理を行う。この処理は構文解析（パーサー）の前段階として必須であり、コンパイルパイプラインの最初のステップとなる。

**業務上の目的・背景**：プログラミング言語のコンパイルにおいて、ソースコードは最初に字句解析を行う必要がある。レキサーは文字列からトークン列を生成し、後続のパーサーが構文木を構築するための入力を提供する。Nimレキサーは高効率を目指して手書きで実装されており、行単位でバッファリングを行う独自の手法を採用している。これにより、Unix、DOS、Macintoshのいずれのテキストファイル形式にも対応できる。

**機能の利用シーン**：
- ソースコードのコンパイル時（全てのコンパイルコマンドで使用）
- シンタックスハイライト処理時
- IDEでのコード補完時（nimsuggestとの連携）
- コードフォーマッタ（nimpretty）での使用
- ドキュメント生成時

**主要な処理内容**：
1. ソースファイルのオープンとバッファリング初期化
2. 空白・改行・コメントのスキップ処理
3. 識別子（シンボル）の認識とキーワード判定
4. 数値リテラル（整数・浮動小数点）の解析
5. 文字列リテラル（通常・raw・トリプルクォート）の解析
6. 文字リテラルの解析
7. 演算子トークンの認識
8. インデント処理（Nimはインデント感知言語）
9. ドキュメンテーションコメントの抽出

**関連システム・外部連携**：
- パーサー（parser.nim）へトークン列を提供
- nimsuggest（IDE連携ツール）との統合
- nimpretty（コードフォーマッタ）との統合
- highlite（シンタックスハイライト）モジュールとの連携

**権限による制御**：レキサー自体には権限制御はない。ファイルアクセスはllstream経由で行われる。

## 関連画面

| 画面No | 画面名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| N/A | コマンドラインインターフェース | 参照画面 | コンパイルエラー時のトークン位置表示 |

## 機能種別

字句解析処理 / トークン化処理

## 入力仕様

### 入力パラメータ

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| inputstream | PLLStream | Yes | ソースコードのストリーム | 有効なストリーム |
| fileIdx | FileIndex | Yes | ファイルインデックス | 有効なインデックス |
| cache | IdentCache | Yes | 識別子キャッシュ | 非nil |
| config | ConfigRef | Yes | コンパイラ設定 | 非nil |

### 入力データソース

- Nimソースファイル（.nim）
- NimScriptファイル（.nims）
- 設定ファイル（.cfg内のNimコード）
- 標準入力（対話モード時）

## 出力仕様

### 出力データ

| 項目名 | 型 | 説明 |
|--------|-----|------|
| tok.tokType | TokType | トークンの種類 |
| tok.ident | PIdent | 識別子（シンボル・キーワード） |
| tok.iNumber | BiggestInt | 整数リテラル値 |
| tok.fNumber | BiggestFloat | 浮動小数点リテラル値 |
| tok.literal | string | 文字列リテラル・コメント内容 |
| tok.line | int | トークンの行番号 |
| tok.col | int | トークンの列番号 |
| tok.indent | int | インデント深度 |
| tok.base | NumericalBase | 数値の基数（2/8/10/16進数） |

### 出力先

- Token構造体（パーサーに渡される）
- エラーメッセージ（標準エラー出力）

## 処理フロー

### 処理シーケンス

```
1. レキサーのオープン（openLexer）
   └─ バッファ初期化、ファイルインデックス設定
2. トークン取得ループ（rawGetTok）
   └─ 空白・コメントスキップ（skip）
3. 文字種に応じた処理分岐
   ├─ 識別子開始文字 → getSymbol
   ├─ 数字 → getNumber
   ├─ 引用符 → getString
   ├─ シングルクォート → getCharacter
   ├─ # → scanComment
   ├─ 演算子文字 → getOperator
   └─ その他 → 単一文字トークン
4. トークン情報の設定
   └─ 行・列番号、インデント設定
5. レキサーのクローズ（closeLexer）
   └─ コンパイル行数の更新
```

### フローチャート

```mermaid
flowchart TD
    A[openLexer] --> B[rawGetTok]
    B --> C[skip: 空白・コメントスキップ]
    C --> D{現在の文字}
    D -->|a-zA-Z| E[getSymbol]
    D -->|0-9| F[getNumber]
    D -->|"| G[getString]
    D -->|'| H[getCharacter]
    D -->|#| I[scanComment]
    D -->|演算子文字| J[getOperator]
    D -->|括弧等| K[単一文字トークン]
    D -->|EOF| L[tkEof]
    E --> M[トークン返却]
    F --> M
    G --> M
    H --> M
    I --> M
    J --> M
    K --> M
    L --> N[closeLexer]
    M --> B
```

## ビジネスルール

### 業務ルール

| ルールNo | ルール名 | 内容 | 適用条件 |
|---------|---------|------|---------|
| BR-01 | タブ禁止 | タブ文字はエラーとする | タブ文字検出時 |
| BR-02 | アンダースコア規則 | 単独アンダースコアのみ許可、末尾アンダースコア禁止 | 識別子解析時 |
| BR-03 | キーワード判定 | 識別子がキーワードリストに含まれる場合はキーワードトークンに変換 | getSymbol時 |
| BR-04 | 数値接尾辞 | i8, i16, i32, i64, u, u8, u16, u32, u64, f, f32, f64, f128をサポート | 数値リテラル解析時 |
| BR-05 | raw文字列 | r"..."形式でエスケープシーケンスを無効化 | r/R開始時 |
| BR-06 | トリプルクォート | """..."""で複数行文字列リテラル | 文字列解析時 |

### 計算ロジック

- **演算子優先順位計算**（getPrecedence）：演算子の先頭文字に基づいて優先順位を決定
  - `$`, `^`: 10（代入時は1）
  - `*`, `%`, `/`, `\`: 9
  - `+`, `-`, `|`: 8
  - `&`: 7
  - `.`: 6
  - `=`, `<`, `>`, `!`: 5
  - `and`: 4
  - `or`, `xor`: 3
  - `?`: 2

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

該当なし（レキサーはデータベースを使用しない）

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

| 操作 | 対象テーブル | 操作種別 | 概要 |
|-----|-------------|---------|------|
| N/A | N/A | N/A | データベース操作なし |

## エラー処理

### エラーケース一覧

| エラーコード | エラー種別 | 発生条件 | 対処方法 |
|------------|----------|---------|---------|
| errGenerated | タブエラー | タブ文字が使用された | スペースに置換 |
| errGenerated | 無効トークン | 認識できない文字 | 文字を確認・修正 |
| errGenerated | 閉じ引用符なし | 文字列リテラルが閉じられていない | 引用符を追加 |
| errGenerated | 無効数値 | 数値リテラルの形式エラー | 形式を修正 |
| errGenerated | 数値範囲外 | 数値が型の範囲を超過 | 適切な型を使用 |
| errGenerated | 無効文字定数 | 文字リテラルの形式エラー | 形式を修正 |
| warnOctalEscape | 警告 | 8進エスケープ使用 | \xを使用推奨 |
| warnDeprecated | 警告 | 0c/0C形式の8進リテラル | 0oを使用推奨 |

### リトライ仕様

レキサーはリトライを行わない。エラー発生時はエラーメッセージを出力し、可能な限り処理を継続する（エラーリカバリー）。

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

該当なし

## パフォーマンス要件

- 行単位バッファリングによる効率的なメモリ使用
- 識別子キャッシュ（IdentCache）による重複文字列の削減
- ハッシュ計算による高速なキーワード・識別子検索
- レジスタ変数使用による最適化（pos変数）

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

- 入力ファイルサイズに対する暗黙の制限（メモリ制約）
- Unicode文字の適切な処理

## 備考

- レキサーは効率性のために手書きで実装されている
- Unix/DOS/Macintosh形式のテキストファイルに対応
- nimsuggest向けの拡張（previousToken、tokenEnd等）
- nimpretty向けの拡張（offsetA、offsetB等）

---

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

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

### 推奨読解順序

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

まず、トークンとレキサーのデータ構造を理解することが重要である。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | lexer.nim | `compiler/lexer.nim` | TokType列挙型（38-80行目）でトークン種別を理解 |
| 1-2 | lexer.nim | `compiler/lexer.nim` | Token構造体（100-116行目）でトークンのデータ構造を理解 |
| 1-3 | lexer.nim | `compiler/lexer.nim` | Lexer構造体（119-131行目）でレキサーの状態管理を理解 |

**読解のコツ**:
- `TokType`は`tkInvalid`から始まり、キーワード（`tkAddr`〜`tkYield`）、リテラル、記号の順で定義されている
- `tokKeywordLow`と`tokKeywordHigh`の範囲でキーワード判定を行う（88-89行目）
- `Token`構造体の`when defined(nimpretty)`や`when defined(nimsuggest)`で条件付きフィールドがある

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

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | lexer.nim | `compiler/lexer.nim` | openLexer（178-192行目）でレキサーの初期化を理解 |
| 2-2 | lexer.nim | `compiler/lexer.nim` | rawGetTok（1202-1378行目）でメイントークン取得処理を理解 |
| 2-3 | lexer.nim | `compiler/lexer.nim` | closeLexer（194-197行目）で後処理を理解 |

**主要処理フロー**:
1. **1214行目**: `reset(tok)`でトークンを初期化
2. **1215-1220行目**: インデント処理（先読みインデントがあれば使用）
3. **1221行目**: `skip(L, tok)`で空白・コメントをスキップ
4. **1226-1228行目**: 現在位置の文字取得と行・列番号設定
5. **1229-1377行目**: 文字種に応じた処理分岐

#### Step 3: 識別子・キーワード解析を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | lexer.nim | `compiler/lexer.nim` | getSymbol（889-928行目）で識別子解析を理解 |

**主要処理フロー**:
- **890行目**: ハッシュ値`h`を初期化
- **894-917行目**: 識別子文字をループで処理、大文字を小文字に正規化
- **919行目**: `!$h`でハッシュをファイナライズ
- **920行目**: `L.cache.getIdent`でキャッシュから識別子を取得
- **921-925行目**: キーワード範囲内かチェックし、トークン種別を決定

#### Step 4: 数値リテラル解析を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 4-1 | lexer.nim | `compiler/lexer.nim` | getNumber（271-587行目）で数値解析を理解 |

**主要処理フロー**:
- **350-353行目**: 初期設定（tkIntLit、base10）
- **367-406行目**: 基数プレフィックス（0x, 0o, 0b）の処理
- **412-449行目**: 型接尾辞（i8, u64, f32等）の処理
- **460-586行目**: 実際の数値変換（基数に応じた処理）

#### Step 5: 文字列リテラル解析を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 5-1 | lexer.nim | `compiler/lexer.nim` | getString（756-820行目）で文字列解析を理解 |
| 5-2 | lexer.nim | `compiler/lexer.nim` | getEscapedChar（657-735行目）でエスケープ処理を理解 |

**主要処理フロー**:
- **761-794行目**: トリプルクォート文字列（"""..."""）の処理
- **795-820行目**: 通常文字列の処理
- **813-816行目**: エスケープシーケンス処理の呼び出し

#### Step 6: 演算子解析を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 6-1 | lexer.nim | `compiler/lexer.nim` | getOperator（939-968行目）で演算子解析を理解 |
| 6-2 | lexer.nim | `compiler/lexer.nim` | getPrecedence（970-1010行目）で優先順位計算を理解 |

**主要処理フロー**:
- **943-955行目**: 演算子文字を連続で読み取り、ハッシュを計算
- **956行目**: `endOperator`で演算子トークンを確定
- **970-1010行目**: 演算子の先頭文字に基づいて優先順位を返す

#### Step 7: スキップ処理を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 7-1 | lexer.nim | `compiler/lexer.nim` | skip（1130-1200行目）で空白・コメントスキップを理解 |

**主要処理フロー**:
- **1142-1147行目**: 空白・タブの処理
- **1148-1172行目**: 改行とインデント処理
- **1173-1193行目**: コメント（#で始まる）のスキップ

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

```
rawGetTok (メインエントリ)
    │
    ├─ skip
    │      ├─ handleCRLF
    │      └─ skipMultiLineComment
    │
    ├─ getSymbol
    │      └─ L.cache.getIdent
    │
    ├─ getNumber
    │      ├─ matchUnderscoreChars
    │      ├─ lexMessageLitNum
    │      └─ checkBitWidth
    │
    ├─ getString
    │      ├─ handleCRLF
    │      └─ getEscapedChar
    │             ├─ handleHexChar
    │             ├─ handleDecChars
    │             └─ addUnicodeCodePoint
    │
    ├─ getCharacter
    │      └─ getEscapedChar
    │
    ├─ scanComment
    │      └─ skipMultiLineComment
    │
    └─ getOperator
           └─ endOperator
```

### データフロー図

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

Nimソースファイル ───────────▶ openLexer ────────────────────────▶ Lexer初期化
       │                        (バッファ設定)
       │
       ▼
バッファ内文字列 ─────────────▶ skip ────────────────────────────▶ 空白スキップ済み位置
                                (空白・コメント除去)
       │
       ▼
現在位置の文字 ───────────────▶ rawGetTok ───────────────────────▶ Token構造体
                                (文字種別分岐)
       │
       │  ┌─ getSymbol ─────▶ tkSymbol/tkKeyword
       │  ├─ getNumber ─────▶ tkIntLit/tkFloatLit
       ├──┼─ getString ─────▶ tkStrLit/tkRStrLit/tkTripleStrLit
       │  ├─ getCharacter ──▶ tkCharLit
       │  ├─ scanComment ───▶ tkComment
       │  └─ getOperator ───▶ tkOpr/tk記号
       │
       ▼
Token列 ──────────────────────▶ parser.nim ──────────────────────▶ AST
                                (構文解析)
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| lexer.nim | `compiler/lexer.nim` | ソース | レキサー本体（字句解析） |
| nimlexbase.nim | `compiler/nimlexbase.nim` | ソース | レキサーのベースクラス（バッファリング） |
| llstream.nim | `compiler/llstream.nim` | ソース | 軽量ストリーム（ファイル・文字列入力） |
| idents.nim | `compiler/idents.nim` | ソース | 識別子キャッシュ |
| wordrecg.nim | `compiler/wordrecg.nim` | ソース | キーワード認識 |
| lineinfos.nim | `compiler/lineinfos.nim` | ソース | 行情報（TLineInfo） |
| msgs.nim | `compiler/msgs.nim` | ソース | メッセージ出力 |
| highlite.nim | `lib/packages/docutils/highlite.nim` | ソース | シンタックスハイライト（字句解析共有） |
