# 機能設計書: parsexml - XMLストリームパーサー

## 1. 機能概要

### 1.1 目的
parsexmlモジュールは、高性能なXML/HTMLストリームパーサーを提供する。イベント駆動型のパーサーであり、大きなXMLファイルでもメモリ効率よく処理できる。ある程度のエラー訂正機能を持ち、「野生のHTML」も解析可能である。

### 1.2 機能範囲
- XMLストリームの解析
- HTMLストリームの解析（エラー訂正付き）
- イベント駆動型パーシング
- 要素、属性、コメント、CDATAの検出
- エンティティ参照の解析
- 処理命令（PI）の検出

### 1.3 関連コンポーネント
- `streams` モジュール: 入力ストリーム
- `lexbase` モジュール: 基盤となるレクサー
- `xmltree` モジュール: 高レベルXMLツリー構築
- `htmlparser` モジュール: HTML解析

## 2. データ構造

### 2.1 型定義

```nim
type
  XmlEventKind* = enum  ## パース中に発生する全イベント
    xmlError,           ## パース中にエラー発生
    xmlEof,             ## ファイル終端に到達
    xmlCharData,        ## 文字データ
    xmlWhitespace,      ## 空白
    xmlComment,         ## コメント
    xmlPI,              ## 処理命令 (<?name something ?>)
    xmlElementStart,    ## <elem>
    xmlElementEnd,      ## </elem>
    xmlElementOpen,     ## <elem（属性付き開始タグ）
    xmlAttribute,       ## key = "value" ペア
    xmlElementClose,    ## >
    xmlCData,           ## <![CDATA[...]]>
    xmlEntity,          ## &entity;
    xmlSpecial          ## <!...>

  XmlErrorKind* = enum  ## 発生しうるエラー一覧
    errNone,
    errEndOfCDataExpected,
    errNameExpected,
    errSemicolonExpected,
    errQmGtExpected,
    errGtExpected,
    errEqExpected,
    errQuoteExpected,
    errEndOfCommentExpected,
    errAttributeValueExpected

  XmlParseOption* = enum  ## パーサーオプション
    reportWhitespace,     ## 空白を報告
    reportComments,       ## コメントを報告
    allowUnquotedAttribs, ## 引用符なし属性を許可（HTML用）
    allowEmptyAttribs     ## 空属性を許可

  XmlParser* = object of BaseLexer  ## パーサーオブジェクト
    a, b, c: string
    kind: XmlEventKind
    err: XmlErrorKind
    state: ParserState
    cIsEmpty: bool
    filename: string
    options: set[XmlParseOption]
```

### 2.2 特徴
- **イベント駆動型**: `next()`を呼び出すごとに次のイベントを返す
- **低メモリ消費**: 全体をメモリに読み込まない
- **エラー訂正**: 不正なHTMLでも可能な限り解析を継続
- **UTF-8専用**: サポートするエンコーディングはUTF-8のみ

## 3. 主要機能

### 3.1 パーサー初期化

#### open
```nim
proc open*(my: var XmlParser, input: Stream, filename: string,
           options: set[XmlParseOption] = {})
```
- **目的**: 入力ストリームでパーサーを初期化
- **パラメータ**:
  - `input`: 入力ストリーム
  - `filename`: エラーメッセージ用のファイル名
  - `options`: パーサーオプション
- **ソース位置**: `lib/pure/parsexml.nim:220-236`

#### close
```nim
proc close*(my: var XmlParser) {.inline.}
```
- **目的**: パーサーと関連ストリームを閉じる
- **ソース位置**: `lib/pure/parsexml.nim:238-240`

### 3.2 イベント取得

#### next
```nim
proc next*(my: var XmlParser)
```
- **目的**: 次のイベントを取得
- **注意**: `kind`プロパティで現在のイベント種類を確認

#### kind
```nim
proc kind*(my: XmlParser): XmlEventKind {.inline.}
```
- **目的**: 現在のイベント種類を返す
- **ソース位置**: `lib/pure/parsexml.nim:242-244`

### 3.3 データアクセス

| テンプレート | 対象イベント | 説明 |
|-------------|-------------|------|
| `charData` | xmlCharData, xmlWhitespace, xmlComment, xmlCData, xmlSpecial | 文字データ取得 |
| `elementName` | xmlElementStart, xmlElementEnd, xmlElementOpen | 要素名取得 |
| `entityName` | xmlEntity | エンティティ名取得 |
| `attrKey` | xmlAttribute | 属性キー取得 |
| `attrValue` | xmlAttribute | 属性値取得 |
| `piName` | xmlPI | 処理命令名取得 |
| `piRest` | xmlPI | 処理命令の残り部分取得 |

- **ソース位置**: `lib/pure/parsexml.nim:246-303`

### 3.4 位置情報

```nim
proc getColumn*(my: XmlParser): int {.inline.}  ## 現在の列番号
proc getLine*(my: XmlParser): int {.inline.}    ## 現在の行番号
proc getFilename*(my: XmlParser): string {.inline.}  ## ファイル名
```
- **ソース位置**: `lib/pure/parsexml.nim:315-325`

### 3.5 エラーメッセージ

```nim
proc errorMsg*(my: XmlParser): string                  ## xmlErrorイベント用
proc errorMsgExpected*(my: XmlParser, tag: string): string  ## 期待タグ用
proc errorMsg*(my: XmlParser, msg: string): string      ## カスタムメッセージ用
```
- **ソース位置**: `lib/pure/parsexml.nim:327-343`

## 4. 使用例

### 4.1 HTMLタイトル取得
```nim
import std/[streams, parsexml, strutils]

var s = newFileStream("document.html", fmRead)
var x: XmlParser
open(x, s, "document.html")
while true:
  x.next()
  case x.kind
  of xmlElementStart:
    if cmpIgnoreCase(x.elementName, "title") == 0:
      var title = ""
      x.next()
      while x.kind == xmlCharData:
        title.add(x.charData)
        x.next()
      echo "Title: " & title
      break
  of xmlEof: break
  else: discard
x.close()
```

### 4.2 リンク抽出
```nim
import std/[streams, parsexml, strutils]

var x: XmlParser
open(x, newFileStream("page.html", fmRead), "page.html")
next(x)
while x.kind != xmlEof:
  if x.kind == xmlElementOpen and x.elementName.cmpIgnoreCase("a") == 0:
    x.next()
    if x.kind == xmlAttribute and x.attrKey.cmpIgnoreCase("href") == 0:
      echo x.attrValue
  x.next()
x.close()
```

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

### 5.1 推奨読解順序

1. **型定義** (159-204行目)
   - `XmlEventKind`, `XmlErrorKind`, `XmlParseOption`, `XmlParser`の構造

2. **初期化と終了** (220-240行目)
   - `open`, `close`プロシージャ

3. **データアクセス** (242-313行目)
   - 各種テンプレート（`charData`, `elementName`等）

4. **解析ロジック** (349-700行目付近)
   - `parseCDATA`, `parseComment`, `parseSpecial`, `parseName`等

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

```
open(x, s, filename) [220行目]
└── lexbase.open()

next(x) [パース処理]
├── stateStart
│   ├── '<' → parseTag()
│   │   ├── '!' → parseComment() / parseCDATA() / parseSpecial()
│   │   ├── '?' → parsePI()
│   │   ├── '/' → xmlElementEnd
│   │   └── その他 → xmlElementStart / xmlElementOpen
│   └── その他 → parseCharData()
├── stateAttr
│   └── xmlAttribute の処理
└── stateEmptyElementTag
    └── xmlElementEnd (空要素)

close(x) [238行目]
└── lexbase.close()
```

### 5.3 データフロー図

```
[入力ストリーム]
       │
       ▼
┌─────────────────────────────────────┐
│           XmlParser                  │
│  ┌─────────────────────────────┐    │
│  │     BaseLexer (バッファ)     │    │
│  └───────────────┬─────────────┘    │
│                  │                   │
│  ┌───────────────▼─────────────┐    │
│  │    状態マシン (state)        │    │
│  │  stateStart → stateNormal   │    │
│  │  stateAttr → stateEmptyTag  │    │
│  └───────────────┬─────────────┘    │
└──────────────────┼───────────────────┘
                   │
                   ▼
         XmlEventKind (kind)
                   │
     ┌─────────────┼─────────────┐
     │             │             │
     ▼             ▼             ▼
xmlElement*    xmlAttribute   xmlCharData
     │             │             │
     ▼             ▼             ▼
elementName    attrKey       charData
               attrValue
```

### 5.4 読解のコツ

- **状態マシン**: `ParserState`（stateStart, stateNormal, stateAttr等）で現在の解析状態を管理
- **イベント駆動**: `next()`呼び出しで状態が遷移し、適切なイベントが設定される
- **バッファ管理**: `BaseLexer`を継承し、効率的なバッファリングを実現
- **エラー訂正**: HTMLの不正な構文（閉じタグなし等）を許容する設計

## 6. 関連ファイル一覧

| ファイルパス | 種別 | 役割 |
|-------------|------|------|
| `lib/pure/parsexml.nim` | ソース | メイン実装 |
| `lib/pure/lexbase.nim` | ソース | 基盤レクサー |
| `lib/pure/streams.nim` | ソース | ストリーム入力 |
| `lib/pure/unicode.nim` | ソース | Unicode処理 |

## 7. 設計上の考慮事項

### 7.1 パフォーマンス
- イベント駆動型のためメモリ効率が良い
- 大規模XMLファイルの処理に適している
- バッファサイズは8192バイトで初期化

### 7.2 制限事項
- UTF-8エンコーディングのみサポート
- タグの開閉整合性チェックはクライアント責任
- DTD検証は行わない

### 7.3 HTMLとの互換性
- `allowUnquotedAttribs`: 引用符なしの属性値を許可
- `allowEmptyAttribs`: 値なし属性を許可
- タグ名の大文字小文字を区別しない比較はクライアントで実装
