content: {-# 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 "<stdin>" =<< 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)
  