Parser Combinatorって なんなのさ 
開発解析事業部東 
2014/08/21
文字列を解釈する処理 
基本的に作るのが面倒 
→ Parser Combinatorを使って楽しよう 
既存手法 
補足 
頑張って手書き 
書くのが面倒、デバックが面倒 
Parser Generator 
使い方覚えるのが面倒
Parser Combinator 
•Parser 
–入力: 文字列 
–出力: 解析結果(数値など),読み残し文字列 
•Parser Combinator 
–入力: 1つ以上のParser 
–出力: 新しいParser 
→ 単純なParserから複雑なParserを構築
例: 計算機を作ろう 
0〜9の数字,+,* のみを含む文字列から 
なる計算式を解釈,計算結果を返す 
計算機 
1 
0 
+ 
2 
* 
3 
16 
計算機 
* 
3 
_人人人人人_ 
>エラー< 
 ̄Y^Y^Y^Y ̄
文字列、数値、Parserの表記 
文字列 
1 
0 
+ 
2 
* 
3 
空 
数値 
16 
Parser 
int 
1 
0 
+ 
2 
* 
3 
10 
+ 
2 
* 
3 
読み残し 
解析結果 
in 
out
関数、Parser Combinatorの表記 
関数 
Parser Combinator 
sum 
2 
1 
4 
7 
Chain 
mul 
2 
* 
3 
6 
空 
in 
out 
in 
out
どうやって解釈しよう? 
1 
0 
+ 
2 
3 
4 
* 
5 
+ 
6 
* 
7 
*
+ 区切りで分割してみる 
1 
0 
+ 
2 
3 
4 
* 
5 
+ 
6 
* 
7 
* 
「整数が* 区切りで並んでいる文字列」が 
+ 区切りで並んでいる
3 つのParser が必要? 
1 
0 
+ 
2 
3 
4 
* 
5 
+ 
6 
* 
7 
* 
「整数が*区切りで並んでいる文字列」が 
+区切りで並んでいる 
Integer Parser 
Multiple Parser 
Plus Parser
1 つのParser Combinatorが必要? 
1 
0 
+ 
2 
3 
4 
* 
5 
+ 
6 
* 
7 
* 
「整数が*区切りで並んでいる文字列」が 
+区切りで並んでいる
1 つのParser Combinatorが必要? 
1 
0 
+ 
2 
3 
4 
* 
5 
+ 
6 
* 
7 
* 
「整数が*区切りで並んでいる文字列」が 
+区切りで並んでいる
Integer parser 
int 
1 
0 
+ 
2 
* 
3 
10 
+ 
2 
* 
3 
int 
1 
0 
0 
100 
空 
int 
+ 
2 
* 
3 
_人人人人人人_ 
>解析失敗< 
 ̄Y^Y^Y^Y^Y ̄
Character parser 
char 
+ 
2 
* 
3 
0 
2 
* 
3 
char 
+ 
0 
空 
char 
* 
3 
+ 
+ 
+ 
0 
0 
0 
_人人人人人人_ 
>解析失敗< 
 ̄Y^Y^Y^Y^Y ̄
ChainCombinator 
Chain 
char 
int 
* 
1 
mul 
2 
* 
3 
0 
* 
1 
•入力を右、左、右と交互にParserへ与える 
•左のParserで解析できなかったら解析成功
ChainCombinator 
Chain 
char 
int 
* 
1 
mul 
2 
* 
3 
0 
* 
1 
読み始める位置 
入力を右側のparserで解釈する
ChainCombinator 
Chain 
char 
int 
* 
1 
mul 
2 
* 
3 
0 
* 
1 
入力を右側のparserで解析する 
10
ChainCombinator 
Chain 
char 
int 
* 
1 
mul 
2 
* 
3 
0 
* 
1 
残りを左側のparserで解析する 
10
ChainCombinator 
Chain 
char 
int 
* 
1 
mul 
2 
* 
3 
0 
* 
1 
残りを左側のparserで解析する 
10 
1
ChainCombinator 
Chain 
char 
int 
* 
1 
mul 
2 
* 
3 
0 
* 
1 
残りを右側のparserで解析する 
10 
1 
2
ChainCombinator 
Chain 
char 
int 
* 
1 
mul 
2 
* 
3 
0 
* 
1 
残りを左側のparserで解析する 
10 
1 
2 
1
ChainCombinator 
Chain 
char 
int 
* 
1 
mul 
2 
* 
3 
0 
* 
1 
残りを右側のparserで解析する 
10 
1 
2 
1 
3
ChainCombinator 
Chain 
char 
int 
* 
1 
mul 
2 
* 
3 
0 
* 
1 
残りを左側のparserで解析する→ 失敗 
→ (全体としては) 解析成功 
10 
1 
2 
1 
3
ChainCombinator 
Chain 
char 
int 
* 
1 
mul 
2 
* 
3 
0 
* 
1 
途中で得られた解析結果を関数で評価して 
全体の解析結果として返す 
10 
1 
2 
1 
3 
60 
空
Chain 
_人人人人人_ >計算機<  ̄Y^Y^Y^Y ̄ 
Chain 
char 
int 
* 
1 
mul 
sum 
char 
+ 
0
Parser Combinator何が嬉しいの? 
単純なParserから 
複雑なParser を構築できる 
–Parser 個別のテストがしやすい 
–Parser の再利用性が高い 
–Parser Combinatorの再利用性が高い
Parser CombinatorLibrary 
Language 
Library 
Haskell 
Parsec, Attoparsec 
F# 
FParsec 
Scala 
scala.util.parsing.combinator.Parsers 
C# 
Sparche, csharp-monad 
Python 
PyParsing
Sparcheを用いた計算機の実装 varInt=Parse.Regex("[0-9]+").Select(int.Parse); varMul=Parse.String("*").Select(_ =>1); varPlus =Parse.String("+").Select(_ =>0); varMulExpr= Parse.ChainOperator(Mul, Int, (op, acc, elem) =>acc*elem); varPlusExpr= Parse.ChainOperator(Plus, MulExpr, (op, acc, elem) =>acc+elem); varresult = PlusExpr.Parse("10+2*3");
典型的なParser Combinator 
•Combine(P1, P2) 
P1 で解析成功したらその読み残しをP2で解析、 それも解析成功したら、解析成功 
•Or(P1, P2) 
1. P1で解析成功したら解析成功 
2. P2で解析成功したら解析成功 
3. 両方失敗したらその解析失敗 
•ZeroOrMore(P) 
P で0回以上解析できたら成功
まとめ 
•Parser Combinatorをざっくり説明した 
•利用例として簡単な計算機を定義した 
•Parser CombinatorLibrary を紹介した

Parser combinatorってなんなのさ