rust-peg の文法について、 README に載っていない範囲のうちよく使うと思われるものについて補足する。
README に書いてあること
-
.
- 任意の1文字にマッチ -
"literal"
- リテラル文字列にマッチ -
[A-Za-z]
- セット内の1文字にマッチ -
[^A-Za-z]
- セットに含まれない1文字にマッチ -
rule
- その名前で定義されたルールにマッチし、マッチした結果を返す -
expr*
- 式の0回以上の繰り返しにマッチし、マッチした結果をVec
で返す -
expr+
- 式の1回以上の繰り返しにマッチし、マッチした結果をVec
で返す -
expr?
- 式の0回または1回の出現にマッチし、マッチした結果をOption
で返す -
&expr
- この位置で式にマッチすればマッチする。文字列を消費しない -
!expr
- この位置で式にマッチしなければマッチする。文字列を消費しない -
expr ** delim
- デリミタで区切られた式の0回以上の繰り返しにマッチし、マッチした結果をVec
で返す -
expr ++ delim
- デリミタで区切られた式の1回以上の繰り返しにマッチし、マッチした結果をVec
で返す -
e1 / e2 / e3
- 式1にマッチすればその結果を返す。マッチしなければ式2のマッチを試す -
e1 e2 e3
- この順序で並んだ式にマッチする。最後の式の結果(ここではe3
)を返す -
a:e1 b:e2 c:e3 { rust }
- この順で並んだ式にマッチする。マッチが成功したらブロックの中の rust 式または文を評価する。式にマッチした結果はコロン左側の文字列と同じ名前の変数でブロック内から参照できる。ブロックの返り値が式の結果となる
README に書いてないこと
カッコについて
式はカッコ ( )
でグルーピングできる。たとえば (e1 e2)+
は「『e1 に続いて e2 にマッチする』が1回以上繰り返される」を表す。
結合強度について
/
の結合強度はスペースより弱い。 e1 e2 / e3 e4
は「『e1 に続いて e2 にマッチする』または『e3 に続いて e4 にマッチする』」を表す。
結合順を変えるにはカッコでグルーピングする。 e1 (e2 / e3) e4
は「e1 に続いて『e2 または e3』にマッチし、続いて e4 にマッチする」を表す。
式の結果について
複数の式からなる式は、最後の式の結果が全体の結果となる。たとえば、 i -> int
, u -> uint
, s -> String
なる3つのルールがあるとき、式 i u s
の結果は String
になる。
/
で区切られた複数の式は、それぞれの節の結果の型が等しくなる必要がある。 i s / u s
は、それぞれの節の結果が String
となるので正しいが、 i s / u s?
は左辺が String
で右辺が Option<String>
なので不正である。
結果の型をそろえるには、たとえば i s:s { Some(s) } / u s?
のようにする。この式の型は Option<String>
である。
返り値のないルールについて
「マッチした結果を返す」と書かれていないパターン(.
や [a-z]
) は返り値を持たない。このような式の型は ()
(ユニット)である。これらの式と返り値を持つ式を /
で組み合わせるとき、全体の式の型を ()
としたければ、返り値を持つ式の後に { () }
を書くとよい。たとえば r = [a-z] / [A-Z] [a-z]? { () }
とする。
{ }
について
{ }
は任意の式の位置に出現することができる。また、 { }
の前の a:e1
は必ずしも直前にある必要はなく、出現しなくてもよい。たとえば、これらは式として正しい:
{ 42 }
e1 e2 { 42 } e3
e1 a:e2 e3 { a }
部分文字列の取得について
C の peg ライブラリでは、 < e1 e2 > e3 { ... }
のようにアングルブラケットを使うと、ブロック内で e1 e2
にマッチした部分文字列を参照できる。
rust-peg では部分文字列を参照することはできない。部分文字列を取得するには、部分式をサブルールとしてくくり出し、マッチ全体を文字列として返すようにする:
r -> String = a:e1_e2 e3 { a }
e1_e2 -> String = e1 e2 { match_str.to_string() }