TOMLパーサを作るために

# はじめに
[公式サイト](https://toml.io/en/)
TOMLは, 人が書く設定ファイルのためのフォーマットです.
設定ファイルに使われるフォーマットとして代表的なものについて, 私の意見は次になります. YAMLかTOMLを推していきたいです.

| フォーマット | |
| :--- | :--- |
| INI | 旧いため機能が少ないです. 仕様がないため独自拡張が多いです. WindowsではCP932と思われるがASCII以外がまともに扱えると思わない方がいいです. |
| JSON | Javascriptのオブジェクトを表記するためのもので, 人が書く設定ファイルに向いていないです. ネスト数に仕様上の制限がないので実装次第でスタックがあふれることがあります, 通信メッセージとして使う場合は注意が必要です. 末尾の`,`の問題が, とよく言われますが元来設定ファイル用ではないため, ほとんどのセパレータとして`,`を使うことが問題です. 他の形式は文法上`,`だけでなく, 改行も活用しているため人が理解しやすくなっています. 末尾の`,`を許すかどうかなど, パーサを書く側としては些細な問題です. |
| YAML | 機能的には必要十分と思われます. 配列がプログラマには直感的に理解できないです. インデントに意味があるためネストが深くなると分かり辛くなります. |
| TOML | 後発のため必要十分な機能があります. シンプルと言いつつ実は多機能で他と比較すると仕様は複雑です. 見た目はシンプルながら複雑な構造を簡単に書けてしまいます. 同じ構造を様々な書き方で表現できる点が最大の欠点です. INIと似ているため, INIの後継としてシンプルに使うなら分かり易いです. |

TOMLの仕様についてパーサを書く側からのノートですが, 公式ドキュメントからは読み取ることが難しい知識も書けるかと思います.

## 不正確な情報
- 型を指定できる
    - できない. `-`,`.`, `e`や`E`があったら分岐させているだけ.
        - 20010113101221は日付とは認識しない
    - 文字列以外の型が使える, ぐらいの表現がいいと思います.
- 仕様が単純
    - ここに挙げた競合より複雑
    - 競合の不満点を改善しようとしたのであって, 比較して仕様を単純にしようとしていない.

# 検証
[BurntSushi/toml-test](https://github.com/BurntSushi/toml-test)のデータを使用します.

# 仕様
ABNFの文法と仕様が, 明らかに矛盾しているので注意します.

## 文字コード
UTF-8, 改行はLFかCRLF, 空白は水平タブとスペース. `BOM`についての記述はないので, 私は検出してスキップします.

## コメント
`#`以降次の改行までコメントです.
```TOML
# コメント
```

## キーバリュー
キーとバリューの組, これが後述のテーブルに入ります. 空白は前後どこでも入れることができますが, 改行を入れることはできません.
```TOML
  key0= value0
key1   = 1234  # 好きなだけ空白を
```

空のキー, 空のバリューは不正です.
```TOML
= value0
key1 =
```

### キー
後述の文字列とクウォーテンションなしの文字列が有効です. 空文字も有効なことに注意します.

```TOML
key0 = value
"" = value
' ' = value
```

## テーブル
JSONと同じくトップレベルのテーブルが存在します.

空のTOMLファイルはJSONでは次のようになります.
```JSON
{
}
```
`[]`でテーブルを開始します, 以降のキーバリューはこのテーブルに入ります.
`.`でキーを連結することで, サブテーブルを作ることができます.

```TOML
[a.b.c]
d = e
```

`.`でサブテーブルを作ることは, キーバリューでもできます. 先の書き方と次の書き方は同じになります. 違いは, 次の書き方は"現在のテーブル"は`a`で変化しないことです.

```TOML
[a]
b.c.d = e
```

次のようなテーブルのネストは,
```TOML
key0 = 0
[table0.sub0]
key0 = 0
[table0]
key0 = 0
```

次のようになりますが, 深さや書き方に気を付けないと分かり辛くなります.
```JSON
{
    "key0": "0",
    "table0": {
        "sub0": {
            "key0": "0"
        },
        "key0": "0"
    }
}
```

次を合法にするために, トップレベルのテーブルだけは特別扱いします. トップレベルは, キーの連鎖が重複してはいけません.
次は合法なはずです.
```TOML
[table0.sub0]
[table0]
sub0.key0 = 0
```

## 整数
`[−2^63 2^63−1]`の整数なのでオーバーフローしたらエラーにします. `12_34`のように`_`で分割できますが, 先頭, 末尾, `_`の連続は不正になります. 2進数, 8進数, 16進数が記述できます.

```TOML
digit = -42
hex = 0xF0F0
oct = 0o7070
bin = 0b1010
```

## 浮動小数点数
`inf`や`nan`を使えますが, それが実際にどんな値になるかはデコーダ依存です. オバーフローについては記述がないので, 何もしないでデコードします.

## 文字列
`"`で囲みエスケープ`\`が使える書き方と, `'`で囲みエスケープが使えない書き方があります. Unicodeのエスケープ`\uXXXX`と`\UXXXXXXXX`は正しいコードポイントである必要があります.
複数行バージョンの`"""`と`'''`や, 空白と改行をスキップする`\`があります.

### 複数行リテラル文字列
次は, ABNFでは不当, 仕様では正当になります.

```
oneline = '''This string has a ' quote character.'''
```

ABNFでは, "'"が連続するのは, ３回, ２回, ５回の３種類になります.

## 時刻
`RFC 3339`の書き方を受け入れますが, それが存在する時刻なのかはチェックしません.

## 配列
公式ドキュメントでは複数の型が混在してもOK, 検証データではNGなのでどちらが正しいか要確認です.
最後に`,`を付けてもいいです.

```TOML
ints-and-floats = [1, 1.1]
```

## テーブル配列
TOMLで検索しても, `いかがでしたでしょうか`記事では解説することがない筆頭の機能です. 
仕様を読み解けていないため, 間違っていると思います.

テーブルのように`[[]]`で開始しますが, テーブルとは別に"テーブル配列の状態"があります. 重複した場合, テーブルを"現在のテーブル配列"に追加し, そのテーブルを"現在のテーブル"に設定します.

```TOML
[[table]]
key=value
[[table]]
[[table]]
key=value
```

```JSON
{
  "table": [
    { "key": "value"},
    { },
    { "key": "value"}
  ]
}
```

これが,
```TOML
[[fruits]]
name = "apple"
[fruits.physical]
color = "red"
```

JSONではこうなるため,
```JSON
{
  "fruits": [
    {
      "name": "apple",
      "physical": {
        "color": "red"
      }
    }
  ]
}
```

`.`で区切られたキーの末尾まではテーブルになり, 最後だけテーブルの配列と解釈します.
内部では, 現在対象とする"テーブル"と"テーブルの配列"の２つの状態があるのですが, ユーザ側では状態変化がどこで起こるか分かり辛いです.

このTOMLは,
```TOML
[[fruits.varieties]]
name = "red delicious"
```
こうなります.
```JSON
{
  "fruits": {
    "varieties":[
        {
            "name": "red delicious"
        }
    ]
  }
}
```

# インラインテーブル
キーバリューのバリューに書くテーブル.

# まとめ
私の使い方としては, 次のようになります. 

## テーブル
`[]`に統一します. 
```TOML
[main0]
[main0.sub0]
[main1]
[main1.sub0]
```
`.`は冗長になるのと, テーブルの規則を壊すので使わないようにします. 有用な場面があれば教えていただきたいです.

## テーブル配列
私にとっては, その利益に対して, 理解するコストが下回る場面が理解できない機能です. 使わないで解決できるならその方がいいです.