パーサコンビネータ

小さなパーサを高階関数(combinator)で合成して、大きな文法のパーサを組み立てる技法。Parser a =「入力を消費して a を生む(失敗・残り入力を扱う)」関数の抽象。文法をホスト言語の値として書く点が、外部のパーサジェネレータと対照的。

構成

satisfy :: (Char -> Bool) -> Parser Char   -- 原子
(<|>)   :: Parser a -> Parser a -> Parser a -- 順序付き選択
many    :: Parser a -> Parser [a]           -- 繰り返し

位置づけ

3つの近縁アプローチを並べると位置づけが鮮明になる:

観点パーサコンビネータパーサジェネレータ(生成系)デコーダ
入力テキスト(文字列)テキスト(文字列)構造化データ(JSON/値)
文法の表現ホスト言語の値(関数合成)外部 DSL(.y/.peg 文法ファイル)ホスト言語の値(関数合成)
生成タイミング実行時に合成(ライブラリ)ビルド時にコード生成実行時に合成(ライブラリ)
扱える文法再帰下降=文脈自由、左再帰は不可(要変換)LALR/LR/GLR/PEG など方式依存で広いスキーマ(再帰可)
性能バックトラックで悪化しうる(packrat で線形化)テーブル駆動で高速・予測可能入力が小さく軽い
エラー回復弱い(工夫が要る)強い(同期トークン等の機構)失敗を型で返す
手軽さ高(言語内で完結、ビルド工程不要)低(別ツール・別文法言語)高
代表parsec/megaparsec (Haskell), FastParse (Scala), nom (Rust)yacc/bison, ANTLR, tree-sitteraeson, elm/Json.Decode, zod

なぜ重要か

文法を第一級の値として部品から組み立てられる。型で結果が保証され、パースの段階で型付き値に落とす「parse, don’t validate」の実装手段になる。

押さえどころ(カード化候補)

関連

Graph