Parsec でパーサを自動作成する。
パーサの自動作成
Text.Parsec に Text.Parsec.Token と Text.Parsec.Language を追加インポートすると、パーサを自動作成してくれる。詳しい理論は置いておいて、とにかく試してみた。
Prelude> :set prompt "Parsec> " Parsec> import Text.Parsec Parsec> import Text.Parsec.Token Parsec> import Text.Parsec.Language
makeTokenParser
Text.Parsec.Token モジュールの makeTokenParser 関数を使うと、プログラム言語に必要なパーサを自動作成してくれる。ユーザーが用意した言語仕様のデータに makeTokenParser 関数を適用すると、様々なパーサを記述したデータを生成してくれるのだ。
ユーザー定義の言語仕様というのが謎だが、デフォールトのデータが Text.Parsec.Language に emptyDef として定義してある。そこでとにかくそれに makeTokenParser を適用して、mytoken というパーサのいっぱい詰まったデータを作成する。
Parsec> mytoken = makeTokenParser emptyDef
mytoken
mytoken からパーサを引き出すためには、フィールド名でコンテナのパーサを呼び出せばいい。たとえば整数にマッチするパーサは integer というフィールドの中にあるので、integer mytoken のようにすればいい。
また parens というパーサは、カッコの中身を取り出してくれる。
Parsec> parseTest (parens mytoken $ integer mytoken) "(123)" 123
さらに symbol というパーサは引数の文字列のシンボルにマッチする。
Parsec> parseTest (symbol mytoken $ "*") "*" "*"
驚いたことに、この integer, parens, symbol という3つのパーサだけで、カッコと四則演算の揃った整数の電卓が完成するのだ。実行例を次に示す。
Parsec> :l calculator.hs [1 of 1] Compiling Calculator ( calculator.hs, interpreted ) Ok, modules loaded: Calculator. Parsec> parseTest expr "(1+2)*(3+4)" 21
整数の電卓プログラム
整数の電卓のプログラムのソースは次のようになる。
ファイル名:calculator.hs
module Calculator whereimport Text.Parsec import qualified Text.Parsec.Token as P import Text.Parsec.Language
mytoken = P.makeTokenParser emptyDef symbol = P.symbol mytoken integer = P.integer mytoken parens = P.parens mytoken
expr = term `chainl1` addop term = factor `chainl1` mulop factor = parens expr <|> integer
mulop = (symbol "*" >> return (*)) <|> (symbol "/" >> return (div)) addop = (symbol "+" >> return (+)) <|> (symbol "-" >> return (-))
emptyDef のような言語仕様のデータの作り方は、Text.Parsec.Language のソースの中に Haskell の言語仕様の例などが定義されているので、Hackage で調べることができる。Parsec で自分仕様の言語のパーサを作ることも夢ではないような気がしてきた。