antiquoter 0.1.0.0 → 0.1.1.0
raw patch · 5 files changed
+186/−57 lines, 5 filesPVP ok
version bump matches the API change (PVP)
API changes (from Hackage documentation)
+ Language.Haskell.AntiQuoter.Base: (<<>>) :: AntiQuoter q -> AntiQuoter q -> AntiQuoter q
+ Language.Haskell.AntiQuoter.Base: noAntiQuoter :: AntiQuoter q
Files
- antiquoter.cabal +9/−7
- src/Language/Haskell/AntiQuoter.hs +2/−0
- src/Language/Haskell/AntiQuoter/Base.hs +127/−18
- src/Language/Haskell/AntiQuoter/Combinators.hs +6/−1
- src/Language/Haskell/AntiQuoter/ExpPat.hs +42/−31
antiquoter.cabal view
@@ -1,17 +1,19 @@ name: antiquoter-version: 0.1.0.0+version: 0.1.1.0 synopsis: Combinator library for quasi- and anti-quoting. description: A combinator library to improve the building of anti-quoters.- Especially aimed at making user extensible antiquoters and- removing copy-and-paste programming from their definition.+ Especially aimed at removing copy-and-paste programming from+ their definition. Other antiquoting related features could be+ included in the future. . The modules are .- * "Language.Haskell.AntiQuoter.Base" basic type aliases for- building antiquoters.+ * "Language.Haskell.AntiQuoter.Base" basic types for building+ antiquoters. It also contains the most detailed example of+ the basic usage. . * "Language.Haskell.AntiQuoter.ExpPat" making antiquoters which- can result in expressions as well as patters. Therefore only+ can antiquote both expressions and patters. Therefore only one antiquoter has to be defined in stead of two. . * "Language.Haskell.AntiQuoter.Combinators" more useful@@ -28,7 +30,7 @@ build-type: Simple cabal-version: >=1.8-tested-with: GHC == 7.4.1, GHC == 7.6.2+tested-with: GHC==7.4.1, GHC==7.6.2 library exposed-modules:
src/Language/Haskell/AntiQuoter.hs view
@@ -1,3 +1,5 @@+{- | Simple reexport of all modules hiding the exports marked as internal.+-} module Language.Haskell.AntiQuoter ( module Language.Haskell.AntiQuoter.Base,
src/Language/Haskell/AntiQuoter/Base.hs view
@@ -1,17 +1,97 @@ {-# LANGUAGE CPP #-} {-# LANGUAGE RankNTypes #-}--- | Base module for `AntiQuoter`s, which transform parsed syntax into--- template-haskell syntax to make `QuasiQuoter`s.+{- | Base module for `AntiQuoter`s, defining some basic type-aliases and and+combinators for antiquoting.++To for examples in the documentation of this library the following data+types defining the untyped lambda calculus syntax:++@+data Expr+ = VarE Var+ | Lam Var Expr+ | App Expr Expr+ | AntiExpr String+ deriving (Typeable, Data)+data Var+ = Var String+ | AntiVar String+ deriving (Typeable, Data)+@++(note: the idea for using lambda calculus comes from the original paper on+quasi-quoting <http://www.eecs.harvard.edu/~mainland/ghc-quasiquoting/mainland07quasiquoting.pdf>)++A simple quasi-quoter without support for antiquoting can be defined by:++@+lExp = QuasiQuoter+ { quoteExp = dataToExpQ (const Nothing) . parseExpr+ , quotePat = dataToPatQ (const Nothing) . parseExpr+ , quoteType = error \"No type quoter\"+ , quoteDec = error \"No declaration quoter\"+ }+parseExpr :: String -> Expr+parseExpr = undefined -- implementation omitted+@++Now to add antiquoting it is needed to treat the AntiExpr and AntiVar+constructors as special and translate them ourselves. This introduces an+@`AntiQuoterPass` e p@, which is a specific translation rule from source syntax+@e@ to template haskell type @p@. In the example this can be used to defined:++@+antiExprE :: AntiQuoterPass Expr Exp+antiExprE (AntiExpr s) = Just . varE $ mkName s+antiExprE _ = Nothing+antiVarE :: AntiQuoterPass Var Exp+antiVarE (AntiVar s) = Just . varE $ mkName s+antiVarE _ = Nothing++antiExprP :: AntiQuoterPass Expr Pat+antiExprP (AntiExpr s) = Just . varP $ mkName s+antiExprP _ = Nothing+antiVarP :: AntiQuoterPass Var Pat+antiVarP (AntiVar s) = Just . varP $ mkName s+antiVarP _ = Nothing+@++Both rules should be used when antiquoting as an exception to the base case+(using the default translation, @const Nothing@). Which can be done using+@(`<>>`)@, creating an `AntiQuoter`. Where an `AntiQuoter` represents a+combination of `AntiQuoterPass`es which can be used to antiquote multiple+datatypes. In the example this would result in++@+lExp = QuasiQuoter+ { quoteExp = dataToExpQ antiE . parseExpr+ , quotePat = dataToPatQ antiP . parseExpr+ , quoteType = error \"No type quoter\"+ , quoteDec = error \"No declaration quoter\"+ }+ where+ antiE :: AntiQuoter Exp+ antiE = antiExprE \<>> antiVarE \<>> const Nothing+ antiP :: AntiQuoter Pat+ antiP = antiExprP \<>> antiVarP \<>> const Nothing+@++Two little improvements could be made, @const Nothing@ could be replaced by+`noAntiQuoter` and the building of the `QuasiQuoter` could be simplified by+using `mkQuasiQuoter`.+-} module Language.Haskell.AntiQuoter.Base( -- * AntiQuoters AntiQuoterPass, AntiQuoter,- mkQuasiQuoter, AQResult,- fromPass, (<<>), (<>>),+ -- ** Using AntiQuoters+ mkQuasiQuoter,+ fromPass,+ noAntiQuoter, (<<>>),+ (<<>), (<>>), -- ** Convenience reexport -- | WARNING: when combining AntiQuoter(Pass)es using `extQ` only the- -- WARNING: when combining AntiQuoter(Pass)es using `extQ` only the -- last (rightmost) pass will be used for any source type. The `<<>` -- and `<>>` don't suffer from this problem. extQ,@@ -24,37 +104,66 @@ import Language.Haskell.TH import Language.Haskell.TH.Quote -infixl 1 <<>+infixl 1 <<>,<<>> infixr 2 <>> --- | Result of an `AntiQuoterPass`+-- | A single antiquotation for a specific source type. Usually @e@ is a type+-- from the parsed language and @q@ is the target type (usually `Pat` or+-- `Exp`). A @Just result@ indicates that the input should be antiquoted into+-- @result@ while @Nothing@ indicates that there is no special antiquotation.+type AntiQuoterPass e q = e -> Maybe (Q q)+-- Note, `AQResult` is not used as that would lead to too unclear code and+-- documentation.++-- | Result of an `AntiQuoterPass` (AntiQuoterPass e q = e -> AQResult q).+-- This type-alias is mostly used for combinators which only provides the+-- result of the antiquotation and the usecase (thus the pattern to match)+-- should be filled in by the user.+--+-- See `AntiQuoterPass` on what @Nothing@ and @Just@ mean. type AQResult q = Maybe (Q q) -- | Wrapper for `AQResult`, needed for the typechecker. newtype WrappedAQResult q = AQRW { unAQRW :: AQResult q } --- | A single quotation pass possibly transforming an @e@ into a @q@.-type AntiQuoterPass e q = e -> Maybe (Q q) --- | An `AntiQuoter` is the combination of several `AntiQuoterPass`es, trying--- each of them in order until one passes.+-- | An `AntiQuoter` is the combination of several `AntiQuoterPass`es, which+-- could have different source types. In the example the+-- @AntiQuoterPass Expr Exp@ and @AntiQuoterPass Var Exp@ were combined into+-- a single @AntiQuoter Exp@, which antiquoted both @Expr@ and @Pat@. type AntiQuoter q = forall e. Typeable e => AntiQuoterPass e q -- | Create an `AntiQuoter` from an single pass. fromPass :: Typeable e => AntiQuoterPass e q -> AntiQuoter q fromPass aqp = mkQ Nothing aqp --- | Combine an existing `AntiQuoter` with an extra pass, where the extra pass--- is tried if the current quoter fails.+-- | An `AnitQuoter` that does no antiquoting by only return Nothing,+--+-- > noAntiQuoter = const Nothing+--+noAntiQuoter :: AntiQuoter q+noAntiQuoter = const Nothing++-- | Create an `AntiQuoter` by combining an `AntiQuoter` and an+-- `AntiQuoterPass`. This is left biased, see (`<<>>`). (<<>) :: Typeable e => AntiQuoter q -> AntiQuoterPass e q -> AntiQuoter q-aq <<> aqp = \e -> aq e `mplus` fromPass aqp e--- | Like `<>>` with flipped arguments, but also trying the extra pass before--- the quoter.+aq <<> aqp = aq <<>> fromPass aqp+-- | Create an `AntiQuoter` by combining an `AntiQuoterPass` and an+-- `AntiQuoter`. This is left biased, see (`<<>>`). (<>>) :: Typeable e => AntiQuoterPass e q -> AntiQuoter q -> AntiQuoter q-aqp <>> aq = \e -> fromPass aqp e `mplus` aq e+aqp <>> aq = fromPass aqp <<>> aq +-- | Combines two `AntiQuoter`s with the same result. It is left biased, thus+-- if the first antiquoter returns @Just result@ that is used, otherwise the+-- second AntiQuoter is tried.+-- Together with `noAntiQuoter` this forms a monoid, but as AntiQuoter is a+-- type synonyme no instance is declared.+(<<>>) :: AntiQuoter q -> AntiQuoter q -> AntiQuoter q+aq1 <<>> aq2 = \e -> aq1 e `mplus` aq2 e+ -- | Create an QuasiQuoter for expressions and patterns from a parser and two--- antiquoters.+-- antiquoters. The quasiquoter from the example could also have been+-- constructed by using @mkQuasiQuoter (return . parse) antiE antiP @. mkQuasiQuoter :: Data a => (String -> Q a) -> AntiQuoter Exp
src/Language/Haskell/AntiQuoter/Combinators.hs view
@@ -1,5 +1,5 @@ {-# LANGUAGE RankNTypes #-}--- | Several combinators for `AntiQuoters`.+-- | Several combinators for `AntiQuoters`, mainly `EP` related. module Language.Haskell.AntiQuoter.Combinators( -- * Lifted constructors. varQ, conQ, litQ, tupQ, listQ,@@ -16,17 +16,22 @@ import Language.Haskell.AntiQuoter.Base import Language.Haskell.AntiQuoter.ExpPat +-- | Generalized `varE`/`varP`. varQ :: EP q => Name -> Q q varQ = return . var +-- | Generalized `conP` or combination of `conE` and `appE`. conQ :: EP q => Name -> [Q q] -> Q q conQ n = fmap (con n) . sequence +-- | Generalized `litE`/`litP`. litQ :: EP q => Lit -> Q q litQ = return . lit tupQ, listQ :: EP q => [Q q] -> Q q+-- | Generalized `tupE`/`tupP`. tupQ = fmap tup . sequence+-- | Generalized `listE`/`listP`. listQ = fmap list . sequence -- | Uses/Binds a variable of the given name.
src/Language/Haskell/AntiQuoter/ExpPat.hs view
@@ -1,38 +1,48 @@--- | Tools for writing one `AntiQuoter` which can be used for both expressions--- and patterns, thereby reducing copy-and-paste programming.------ For example in the original paper on quasi quoting <http://www.eecs.harvard.edu/~mainland/ghc-quasiquoting/mainland07quasiquoting.pdf>--- antiquoting is demonstrated by defining the following antiquoters:------ @--- antiVarE :: Var -> Maybe ExpQ--- antiVarE (AV v ) = Just $ varE $ mkName v--- antiVarE _ = Nothing--- antiVarP :: Var -> Maybe PatQ--- antiVarP (AV v ) = Just $ varP $ mkName v--- antiVarP _ = Nothing--- @------ and a simmilar pair for antiquoting variables. The problem is that the--- definition for the pattern antiquoter is almost a duplicate of the one for--- expressions. This similarity between antiquoting expressions and patterns--- is captured in the `EP` class which can be used to write antiquoters which--- can yield both expressions and patterns. Using the combinators defined on--- top of this class (see "Language.Haskell.AntiQuoter.Combinators") the--- example can be rewritten as------ @--- antiVar :: EP q => Var -> Maybe (Q q) -- equivalent to antiVar :: EPAntiQuoterPass Var--- antiVar (AV v) = Just $ varQ $ mkName v--- antiVar _ = Nothing--- @- {-# LANGUAGE RankNTypes #-}+{- | `Exp` and `Pat` are for most part used in simmilar fashion. Most+AntiQuoter(Pass)es have to be written for both datatypes and their+implementation is more or less identical in structure. To reduce copy-and-paste+programming it would be best if it would only need one AntiQuoter(Pass) that+works on both `Exp` and `Pat`.++This module defines the `EP` typeclass expressing the similarity between `Exp`+and `Pat` and some basic functions to use them with `AntiQuoterPass`es. The+"Language.Haskell.AntiQuoter.Combinators" defines the combinator functions on+top of these functions, which are probably more suitable for users.+++As an example of the problem take the antiquoters in+"Language.Haskell.AntiQuoter.Base" where there are two AntiQuoterPasses for+each source type, for Var they are++@+antiVarE :: AntiQuoterPass Var Exp+antiVarE (AV v ) = Just $ varE $ mkName v+antiVarE _ = Nothing+antiVarP :: AntiQuoterPass Var Pat+antiVarP (AV v ) = Just $ varP $ mkName v+antiVarP _ = Nothing+@++The problem is that the definition for the pattern antiquoter is almost a+duplicate of the one for expressions. This similarity between antiquoting+expressions and patterns is captured in the `EP` class which can be used to+write antiquoters which an yield both expressions and patterns. Using the+combinators defined on top of this class (see+"Language.Haskell.AntiQuoter.Combinators") the example can be rewritten as++@+antiVar :: EP q => AntiQuoterPass Var q -- equivalent to antiVar :: EPAntiQuoterPass Var+antiVar (AV v) = Just $ varQ $ mkName v+antiVar _ = Nothing+@+-} module Language.Haskell.AntiQuoter.ExpPat ( -- * Template syntax class EP(..), EPAntiQuoter, EPAntiQuoterPass, mkEPQuasiQuoter,+ -- ** Low level functions used when the result for `Exp` and `Pat` differs. epPass, epPass', epPass'', epResult, epValue, epPure, @@ -78,12 +88,13 @@ -> QuasiQuoter mkEPQuasiQuoter parse aq = mkQuasiQuoter parse aq aq --- | An `AntiQuoter` that works for `Exp` and `Pat`s.+-- | An `AntiQuoter` that works for `Exp` and `Pat` results. type EPAntiQuoter = forall q. EP q => AntiQuoter q+-- | An `AntiQuoterPass` that works for `Exp` and `Pat` results. type EPAntiQuoterPass e = forall q. EP q => AntiQuoterPass e q -- | Combine two `AntiQuoterPass`es, one for expression context and another for--- pattern context, into a single pass for an expression of patter context.+-- pattern context, into a single pass which can be used in both contexts. epPass :: Typeable e => AntiQuoterPass e Exp -> AntiQuoterPass e Pat -> EPAntiQuoterPass e epPass pe pp = \e -> epResult (pe e) (pp e)