Peggy - チュートリアル
インストール
まず始めに、Peggyをあなたのシステムにインストールしましょう。 既にHaskell Platformがインストールされているなら、 次のコマンドをタイプするだけでインストールできます。 Haskell Platformがインストールされていない場合は、 各プラットフォームでの手順に従い、 まずそちらをインストールしてください。
$ cabal update $ cabal install Peggy
さあ、Peggyが利用できるようになりました。
最初の例
まず、簡単な例を用いて文法を見ていきます。
{-# Language TemplateHaskell, QuasiQuotes, FlexibleContexts #-} import Text.Peggy [peggy| nums :: [Int] = num* num ::: Int = [0-9]+ { read $1 } |] main :: IO () main = print . parseString nums "<stdin>" =<< getContents
これは、数の列をパーズするパーザです。 実際に動かしてみましょう。
$ cat input 1 2 3 4 5 $ runhaskell Test.hs < input Right [1,2,3,4,5]
正しく動いているようですね(´・_・`)!
Haskell言語拡張
Peggyを使用するにあたって、最初の行に次のプラグマを書く必要があります。
{-# Language TemplateHaskell, QuasiQuotes, FlexibleContexts #-}
これらの拡張は、peggyをEDSLとして使うためと、 peggyが内部的に生成するコードをコンパイルさせるために必要になります。
Peggyモジュールのimport
Peggyを使うためには、Peggyのモジュールをimportする必要があります。 次の一つの行を単純に追加するだけです。
import Text.Peggy
パーザの定義
さて、ようやくパーザを書く準備が整いました。 Peggyは embeded DSL (EDSL) スタイルのパーザジェネレータですので、 パーザはHaskellのソースコード中に直に書き込みます。
Peggyはquasi-quoterとして提供されます。 パーザを定義するためには、quasi-quoter [peggy| … |] にて、パーザの定義を囲います。
[peggy| nums :: [Int] = num* ... ここに文法を記述 |]
パーザは定義の集合です。 定義にはPEGのフル機能と、幾つかの独自拡張が利用できます。 Peggyの構文の詳細はこちらを参照してください。
典型的な定義の形
典型的な構文定義は次のような形になっています。
<name> :: <Haskell-Type> = <expr> ... { <semantic-by-haskell-code> } / <expr> ... { ... } ...
各定義は幾つかの代替部を持っています。 そしてそのそれぞれは、セマンティクスの付いた式の列になっています。 セマンティクスは、プレースホルダを含む、単一のHaskellの式として表現されます。 これは各非終端記号のパーズ結果の値として用いられます。
トークン(字句)
Peggyは暗黙的にトークンを扱います。 トークンを定義するには、型名の前のdouble-colon (::) を triple-colon (:::) に変えます。
num ::: Int = [0-9]+ { read $1 }
これで、前後の空白を読み飛ばし、デミリタ区切りになりました。 トークンの振る舞いの詳細は構文マニュアルを参照してください。
生成されたパーザ
Peggyのquasi-quoterは パーザ の集合を生成します。 各非終端記号の定義一つに対し、一つのパーザが定義されます。
nums :: Parser [Int] -- これは単純化した型であり、実際の型とは異なります nums = ... (Haskell code generated by Peggy)
パーザを起動する
ついに、パーザを動作させる準備が整いました。 次の関数を用いて、任意の文字列をパーズすることができます。
parseString :: ListLike str Char => Parser a -> String -> str -> Either ParseError a parseString parser inputName input = ...
quasi-quoter を生成する
Peggyはユーザ定義のquasi-quoterを簡単に生成することができます。
module Nums (numsqq) where genParser [("numsqq", "nums")] [peggy| nums :: [Int] = num* num ::: Int = [0-9]+ { read $1 } |]
genParserという関数を用いてパーザの生成を行います。 このとき、第一引数には定義したいquasi-quoterの情報を渡します。 これは、定義するquasi-quoterの名前と、それが利用する非終端記号の名前のペアのリストです。 この場合だと、numsqqというquasi-quoterが生成されます。
Template Haskellの制限により、この定義は利用するモジュールから分離しなければなりません。
{-# Language TemplateHaskell, QuasiQuotes, FlexibleContexts #-} import Nums main :: IO () main = print [numsqq| 1 2 3 4 5 |]
さて、使ってみましょう。
$ runhaskell Test.hs [1, 2, 3, 4, 5]
期待した動作です。