egison-pattern-src-th-mode-0.2.0.0: src/Language/Egison/Parser/Pattern/Mode/Haskell/TH.hs
-- |
--
-- Module: Language.Egison.Parser.Pattern.Mode.Haskell
-- Description: Parser for Egison pattern expressions to use with Template Haskell
-- Stability: experimental
--
-- A parser for Egison pattern expressions to use with Template Haskell.
module Language.Egison.Parser.Pattern.Mode.Haskell.TH
(
-- * Parsers
Expr
, ExprL
, ParseMode(..)
, parseExpr
, parseExprL
-- * Converting @haskell-src-exts@'s entities
, makeParseMode
, makeFixity
, makeParseFixity
)
where
import Control.Monad.Except ( MonadError )
import qualified Text.PrettyPrint as PP
( render )
import qualified Language.Haskell.TH.Syntax as TH
( Name
, Exp(..)
, NameIs(..)
)
import qualified Language.Haskell.TH.PprLib as TH
( to_HPJ_Doc
, pprName'
)
import qualified Language.Haskell.Meta.Syntax.Translate
as TH
( toExp
, toName
)
import qualified Language.Haskell.Exts.Fixity as Haskell
( Fixity(..) )
import qualified Language.Haskell.Exts.Syntax as Haskell
( Assoc(..) )
import qualified Language.Haskell.Exts.Parser as Haskell
( ParseMode(..)
, ParseResult(..)
, parseExpWithMode
)
import qualified Language.Egison.Syntax.Pattern
as Egison
( Expr )
import qualified Language.Egison.Parser.Pattern
as Egison
( ExprL
, Fixity(..)
, Associativity(..)
, ParseFixity(..)
, ParseMode(..)
)
import Language.Egison.Parser.Pattern ( Precedence(..)
, Parsable(..)
, Errors
)
-- | Type synonym of 'Egison.Expr' to be used with Template Haskell.
type Expr = Egison.Expr TH.Name TH.Name TH.Exp
-- | Type synonym of 'Egison.ExprL' to be used with Template Haskell.
type ExprL = Egison.ExprL TH.Name TH.Name TH.Exp
-- | Parser configuration in @egison-pattern-src-th-mode@.
data ParseMode
= ParseMode {
-- | 'Haskell.ParseMode' from @haskell-src-exts@ for our parsers to base on.
haskellMode :: Haskell.ParseMode
-- | List of fixities to parse infix pattern operators.
-- If @fixities = Just xs@, @xs@ overrides fixities obtained from 'haskellMode'.
-- Otherwise, our parsers use fixities from 'haskellMode'.
, fixities :: Maybe [Egison.ParseFixity TH.Name String]
}
resultToEither :: Haskell.ParseResult a -> Either String a
resultToEither (Haskell.ParseOk a ) = Right a
resultToEither (Haskell.ParseFailed _ e) = Left e
parseNameWithMode :: Haskell.ParseMode -> String -> Either String TH.Name
parseNameWithMode mode content =
case resultToEither . fmap TH.toExp $ Haskell.parseExpWithMode mode content of
Right (TH.VarE name) -> Right name
Right (TH.ConE name) -> Right name
Right e -> Left (show e ++ " is not a variable")
Left err -> Left err
-- | Build 'Egison.Fixity' using 'Haskell.Fixity' from @haskell-src-exts@.
makeFixity :: Haskell.Fixity -> Egison.Fixity TH.Name
makeFixity (Haskell.Fixity assoc prec name) = fixity
where
fixity = Egison.Fixity (makeAssoc assoc) (Precedence prec) $ TH.toName name
makeAssoc (Haskell.AssocRight ()) = Egison.AssocRight
makeAssoc (Haskell.AssocLeft ()) = Egison.AssocLeft
makeAssoc (Haskell.AssocNone ()) = Egison.AssocNone
-- | Build 'Egison.ParseFixity' using 'Egison.Fixity' to parse Haskell-style operators
makeParseFixity :: Egison.Fixity TH.Name -> Egison.ParseFixity TH.Name String
makeParseFixity fixity = Egison.ParseFixity fixity $ makeNameParser symbol
where
Egison.Fixity { Egison.symbol } = fixity
printSym = PP.render . TH.to_HPJ_Doc . TH.pprName' TH.Infix
makeNameParser s input | input == printSym s = Right ()
| otherwise = Left "not an operator name"
-- | Build 'Egison.ParseMode' using 'Haskell.ParseMode' from @haskell-src-exts@.
makeParseMode
:: Haskell.ParseMode -> Egison.ParseMode TH.Name TH.Name TH.Exp String
makeParseMode mode@Haskell.ParseMode { Haskell.parseFilename, Haskell.fixities }
= Egison.ParseMode
{ Egison.filename = parseFilename
, Egison.fixities = maybe [] makeParseFixities fixities
, Egison.blockComment = Just ("{-", "-}")
, Egison.lineComment = Just "--"
, Egison.varNameParser = parseNameWithMode mode
, Egison.nameParser = parseNameWithMode mode
, Egison.valueExprParser = resultToEither
. fmap TH.toExp
. Haskell.parseExpWithMode mode
}
where
makeParseFixities :: [Haskell.Fixity] -> [Egison.ParseFixity TH.Name String]
makeParseFixities = map $ makeParseFixity . makeFixity
instance Parsable Expr String ParseMode where
parseNonGreedyWithLocation ParseMode { haskellMode, fixities } =
parseNonGreedyWithLocation @Expr mode'
where
mode = makeParseMode haskellMode
mode' = case fixities of
Just xs -> mode { Egison.fixities = xs }
Nothing -> mode
-- | Parse 'Expr' using 'ParseMode'.
parseExpr :: MonadError (Errors String) m => ParseMode -> String -> m Expr
parseExpr = parse @Expr
-- | Parse 'Expr' using 'ParseMode' with locations annotated.
parseExprL :: MonadError (Errors String) m => ParseMode -> String -> m ExprL
parseExprL = parseWithLocation @Expr