# 機能設計書: htmlparser - HTMLパーサー

## 1. 機能概要

### 1.1 目的
htmlparserモジュールは、「野生のHTML」を解析し、XmlNodeツリーとして出力する機能を提供する。ある程度のエラー訂正機能を持ち、不完全なHTMLでも可能な限り解析を継続する。

**注意**: このモジュールは非推奨（deprecated）であり、代わりに `nimble install htmlparser` でインストールしたパッケージの使用が推奨される。

### 1.2 機能範囲
- HTMLファイル・文字列の解析
- XmlNodeツリーへの変換
- HTMLタグの列挙型定義
- HTMLエンティティのUnicode変換
- タグのブロック/インライン/単独分類

### 1.3 関連コンポーネント
- `xmltree` モジュール: XmlNode型の提供
- `parsexml` モジュール: 低レベルパーサー
- `strtabs` モジュール: 属性テーブル
- `unicode` モジュール: Rune型

## 2. データ構造

### 2.1 型定義

```nim
type
  HtmlTag* = enum  ## サポートするHTMLタグ一覧（アルファベット順）
    tagUnknown,    ## 未知のHTML要素
    tagA,          ## <a>
    tagAbbr,       ## <abbr> (deprecated)
    tagAcronym,    ## <acronym>
    # ... 約130種類のタグ
    tagVideo,      ## <video>
    tagWbr         ## <wbr>
```

### 2.2 タグ分類定数

```nim
const
  InlineTags* = {tagA, tagAbbr, tagB, tagButton, ...}   ## インライン要素
  BlockTags* = {tagAddress, tagBlockquote, tagDiv, ...} ## ブロック要素
  SingleTags* = {tagArea, tagBr, tagCol, tagHr, ...}    ## 単独タグ（閉じタグ不要）
  tagToStr* = ["a", "abbr", "acronym", ...]             ## タグ名文字列配列
```

### 2.3 特徴
- **タグは小文字**: 結果ツリーのすべてのタグは小文字に正規化される
- **clientData使用**: 結果の`XmlNode`は`clientData`フィールドを使用するため、クライアントコードでは使用不可
- **エラー訂正**: 閉じタグの欠落などを許容

## 3. 主要機能

### 3.1 HTML解析

#### parseHtml（ストリーム版、エラー付き）
```nim
proc parseHtml*(s: Stream, filename: string,
                errors: var seq[string]): XmlNode
```
- **目的**: ストリームからHTMLを解析し、XmlNodeを返す
- **パラメータ**:
  - `s`: 入力ストリーム
  - `filename`: エラーメッセージ用のファイル名
  - `errors`: パースエラーを格納するシーケンス
- **ソース位置**: `lib/pure/htmlparser.nim:2017-2040`

#### parseHtml（ストリーム版、簡易）
```nim
proc parseHtml*(s: Stream): XmlNode
```
- **目的**: エラーを無視してストリームからHTMLを解析
- **ソース位置**: `lib/pure/htmlparser.nim:2042-2046`

#### parseHtml（文字列版）
```nim
proc parseHtml*(html: string): XmlNode
```
- **目的**: 文字列からHTMLを解析
- **ソース位置**: `lib/pure/htmlparser.nim:2048-2051`

#### loadHtml（ファイル版、エラー付き）
```nim
proc loadHtml*(path: string, errors: var seq[string]): XmlNode
```
- **目的**: ファイルからHTMLを読み込み解析
- **ソース位置**: `lib/pure/htmlparser.nim:2053-2059`

#### loadHtml（ファイル版、簡易）
```nim
proc loadHtml*(path: string): XmlNode
```
- **目的**: エラーを無視してファイルからHTMLを読み込み
- **ソース位置**: `lib/pure/htmlparser.nim:2061-2065`

### 3.2 タグ操作

#### htmlTag（XmlNode版）
```nim
proc htmlTag*(n: XmlNode): HtmlTag
```
- **目的**: XmlNodeのタグをHtmlTag列挙型として取得
- **注意**: `clientData`フィールドにキャッシュされる
- **ソース位置**: `lib/pure/htmlparser.nim:356-360`

#### htmlTag（文字列版）
```nim
proc htmlTag*(s: string): HtmlTag
```
- **目的**: 文字列をHtmlTag列挙型に変換
- **戻り値**: 未知のタグは`tagUnknown`
- **ソース位置**: `lib/pure/htmlparser.nim:362-366`

### 3.3 エンティティ変換

#### entityToRune
```nim
proc entityToRune*(entity: string): Rune
```
- **目的**: HTMLエンティティ名をUnicode Runeに変換
- **対応形式**:
  - 名前付きエンティティ: `&gt;` → `>`
  - 10進数エンティティ: `&#220;` → `Ü`
  - 16進数エンティティ: `&#x000DC;` → `Ü`
- **ソース位置**: `lib/pure/htmlparser.nim:379-1898`

#### runeToEntity
```nim
proc runeToEntity*(rune: Rune): string
```
- **目的**: RuneをHTMLの数値エンティティに変換
- **例**: `Ü` → `#220`
- **ソース位置**: `lib/pure/htmlparser.nim:368-377`

## 4. 使用例

### 4.1 基本的な使用法
```nim
import std/htmlparser
import std/xmltree

# ファイルから読み込み
let html = loadHtml("mydirty.html")
echo html

# 文字列から解析
let doc = parseHtml("<html><body><p>Hello</p></body></html>")
echo doc
```

### 4.2 ハイパーリンクの変換
```nim
import std/htmlparser
import std/xmltree
import std/strtabs
import std/os
import std/strutils

let html = loadHtml("input.html")

for a in html.findAll("a"):
  if a.attrs.hasKey "href":
    let (dir, filename, ext) = splitFile(a.attrs["href"])
    if cmpIgnoreCase(ext, ".rst") == 0:
      a.attrs["href"] = dir / filename & ".html"

writeFile("output.html", $html)
```

### 4.3 エラーハンドリング
```nim
import std/htmlparser

var errors: seq[string] = @[]
let html = loadHtml("broken.html", errors)

for e in errors:
  echo "Parse error: ", e
```

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

### 5.1 推奨読解順序

1. **タグ定義** (57-183行目)
   - `HtmlTag`列挙型と分類定数

2. **タグ変換** (228-366行目)
   - `toHtmlTag`, `htmlTag`プロシージャ

3. **エンティティ変換** (368-1898行目)
   - `runeToEntity`, `entityToRune`（膨大なエンティティマッピング）

4. **パース処理** (1901-2015行目)
   - `parse`, `untilElementEnd`（再帰的パース）

5. **公開API** (2017-2065行目)
   - `parseHtml`, `loadHtml`

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

```
loadHtml(path, errors) [2053行目]
├── newFileStream(path, fmRead)
└── parseHtml(s, path, errors)
    ├── XmlParser.open(s, filename, options)
    ├── 最初のnext()でDOCTYPEスキップ
    ├── newElement("document")
    └── parse(x, errors) [再帰]
        ├── xmlComment → newComment()
        ├── xmlCharData → newText()
        ├── xmlElementStart
        │   ├── newElement(toLowerAscii)
        │   └── untilElementEnd()
        │       ├── SingleTags → 即座に終了
        │       ├── 自動閉じタグ推測
        │       └── parse() [再帰]
        ├── xmlElementOpen
        │   ├── newElement()
        │   ├── 属性解析
        │   └── untilElementEnd()
        ├── xmlCData → newCData()
        └── xmlEntity → entityToUtf8()
```

### 5.3 データフロー図

```
[HTMLファイル/文字列]
        │
        ▼
┌─────────────────────────────────────┐
│          loadHtml / parseHtml        │
│  ┌─────────────────────────────┐    │
│  │ XmlParser (parsexml使用)     │    │
│  │  - reportComments           │    │
│  │  - reportWhitespace         │    │
│  │  - allowUnquotedAttribs     │    │
│  │  - allowEmptyAttribs        │    │
│  └───────────────┬─────────────┘    │
└──────────────────┼───────────────────┘
                   │
                   ▼
         ┌─────────────────┐
         │  parse()        │
         │  (再帰的解析)    │
         └────────┬────────┘
                  │
     ┌────────────┼────────────┐
     │            │            │
     ▼            ▼            ▼
 newElement   newText      newComment
     │            │            │
     └────────────┼────────────┘
                  │
                  ▼
            XmlNode ツリー
            (タグは小文字)
```

### 5.4 読解のコツ

- **エラー訂正ロジック**: `untilElementEnd`内で`SingleTags`や自動閉じタグを処理
- **タグ小文字化**: `newElement(toLowerAscii(x.elemName))`で統一
- **エンティティ変換**: 1500以上のエンティティを`case`文でマッピング
- **clientDataキャッシュ**: `htmlTag`は結果を`clientData`にキャッシュして高速化

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

| ファイルパス | 種別 | 役割 |
|-------------|------|------|
| `lib/pure/htmlparser.nim` | ソース | メイン実装 |
| `lib/pure/parsexml.nim` | ソース | 低レベルパーサー |
| `lib/pure/xmltree.nim` | ソース | XmlNode型 |
| `lib/pure/strtabs.nim` | ソース | 属性テーブル |
| `lib/pure/unicode.nim` | ソース | Rune型 |

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

### 7.1 非推奨ステータス
- バージョン情報なしで非推奨
- 代替: `nimble install htmlparser` でインストールしたパッケージ
- 理由: 「野生のHTML」の定義が曖昧、動作が将来変更される可能性

### 7.2 エラー訂正の特徴
- 閉じタグがなくても解析継続
- `<p>`, `<li>`, `<dd>`, `<dt>`は次の同種タグで自動閉じ
- `<td>`, `<th>`は`<tr>`等で自動閉じ
- `SingleTags`（`<br>`, `<hr>`等）は閉じタグ不要

### 7.3 制限事項
- 結果ツリーの`clientData`フィールドはhtmlparser内部で使用
- HTMLの大文字小文字は区別されない（すべて小文字に変換）
- DOCTYPE宣言はスキップされる

### 7.4 パフォーマンス
- `htmlTag`は`clientData`にキャッシュされるため2回目以降は高速
- `entityToRune`は巨大な`case`文だが、コンパイラ最適化により効率的
