Catyでは、JSONにタグを許したXJSONというデータ形式をいたるところで使っています。タグ付きのデータとは、@tel "03-1234-5678", @YEN 1200, @person {"name": "m-hiyama", "gender": "male"} とかです。
XJSONデータの一部を抜き出すセレクター式/パス式については、「JSON向けのシンプルセレクター」で述べました。が、もっと単純化できないかと考えていて、やっと満足できるほどに単純な構文にたどり着きました。
セレクターのコア構文では、文字列、番号、名前というトークンと、メンバーアクセス演算子のドット'.'、2種のワイルドカード'*'と'#'しか使いません。
トークン
文字列はJSONと同じ構文ですが、シングルクォートで囲んだ文字列も許します。
Esc ::= '\"' | "\'" | '\\' | '\/' |
'\b' | '\f' | '\n' | '\r' | '\t' |
'\u' FourHexDigitsPrintable ::= {任意の印字可能Unicode文字、間隔空白文字とタブ文字を含める}
PrintableSpecial ::= '"' | "'" | '\'
StringContentChar ::= (Printable - PrintableSpecial) | Esc
DQString ::= '"' (StringContentChar | "'")* '"'
SQString ::= "'" (StringContentChar | '"')* "'"
名前の定義は、最近は次を使っています。
Name ::= NameStartChar (NameChar)*NameStartChar ::= [A-Z] | "_" | [a-z] | ExtNameStartChar
NameChar ::= NameStartChar | "-" | [0-9] | ExtNameChar
ExtNameStartChar :: = [#xC0-#xD6] | [#xD8-#xF6] | [#xF8-#x2FF] | [#x370-#x37D] |
[#x37F-#x1FFF] | [#x200C-#x200D] | [#x2070-#x218F] |
[#x2C00-#x2FEF] | [#x3001-#xD7FF] | [#xF900-#xFDCF] |
[#xFDF0-#xFFFD] | [#x10000-#xEFFFF]ExtNameChar ::= #xB7 | [#x0300-#x036F] | [#x203F-#x2040]
番号(インデックス)は、非負整数を表す表現です。
Index ::= '0' | [1-9][0-9]*
セレクター式/パス式
セレクター式の一般的な構文は次のようになります。
Member ::= Name '()'? | DQString | SQString | Index | '*' | '#'Selector ::= Mebmer ('.' Member)*
このなかで、「JSON向けのシンプルセレクター」に出てこないのは、Name '()' というお尻に丸括弧が付いたメンバーだけです。これは後で説明することにして、セレクター式の例をいくつか出します。
- authors
- authors.#
- authors.2.name
- authors.2.*
- authors.2.name.first
- authors.2.contact.tag()
- authors.length()
セレクター式で、ワイルドカード('*', '#')を含まないものがパス式です。
メソッド風の記法
先の例で、authors.2.contact.tag(), authors.length() では、tag(), length() というメソッド風のメンバーが出てきます。お尻に'()'が付いた名前は、実際のデータプロパティではなくて、そのデータになんらかの操作をして得られる値です。見た目のとおり、メソッドと思ってかまいません。
現在考えているメソッド風の記法は次のものです*1。
- length() -- 配列の長さ
- tag() -- XJSONのタグ
- untagged() または content() -- XJSONのタグを取り除いた部分
互換性や拡張
以前から、与えられたデータそのもの(ルートデータ)を '$' で表していました。この'$'は、「あってもなくてもいい」という位置付けです。ブラケットを使った記法、authors[2] とか authors[2]["name"] とかもあったほうがいいでしょう。セレクターの場合は、論理条件式によるフィルタリングがあれば便利です。これもサポートするつもりです。
しかし、「ドットとワイルドカードだけ」という最小限の構文でも、実用上けっこう間にあうことは経験済みです。これより簡単にはできないので、究極に単純な構文だと思います。
*1:もっと追加するかも知れません。