0
Introduction to PEG
構文解析友の会
水島 宏太
背景
 多様な入力文字列を構文解析する必要性
 (色々なフォーマットの)設定ファイル
 Webのクローリング
 "Cargo cult parsing" (from Yacc is dead)
の流行
 Googleで検索して正規表現...
構文解析って何?
 一言でいうと:
 入力文字列を木構造(Abstract Syntax Tree)
に組み立てる捜査
 二種類に分けられる
 自然言語の構文解析
 構文解析の結果あいまい性が生じることがある
 非自然言語の構文解析...
様々な構文解析アルゴリズム
 CYK - O(n3)
 (S|LA)?LR(k) - O(n)
 LL(k) - O(n)
 LL(*) - O(n)
 GLR (Generalized LR) - O(n3)
 GLL (Gene...
構文解析器をどうやって書く?
 手書き
 どんな文法でも大体作れる
 文法のメンテナンス/デバッグが大変
 パーザジェネレータ
 文法定義からパーザを自動生成
 パーザコンビネータ
 プログラミング言語のDSLとしてパーザを組み立...
YACC (LALR(1))
 最もメジャーなパーザジェネレータ
 Rubyを初めとする様々な言語で採用
 上位互換のbisonが一般に採用される
 空白の有無等で意味が変わる文法は苦手
 空白は通常字句解析で処理するため
 トーク...
JavaCC (LL(k) + α)
 Javaでメジャーなパーザジェネレータ
 構文木の生成がちょっと楽
 jjtree(プリプロセッサ)を使用した場合
 空白の有無で意味が変わる文法は苦手
 理由はyaccと同じ
 トークンが再...
何が言いたいか
 一般的な構文解析アルゴリズムでは字句解析
と構文解析の分離が前提
 プログラミング言語用の字句解析器の限界
 字句解析器は通常正規表現によって記述
 再帰的な構造をうまく扱えない
 パーザの文脈によってトークンの切り...
そこでPEG(Ford04)ですよ
 プログラミング言語等の文法の表記法
 BNFと目的は類似
 構文解析アルゴリズムの一種とも言える
 決定的な任意のLR(k)言語や一部の文脈依存言
語を扱える
 構文規則の集合
 (N ← e)...
Parsing Expression(1)
 N : 規則(非終端記号)の参照
 "a" : 文字列a
 ε : 空文字列
 . : 任意の一文字
 [...] : 文字クラス
 e1 e2 : e1とe2の並び
 e1 / e2...
Parsing Expression(2)
 e? : 0回または1回の繰り返し
 e* : 0回以上の繰り返し
 e+ : 1回以上の繰り返し
 &e : And-predicate
 eがマッチしたら成功。入力を消費しない
 !...
Desugaring Parsing Expression
 e? : (e / ε)
 e* : N'; N'← e N' / ε
 e+ : e e*
 !e : !!(e)
PEGの解釈
 入力に対して式がマッチするかを判定する
 結果の値
 マッチに成功した場合: 「消費」した文字列
 失敗した場合: f(失敗したことを意味する値)
 例: (式, 入力) → 結果 という形とする
 ("a", "a...
PEGの例
 Eが表している言語
 a,b,cの文字と+から成る算術式
 a,a+b,a+b+c,a+c,a+b+c+a, ...
E ← V "+" E / V
V ← "a" / "b" / "c"
PEGで嬉しいこと
 無限長の先読みが可能
 字句解析不要→柔軟な文法が記述し易い
 String interpolation, Here document等
 曖昧性が無い
 文法のconflictが起きない
 C言語のif文のP...
PEGで嬉しくないこと
 PEGの利点と表裏一体
 空白の読み飛ばしなどを明示する必要がある
 通常は字句解析で空白が処理される
 マクロを導入すれば軽減できる
 順序を入れ替えると意味が変わってしまう
 if_stmt ← IF ...
PEGパーザの実装(関数型)
 各構文規則を純粋な関数として実装
 入力: 入力文字列
 出力: 成功したかどうか
 成功した場合は残りの文字列も一緒に返す
 非終端記号の参照を関数呼び出しとして実装
PEGパーザの実装(関数型)
規則 V ← "a" V / "c"の実装を考える
def parse_V(input)
r1 = match("a", input)
if r1.succeed?
r2 = parse_V(r1.output)
...
PEGパーザの実装(手続き型)
 パーザが状態を持つ
 入力文字列
 今何文字目を解析しているか(カーソル)
 構文規則を副作用のある関数として実装
 入力: 無し
 出力: 成功したかどうか
 関数の中でカーソルを破壊的に更新
PEGパーザの実装(手続き型)
規則 V ← "a" V / "c"の実装を考える
def parse_V
backup_pos = current_pos
if match("a")
if parse_V then return true
r...
Packrat Parsing(Ford02)
 PEGをベースにした構文解析アルゴリズム
 入力の長さに対して線形時間で解析可能
 実用的
 アルゴリズムが非常に単純
 Backtrack parsing + メモ化
 プログラマ...
メモ化(memoize)
 ≠memorize
 一度計算した関数の結果を記憶しておく
 同じ引数で呼び出された場合、結果を再利用
 原則的に副作用の無い関数にしか使えない
 副作用があるとメモ化した場合に結果が異なる
フィボナッチ関数
 再帰的な定義:
 fib(n) = 1 if n = 1 or 2
 fib(n) = fib(n – 1) + fib(n – 2)
 定義にしたがって計算すると指数関数時間
メモ化されたフィボナッチ関数
 同じ引数に対して、計算結果を再利用
 fib(n) = m[n] if m[n] != null
 fib(n) = m[n]:=1 ; m[n] if n = 1 or 2
 fib(n) = m[n]...
Backtrack parsing
E ← V "+" E / V
V ← "a" / "b" / "c"
E(1)
V(1)
"a"
"+" E(3)
V(3)
"a" "b"
V(3)
"a" "b"
"+"
 式: E, 入力:"a+b...
Packrat parsing
E ← V "+" E / V
V ← "a" / "b" / "c"
E(1)
V(1)
"a"
"+" E(3)
V(3)
"a" "b"
"+"
 式: E, 入力:"a+b"
Vの解析結果を再利用
Backtrack Parsingと
Packrat Parsingの性能比較
 次の文法の言語を解析する時の性能を比較
 Backtrackが頻発する入力を与える
 入力: '(1)', '((1))', '(((1)))', …
E ...
Backtrack Parsingと
Packrat Parsingの性能比較
Packrat Parsingの欠点
 必要な記憶領域が多い
 入力文字列の長さに対して線形の記憶領域
 大規模なファイルの解析には向かない
 実行効率がイマイチ
 バックトラックやメモ化が影響
 ほとんどのメモ化は無駄になっている...
PEG/Packrat Parser Generator
 PEGが提案されて10年以上
 様々なものがある
 Rats! (Java)
 Parboiled2 (Scala)
 Treetop (Ruby)
 Lpeg (Lua)...
Rats!
 Javaのソースコードを生成
 http://cs.nyu.edu/rgrimm/xtc/
 (状態つき)Packrat Parserを生成
 文法定義をモジュールに分割可能
 ASTの自動生成
Parboiled2
 Scala用
 https://github.com/sirthias/parboiled
2
 Scalaマクロベースのジェネレータ
 Scalaコードの一部として文法を記述
LPeg
 Lua用
 http://www.inf.puc-rio.br/~roberto/lp
eg/
 文字列とのパターンマッチングライブラリ
Treetop
 Ruby用のPEGパーザジェネレータ
 http://treetop.rubyforge.org/
 Rubyプログラムに似たシンプルな文法記述
 既存のgrammarを取り込んで新しいパーザ
を合成できる
PEG.js
 JavaScript用
 http://pegjs.org/
 Node.js対応
PEGに関する有名な未解決問題
 PEL ⊃ CFL or not
 PEGはいくつかの文脈依存言語を表現可能
 a^n b^n c^n
 CFGでは表現できない
 では、逆は?
 不明
 回文がそれに相当するのではないかと考えら...
PEGに関するその他の研究
 左再帰の導入(Warth et al, 2007)
 PEGの仮想機械(Medeiros et al, 2008)
 カットおよびカット自動挿入アルゴリズムの導
入 (Mizushima, et al., 2...
Upcoming SlideShare
Loading in...5
×

Introduction to PEG

0

Published on

PEGと構文解析に関するアレコレの勉強会 Vol.1
http://connpass.com/event/16630/
の発表資料です。

Published in: Technology
0 Comments
1 Like
Statistics
Notes
  • Be the first to comment

No Downloads
Views
Total Views
0
On Slideshare
0
From Embeds
0
Number of Embeds
0
Actions
Shares
0
Downloads
0
Comments
0
Likes
2
Embeds 0
No embeds

No notes for slide

Transcript of "Introduction to PEG"

  1. 1. Introduction to PEG 構文解析友の会 水島 宏太
  2. 2. 背景  多様な入力文字列を構文解析する必要性  (色々なフォーマットの)設定ファイル  Webのクローリング  "Cargo cult parsing" (from Yacc is dead) の流行  Googleで検索して正規表現を拾ってきて、テキトウにコピー &ペーストしてパーザを作ること  「だいたいの入力に対してそれなりにうまく働く」不完全なパー ザ  Cargo cult parsingを追放せよ!
  3. 3. 構文解析って何?  一言でいうと:  入力文字列を木構造(Abstract Syntax Tree) に組み立てる捜査  二種類に分けられる  自然言語の構文解析  構文解析の結果あいまい性が生じることがある  非自然言語の構文解析 ←今回扱うもの  構文解析の結果あいまい性が生じない
  4. 4. 様々な構文解析アルゴリズム  CYK - O(n3)  (S|LA)?LR(k) - O(n)  LL(k) - O(n)  LL(*) - O(n)  GLR (Generalized LR) - O(n3)  GLL (Generalized LL) - O(n3)  PEG(or Packrat Parsing) - O(kn) (O(n)) 一長一短がある
  5. 5. 構文解析器をどうやって書く?  手書き  どんな文法でも大体作れる  文法のメンテナンス/デバッグが大変  パーザジェネレータ  文法定義からパーザを自動生成  パーザコンビネータ  プログラミング言語のDSLとしてパーザを組み立 てるものを用意する  ホスト言語のデバッグ環境をそのまま用意できる
  6. 6. YACC (LALR(1))  最もメジャーなパーザジェネレータ  Rubyを初めとする様々な言語で採用  上位互換のbisonが一般に採用される  空白の有無等で意味が変わる文法は苦手  空白は通常字句解析で処理するため  トークンが再帰的な構造を含む文法は苦手  式埋め込み可能な文字列リテラル(Ruby)  "1+2 = #{1+2}"  ヒアドキュメント
  7. 7. JavaCC (LL(k) + α)  Javaでメジャーなパーザジェネレータ  構文木の生成がちょっと楽  jjtree(プリプロセッサ)を使用した場合  空白の有無で意味が変わる文法は苦手  理由はyaccと同じ  トークンが再帰的な構造を含む文法は苦手  理由はyaccと同じ
  8. 8. 何が言いたいか  一般的な構文解析アルゴリズムでは字句解析 と構文解析の分離が前提  プログラミング言語用の字句解析器の限界  字句解析器は通常正規表現によって記述  再帰的な構造をうまく扱えない  パーザの文脈によってトークンの切り出し方が変わる ような文法は苦手
  9. 9. そこでPEG(Ford04)ですよ  プログラミング言語等の文法の表記法  BNFと目的は類似  構文解析アルゴリズムの一種とも言える  決定的な任意のLR(k)言語や一部の文脈依存言 語を扱える  構文規則の集合  (N ← e)*  N: 構文規則名  e: 式(Parsing Expression)
  10. 10. Parsing Expression(1)  N : 規則(非終端記号)の参照  "a" : 文字列a  ε : 空文字列  . : 任意の一文字  [...] : 文字クラス  e1 e2 : e1とe2の並び  e1 / e2 : e1を試し、失敗したらe2を試す  e1 / e2 ≠ e2 / e1
  11. 11. Parsing Expression(2)  e? : 0回または1回の繰り返し  e* : 0回以上の繰り返し  e+ : 1回以上の繰り返し  &e : And-predicate  eがマッチしたら成功。入力を消費しない  !e : Not-predicate  eがマッチしなければ成功。入力を消費しない シンプル!
  12. 12. Desugaring Parsing Expression  e? : (e / ε)  e* : N'; N'← e N' / ε  e+ : e e*  !e : !!(e)
  13. 13. PEGの解釈  入力に対して式がマッチするかを判定する  結果の値  マッチに成功した場合: 「消費」した文字列  失敗した場合: f(失敗したことを意味する値)  例: (式, 入力) → 結果 という形とする  ("a", "ab") → "a"  ("b", "ab") → f  (&"a", "ab") → ""  ("a"/"b", "ab") → "a"  (.*, "ab") → "ab"
  14. 14. PEGの例  Eが表している言語  a,b,cの文字と+から成る算術式  a,a+b,a+b+c,a+c,a+b+c+a, ... E ← V "+" E / V V ← "a" / "b" / "c"
  15. 15. PEGで嬉しいこと  無限長の先読みが可能  字句解析不要→柔軟な文法が記述し易い  String interpolation, Here document等  曖昧性が無い  文法のconflictが起きない  C言語のif文のPEGによる記述  if_stmt ← IF LP expr RP stmt (ELSE stmt)?
  16. 16. PEGで嬉しくないこと  PEGの利点と表裏一体  空白の読み飛ばしなどを明示する必要がある  通常は字句解析で空白が処理される  マクロを導入すれば軽減できる  順序を入れ替えると意味が変わってしまう  if_stmt ← IF LP expr RP stmt / IF LP expr RP stmt ELSE stmt  elseを含む文が解析できないPEG
  17. 17. PEGパーザの実装(関数型)  各構文規則を純粋な関数として実装  入力: 入力文字列  出力: 成功したかどうか  成功した場合は残りの文字列も一緒に返す  非終端記号の参照を関数呼び出しとして実装
  18. 18. PEGパーザの実装(関数型) 規則 V ← "a" V / "c"の実装を考える def parse_V(input) r1 = match("a", input) if r1.succeed? r2 = parse_V(r1.output) if r2.succeed? then r2 else match("c", input) end else match("c", input) end end
  19. 19. PEGパーザの実装(手続き型)  パーザが状態を持つ  入力文字列  今何文字目を解析しているか(カーソル)  構文規則を副作用のある関数として実装  入力: 無し  出力: 成功したかどうか  関数の中でカーソルを破壊的に更新
  20. 20. PEGパーザの実装(手続き型) 規則 V ← "a" V / "c"の実装を考える def parse_V backup_pos = current_pos if match("a") if parse_V then return true rewind(backup_pos); return match("c") else return match("c") end end
  21. 21. Packrat Parsing(Ford02)  PEGをベースにした構文解析アルゴリズム  入力の長さに対して線形時間で解析可能  実用的  アルゴリズムが非常に単純  Backtrack parsing + メモ化  プログラマが挙動を理解しやすい
  22. 22. メモ化(memoize)  ≠memorize  一度計算した関数の結果を記憶しておく  同じ引数で呼び出された場合、結果を再利用  原則的に副作用の無い関数にしか使えない  副作用があるとメモ化した場合に結果が異なる
  23. 23. フィボナッチ関数  再帰的な定義:  fib(n) = 1 if n = 1 or 2  fib(n) = fib(n – 1) + fib(n – 2)  定義にしたがって計算すると指数関数時間
  24. 24. メモ化されたフィボナッチ関数  同じ引数に対して、計算結果を再利用  fib(n) = m[n] if m[n] != null  fib(n) = m[n]:=1 ; m[n] if n = 1 or 2  fib(n) = m[n]:=fib(n–1)+fib(n–2); m[n]  入力の大きさに対して線形時間
  25. 25. Backtrack parsing E ← V "+" E / V V ← "a" / "b" / "c" E(1) V(1) "a" "+" E(3) V(3) "a" "b" V(3) "a" "b" "+"  式: E, 入力:"a+b" 同じ計算を二度行う
  26. 26. Packrat parsing E ← V "+" E / V V ← "a" / "b" / "c" E(1) V(1) "a" "+" E(3) V(3) "a" "b" "+"  式: E, 入力:"a+b" Vの解析結果を再利用
  27. 27. Backtrack Parsingと Packrat Parsingの性能比較  次の文法の言語を解析する時の性能を比較  Backtrackが頻発する入力を与える  入力: '(1)', '((1))', '(((1)))', … E ← M M ← A Spacing MS / A MS ← "*" Spacing M / "/" Spacing M / "%" Spacing M A ← P Spacing AS / P AS ← "+" Spacing A / "-" Spacing A P ← Number / "(" Spacing E Spacing ")"
  28. 28. Backtrack Parsingと Packrat Parsingの性能比較
  29. 29. Packrat Parsingの欠点  必要な記憶領域が多い  入力文字列の長さに対して線形の記憶領域  大規模なファイルの解析には向かない  実行効率がイマイチ  バックトラックやメモ化が影響  ほとんどのメモ化は無駄になっている可能性
  30. 30. PEG/Packrat Parser Generator  PEGが提案されて10年以上  様々なものがある  Rats! (Java)  Parboiled2 (Scala)  Treetop (Ruby)  Lpeg (Lua)  PEG.js (JavaScript)
  31. 31. Rats!  Javaのソースコードを生成  http://cs.nyu.edu/rgrimm/xtc/  (状態つき)Packrat Parserを生成  文法定義をモジュールに分割可能  ASTの自動生成
  32. 32. Parboiled2  Scala用  https://github.com/sirthias/parboiled 2  Scalaマクロベースのジェネレータ  Scalaコードの一部として文法を記述
  33. 33. LPeg  Lua用  http://www.inf.puc-rio.br/~roberto/lp eg/  文字列とのパターンマッチングライブラリ
  34. 34. Treetop  Ruby用のPEGパーザジェネレータ  http://treetop.rubyforge.org/  Rubyプログラムに似たシンプルな文法記述  既存のgrammarを取り込んで新しいパーザ を合成できる
  35. 35. PEG.js  JavaScript用  http://pegjs.org/  Node.js対応
  36. 36. PEGに関する有名な未解決問題  PEL ⊃ CFL or not  PEGはいくつかの文脈依存言語を表現可能  a^n b^n c^n  CFGでは表現できない  では、逆は?  不明  回文がそれに相当するのではないかと考えられて いる
  37. 37. PEGに関するその他の研究  左再帰の導入(Warth et al, 2007)  PEGの仮想機械(Medeiros et al, 2008)  カットおよびカット自動挿入アルゴリズムの導 入 (Mizushima, et al., 2010)  LL(*)アルゴリズム (Parr, et al., 2011)  PEGとカバーする範囲が重複する 
  1. A particular slide catching your eye?

    Clipping is a handy way to collect important slides you want to go back to later.

×