{-# LANGUAGE RankNTypes #-} -- | -- Module : Text.Peggy.Quote -- Copyright : (c) Hideyuki Tanaka 2011 -- License : BSD-style -- -- Maintainer : tanaka.hideyuki@gmail.com -- Stability : experimental -- Portability : portable -- -- The quasi-quoters of peggy syntax. -- module Text.Peggy.Quote ( -- * Quasiquoter peggy, peggyFile, -- * Parser and Quasiquoter generating function genParser, ) where import Language.Haskell.TH import Language.Haskell.TH.Quote import Text.Peggy.Parser import Text.Peggy.Prim import Text.Peggy.Syntax import Text.Peggy.SrcLoc import Text.Peggy.CodeGen.TH -- | quasi-quoter for peggy syntax -- When it is used at top-level of source code, -- definitions of parsers are generated. -- -- > {-# LANGUAGE TemplateHaskell, QuasiQuotes, FlexibleContexts #-} -- > import Text.Peggy -- > -- > [peggy| -- > foo :: [Int] -- > = num* -- > num ::: Int -- > = [0-9]+ { read $1 } -- > |] -- > -- > main :: IO () -- > main = print . parseString foo "" =<< getContents -- -- When it is used as expression, -- the result value is 'Syntax'. -- -- > main = print [peggy| -- > num :: Int -- > = [0-9]+ { read $1 } -- > |] -- -- The result is: -- -- > $ runhaskell Test.hs -- > [Definition "num" "Int\n " (Choice [Semantic (Sequence [Some (TerminalSet [CharRange '0' '9'])]) [Snippet "read ",Argument 1,Snippet " "]])] -- peggy :: QuasiQuoter peggy = QuasiQuoter { quoteDec = qDecs, quoteExp = qExp, quotePat = undefined, quoteType = undefined } -- | Parse peggy syntax from File -- Parse a peggy syntax file and return a 'Syntax' as a result value. -- -- > $ cat test.peggy -- > num :: Int -- > = [0-9]+ { return $1 } -- -- > main = print $(peggyFile "test.peggy") -- -- The result is: -- -- > [Definition "num" "Int\n " (Choice [Semantic (Sequence [Some (TerminalSet [CharRange '0' '9'])]) [Snippet "read ",Argument 1,Snippet " "]])] -- peggyFile :: FilePath -> Q Exp peggyFile filename = do res <- runIO $ parseFile syntax filename case res of Left err -> error $ show err Right syn -> dataToExpQ (const Nothing) syn qDecs :: String -> Q [Dec] qDecs txt = do loc <- location genDecs $ parseSyntax (locToPos loc) txt qExp :: String -> Q Exp qExp txt = do loc <- location dataToExpQ (const Nothing) $ parseSyntax (locToPos loc) txt -- | Generates parsers and quasi-quoters. -- First argument is a list of names of quasi-quoter you want to define. -- For example: -- -- > genParser [("fooqq", "foo")] [peggy| -- > foo :: [Int] -- > = num* -- > num ::: Int -- > = [0-9]+ { read $1 } -- > |] -- -- this code defines parsers named 'foo', 'num' and -- quasi-quoter named 'fooqq'. -- -- It can use it as follow: -- -- > main :: IO () -- > main = print [fooqq| 1 2 3 4 5 |] -- genParser :: [(String, String)] -- ^ a list of pair of name of -- quasi-quoter and its start nonterminal -> Syntax -- ^ syntax -> Q [Dec] -- ^ definitions of parsers and quasi-quoters genParser qqs syn = do qq <- mapM (genQQ syn) qqs dec <- genDecs syn return $ concat qq ++ dec -- parseSyntax :: SrcPos -> String -> Syntax parseSyntax pos txt = case parse syntax pos txt of Left err -> error $ "peggy syntax-error: " ++ show err Right defs -> defs locToPos :: Loc -> SrcPos locToPos loc = SrcPos (loc_filename loc) 0 (fst $ loc_start loc) (snd $ loc_start loc)