tpac記法
概要
tpac記法はテキストの記述しやすさに配慮した記法です。
インデントの挿入やエスケープなしに、自由にテキストを記述できます。
以下のとおり、既存の記法では難しいことから独自に作成しました。
- XMLはテキストを記述できますが、エスケープが必要で表現が冗長です。
Markdownもテキストを記述できますが、データの階層構造を表現することには向いていません。 - JSONにはテキストのための記法がありません。
YAMLにはありますが、インデントを入れる手間がかかります。 - Groovy DSLは文法的な誤りを含むコードを記述できません。
tpac文書はハンドルの集合です。
ハンドルとはマップにタグと名前を与えたものです。
頂点にひとつのハンドルがあり、そこに他のハンドルが子孫としてぶら下がります。
ハンドルは値として、文字列や数値、複数行のテキストなどを保持します。
識別のためハンドルはタグと名前を持ちます。
これは HTMLのタグと id属性の考え方を参考にしています。
種類として同じものには同じタグを与えます。
同じタグのハンドルからひとつを区別するために名前を付与します。
宣言
tpac文書は宣言で開始します。
宣言より前の記述はすべて無視します。
行頭を半角シャープ(#)+半角エクスクラメーションマーク(!)+半角スペースで開始します。
半角スペースが必要なことに注意してください。
#! tea:アッサム
行頭記号が異なるだけで、宣言もハンドルのひとつです。
tpac文書の最上位にあるハンドルです。
宣言終端
tpac文書を明示的に終了するには宣言終端を記述します。
宣言より後の記述は、他の tpac文書の宣言がない限り、すべて無視します。
半角シャープ(#)+半角エクスクラメーションマーク(!)のみの行を記述してください。
#!
宣言終端は記述を省略できます。
他の宣言があれば新たな tpac文書の開始とみなします。
ハンドル
tpac文書はひとつ以上のハンドルによって構成されます。
ハンドルは、タグと名前によって識別されるマップです。
ハンドルの構造
以下にハンドルの構造を示します。
ハンドルは開始行、コメント、マップ、終端の順番で記述します。
#> user:Smith ← ハンドル開始行(一行のみ、必須)
#:User info of Smith ← コメント(複数行、省略可)
#-age 17 ← マップ(複数行、省略可)
#> ← ハンドル終端(一行のみ、省略可)
ハンドルの親子関係
ハンドル同士の親子関係を記述できます。
ハンドル開始行の行頭を半角シャープ(#)+半角数字+半角大なり(>)+半角スペースで開始します。
半角スペースが必要なことに注意してください。
半角数字は階層を表します。1以上の値を指定してください。
階層をスキップすることはできません。たとえば階層1のハンドルの後ろに階層3のハンドルを記述することはできません。
たとえば以下の親子関係を表したいとします。
親
├子1
│└孫11
├子2
└子3
├孫31
└孫32
以下のように表現できます。
#1> family:親
#2> family:子1
#3> family:孫11
#2> family:子2
#2> family:子3
#3> family:孫31
#3> family:孫32
階層が 1~3の場合のみ、省略記法が使えます。
数値の代わりに半角大なり(>)のくりかえしを用います。
以下は、上記と同じ親子関係を表しています。
#> family:親
#>> family:子1
#>>> family:孫11
#>> family:子2
#>> family:子3
#>>> family:孫31
#>>> family:孫32
宣言もハンドルです。
ひとつの tpac文書に宣言はひとつのみ、最上位にあるハンドルとなります。
ハンドル開始行
ハンドル開始行には以下を記述します。
デフォルトキーについては デフォルトキーを参照してください。
- 親子関係の階層(必須)
- タグ(必須)
- 名前(省力可)
- デフォルトキーに紐づけられたスカラー値(省力可)
以下にハンドルの例を示します。
#> hobby:groovy It's programming language.
上記のハンドルには以下が記述されています。
- 親子関係の階層:1
- タグ:文字列 "hobby"
- 名前:文字列 "groovy"
- デフォルトキーに紐づけられたスカラー値:文字列 "It's programming language."
タグと名前は半角コロン(:)区切りで記述します。
デフォルトキーに紐づけられたスカラー値を指定する場合は、タグと名前の後ろに半角スペース区切りで記述します。
タグは必須です。
同じ宣言あるいは同じ親ハンドルに属す複数のハンドルがあるとき、タグは重複しても構いません。
タグに改行コード、半角スペース、半角シャープ(#)、半角スラッシュ(/)、半角コロン(:)は使用できません。
名前は省略可です。
省略時は文字列"dflt"を指定したとみなします。タグとの区切り記号である半角コロン(:)は記述しません。
同じ宣言あるいは同じ親ハンドルに属す複数のハンドルがあるとき、タグと名前の組合せに重複は許されません。
名前に改行コード、半角スペース、半角シャープ(#)、半角スラッシュ(/)、半角コロン(:)は使用できません。
ハンドル終端
ハンドルを明示的に終了するにはハンドル終端を記述します。
ハンドル終端より後の記述は、他の宣言やハンドル開始行がない限り、すべて無視します。
半角シャープ(#)+半角大なり(>)のみの行を記述してください。
#>
ハンドル終端は記述を省略できます。
他の宣言があれば新たな tpac文書の開始とみなします。
他のハンドル開始行があれば新たなハンドルの開始とみなします。
コメント
コメントはハンドルについて説明や注記を記述します。
行頭を半角シャープ(#)+半角コロン(:)で開始します。
#> life
#:Life is a dream.
コメントは各行をリストとして保持します。
行末の改行コードは保持しないことに注意してください。
マップ
マップは、キーと値の集合です。
ひとつのキーに指定できる値はひとつのみです。
行頭を半角シャープ(#)+半角(-)で開始します。
その後にキーとスカラー値を記述します。
キーとスカラー値は半角スペースで区切ります。
テキストを値にするには、キーの後に改行を入れて記述します。
#> capulet:juliet
#-age 13
#-detail
She loves Romeo.
以下のように、本来ならテキストを指定すべきところになにも指定しないことはできません。
#> capulet:juliet
#-detail
#>
キーは必須です。
ひとつのマップ内でキーの重複は許されません。
キーに改行コード、半角スペース、半角シャープ(#)、半角スラッシュ(/)、半角コロン(:)は使用できません。
キーのデータ型は文字列です。他のデータ型をキーとすることはできません。
値は必須です。
スカラー値あるいはテキストを指定可能です。
一般的にはテキストもスカラー値です。tpac記法では複数行のテキストを、各行を要素とするリストとみなすため、スカラー値とは区別しています。
デフォルトキー
マップは特殊なキーとしてデフォルトキーを持ちます。
ひとつのマップにひとつだけ、キーを省略して値を指定することができます。
デフォルトキーの実体は文字列"dflt"です。
デフォルトキーに値を指定するには以下の方法があります。
- ハンドル開始行にスカラー値を記述する
- キーが指定されていないテキストを記述する
ハンドル開始行にスカラー値を記述する例を示します。
文字列 "Cutest animals."がデフォルトキーの値です。
#> cat Cutest animals.
デフォルトキーの実体は"dflt"のため、以下は上記と同義です。
#> cat
#-dflt Cutest animals.
キーが指定されていないテキストを記述する例を示します。
文字列 "Cutest animals."のみを要素に持つリストがデフォルトキーの値です。
#> cat
Cutest animals.
同じキーに値はひとつしか指定できません。これはデフォルトキーも同じです。
以下のようにキーが指定されていないテキストを複数記述することはできません。
#> cat
Cutest animals.
#-question really?
Believe me!
スカラー値
スカラー値は、なんらかのデータ型の、単体の値です。
データ型
以下のデータ型のスカラー値を指定できます。
- Null値(固定文字列 "null")
- 真偽(固定文字列 "true", "false")
- 整数(12, 0, -3など)
- 正規表現 /-?\d+/とマッチする文字列。
java.lang.Integerとして保持します。
- 正規表現 /-?\d+/とマッチする文字列。
- 浮動小数点(0.05, -1.0など)
- 正規表現 /-?\d+.\d+/とマッチする文字列。
java.math.BigDecimalとして保持します。
- 正規表現 /-?\d+.\d+/とマッチする文字列。
- 参照(@始まり)
- 他のハンドルやハンドルが保持する値への参照。
- 正規表現(:始まり)
- java.util.regex.Pattern#compile(String)の戻り値を保持します。
- Groovy評価値(=始まり)
- 指定された文字列を Groovyスクリプトとして評価した値を返します。
groovy.util.Eval#meメソッドでの実行結果を返します。
たとえば一日の秒数を"= 60 * 60 * 24"と表現することができます。
- 指定された文字列を Groovyスクリプトとして評価した値を返します。
- 文字列(_始まり)
- 文字列であることを明示的に示したい場合に使います。
- 空文字を指定したい場合は半角アンダーバーのみ記述してください。
- Javaのエスケープシーケンス(たとえば"\n")が使えます。
- 文字列(上記以外)
- Javaのエスケープシーケンス(たとえば"\n")が使えません。
そのまま文字列として解釈します。
- Javaのエスケープシーケンス(たとえば"\n")が使えません。
スカラー値は tpac文書を解析した時点で、目的のデータ型に変換します。
参照、Groovy評価値は遅延評価します。値を参照されたときに初めて値を取得します。
参照とパス
参照は、他のハンドルやハンドルが保持する値を参照するためのものです。
パスの作り方は以下のとおりです。
ハンドルのタグと名前は半角コロン(:)で連結します。名前がない場合はタグのみです。
ハンドル同士の親子関係を半角スラッシュ(/)で連結します。
絶対パスの場合はパスの先頭に半角スラッシュ(/)を記述します。
相対的に階層がひとつ上位のハンドルを指すには「..」を用います。
参照として記述するときは、半角アットマーク(@)から開始し、その後ろにパスを記述します。
キーに紐づく値を参照するには、半角シャープ(#)の後ろにキーを記述します。
キーとして空文字を指定(半角シャープのみ記述)した場合、デフォルトキーを指定したとみなします。
たとえば以下の tpac文書があったとします。
/accounts/persons/person:山田太郎#countryの値は「日本」です。
/accounts/persons/person:山田太郎#familyの値は「/family:山田家/person:山田太郎」のパスで示されるハンドルとなります。
#! accounts
#> persons
#>> person:山田太郎
#-country @../enum/country#jp
#-family @/family:山田家/person:山田太郎
#> enum
#>> country
#-jp 日本
#-us アメリカ
#!
#! family:山田家
#> person:山田太郎
#-father 孝太郎
#-mother 花子
#!
テキスト
テキストは文字列のリストです。
各行をリストの要素として保持します。
行末の改行コードは保持しないことに注意してください。
以下の指定方法があります。
- 範囲指定を省略する
- 範囲を明示する
範囲指定を省略する場合の例を以下に示します。
キー memoに対し、値としてリスト [ '吾輩は猫である。', '名前はまだ無い。' ]を指定しています。
制限として、先頭が半角シャープで開始する行は記述できません。そのような行を含むテキストを記述したい場合は、範囲を明示する方法を利用してください。
#-memo
吾輩は猫である。
名前はまだ無い。
範囲を明示する方法は以下のとおりです。
半角シャープ(#)と 3つ以上の半角イコール(=)のみから成る行を開始行、終端行とします。
開始行と終端行は半角イコールの数が一致する必要があります。
範囲を明示する場合、行の先頭に半角シャープ(#)を記述しても構いません。
tpac記法特有の記述をしてもテキストとみなされることに注意してください。
たとえば以下は、キーmemoに対してリスト [ '吾輩は猫である。', '#> attention 名前はまだ無い。' ]という値を指定しています。二番目の要素 '#> attention 名前はまだ無い。'はハンドルとみなされません。
#-memo
#===
吾輩は猫である。
#> attention 名前はまだ無い。
#===
空リストをテキストとして指定することはできません。
たとえば以下の記述はできません。
#-vacant
#===
#===
空行について
ハンドル内の空行はテキストとみなすことに注意してください。
たとえば以下のハンドルは、デフォルトキーに値として 2行目でリスト [ '' ]を紐づけているとみなします。
#> hello
#-jp こんにちは
#>
以下は、上記の「#-jp こんにちは」の後ろに空行を足しただけですが、このような記述はできません。
デフォルトキーに対して 2行目、4行目それぞれでリスト [ '' ]を紐づけたとみなすためです。同じキーに値はひとつしか指定できません。
#> hello
#-jp こんにちは
#>