設定ファイル記述言語 TOML

  • 184
    いいね
  • 3
    コメント
この記事は最終更新日から1年以上が経過しています。

TOML

TOML は、GitHub の中の人が提案した、設定ファイルを記述するための小さな言語である。明瞭な文法なため、人間が読みやすい。また、TOML はハッシュテーブルに明確にマップするように設計されているので、様々な言語でのデータ構造へパースしやすい。

また、Vim で有名な NeoBundle が TOML パーサを搭載したことや、勢いづいている Go でのサポートも熱いことから、すごく伸びそうなフォーマットである。

仕様

TOML の仕様は単純だ。

  • 大文字小文字を区別する
  • UTF-8 である必要がある
  • ホワイトスペースとは tab (0x09) と space (0x20) のみ
  • 改行とは LF (0x0A) と CRLF (0x0D0A) のみ

コメント

ハッシュシンボルから始まる行がコメントとして扱われる。もちろん、行頭である必要はない。

# This is a full-line comment
key = "value" # This is a comment at the end of a line

String

TOML では、文字列 String は 4 つの表現パターンがある。basic、multi-line basic、literal、multi-line literal、の 4 つだ。また、すべての文字列は UTF-8 のみで構成されている必要がある。

それでは一つずつ見ていく。

Basic strings

Basic strings はクォーテーションマークで囲われた文字列を意味する。ただし、クォーテーションマーク、バックスラッシュ、制御文字 (U+0000 to U+001F) に関してはエスケープしてやる必要がある。

str = "I'm a string. \"You can quote me\". Name\tJos\u00E9\nLocation\tSF."

また、利便性のためにいくつかの有名な制御文字に関してはエイリアスが用意されている(よく他の言語でも見かけるよね)。

\b         - backspace       (U+0008)
\t         - tab             (U+0009)
\n         - linefeed        (U+000A)
\f         - form feed       (U+000C)
\r         - carriage return (U+000D)
\"         - quote           (U+0022)
\\         - backslash       (U+005C)
\uXXXX     - unicode         (U+XXXX)
\UXXXXXXXX - unicode         (U+XXXXXXXX)

Any Unicode character may be escaped with the \uXXXX or \UXXXXXXXX forms. The escape codes must be valid Unicode scalar values.

All other escape sequences not listed above are reserved and, if used, TOML should produce an error.

またテキストの一節、例えば翻訳ファイルなど、を引用する必要があったり、複数の非常に長い文字列を分割したいと思うことが往々にしてあるだろう。TOML ではそれを簡単に扱うことができる。

Multi-line basic strings

multi-line basic strings は文字列の両端を 3 つのクォーテーションマークで囲って表現され、それにより改行することが可能になる。次の例のような開き区切りの直後にある改行は削除されるが、他のすべての空白や改行文字はそのまま残る。

str1 = """
Roses are red
Violets are blue"""

TOML パーサは各プラットフォームにおいて一般的な改行文字をきちんと正規化できるべきである。

# On a Unix system, the above multi-line string will most likely be the same as:
str2 = "Roses are red\nViolets are blue"

# On a Windows system, it will most likely be equivalent to:
str3 = "Roses are red\r\nViolets are blue"

TOML では余分な空白を挿入することなく、長い文字列を書き込むために、 \ で行を終了する。\ は次の非空白文字または閉じ区切りまで、すべてのホワイトスペースと改行は削除される。また、開き区切り直後の最初の文字はバックスラッシュと改行がある場合、これらは両方とも次の非空白文字または閉じ区切りまでのすべてのホワイトスペースや改行とともに削除される。

# The following strings are byte-for-byte equivalent:
str1 = "The quick brown fox jumps over the lazy dog."

str2 = """
The quick brown \


  fox jumps over \
    the lazy dog."""

key3 = """\
       The quick brown \
       fox jumps over \
       the lazy dog.\
       """

Any Unicode character may be used except those that must be escaped: backslash and the control characters (U+0000 to U+001F). Quotation marks need not be escaped unless their presence would create a premature closing delimiter.

バックスラッシュをエスケープすることは、Windows のパスや正規表現を頻繁に指定している場合、うんざりするだろうしでエラーになりがちにである。これを救うために TOML にはエスケーピングを全く必要としない文字列表現をサポートしている。

Literal strings

literal strings はシングルクォートで囲うことで表現し、basic strings と似ていて 1 行である必要がある。

# What you see is what you get.
winpath  = 'C:\Users\nodejs\templates'
winpath2 = '\\ServerX\admin$\system32\'
quoted   = 'Tom "Dubs" Preston-Werner'
regex    = '<\i\c*\s*>'

エスケープする手段がないので、シングルクォートで囲まれた文字列リテラル内にシングルクォートを記述する方法はない。幸いにも、この問題点を解決するために TOML では複数行バージョンの literal strings が用意されている。

Multi-line literal strings

multi-line literal strings は文字列の両端を 3 つのシングルクォートで囲って表現され、それにより改行することが可能になる。Literal strings と同様に、どんなエスケープも必要がない。また、開き区切りの直後にある改行は削除され、他のすべての区切り文字の間の内容はそのままに解釈される。

regex2 = '''I [dw]on't need \d{2} apples'''
lines  = '''
The first newline is
trimmed in raw strings.
   All other whitespace
   is preserved.
'''

TOML ではバイナリデータを扱い場合、Base64 エンコードするか、別の適切な ASCII または UTF-8 エンコーディングを使用することをお勧めしている。そのエンコーディングの扱いはアプリケーション固有のものになる。

Integer

Integer は整数値を表す。正の数にはプラス記号を付けてもよい。負の数はマイナス記号で始まる。

int1 = +99
int2 = 42
int3 = 0
int4 = -17

大きな数の場合、読みやすさを向上させるためにアンダースコアを使用することができる。各アンダースコアは少なくとも一桁で囲む必要がある。

int5 = 1_000
int6 = 5_349_221
int7 = 1_2_3_4_5     # valid but inadvisable

行頭ゼロは許可されていない。8 進数、16 進数、およびその他バイナリ形式を使用することはできない。一連の数字として表現することができない「無限」や「非数」などの値は許可されない。

64 bit (signed long) range expected (−9,223,372,036,854,775,808 to 9,223,372,036,854,775,807).

Float

Float は、小数部分または指数部が続く(プラスまたはマイナス記号を付けてもよい)整数部で構成される。小数部と指数部の両方が存在する場合は、小数部分は指数部の前になければならない。

# fractional
flt1 = +1.0
flt2 = 3.1415
flt3 = -0.01

# exponent
flt4 = 5e+22
flt5 = 1e6
flt6 = -2E-2

# both
flt7 = 6.626e-34

小数部分は 1 桁以上の数字が続く小数点である。指数部(プラスまたはマイナス記号を付けてもよい)は整数部に続いて E(大文字または小文字)である。

Integer と同様に、読みやすさを向上させるためにアンダースコアを使用してもよい。各アンダースコアは少なくとも一桁で囲む必要がある。

flt8 = 9_224_617.445_991_228_313
flt9 = 1e1_000

64-bit (double) precision expected.

Boolean

ブール値は常に小文字で表します。

bool1 = true
bool2 = false

Datetime

TOML では他にはない特徴として日付型をサポートしています。Datetime は RFC 3339 形式です。

date1 = 1979-05-27T07:32:00Z
date2 = 1979-05-27T00:32:00-07:00
date3 = 1979-05-27T00:32:00.999999-07:00

Array

Arrays are square brackets with other primitives inside. ホワイトスペースは無視され、要素はカンマ区切りで記述する。

Array 内ではデータ型は混在できない(ただし、すべての String は同一の型とみなされる)。

arr1 = [ 1, 2, 3 ]
arr2 = [ "red", "yellow", "green" ]
arr3 = [ [ 1, 2 ], [3, 4, 5] ]
arr4 = [ "all", 'strings', """are the same""", '''type'''] # this is ok
arr5 = [ [ 1, 2 ], ["a", "b", "c"] ] # this is ok
arr6 = [ 1, 2.0 ] # note: this is NOT ok

また、Array は複数行にわたってよい。また、ホワイトスペースは無視されることに加えて、角括弧にまたがる改行も無視される。また閉じ角括弧の終末にはカンマを置いてもよい。

arr7 = [
  1, 2, 3
]

arr8 = [
  1,
  2, # this is ok
]

Table

Table(またの名をハッシュ、辞書)とはキーと値からなる集合体である。

テーブル名は [ ] で囲って1行で書く。 Array は値を書くべき場所にしか書けないので、テーブル名とは区別できる。

[table]

その下は、次の Table または EOF までがその Table のキーと値になる。キーはイコール記号の左側で、値は右側である。キーと値の周りのホワイトスペースは無視される。
キーと値とイコール記号は同一行上である必要がある(ただし、一部の値は複数行に渡って書くことができる)。

キーは、裸またはクォーテーションマークで囲まれた、いずれであってもよい。Bare keys(裸のキー)は、文字、数字、アンダースコア、およびダッシュ(A-Za-z0-9_-)を含めることができる。Quoted keys(クォーテーションマークで囲まれたキー)は、基本的な文字列とまったく同じ規則に従い、キー名のより広範なセットを使用することができる。ベストプラクティスは、絶対に必要な場合を除き、裸のキーを使用することだ。

テーブル内のキー/値のペアは、任意の特定の順序であることが保証されていない。

[table]
key = "value"
bare_key = "value"
bare-key = "value"

"127.0.0.1" = "value"
"character encoding" = "value"
"ʎǝʞ" = "value"

ドット(.)はネストした Table を示すために使用されるため、ドットは裸キーでの使用が禁止されてる!それぞれのドットで区切られた部分(上記参照)の命名規則はキーのそれと同じである。

[dog."tater.man"]
type = "pug"

JSON の書き方では、次のような構造になるだろう:

{ "dog": { "tater.man": { "type": "pug" } } }

ドットで区切られた部分の周りのホワイトスペースは無視されるが、ベストプラクティスは余分なホワイトスペースを使用しないことである。

[a.b.c]          # this is best practice
[ d.e.f ]        # same as [d.e.f]
[ g .  h  . i ]  # same as [g.h.i]
[ j . "ʞ" . l ]  # same as [j."ʞ".l]

あなたが書きたくないのなら、すべての上位 Table を指定する必要はない。 TOML は省略された Table を正しく解釈する。

# [x] you
# [x.y] don't
# [x.y.z] need these
[x.y.z.w] # for this to work

空の Table は許可されていて、それには単にキー/値を持たないペアが代入されている。

上位 Table が直接定義されてなく、また特定のキーが定義されていない限り、その Table にはまだ書き込むことができる。

[a.b]
c = 1

[a]
d = 2

複数回のキーまたは Table の定義はできない。

# DO NOT DO THIS

[a]
b = 1

[a]
c = 2
# DO NOT DO THIS EITHER

[a]
b = 1

[a.b]
c = 2

また、すべての Table 名とキーは空であってはならない。

# NOT VALID TOML
[]
[a.]
[a..b]
[.b]
[.]
 = "no key name" # not allowed

Inline Table

Inline table は Table を表現するためのよりコンパクトな構文を提供する。Inline table は冗長になりがちなグループ化されたデータを表現するのに特に有用である。Inline table は中括弧({})で囲まれている。中括弧内には、ゼロ以上のカンマで区切られたキー/値のペアがある必要がある。キー/値のペアは、標準の Table 内のキー/値のペアと同じ形をとる。また、インラインテーブルなどの値にはすべての型の使用が許可されている。

Inline table は 1 行に表示するように意図されている。改行は中括弧の間に書いてはならないが、(複数行文字列などの)改行が現れる値を含むのは構わない。そうではあっても、 Inline table を複数行に渡って書くのは推奨しない。そうしたければ標準の Table を使うべきである。

name = { first = "Tom", last = "Preston-Werner" }
point = { x = 1, y = 2 }

上記の Inline table は、次の標準な Table の定義に同じ:

[name]
first = "Tom"
last = "Preston-Werner"

[point]
x = 1
y = 2

Array of Tables

最後の Table タイプは、Table の配列である。二重角括弧内に Table 名を書くことで定義する。同じ二重角括弧に囲まれた名前を持つ各 Table は同じ配列の要素になる。任意のキー/値のペアがない場合、空の Array of Table として扱われる。

[[products]]
name = "Hammer"
sku = 738594937

[[products]]

[[products]]
name = "Nail"
sku = 284758393
color = "gray"

JSON の書き方では、次の構造と同じになる:

{
  "products": [
    { "name": "Hammer", "sku": 738594937 },
    { },
    { "name": "Nail", "sku": 284758393, "color": "gray" }
  ]
}

Table のネストされた配列を作成することがきでる。ただ単に、sub-table で同じ二重中括弧の構文を使用する。各二重中括弧の sub-table は直近で定義された Table の要素に属する。

[[fruit]]
  name = "apple"

  [fruit.physical]
    color = "red"
    shape = "round"

  [[fruit.variety]]
    name = "red delicious"

  [[fruit.variety]]
    name = "granny smith"

[[fruit]]
  name = "banana"

  [[fruit.variety]]
    name = "plantain"

上記 TOML は JSON にするとこうなる:

{
  "fruit": [
    {
      "name": "apple",
      "physical": {
        "color": "red",
        "shape": "round"
      },
      "variety": [
        { "name": "red delicious" },
        { "name": "granny smith" }
      ]
    },
    {
      "name": "banana",
      "variety": [
        { "name": "plantain" }
      ]
    }
  ]
}

Array of Tables として既に存在する名前を Table につけようとするとパースエラーになる。

# INVALID TOML DOC
[[fruit]]
  name = "apple"

  [[fruit.variety]]
    name = "red delicious"

  # This table conflicts with the previous table
  [fruit.variety]
    name = "granny smith"

インラインテーブルを使用することが適切な場合:

points = [ { x = 1, y = 2, z = 3 },
           { x = 7, y = 8, z = 9 },
           { x = 2, y = 4, z = 8 } ]

他のフォーマットとの比較

TOML は、JSON と非常によく似ている。単純なフォーマットで、いかなるデータ型にも簡単に落とし込める。JSON は主にコンピュータプログラムによって読み書きされるシリアルデータとしてはとても優秀である。TOML が JSON と異なる点は、人間にとって読み書きしやすいことに重点を置いているところだ。コメントはいい例で、プログラム間でのデータのやり取りの際には全く意味は無いが、手動で編集する事が多い設定ファイルでは非常に大事な概念である。

YAML は、TOML が得意とする設定ファイルの記述などに向いている。しかし、多くをこなしすぎる YAML は、とても複雑なソリューションだ。TOML は簡単・明瞭であることと、YAML では明らかにしていない仕様

INI 形式もまた、よく設定ファイルの記述などで使用されることが多い。しかし残念なことに、INI は標準化されてなく、入れ子の 1 つまたは 2 つ以上のレベルを処理することができない。

TOML が使われているプロジェクト

  • Cargo - The Rust language's package manager.
  • InfluxDB - Distributed time series database.
  • Heka - Stream processing system by Mozilla.
  • Hugo - Static site generator in Go.
  • bloom.api - Create APIs out of public datasources.
  • MeTA - Modern C++ data science toolkit.

Implementations

If you have an implementation, send a pull request adding to this list. Please
note the version tag that your parser supports in your Readme.

v0.4.0 compliant

それ以降は省略

バリデータ

テスト

エディタサポート

エンコーダ

TOML2anything

最後に

参考文献

README.md

公式の README に載っていることが大半です。しかし、ところどころ英訳のバグや正確な訳が取れず終いなので Fix した PR があると助かります。