JSON問い合わせ言語
説明文書としてはメチャクチャ出来が悪い。まーいいや。いつか書き直す。
[追記]あんまり意味がないところは取消線を付けた。[/追記]
●基本事項
関係から述語へ
JはJSONデータ全体の集合だとして、f:Jn→Boolean (Boolean = {true, false})を関係の特性関数と呼ぶ。以下、関係の特性関数を単に関係と呼ぶ。関係は部分関数でもよい。n = 0 のとき、つまり0項関係はブール定数なので相手にしない。n = 1 のときは述語、n = 2 なら二項関係、以下同様。
n項関係(ただし、n≧1)Fを、次の方法で、すべて二項関係fとみなす。
- F(x) に対して、f(x, 0) := F(x) とする。f:J×{0}→Boolean
- F(x, y)はそのまま。f(x, y) := F(x, y)
- F(x, y1, ..., yk) に対して、f(x, [y1, ..., yk]) := F(x, y1, ..., yk) とする。f:J×Array → Boolean
fは二項関係、aを定数として、φ(x) := f(x, a) の形で定義されるφをfの述語化と呼ぶ。φは1変数ブール値関数であると同時に、後で構文構成素としても使う。任意のn項関係Fから出発して、F→f、f→φ とすることにより、述語=一項関係が得られる。
φ をラムダ記法で書くと、φ = λx.f(x, a) となり、変数xは束縛され、自由変数(パラメータ)として残るのはaのみ、そこれで、φを @f a と書く。次が成立する。
- φ(x) = (@f a)(x) = f(x, a)
パス修飾を持つ論理式
次が論理式の定義:
- 述語φは論理式である。
- Aが論理式のとき、¬A は論理式である。
- A, Bが論理式のとき、A∧B は論理式である。
- A, Bが論理式のとき、A∨B は論理式である。
- Aが論理式、pが単純JSONパスのとき、[p]A は論理式である。
[p]A 以外の論理式の解釈は通常通り。
Aが論理式のとき、JSONデータxに対して、A(x)が定義できる。not, and, or はブール演算だとする。x.p は、get(x, p) のようなパス式pによるアクセスの略記である。
- Aが述語φのとき、A(x) = φ(x)
- (¬A)(x) = not(A(x))
- (A∧B)(x) = and(A(x), B(x))
- (A∨B)(x) = or(A(x), B(x))
- ([p]A)(x) = A(x.p)
TはJの部分集合として、{x∈T | A(x)} を select from T where A とも書く。Tが集合でなく、リスト、バッグのときも同様の記法を使う。
●論理式のJSON表現
翻訳規則
- fが二項関係のとき、述語φ = λx.f(x, a) を @f a で示す。実際の構文では、関係名をすべて大文字にして先頭にアンダスコアを付ける。
- (¬A) の翻訳は、Aの翻訳にタグ @_NOT を付けたものである。
- (A∧B) の翻訳は、@_AND [Aの翻訳, Bの翻訳] である。
- (A∨B) の翻訳は、@_OR [Aの翻訳, Bの翻訳] である。
([p]A) の翻訳は、単純JSONパス式の構成に従って:
- pが名前αのとき、[α]A の翻訳は {α: Aの翻訳}
- pがインデックスiのとき、[i]A の翻訳は [..., Aの翻訳](i番目の項目)
- pがq.r の形のとき、[p]A の翻訳は、[q]X の翻訳に [r]A の翻訳を入れ子にする。
これは分かりにくいので、逆向きの翻訳と一緒に考えたほうがいい。
基本関係
基本関係を示す。
- eq:J×J→Boolean は等号関係
- like:String×String→Boolean はパターンマッチ(右がパターン)
- tag:J×String→Boolean は、tag(x) == a のこと。
type:J×Strig→Boolean は、typeof(x) が a とマッチすること。typeofはタグを無視して型名を返す。typeofの値は、"null", "boolean", "number", "array", "object" のいずれか。ただし、typeには、"integer", "any" も渡せる。
●逆翻訳
aがJSONリテラルのとき、aを論理式に逆翻訳したものを【a】で示す。
スカラーリテラル
- 【c】 = λx.eq(x, c)
オブジェクトとプロパティ
α1などは、プロパティ名(文字列)、v1などはプロパティ値として:
- 【{α1: v1, ..., αn : v1} 】 = λx.(【v1】(x[α1])∧...∧【vn】(x[αn])
プロパティ名 "*" は予約されていて、"*" : v は、明示されてないプロパティ名すべてに【v】を適用することを示す。
配列と項目
- 【[v0, ..., vn] 】 = λx.(【v0】(x[0])∧...∧【vn】(x[n]))
ただし、最後の項目に特殊タグ @_REST が付いていたときは、次の解釈になる。
- 【[v0, ..., @_REST vn] 】 = λx.(【v0】(x[0])∧...∧【vn-1】(x[n-1])∧【vn】(x[n])∧【vn】(x[n+1])∧...)
nより先のインデックスにはすべて【vn】が適用される。項目が1つしかないときは、@_REST の代わりに @_ALL が使える。
タグ
アンダスコアからはじまるタグは予約されていて、ユーザーレベルデータの中では使えないとする。αが通常のタグなら:
- 【@α v】 = λx.[tag(x) = α ∧ 【v】(val(x))]
特殊タグ
述語に対応するタグは、プラグイン可能である(が、ユーザーにはたぶん解放しない)。次は予約された特殊タグになり、述語名には使えない。
- @_REST データ(配列の末尾のみ)
@_ALL データ(配列の先頭のみ)- @_NOT データ
- @_AND 配列
- @_OR 配列
@_XOR 配列- @_ANY
0 @_ 0 (@_ANY 0 の短縮形)
[追記]ブツブツブツブツ:
@_TYPE "typename" はやめて、@_OBJECT, @_NUMBER などを使うほうがいい。
- [@_INTEGER, @_INTEGER, @_REST @_STRING]
は、スキーマにすると:
- array [integer, integer, string*]
だが、
- [@_INTEGER, @_INTEGER]
は、tuple [integer, integer]を意味しない。array [integer, integer, any*] と同じ意味になる。
- @_AND [@_LENGTH 2, [@_INTEGER, @_INTEGER]]
は酷すぎる。@_REST と対になる @_END(明示的に配列を終端する)を入れて
- [@_INTEGER, @_INTEGER, @_END]
のほうがいいだろう。
キーボードを打った感触では、全部大文字はシフト押しっぱなしが嫌だな。
- [@_Integer, @_Integer, @_End]
しかし、他のところで、$_CONTEXT とか使っているし。
- [@_integer, @_integer, @_end]
これ書きやすい。が、視認性が悪い。
- [@_REST @_GTEQ 0 ]
これは、合法だが気持ち悪い。
- @_ALL @_GTEQ 0
- [@_ALL @_GTEQ 0]
- @_ALL [@_GTEQ 0]
- @_EACH @_GTEQ 0
- @_EACH [@_GTEQ 0]
- @_LIST [@_GTEQ 0]
Catyスクリプトをしばらく使っていれば、@_EACH [@_GTEQ 0] が書きやすい気がする。
[/追記]