Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
1 changed file
with
88 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,88 @@ | ||
{-# LANGUAGE TemplateHaskell #-} | ||
module Text.TDoc.TH | ||
(node | ||
,nodeChildren | ||
,nodeAttributes | ||
,attribute | ||
,attributes | ||
,tagInstances | ||
,tagInstance | ||
,NodeOpt(..)) | ||
where | ||
|
||
import Data.Char (toLower) | ||
import Text.TDoc.Core (IsNode, IsChildOf, IsAttributeOf, IsAttribute | ||
,IsInline, IsBlock, IsBlockOrInline) | ||
import Language.Haskell.TH | ||
|
||
data NodeOpt = NoTag | Inline | Block | BlockOrInline | ||
deriving (Eq) | ||
type NodeOpts = [NodeOpt] | ||
|
||
mkIs :: Name -> Name -> Dec | ||
mkIs cl ty = InstanceD [] (ConT cl `AppT` ConT ty) [] | ||
|
||
node :: String -> NodeOpts -> [Name] -> [Name] -> Q [Dec] | ||
node nod opts attrs children | ||
= return . concat | ||
$ [ [ DataD [] nodeNm [] [] [] | ||
, mkIs ''IsNode nodeNm | ||
] | ||
, [ mkIs ''IsInline nodeNm | Inline `elem` opts ] | ||
, [ mkIs ''IsBlock nodeNm | Block `elem` opts ] | ||
, [ mkIs ''IsBlockOrInline nodeNm | BlockOrInline `elem` opts | ||
|| Block `elem` opts | ||
|| Inline `elem` opts ] | ||
, [ mkTagClass nodeNm | NoTag `notElem` opts ] | ||
, map (`mkIsAttributeOf` nodeNm) attrs | ||
, map (`mkIsChildOf` nodeNm) children | ||
] | ||
where nodeNm = mkName nod | ||
|
||
lowerFirst :: String -> String | ||
lowerFirst [] = error "Text.TDoc.TH.lowerFirst: []" | ||
lowerFirst (x:xs) = toLower x : xs | ||
|
||
mkTagName :: String -> Name | ||
mkTagName x = mkName $ x ++ "Tag" | ||
|
||
mkTagClass :: Name -> Dec | ||
mkTagClass nm = ClassD [] nmTagTy [PlainTV t] [] | ||
[SigD nmTagFun (VarT t `AppT` ConT nm)] | ||
where nmBase = nameBase nm | ||
nmTagTy = mkTagName nmBase | ||
nmTagFun = mkTagName $ lowerFirst nmBase | ||
t = mkName "t" | ||
|
||
attribute :: Name -> Q [Dec] | ||
attribute attr = return | ||
[ InstanceD [] (ConT ''IsAttribute `AppT` ConT attr) [] | ||
, mkTagClass attr | ||
] | ||
|
||
attributes :: [Name] -> Q [Dec] | ||
attributes = fmap concat . mapM attribute | ||
|
||
tagInstances :: Name -> [Name] -> Q [Dec] | ||
tagInstances tagTy = return . map (tagInstance tagTy) | ||
|
||
tagInstance :: Name -> Name -> Dec | ||
tagInstance tagTy nm = | ||
InstanceD [] (ConT nmTagCon `AppT` ConT tagTy) | ||
[ValD (VarP nmTagFun) (NormalB (ConE nmTagCon)) []] | ||
where | ||
nmBase = nameBase nm | ||
nmTagFun = mkTagName . lowerFirst $ nmBase | ||
nmTagCon = mkTagName nmBase | ||
|
||
mkIsChildOf :: Name -> Name -> Dec | ||
mkIsChildOf child nod = InstanceD [] (ConT ''IsChildOf `AppT` ConT child `AppT` ConT nod) [] | ||
|
||
mkIsAttributeOf :: Name -> Name -> Dec | ||
mkIsAttributeOf attr nod = InstanceD [] (ConT ''IsAttributeOf `AppT` ConT attr `AppT` ConT nod) [] | ||
|
||
nodeChildren :: Name -> [Name] -> Q [Dec] | ||
nodeChildren nod = return . map (`mkIsChildOf` nod) | ||
|
||
nodeAttributes :: Name -> [Name] -> Q [Dec] | ||
nodeAttributes nod = return . map (`mkIsAttributeOf` nod) |