chainl
Parsecのparser combinator
左再帰のparserを返すコンビネータ
左再帰のあるparserから左再帰の除去をしたBNF記法に書き換える
左結合になる
右結合にしたければchainrを使えばいい
よくあるExpr + Expr + Exprのような連続する二項演算子をパースするparserを作成できる
chainl
chainl :: Parser a -> Parser (a -> a -> a) -> a -> Parser a
chainl1
document
chainl1 :: Parser a -> Parser (a -> a -> a) -> Parser a
第1引数
Parser a
左再帰の除去E → T (+ T)*のT
型的には普通のParsecのparserだな
第2引数
Parser (a -> a -> a)
演算子を消化して二分木を合成する関数を返すパーサを
内部実装の解説
https://ksmakoto.hatenadiary.com/entry/2014/04/26/131424
https://kazu-yamamoto.hatenablog.jp/entry/20110127/1296098875
左再帰の除去をするためにchainlを実装していく
https://kazu-yamamoto.hatenablog.jp/entry/20080920/1221881130
http://www.cs.nott.ac.uk/~pszgmh/pearl.pdf
全然理解していないmrsekut.icon
抽象化がすごいので理解したい
#あとで調べる
実装例
https://noritsugu.hatenablog.com/entry/20081102/parser
以下の例は、書き換え前は右結合で、書き換え後は左結合になっているので、例としてはあまり良くない
code:hs
- add ::= term | term ('+' add | "-" add)
-- 書き換え前
add :: Parser Expr
add = do
t <- spaces *> term
spaces
*> (Add t <$> (char '+' *> spaces *> add))
<|> (Sub t <$> (char '-' *> spaces *> add))
<|> pure t
-- 書き換え
add :: Parser Expr
add = term chainl1 addop
addop :: Parser (Expr -> Expr -> Expr)
addop = Add <$ char '+' <|> Sub <$ char '-'
実際にループを使ったものとchainlを使ったものを比較している記事はこれを参考
参考
Parsec 連続する計算をパースするパーサコンビネータ chainl1 : tnomuraのブログ
実装例など、わかりやすい