Parsing Left Recursive PEG

2,243
-1

Published on

the algorithm to parsing left recursive PEG rules
with packrat parsing

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

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

No notes for slide

Parsing Left Recursive PEG

  1. 1. Parsing Left Recursive PEG with C++ www.kstm.org id:eldesh [email_address]
  2. 2. <ul><li>Hatena id:eldesh </li></ul><ul><li>[email_address] </li></ul><ul><li>C++er, SMLer(?) </li></ul><ul><li>Clean(Haskell)er ワナビー </li></ul><ul><li>Vimmer </li></ul><ul><li>FScheme </li></ul><ul><ul><li>http://fscheme.codeplex.com/team/view </li></ul></ul>Who am I ?
  3. 3. <ul><li>PEG introduction </li></ul><ul><li>Packrat Parsing </li></ul><ul><li>Supporting Direct Left Recursion </li></ul><ul><li>Supporting Indirect Left Recursion </li></ul>Table of Contents
  4. 4. PEG とは <ul><li>Parsing Expression Grammar の (ry </li></ul><ul><li>再帰下降解析文法定義の方法 </li></ul><ul><li>(CFG と異なり ) 曖昧さが存在しない </li></ul><ul><ul><li><規則名> ← < Syntax > という形式の定義 ( ルール ) を並べる </li></ul></ul><ul><li>PEG は、文字列上で位置を変更せずにある規則が自分自身を呼び出すという左再帰規則を表現できない。 (ja.wikipedia.org 談 ) </li></ul>
  5. 5. PEG   literal + unary op <ul><li>'' (string literal) </li></ul><ul><li>e? (optional) </li></ul><ul><li>e* (zero-or-more) </li></ul><ul><li>e+ (one-or-more) </li></ul><ul><li>. (any character) </li></ul><ul><li>e.g. </li></ul><ul><ul><li>'a'* 'b'+ 'hoge'? </li></ul></ul>
  6. 6. PEG   sequence <ul><li>A ← 'a' ; B ← 'b' </li></ul><ul><li>R ← A B </li></ul><ul><li>Match? </li></ul><ul><ul><li>“ ab” -> true </li></ul></ul><ul><ul><li>“ a” -> false </li></ul></ul><ul><ul><li>“ b” -> false </li></ul></ul><ul><ul><li>“ abc” -> false </li></ul></ul>
  7. 7. PEG   predicate <ul><li>zoo ← 011 ; zop ← 01+ </li></ul><ul><li>R ← ! zoo zop </li></ul><ul><li>Match? </li></ul><ul><ul><li>“ 011” -> false </li></ul></ul><ul><ul><li>“ 01” -> true </li></ul></ul><ul><ul><li>“ 0111” -> false </li></ul></ul><ul><ul><li>“ 01111” -> false </li></ul></ul>
  8. 8. PEG   priority choice (1) <ul><li>zero ← '0' ; one ← '1' </li></ul><ul><li>R ← zero / one </li></ul><ul><li>Match? </li></ul><ul><ul><li>“ 0” -> true </li></ul></ul><ul><ul><li>“ 1” -> true </li></ul></ul><ul><ul><li>“ 01” -> false </li></ul></ul><ul><ul><li>“ 10” -> false </li></ul></ul>
  9. 9. PEG   priority choice (2) <ul><li>zop ← '01+' ; oos ← '11*' </li></ul><ul><li>R ← zop / oos </li></ul><ul><li>Match? </li></ul><ul><ul><li>“ 01” -> true </li></ul></ul><ul><ul><li>“ 11” -> true </li></ul></ul><ul><ul><li>“ 011111” -> true </li></ul></ul><ul><ul><li>“ 0” -> false </li></ul></ul>
  10. 10. PEG まとめ <ul><li>無限先読み </li></ul><ul><li>絶対衝突しない </li></ul><ul><li>実装簡単 </li></ul><ul><li>レキサとパーサの区別が不要 </li></ul><ul><li>パフォーマンスは…? </li></ul><ul><ul><li>最悪指数関数時間かかる </li></ul></ul><ul><ul><li>速くしよう! </li></ul></ul>
  11. 11. Packrat Parsing で高速化 <ul><li>方針 </li></ul><ul><ul><li>部分的なパース結果をキャッシュする </li></ul></ul>memo<pair<rule,pos> , optional<semantic_value>>
  12. 12. キャッシュが効きそうな規則の例 <ul><li>rule </li></ul><ul><ul><li>Add ← Mul '+' Add / Mul </li></ul></ul><ul><ul><li>Mul ← Prim '*' Mul / Prim </li></ul></ul><ul><ul><li>Prim ← '(' Add ')' / Dec </li></ul></ul><ul><ul><li>Dec ← '0'/'1'/'2'/.../'9' </li></ul></ul><ul><li>Input </li></ul><ul><ul><li>'2*(3+4)' </li></ul></ul>
  13. 13. Memoization Table column 1 2 3 4 5 6 7 8 Add Mul Prim Dec input 2 * ( 3 + 4 ) 0
  14. 14. Memoization Table column 1 2 3 4 5 6 7 8 Add 7,7 X 4,7 X X Mul 3,5 X 4,7 X X Prim 3,5 X 4,7 X X Dec X 3,5 X 4,7 X X input 2 * ( 3 + 4 ) 0
  15. 15. Memoization Table semantic value column 1 2 3 4 5 6 7 8 Add 7,7 X 4,7 X X Mul 3,5 X 4,7 X X Prim 3,5 X 4,7 X X Dec X 3,5 X 4,7 X X input 2 * ( 3 + 4 ) 0
  16. 16. Memoization Table column number column 1 2 3 4 5 6 7 8 Add 7,7 X 4,7 X X Mul 3,5 X 4,7 X X Prim 3,5 X 4,7 X X Dec X 3,5 X 4,7 X X input 2 * ( 3 + 4 ) 0
  17. 17. Packrat Parser まとめ <ul><li>無限先読み </li></ul><ul><li>絶対衝突しない </li></ul><ul><li>実装簡単 </li></ul><ul><li>レキサとパーサの区別が不要 </li></ul><ul><li>動作速い! New </li></ul><ul><li>… ? </li></ul>
  18. 18. Left Recursion <ul><li>Match? </li></ul><ul><ul><li>“ 1-0” -> true? </li></ul></ul><ul><ul><li>“ 1-2-3” -> true? </li></ul></ul><ul><ul><li>“ 5-6-7-8”-> true? </li></ul></ul>Expr ← Expr '-' Num / Num Num ← [1-9][0-9]*
  19. 19. マッチの様子? 1 - 3 - 5 Expr Expr Num input Num Num Expr ← Expr '-' Num / Num Num ← [1-9][0-9]*
  20. 20. Infinite Recursion <ul><li>(Expr, 1) </li></ul><ul><ul><li>(Expr '-' Num / Num, 1) </li></ul></ul><ul><ul><ul><li>(Expr '-' Num, 1) </li></ul></ul></ul><ul><ul><ul><ul><li>(Expr, 1) </li></ul></ul></ul></ul><ul><ul><ul><ul><ul><li>(Expr '-' Num / Num, 1) </li></ul></ul></ul></ul></ul><ul><ul><ul><ul><ul><li>(Expr, 1) </li></ul></ul></ul></ul></ul>Expr ← Expr '-' Num / Num Num ← [1-9][0-9]* Input: '1-3-5'
  21. 21. Detecting Left Recursion <ul><li>(Expr, 1) </li></ul><ul><ul><li>(Expr '-' Num, 1) </li></ul></ul><ul><ul><ul><li>(Expr, 1) </li></ul></ul></ul><ul><ul><ul><ul><li>(Expr '-' Num, 1) </li></ul></ul></ul></ul><ul><ul><ul><ul><ul><li>(Expr, 1) </li></ul></ul></ul></ul></ul><ul><ul><ul><ul><ul><li>(Expr '-' Num </li></ul></ul></ul></ul></ul>同じルールで 同じ入力をパース ループが 検出出来る! Expr ← Expr '-' Num / Num Num ← [1-9][0-9]* Input: '1-3-5'
  22. 22. Packrat Parsing 再び <ul><li>ルールと入力のペアを覚えておく </li></ul><ul><ul><li>Packrat Parsing が使える! </li></ul></ul>memo<pair<rule,pos> , optional<svalue>> memo<pair<rule,pos> , pair<LR, optional<svalue>>>
  23. 23. Avoiding Infinite Recursion <ul><li>(Expr, 1) </li></ul><ul><ul><li>(Expr '-' Num / Num, 1) </li></ul></ul><ul><ul><ul><li>(Expr '-' Num, 1) </li></ul></ul></ul><ul><ul><ul><ul><li>MEMO(Expr, 1) = Fail </li></ul></ul></ul></ul><ul><ul><ul><ul><li>Fail する </li></ul></ul></ul></ul><ul><ul><ul><li>(Num, 1) (* match! *) </li></ul></ul></ul><ul><ul><ul><ul><li>MEMO(Expr, 1) = '1' </li></ul></ul></ul></ul>Expr ← Expr '-' Num / Num Input: '1-3-5'
  24. 24. Detecting Left Recursion <ul><li>Expr ← Expr '-' Num / Num </li></ul><ul><li>Expr : '1-3-5' </li></ul><ul><li>Expr '-' Num : '1-3-5' </li></ul><ul><ul><li>Expr : '1-3-5' </li></ul></ul><ul><ul><li>Expr : ← fail </li></ul></ul><ul><li>Num : '1-3-5' </li></ul><ul><li>(Expr (1), '-3-5') </li></ul>
  25. 25. Happy End… ? <ul><li>(Expr, 1) </li></ul><ul><ul><li>(Expr '-' Num / Num, 1) </li></ul></ul><ul><ul><ul><li>( 中略 ) </li></ul></ul></ul><ul><li>Result </li></ul><ul><ul><li>Expr:'1' 残り :'-3-5' </li></ul></ul>(´ ・ ω ・` ) Expr ← Expr '-' Num / Num Input: '1-3-5'
  26. 26. Making Seed <ul><li>APPLY-RULE() </li></ul><ul><ul><li>テーブルを検索 </li></ul></ul><ul><ul><ul><li>エントリが見つからない </li></ul></ul></ul><ul><ul><ul><ul><li>LR フラグをセット </li></ul></ul></ul></ul><ul><ul><ul><ul><li>続き ( 右辺 ) をパース </li></ul></ul></ul></ul><ul><ul><ul><ul><li>左再帰見つけた? </li></ul></ul></ul></ul><ul><ul><ul><ul><ul><li>……… </li></ul></ul></ul></ul></ul><ul><ul><ul><li>エントリ見つかった! </li></ul></ul></ul><ul><ul><ul><ul><li>左再帰なの? </li></ul></ul></ul></ul><ul><ul><ul><ul><ul><li>LR フラグ := true </li></ul></ul></ul></ul></ul><ul><ul><ul><ul><ul><li>( とりあえずこの場は )Fail </li></ul></ul></ul></ul></ul>Seed Parse
  27. 27. Support Direct Left Recursion <ul><li>テーブルを検索 </li></ul><ul><ul><li>エントリが見つからない </li></ul></ul><ul><ul><ul><li>LR フラグをセット </li></ul></ul></ul><ul><ul><ul><li>result = 続き ( 右辺 ) をパース </li></ul></ul></ul><ul><ul><ul><li>左再帰見つけた? </li></ul></ul></ul><ul><ul><ul><ul><li>GROWING(result as Seed) </li></ul></ul></ul></ul><ul><ul><ul><li>そのまま結果を返す </li></ul></ul></ul><ul><ul><li>エントリ見つかった! </li></ul></ul><ul><ul><ul><li>左再帰だった? </li></ul></ul></ul><ul><ul><ul><ul><li>左再帰を検出をマーク </li></ul></ul></ul></ul><ul><ul><ul><ul><li>とりあえずこの場は Fail </li></ul></ul></ul></ul><ul><ul><ul><li>見つけた結果を返す </li></ul></ul></ul>
  28. 28. Growing The Seed! <ul><li>GROWING(Rule, Pos, Result) </li></ul><ul><ul><li>パースする位置をリセット (1) </li></ul></ul><ul><ul><li>パースする </li></ul></ul><ul><ul><ul><li>Seed parse が育ってない? </li></ul></ul></ul><ul><ul><ul><ul><li>終わり </li></ul></ul></ul></ul><ul><ul><ul><li>パース結果 ( エントリ ) を更新 </li></ul></ul></ul><ul><ul><li>(1) に戻る </li></ul></ul>
  29. 29. マッチの様子 ( 再び ) 1 - 3 - 5 Expr (Grow1) Num input Num Num(Seed) Expr (Grow2) Expr Expr ← Expr '-' Num / Num (input: '1-3-5')
  30. 30. MemoizationTable の様子 LR ? LR ! Fail 3,2 3,2 seed parse! Expr ← Expr '-' Num / Num Num ← [1-9][0-9]* ; input: '3-4' 4,4 3-4,4 Growing! column 1 2 3 4 Expr Num input 3 - 4 0
  31. 31. PEG Parser ここまでのまとめ <ul><li>無限先読み </li></ul><ul><li>絶対衝突しない </li></ul><ul><li>実装簡単 </li></ul><ul><li>レキサとパーサの区別が不要 </li></ul><ul><li>動作速い </li></ul><ul><li>左再帰も扱える! New </li></ul><ul><li>… ? </li></ul>
  32. 32. In d irect Left Recursion <ul><li>Match? </li></ul><ul><ul><li>“ 1-0” -> true? </li></ul></ul><ul><ul><li>“ 1-2-3” -> true? </li></ul></ul><ul><ul><li>“ 5-6-7-8”-> true? </li></ul></ul>X ← Expr Expr ← X '-' Num / Num
  33. 33. Table behavior of parsing Indirect Left Recursion X ← Expr Expr ← X '-' Num / Num (Input: '4-3') LR ? LR ? LR ! Fail 4,2 4,2 4,2 seed parse! キャッシュが見つかってしまう! column 1 2 3 4 X Expr Num input 4 - 3 0
  34. 34. In d irect Left Recursion <ul><li>Match? </li></ul><ul><ul><li>“ 1-0” -> (1, '-0') </li></ul></ul><ul><ul><li>“ 1-2-3” -> (1, '-2-3') </li></ul></ul><ul><ul><li>“ 5-6-7-8”-> (5, '-6-7-8') </li></ul></ul>X ← Expr Expr ← X '-' Num / Num
  35. 35. resource <ul><li>hoge </li></ul>LR ? LR ?
  36. 36. Rule Invocation Stack <ul><li>ルールをパースする前に必ずスタックに積む </li></ul><ul><li>既にツンであったら LR が検出できる </li></ul><ul><ul><li>LR に含まれるルールが把握できる </li></ul></ul>Expr X Head Involved
  37. 37. By pass the MemoTable <ul><li>@ </li></ul><ul><ul><li>自身を含む LR の Head rule </li></ul></ul><ul><li>{} </li></ul><ul><ul><li>Involved(Set) </li></ul></ul><ul><li>Head を parse しているときは involvedSet に含まれている規則は キャッシュを使わずに 新たに parse する </li></ul>[email_address] X,{Expr} Head Involved InvolvedSet
  38. 38. Adding Support for Indirect Left Recursion memo<pair<rule,pos> , pair<LR, optional<svalue>>> struct LR { AST seed; Rule rule Head head; LR * next; }; struct Head { Rule rule; RuleSet involvedSet,evalSet; }
  39. 39. Indirect Left Recursion X ← Expr Expr ← X '-' Num / Num (Input: '4-3') LR ? LR ? LR ! Fail 4,2 4,2 4,2 seed parse! [email_address] X,{Expr} 4-3,4 3,4 4-3,4 growing! column 1 2 3 4 X Expr Num input 4 - 3 0
  40. 40. Support Direct Left Recursion <ul><li>enum tag { TAG_LR, TAG_AST }; </li></ul><ul><li>struct LR { bool detected; }; </li></ul><ul><li>struct Entry {tag kind;LR lr;AST * ans;}; </li></ul>
  41. 41. parse_ident(ID, Pos) { Entry * m = MEMO(ID, Pos); if (m==NULL) { MEMO(R, P) = new MEMOENTRY(P, TAG_LR); m->ans = EVAL(R.Body); if (m->lr.detected && ans!=NULL) return GROWLR(R, P, m); else return m; return m; } else { // エントリが見つかった場合 } }
  42. 42. Packrat Parser can support IndirectL.R. ! <ul><li>無限先読み </li></ul><ul><li>絶対衝突しない </li></ul><ul><li>実装簡単 </li></ul><ul><li>レキサとパーサの区別が不要 </li></ul><ul><li>動作速い </li></ul><ul><li>直接左再帰が扱える </li></ul><ul><li>間接左再帰が扱える! New </li></ul>
  43. 43. References <ul><li>http://pdos.csail.mit.edu/~baford/packrat/ </li></ul><ul><li>Packrat parsers can support left recursion </li></ul><ul><li>Packrat Parsing: a Practical Linear-Time Algorithm with Backtracking </li></ul><ul><li>Parsing Expression Grammars: A Recognition-Based Syntactic Foundation </li></ul>

×