You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
cpp-peglib/tutorial/chap_02.md

75 lines
5.5 KiB

# PEGで言語をデザインしてみよう
PEGについて一言で説明としたら,「言語を定義するための言語」です。言語としてのPEGは,C++やJavaScriptなどの言語とはずいぶん毛色が違います。むしろ正規表現,XML Schemer,DBのテーブルスキーマのように「規則を定義」するタイプの言語です。
PEGで文法を書き表すことによって,人間に読みやすくかつ厳密に文法を定義できます。さらにこの文法ファイルを使って,その言語のパーサーをプログラムに自動生成させることもできます。
PEGの記法はとてもシンプルで習得しやすいものです。BNFや正規表現をご存知の方でしたら,ほんのわずかな時間で覚えられるでしょう。
PEGの理論的背景についてもっと知りたい方は,Bryan Fordさんの[2004年の論文[Link_Ford]を読みましょう。このTechnical Paperの2番めのセクションには,PEGをPEGで定義するとても興味深いコードが載せられています。(私もこれを参照しながらパーサジェネレータを実装しました。)
「習うより慣れろ」と古くから言われるように,実際に手を動かしてコードを書くことは理解を深めるための早道です。その際[「PEG Playground」](http://yhirose.github.io/peglint/)を使ってみてください。このWebアプリは,PEGの文法定義コードと定義される言語のコードをリアルタイムにチェックしてくれます。
では始めましょう。最初にデザインするのはCSVフォーマットです。
## CSVフォーマット
おなじみカンマ区切りフォーマットの文法を定義してみましょう。CSVには色んな方言がありますが,以下の仕様を満たすものとします。
* CSVファイルは,一つ以上フィールドでなる,一つ以上の行の集まり
* フィールドは,ダブルクォート無しと有りある
* ダブルクォート無しのフィールドには,カンマとダブルクォートを含むことができない
* ダブルクォート有りのフィールドには,ダブルクォート以外の全ての文字と,連続したダブルクォートを含むことができる
テストのしやすさを考慮して,規則をボトムアップで定義しましょう。まずはダブルクォート無しのフィールドです。
NO_DQUOTE_FIELD <- (![,"\r\n] .)*
この規則はカンマダブルクォート改行以外の任意の文字の0回以上の繰り返しを意味します理解を深めるために規則を構成する個々の要素を見てみましょう
`.`は任意の文にマッチします。`[,"\r\n]`はカンマダブルクォート改行文字のいずれかにマッチします外側の`()*`は0回以上の繰り返しを意味しますどれも正規表現と全く同じですね
`!`否定先読み演算子で後続の`[,"\r\n]`の評価が偽であればマッチしたことになりますマッチしたとしても現在処理している入力テキストの位置は進めません
続いてダブルクォート有りのフィールドです
DQ_FIELD <- '"' (!["] . / '""')* '"'
この規則は両端にダブルクォートがあってその間はダブルクォートでない任意の文字か2連続するダブルクォートの0以上の繰り返しという意味です
ではこの2つの規則をまとめましょう
FIELD <- DQ_FIELD / NO_DQUOTE_FIELD
これはDQ_FIELDかNO_DQUOTE_FIELDにマッチを意味します。`/`Priority Choiseと呼ばれ左から右に式の要素を評価します評価が真になった時点でマッチに成功します
このDQ_FIELDとNO_DQUOTE_FIELDの順序はとても重要ですもしこの順序を逆にするとダブルクォート文字列はNO_DQUOTE_FIELDの規則で誤ってマッチしてしまいDQ_FIELDとしては認識されなくなってしまいます。(ダブルクォート文字列は最初のクオート無しのフィールド規則で長さ0のフィールドとしてマッチしてしまい続くダブルクォート有りのフィールド規則にたどりつかないからです。)
続いてレコード規則を定義します
RECORD <- FIELD (',' FIELD)*
これは1回以上のFIELDの繰り返しを意味しますこの形式は,「要素が最低1回以上出現するリストを表現するイディオムです。(「要素が0回以上出現するリスト`(ELEM (DELM ELEM)*)?`で表します。)
開始規則であるCSV規則は
CSV <- RECORD (NL RECORD)* NL?
と定義できます改行文字を定義を忘れていましたね
NL <- '\r\n' / '\r' / '\n'
この場合も要素の順序に気をつけてください。`\r\n`を`\r`の後に置くなら,`\r\n`は決してマッチしなくなります
CSVの文法が定義できました
CSV <- RECORD (NL RECORD)* NL?
RECORD <- FIELD (',' FIELD)*
FIELD <- DQ_FIELD / NO_DQUOTE_FIELD
NO_DQUOTE_FIELD <- (![,"\r\n] .)*
DQ_FIELD <- '"' (!["] . / '""')* '"'
これが確かに正しいかテストしてみましょう
[Link_BasicGrammar]: http://kmizu.hatenablog.com/entry/20100203/1265183754
[Link_Ford]: http://pdos.csail.mit.edu/papers/parsing:popl04.pdf