Peggy - Tutorial
Install
You can install from Hackage, by using cabal-install.
$ cabal update $ cabal install Peggy
Now, you can use Peggy!
First Example
Here is first example.
{-# 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
It is a parser which parses a sequence of integers. Let’s try it.
$ cat input 1 2 3 4 5 $ runhaskell Test.hs < input Right [1,2,3,4,5]
It seems to run correctly :)
Language Extensions
At first line,
{-# Language TemplateHaskell, QuasiQuotes, FlexibleContexts #-}
add these language extensions for using peggy embeded DSL.
Import Peggy module
To use Peggy, you need simply to import Peggy module.
import Text.Peggy
Define a parser
Now, you can define your parser. Peggy is embeded DSL (eDSL) style parser generator, so you wrote your parser in a Haskell file directly.
Peggy is provided by Quasi-Quoter. In order to define parser, write a syntax in quasi-quoter [peggy| … |].
[peggy| nums :: [Int] = num* ... define syntax here |]
A parser is a set of definitions. You can use full PEG syntax and Peggy extensions. A full Peggy syntax is described here.
Typical form of syntax
A typical form of syntax definition is
<name> :: <Haskell-Type>
= <expr> ... { <semantic-by-haskell-code> }
/ <expr> ... { ... }
...
Each definition has several alternatives, and each alternative is a sequence of primitive expression with semantic. Semantic is a single haskell expression with place holder. It specify the result value of the nonterminal.
Token
Peggy handles implicit token. You can define a nonterminals as a token by using triple-colon (:::)
num ::: Int
= [0-9]+ { read $1 }
It allows any leading/trailing spaces. For more detail about token behaviour, please refer to syntax maual.
Generated parser
Peggy quasi-quoter generates a set of parsers. Parser is defined for each nonterminal symbol has same name.
nums :: Parser [Int] -- It is simplified. Actually, it has more complicated type. nums = ... (Haskell code generated by Peggy)
Invoking parser
At last, you can invoke your parser by using following function.
parseString :: ListLike str Char
=> Parser a
-> String
-> str
-> Either ParseError a
parseString parser inputName input =
...
It can parse any String-like sequence by passed parser.
Generate Quasi-Quoter
Peggy can generate user defined quasi-quoter easily.
module Nums (numsqq) where
genParser [("numsqq", "nums")] [peggy|
nums :: [Int]
= num*
num ::: Int
= [0-9]+ { read $1 }
|]
First argument of genParser is a list of pair, qqname and top-nonterminal. In above example, a quasi-quoter named “numsqq” are defined. It generates a list of integers. Because of a restriction of Template Haskell, you must split definition and use of quasi-quoters.
{-# Language TemplateHaskell, QuasiQuotes, FlexibleContexts #-}
import Nums
main :: IO ()
main = print [numsqq| 1 2 3 4 5 |]
Then, you can use it.
$ runhaskell Test.hs [1, 2, 3, 4, 5]