2008-05-06 [長年日記]
λ. PEGにマッチする文字列をAlloyで生成する
この間の正規表現にマッチする文字列をAlloyで生成すると同様のことを今度はPEG(解析表現文法)に対して行う。文字列の表現などは前回と同じ。
単純化するために、e* と e+ の e が非終端記号Nになるように文法があらかじめ変形されているものとする。
式に対する解釈は大体以下のような感じ。
- [[ ε ]](x,y) = (x==y)
- [[ c ]](x,y) = (x.tail==y && x.head==c)
- [[ N ]](x,y) = y in x.N
- [[ e1 e2 ]](x,y) = some z : String | [[ e1 ]](x,z) && [[ e2 ]](z,y)
- [[ e1 / e2 ]](x,y) = [[ e1 ]](x,y) || ((no z : x.*tail | [[ e1 ]](x,z)) && [[ e2 ]](x,y))
- [[ N* ]](x,y) = y in x.*N && (no z : y.^tail | z in y.N)
- [[ N+ ]](x,y) = y in x.^N && (no z : y.^tail | z in y.N)
- [[ e? ]](x,y) = [[ e / ε ]](x,y)
- [[ &e ]](x,y) = x==y && (some z : String | [[ e ]](x,z))
- [[ !e ]](x,y) = x==y && (no z : String | [[ e ]](x,z))
その上で解析規則の集合
N1 ← e1 … Nn ← en
を
pred Rules(N1, …, Nn : String -> String) all x, y : String { y in x.N1 <=> [[ e1 ]](x,y) … y in x.Nn <=> [[ en ]](x,y) } }
として不動点を表す述語にする。 (これだと最小不動点であることは表せていないけど、Alloyでは最小性を直接記述できないので、とりあえずその辺りは気にしないことにする)
で、Niにマッチする文字列を適当に生成するには以下のようにする。
pred test() { some N1,…,Nn : String -> String { Rules[N1,…,Nn] some x : String | Nil in x.Ni } } run test for 10
具体例: A ← a A a / a
-- A <- a A a / a pred Rules(A : String -> String) { all x, y : String { y in x.A <=> (p[A,x,y] || q[A,x,y]) } } pred p(A : String -> String, x, y : String) { some z, w : String { x.head==C_a x.tail==z w in z.A w.head==C_a w.tail==y } } pred q(A : String -> String, x, y : String) { all z : String | !p[A,x,z] x.head==C_a x.tail==y } pred test() { some A : String -> String { Rules[A] some x : String { Nil in x.A all y : String | y in x.*tail } } } run test for 10 assert no_a5 { all A : String -> String | Rules[A] => no x0 : String { let x1 = x0.tail | let x2 = x1.tail | let x3 = x2.tail | let x4 = x3.tail | let x5 = x4.tail { x0.head==C_a x1.head==C_a x2.head==C_a x3.head==C_a x4.head==C_a x5==Nil Nil in x0.A } } } check no_a5 for 10 assert uniq_A { all A1, A2 : String -> String { Rules[A1] && Rules[A2] => A1==A2 } } check uniq_A for 10
実行例
Alloy Analyzer で「Run test for 10」を実行すると、「a」「aaa」「aaaaaaa」といった文字列を表すモデルが生成される。 例えば、「aaa」の場合にはこんな感じ。
これを見ると二文字目と三文字目のaがAにマッチしているのに対して、一文字目のaはマッチしていないことがわかったりとかする。
一方、他のモデルの探索を繰り返しても「aaaaa」を表すようなモデルは生成されない。 そこで、「Check no_a5 for 10」を実行すると、「No counterexample found」となり、そのようなモデルが(多分)存在しないことが示せる。