module Template.HSML.Internal.Parser
#ifdef TESTING
where
#else
( hsmlParser
, shsmlParser
, hsmlTemplate
, shsmlTemplate
) where
#endif
import qualified Language.Haskell.Exts.Parser as HE
import qualified Language.Haskell.Exts.Extension as HE
import Control.Applicative
import Control.Monad
import Control.Arrow
import Data.Monoid ((<>))
import qualified Text.Parsec.String as P
import qualified Text.Parsec.Prim as P
import qualified Template.HSML.Internal.Types as I
import qualified Template.HSML.Internal.Types.Syntax as IS
import qualified Template.HSML.Internal.Parser.Syntax as IS
hsmlTemplate :: String -> Either String I.Template
hsmlTemplate str =
case P.parse hsmlParser ".HSML" str of
Right tpl -> Right tpl
Left err -> Left $ show err
hsmlParser :: P.Parser I.Template
hsmlParser = IS.hsmlSyntax >>= transform
shsmlTemplate :: String -> Either String I.Template
shsmlTemplate str =
case P.parse shsmlParser ".HSML" str of
Right tpl -> Right tpl
Left err -> Left $ show err
shsmlParser :: P.Parser I.Template
shsmlParser = IS.shsmlSyntax >>= transform
transform :: IS.Syntax -> P.Parser I.Template
transform IS.Syntax{..} = do
args <- mapM transformArg syntaxArgs
(decs, sections) <- transformChunks syntaxChunks
return I.Template
{ I.templateArgs = args
, I.templateDecs = decs
, I.templateSections = sections
}
transformArg :: I.RArg -> P.Parser I.Arg
transformArg (I.RArg name mtype) =
case mtype of
Just stype ->
case parseType stype of
Right t -> return . I.Arg name $ Just t
Left r -> fail $ "Could not parse type \"" <> stype <> "\", reason: " <> r
Nothing -> return $ I.Arg name Nothing
transformExp :: I.RExp -> P.Parser I.Exp
transformExp sexp =
case parseExp sexp of
Right e -> return e
Left r -> fail $ "Could not parse expression \"" <> sexp <> "\", reason: " <> r
transformChunks :: [IS.Chunk] -> P.Parser ([I.Dec], [I.Section])
transformChunks chunks = (reverse *** reverse) <$> foldM transformChunk ([], []) chunks
transformChunk :: ([I.Dec], [I.Section]) -> IS.Chunk -> P.Parser ([I.Dec], [I.Section])
transformChunk (ds, ss) (IS.ElementNode name rattributes chunks) = do
attributes <- mapM transformAttribute rattributes
(decs, sections) <- transformChunks chunks
return (ds, I.ElementNode name attributes sections decs : ss)
transformChunk (ds, ss) (IS.ElementLeaf name rattributes) = do
attributes <- mapM transformAttribute rattributes
return (ds, I.ElementLeaf name attributes : ss)
transformChunk (ds, ss) (IS.Text text) = return (ds, I.Text text : ss)
transformChunk (ds, ss) (IS.TextRaw text) = return (ds, I.TextRaw text : ss)
transformChunk (ds, ss) (IS.Haskell rhs) =
case parseExp rhs of
Right e -> return (ds, I.Expression e : ss)
Left er ->
case parseDec rhs of
Right d -> return (d : ds, ss)
Left dr -> fail $ concat
[ "Could not parse haskell \"", rhs
, "\", as expression, because: ", er
, "; as declaration, because: ", dr
]
transformAttribute :: I.RAttribute -> P.Parser I.Attribute
transformAttribute (I.AttributeExp rexp) = I.AttributeExp <$> transformExp rexp
transformAttribute (I.Attribute rname rvalue) =
I.Attribute <$> transformName rname <*> transformValue rvalue
where
transformName (I.AttributeNameExp rexp) = I.AttributeNameExp <$> transformExp rexp
transformName (I.AttributeNameText text) = return $ I.AttributeNameText text
transformValue (I.AttributeValueExp rexp) = I.AttributeValueExp <$> transformExp rexp
transformValue (I.AttributeValueText text) = return $ I.AttributeValueText text
parseType :: String -> Either String I.Type
parseType = toEither . HE.parseTypeWithMode parseMode
parseExp :: String -> Either String I.Exp
parseExp = toEither . HE.parseExpWithMode parseMode
parseDec :: String -> Either String I.Dec
parseDec = toEither . HE.parseDeclWithMode parseMode
toEither :: HE.ParseResult a -> Either String a
toEither pr =
case pr of
HE.ParseOk x -> Right x
HE.ParseFailed _ e -> Left e
parseMode :: HE.ParseMode
parseMode = HE.defaultParseMode
{ HE.extensions = HE.TemplateHaskell : HE.QuasiQuotes : HE.haskell2010
}