on 965a460 help sign in
14 files changed+359−527
since splitsnapshots
unifiedsplit
.travis.yml+1−2 CHANGELOG.md−10 ghc-802.yaml+1−1 ghc-821.yaml+8 ghc-822.yaml−1 ghc-844.yaml−3 src/Data/THGen/Compat.hs−23 src/Data/THGen/Enum.hs+2−10 src/Data/THGen/XML.hs+334−20 src/Data/THGen/XML/Internal.hs−416 src/Text/XML/DOM/Parser/Internal/Content.hs−28 stack.yaml+5−1 test/TestDefs.hs+6−7 xml-isogen.cabal+2−5
.travis.yml+1−2
@@ -5,8 +5,7 @@ language: generic
55env:
66 - STACK_YAML=stack.yaml
77 - STACK_YAML=ghc-802.yaml
8− - STACK_YAML=ghc-822.yaml
9− - STACK_YAML=ghc-844.yaml
8+ - STACK_YAML=ghc-821.yaml
109
1110cache:
1211 directories:
CHANGELOG.md−10
@@ -1,13 +1,3 @@
1−HEAD
2−------
3−
4−0.3.0
5−------
6−
7−* Add namespaces support
8−* Builds with GHC 8.4
9−* Derive NFData for all generated datatypes
10−
1110.2.1
122------
133
ghc-802.yaml+1−1
@@ -1,4 +1,4 @@
1−resolver: lts-9.21
1+resolver: lts-9.0
22
33packages:
44- .
ghc-821.yamlnew file+8
@@ -0,0 +1,8 @@
1+resolver: nightly-2017-09-14
2+
3+packages:
4+- .
5+
6+extra-deps:
7+- dom-parser-3.0.0
8+- generic-arbitrary-0.1.0
ghc-822.yamldeleted−1
@@ -1 +0,0 @@
1−resolver: lts-11.22
ghc-844.yamldeleted−3
@@ -1,3 +0,0 @@
1−resolver: lts-12.16
2−extra-deps:
3−- dom-parser-3.1.0
src/Data/THGen/Compat.hs−23
@@ -2,8 +2,6 @@
22
33module Data.THGen.Compat
44 ( dataD
5− , newtypeD
6− , nonStrictType
75 , strictType
86 , varStrictType
97 ) where
@@ -23,27 +21,6 @@ dataD name cons derivs = TH.dataD (return []) name [] Nothing cons derivCls
2321dataD name = TH.dataD (return []) name []
2422#endif
2523
26−newtypeD :: TH.Name -> TH.ConQ -> [TH.Name] -> TH.DecQ
27−#if MIN_VERSION_template_haskell(2,12,0)
28−newtypeD name cons derivs = TH.newtypeD (return []) name [] Nothing cons [derivCls]
29− where
30− derivCls = TH.derivClause Nothing $ fmap TH.conT derivs
31−#elif MIN_VERSION_template_haskell(2,11,0)
32−newtypeD name cons derivs = TH.newtypeD (return []) name [] Nothing cons derivCls
33− where
34− derivCls = traverse TH.conT derivs
35−#else
36−newtypeD name = TH.newtypeD (return []) name []
37−#endif
38−
39−#if MIN_VERSION_template_haskell(2,11,0)
40−nonStrictType :: TH.TypeQ -> TH.BangTypeQ
41−nonStrictType = TH.bangType (TH.bang TH.noSourceUnpackedness TH.noSourceStrictness)
42−#else
43−nonStrictType :: TH.TypeQ -> TH.StrictTypeQ
44−nonStrictType = TH.strictType TH.notStrict
45−#endif
46−
4724#if MIN_VERSION_template_haskell(2,11,0)
4825strictType :: TH.TypeQ -> TH.BangTypeQ
4926strictType = TH.bangType (TH.bang TH.noSourceUnpackedness TH.sourceStrict)
src/Data/THGen/Enum.hs+2−10
@@ -25,17 +25,14 @@ module Data.THGen.Enum
2525 ) where
2626
2727import Control.Applicative
28−import Control.DeepSeq
2928import Control.Lens (over, _head, (<&>))
3029import Control.Monad
3130import qualified Data.Char as C
3231import Data.THGen.Compat
33−import GHC.Generics (Generic)
3432import qualified Language.Haskell.TH as TH
3533import qualified Test.QuickCheck as QC
3634import qualified Text.Read as R
3735
38−
3936data Exhaustiveness = Exhaustive | NonExhaustive
4037 deriving (Eq, Ord, Show)
4138
@@ -82,7 +79,7 @@ enumGenerate (EnumDesc exh strName strVals) = do
8279 dataD
8380 name
8481 (constrs ++ unknownConstr)
85− ([''Eq, ''Ord, ''Generic] ++ if (exh == Exhaustive) then [''Enum, ''Bounded] else [])
82+ ([''Eq, ''Ord] ++ if (exh == Exhaustive) then [''Enum, ''Bounded] else [])
8683 showInstDecl <- do
8784 unknownMatch <- case exh of
8885 Exhaustive -> return []
@@ -129,9 +126,4 @@ enumGenerate (EnumDesc exh strName strVals) = do
129126 (return [])
130127 [t|QC.Arbitrary $(TH.conT name)|]
131128 [funSimple 'QC.arbitrary arbExpr]
132− nfDataInst <- do
133− TH.instanceD
134− (return [])
135− [t|NFData $(TH.conT name)|]
136− []
137− return [dataDecl, readInstDecl, showInstDecl, arbInstance, nfDataInst]
129+ return [dataDecl, readInstDecl, showInstDecl, arbInstance]
src/Data/THGen/XML.hs+334−20
@@ -1,4 +1,5 @@
1−{-# LANGUAGE NoImplicitPrelude #-}
1+{-# LANGUAGE UndecidableInstances #-}
2+{-# LANGUAGE FunctionalDependencies #-}
23
34{- |
45
@@ -22,7 +23,7 @@ wrapper around the field type:
2223
2324Example 1.
2425
25−> "Color" =:= enum Both
26+> "Color" =:= enum
2627> & "R"
2728> & "G"
2829> & "B"
@@ -40,7 +41,7 @@ to be either @R@, @G@ or @B@.
4041
4142Example 2.
4243
43−> "Message" =:= record Both
44+> "Message" =:= record
4445> ! "author"
4546> + "recipient"
4647> ? "message" [t|Text|]
@@ -67,10 +68,10 @@ enumeration is exhaustive (contains only the listed constructors) or
6768non-exhaustive (also contains a constructor for unknown values; this
6869is the default):
6970
70−> "Enum1" Exhaustive =:= enum Generator
71+> "Enum1" Exhaustive =:= enum
7172> ...
7273
73−> "Enum2" NonExhaustive =:= enum Generator
74+> "Enum2" NonExhaustive =:= enum
7475> ...
7576
7677To define a record, use the `record` function followed by the name of
@@ -78,7 +79,7 @@ the data type to be generated. The prefix for the record fields is
7879inferred automatically by taking all of the uppercase letters in the
7980name. You can override it manually like so:
8081
81−> "Reference" "ref" =:= record Parser
82+> "Reference" "ref" =:= record
8283> ...
8384
8485To describe a record field you must supply its name as it appears
@@ -93,14 +94,10 @@ The type of the field is inferred automatically from its name, so
9394if the field is called @"author"@ its type will be @Author@. You can
9495override the type by specifying it in quasiquotes like so:
9596
96−> "Message" =:= record Both
97−> ! "author" [t|Person|].
97+> "Message" =:= record
98+> ! "author" [t|Person|]
9899> ...
99100
100−Both @record@ and @enum@ take a @GenType@ argument which tells it to generate
101−generator, parser or both. It's being used to optimize compilation time by
102−skipping the unnecessary TH instance generation.
103−
104101-}
105102
106103
@@ -108,7 +105,6 @@ module Data.THGen.XML
108105 ( Exhaustiveness(..)
109106 , PrefixName(..)
110107 , ExhaustivenessName(..)
111− , GenType(..)
112108 , record
113109 , enum
114110 , (!)
@@ -118,15 +114,333 @@ module Data.THGen.XML
118114 , (!%)
119115 , (?%)
120116 , (&)
121− , (^)
122117 , (=:=)
123118 -- Re-exports
124− , Text
125− , P.Int
126− , P.Integer
119+ , T.Text
120+ , Int
121+ , Integer
127122 ) where
128123
124+import Control.DeepSeq
125+import Control.Lens hiding (repeated, enum, (&))
126+import Control.Lens.Internal.FieldTH (makeFieldOpticsForDec)
127+import qualified Data.Char as C
128+import Data.List.NonEmpty (NonEmpty)
129+import Data.Maybe (maybeToList, mapMaybe)
130+import Data.String
131+import Data.THGen.Compat
129132import Data.THGen.Enum
130−import Data.THGen.XML.Internal
131−import Data.Text
132−import qualified Prelude as P
133+import qualified Data.Text as T
134+import GHC.Generics (Generic)
135+import qualified Language.Haskell.TH as TH
136+import Prelude hiding ((+), (*))
137+import qualified Text.XML as X
138+import Text.XML.DOM.Parser
139+import Text.XML.ParentAttributes
140+import qualified Text.XML.Writer as XW
141+
142+data XmlFieldPlural
143+ = XmlFieldPluralMandatory -- Occurs exactly 1 time (Identity)
144+ | XmlFieldPluralOptional -- Occurs 0 or 1 times (Maybe)
145+ | XmlFieldPluralRepeated -- Occurs 0 or more times (List)
146+ | XmlFieldPluralMultiplied -- Occurs 1 or more times (NonEmpty)
147+
148+data XmlAttributePlural
149+ = XmlAttributePluralMandatory -- Occurs exactly 1 time (Identity)
150+ | XmlAttributePluralOptional -- Occurs 0 or 1 times (Maybe)
151+
152+data PrefixName = PrefixName String String
153+
154+data IsoXmlDescPreField = IsoXmlDescPreField String TH.TypeQ
155+
156+data IsoXmlDescPreAttribute = IsoXmlDescPreAttribute String TH.TypeQ
157+
158+data IsoXmlDescField = IsoXmlDescField XmlFieldPlural String TH.TypeQ
159+
160+data IsoXmlDescAttribute = IsoXmlDescAttribute XmlAttributePlural String TH.TypeQ
161+
162+data IsoXmlDescRecordPart
163+ = IsoXmlDescRecordField IsoXmlDescField
164+ | IsoXmlDescRecordAttribute IsoXmlDescAttribute
165+
166+newtype IsoXmlDescRecord = IsoXmlDescRecord [IsoXmlDescRecordPart]
167+
168+makePrisms ''IsoXmlDescRecord
169+
170+data ExhaustivenessName = ExhaustivenessName String Exhaustiveness
171+
172+newtype IsoXmlDescEnumCon
173+ = IsoXmlDescEnumCon { unIsoXmlDescEnumCon :: String }
174+
175+instance IsString IsoXmlDescEnumCon where
176+ fromString = IsoXmlDescEnumCon
177+
178+data IsoXmlDescEnum = IsoXmlDescEnum [IsoXmlDescEnumCon]
179+
180+makePrisms ''IsoXmlDescEnum
181+
182+appendField
183+ :: XmlFieldPlural
184+ -> IsoXmlDescRecord
185+ -> IsoXmlDescPreField
186+ -> IsoXmlDescRecord
187+appendField plural xrec (IsoXmlDescPreField name ty) =
188+ let xfield = IsoXmlDescRecordField $ IsoXmlDescField plural name ty
189+ in over _IsoXmlDescRecord (xfield:) xrec
190+
191+appendAttribute
192+ :: XmlAttributePlural
193+ -> IsoXmlDescRecord
194+ -> IsoXmlDescPreAttribute
195+ -> IsoXmlDescRecord
196+appendAttribute plural xrec (IsoXmlDescPreAttribute name ty) =
197+ let xattribute = IsoXmlDescRecordAttribute $ IsoXmlDescAttribute plural name ty
198+ in over _IsoXmlDescRecord (xattribute:) xrec
199+
200+(!), (?), (*), (+) :: IsoXmlDescRecord -> IsoXmlDescPreField -> IsoXmlDescRecord
201+(!) = appendField XmlFieldPluralMandatory
202+(?) = appendField XmlFieldPluralOptional
203+(*) = appendField XmlFieldPluralRepeated
204+(+) = appendField XmlFieldPluralMultiplied
205+
206+(!%), (?%) :: IsoXmlDescRecord -> IsoXmlDescPreAttribute -> IsoXmlDescRecord
207+(!%) = appendAttribute XmlAttributePluralMandatory
208+(?%) = appendAttribute XmlAttributePluralOptional
209+
210+infixl 2 !
211+infixl 2 ?
212+infixl 2 *
213+infixl 2 +
214+infixl 2 !%
215+infixl 2 ?%
216+
217+appendEnumCon :: IsoXmlDescEnum -> IsoXmlDescEnumCon -> IsoXmlDescEnum
218+appendEnumCon xenum xenumcon =
219+ over _IsoXmlDescEnum (xenumcon:) xenum
220+
221+(&) :: IsoXmlDescEnum -> IsoXmlDescEnumCon -> IsoXmlDescEnum
222+(&) = appendEnumCon
223+
224+infixl 2 &
225+
226+class Description name desc | desc -> name where
227+ (=:=) :: name -> desc -> TH.DecsQ
228+
229+infix 0 =:=
230+
231+instance Description PrefixName IsoXmlDescRecord where
232+ prefixName =:= descRecord =
233+ let descRecordParts = descRecord ^. _IsoXmlDescRecord
234+ in isoXmlGenerateRecord prefixName (reverse descRecordParts)
235+
236+record :: IsoXmlDescRecord
237+record = IsoXmlDescRecord []
238+
239+enum :: IsoXmlDescEnum
240+enum = IsoXmlDescEnum []
241+
242+instance Description ExhaustivenessName IsoXmlDescEnum where
243+ exhaustivenessName =:= descEnum =
244+ let descEnumCons = descEnum ^. _IsoXmlDescEnum
245+ in isoXmlGenerateEnum exhaustivenessName (reverse descEnumCons)
246+
247+instance IsString (TH.TypeQ -> IsoXmlDescPreField) where
248+ fromString = IsoXmlDescPreField
249+
250+instance IsString IsoXmlDescPreField where
251+ fromString name = IsoXmlDescPreField name ty
252+ where
253+ ty = (TH.conT . TH.mkName) ("Xml" ++ over _head C.toUpper name)
254+
255+instance IsString (TH.TypeQ -> IsoXmlDescPreAttribute) where
256+ fromString = IsoXmlDescPreAttribute
257+
258+instance IsString IsoXmlDescPreAttribute where
259+ fromString name = IsoXmlDescPreAttribute name ty
260+ where
261+ ty = (TH.conT . TH.mkName) ("Xml" ++ over _head C.toUpper name)
262+
263+instance s ~ String => IsString (s -> PrefixName) where
264+ fromString = PrefixName
265+
266+instance IsString PrefixName where
267+ fromString strName = PrefixName strName (makeNamePrefix strName)
268+
269+instance e ~ Exhaustiveness => IsString (e -> ExhaustivenessName) where
270+ fromString = ExhaustivenessName
271+
272+instance IsString ExhaustivenessName where
273+ fromString strName = ExhaustivenessName strName NonExhaustive
274+
275+makeNamePrefix :: String -> String
276+makeNamePrefix = map C.toLower . filter C.isUpper
277+
278+funSimple :: TH.Name -> TH.ExpQ -> TH.DecQ
279+funSimple name body = TH.funD name [ TH.clause [] (TH.normalB body) [] ]
280+
281+isoXmlGenerateEnum
282+ :: ExhaustivenessName -> [IsoXmlDescEnumCon] -> TH.DecsQ
283+isoXmlGenerateEnum (ExhaustivenessName strName' exh) enumCons = do
284+ let
285+ strName = "Xml" ++ strName'
286+ strVals = map unIsoXmlDescEnumCon enumCons
287+ enumDesc = EnumDesc exh strName strVals
288+ name = TH.mkName strName
289+ enumDecls <- enumGenerate enumDesc
290+ toXmlInst <- do
291+ TH.instanceD
292+ (return [])
293+ [t|XW.ToXML $(TH.conT name)|]
294+ [funSimple 'XW.toXML [e|XW.toXML . T.pack . show|]]
295+ toXmlAttributeInst <- do
296+ TH.instanceD
297+ (return [])
298+ [t|ToXmlAttribute $(TH.conT name)|]
299+ [funSimple 'toXmlAttribute [e|T.pack . show|]]
300+ fromDomInst <- do
301+ TH.instanceD
302+ (return [])
303+ [t|FromDom $(TH.conT name)|]
304+ [funSimple 'fromDom [e|parseContent readContent|]]
305+ fromAttributeInst <- do
306+ TH.instanceD
307+ (return [])
308+ [t|FromAttribute $(TH.conT name)|]
309+ [funSimple 'fromAttribute [e|readContent|]]
310+ return $ enumDecls ++ [toXmlInst, toXmlAttributeInst,
311+ fromDomInst, fromAttributeInst]
312+
313+isoXmlGenerateRecord :: PrefixName -> [IsoXmlDescRecordPart] -> TH.DecsQ
314+isoXmlGenerateRecord (PrefixName strName' strPrefix') descRecordParts = do
315+ let
316+ strName = "Xml" ++ strName'
317+ strPrefix = "x" ++ strPrefix'
318+ name = TH.mkName strName
319+ fieldName str = "_" ++ strPrefix ++ over _head C.toUpper str
320+ dataDecl <- do
321+ let
322+ fields = do
323+ descRecordPart <- descRecordParts
324+ return $ case descRecordPart of
325+ IsoXmlDescRecordField descField ->
326+ let
327+ IsoXmlDescField fieldPlural fieldStrName fieldType = descField
328+ fName = TH.mkName (fieldName fieldStrName)
329+ fType = case fieldPlural of
330+ XmlFieldPluralMandatory -> fieldType
331+ XmlFieldPluralOptional -> [t| Maybe $fieldType |]
332+ XmlFieldPluralRepeated -> [t| [$fieldType] |]
333+ XmlFieldPluralMultiplied -> [t| NonEmpty $fieldType |]
334+ in
335+ varStrictType fName (strictType fType)
336+ IsoXmlDescRecordAttribute descAttribute ->
337+ let
338+ IsoXmlDescAttribute
339+ attributePlural attributeStrName attributeType = descAttribute
340+ fName = TH.mkName (fieldName attributeStrName)
341+ fType = case attributePlural of
342+ XmlAttributePluralMandatory -> attributeType
343+ XmlAttributePluralOptional -> [t| Maybe $attributeType |]
344+ in
345+ varStrictType fName (strictType fType)
346+ dataD
347+ name
348+ [TH.recC name fields]
349+ [''Eq, ''Show, ''Generic]
350+ lensDecls <- makeFieldOpticsForDec lensRules dataDecl
351+ nfDataInst <- do
352+ TH.instanceD
353+ (return [])
354+ [t|NFData $(TH.conT name)|]
355+ [ ]
356+ fromDomInst <- do
357+ let
358+ exprHeader = [e|pure $(TH.conE name)|]
359+ exprRecordParts = do
360+ descRecordPart <- descRecordParts
361+ return $ case descRecordPart of
362+ IsoXmlDescRecordField descField ->
363+ let
364+ IsoXmlDescField fieldPlural fieldStrName _ = descField
365+ exprFieldStrName = TH.litE (TH.stringL fieldStrName)
366+ fieldParse = case fieldPlural of
367+ XmlFieldPluralMandatory -> [e|inElem|]
368+ _ -> [e|inElemTrav|]
369+ in
370+ [e|$fieldParse $exprFieldStrName fromDom|]
371+ IsoXmlDescRecordAttribute descAttribute ->
372+ let
373+ IsoXmlDescAttribute attributePlural attributeStrName _ = descAttribute
374+ exprAttributeStrName = TH.litE (TH.stringL attributeStrName)
375+ attributeParse = case attributePlural of
376+ XmlAttributePluralMandatory -> [e|parseAttribute|]
377+ XmlAttributePluralOptional -> [e|parseAttributeMaybe|]
378+ in
379+ [e|$attributeParse $exprAttributeStrName fromAttribute|]
380+ fromDomExpr = foldl (\e fe -> [e| $e <*> $fe |]) exprHeader exprRecordParts
381+ TH.instanceD
382+ (return [])
383+ [t|FromDom $(TH.conT name)|]
384+ [ funSimple 'fromDom fromDomExpr ]
385+
386+ toXmlInst <- do
387+ objName <- TH.newName strPrefix
388+ let
389+ exprFields = do
390+ descRecordPart <- descRecordParts
391+ IsoXmlDescField fieldPlural fieldStrName _ <-
392+ maybeToList $ case descRecordPart of
393+ IsoXmlDescRecordField descField -> Just descField
394+ _ -> Nothing
395+ let
396+ fName = TH.mkName (fieldName fieldStrName)
397+ exprFieldStrName = TH.litE (TH.stringL fieldStrName)
398+ exprForField = case fieldPlural of
399+ XmlFieldPluralMandatory -> [e|id|]
400+ _ -> [e|traverse|]
401+ exprFieldValue = [e|$(TH.varE fName) $(TH.varE objName)|]
402+ exprFieldRender =
403+ [e|(\a -> XW.elementA $exprFieldStrName (toXmlParentAttributes a) a)|]
404+ return [e|$exprForField $exprFieldRender $exprFieldValue|]
405+ toXmlExpr
406+ = TH.lamE [if null exprFields then TH.wildP else TH.varP objName]
407+ $ foldr (\fe e -> [e|$fe *> $e|]) [e|return ()|] exprFields
408+ TH.instanceD
409+ (return [])
410+ [t|XW.ToXML $(TH.conT name)|]
411+ [funSimple 'XW.toXML toXmlExpr]
412+
413+ toXmlParentAttributesInst <- do
414+ objName <- TH.newName strPrefix
415+ let
416+ exprAttributes = do
417+ descRecordPart <- descRecordParts
418+ IsoXmlDescAttribute attributePlural attributeStrName _ <-
419+ maybeToList $ case descRecordPart of
420+ IsoXmlDescRecordAttribute descAttribute -> Just descAttribute
421+ _ -> Nothing
422+ let
423+ fName = TH.mkName (fieldName attributeStrName)
424+ exprAttrStrName = TH.litE (TH.stringL attributeStrName)
425+ exprAttrValue = [e|$(TH.varE fName) $(TH.varE objName)|]
426+ exprAttrWrap = case attributePlural of
427+ XmlAttributePluralMandatory -> [e|Just . toXmlAttribute|]
428+ XmlAttributePluralOptional -> [e|fmap toXmlAttribute|]
429+ return [e|($exprAttrStrName, $exprAttrWrap $exprAttrValue)|]
430+ toXmlParentAttributesExpr
431+ = TH.lamE [if null exprAttributes then TH.wildP else TH.varP objName]
432+ $ [e|mapMaybe distribPair $(TH.listE exprAttributes)|]
433+#if __GLASGOW_HASKELL__ < 800
434+ TH.instanceD
435+#else
436+ TH.instanceWithOverlapD (Just TH.Overlapping)
437+#endif
438+ (return [])
439+ [t|ToXmlParentAttributes $(TH.conT name)|]
440+ [funSimple 'toXmlParentAttributes toXmlParentAttributesExpr]
441+
442+ return $ [dataDecl] ++ lensDecls ++
443+ [fromDomInst, toXmlInst, toXmlParentAttributesInst]
444+
445+distribPair :: Functor f => (a, f b) -> f (a, b)
446+distribPair (a, fb) = (a,) <$> fb
src/Data/THGen/XML/Internal.hsdeleted−416
@@ -1,416 +0,0 @@
1−{-# LANGUAGE CPP #-}
2−{-# LANGUAGE UndecidableInstances #-}
3−{-# LANGUAGE FunctionalDependencies #-}
4−
5−module Data.THGen.XML.Internal where
6−
7−import Control.DeepSeq
8−import Control.Lens hiding (repeated, enum, (&))
9−import Control.Lens.Internal.FieldTH (makeFieldOpticsForDec)
10−import qualified Data.Char as C
11−import Data.List.NonEmpty (NonEmpty)
12−import Data.Maybe (maybeToList, mapMaybe)
13−import Data.String
14−import Data.THGen.Compat as THC
15−import Data.THGen.Enum
16−import qualified Data.Text as T
17−import GHC.Generics (Generic)
18−import Language.Haskell.TH as TH
19−import Prelude hiding ((+), (*), (^))
20−import qualified Text.XML as X
21−import Text.XML.DOM.Parser hiding (parseContent)
22−import Text.XML.DOM.Parser.Internal.Content
23−import Text.XML.ParentAttributes
24−import qualified Text.XML.Writer as XW
25−
26−
27−data GenType = Parser | Generator | Both
28−
29−data XmlFieldPlural
30− = XmlFieldPluralMandatory -- Occurs exactly 1 time (Identity)
31− | XmlFieldPluralOptional -- Occurs 0 or 1 times (Maybe)
32− | XmlFieldPluralRepeated -- Occurs 0 or more times (List)
33− | XmlFieldPluralMultiplied -- Occurs 1 or more times (NonEmpty)
34−
35−data XmlAttributePlural
36− = XmlAttributePluralMandatory -- Occurs exactly 1 time (Identity)
37− | XmlAttributePluralOptional -- Occurs 0 or 1 times (Maybe)
38−
39−data PrefixName = PrefixName String String
40−
41−data IsoXmlDescPreField = IsoXmlDescPreField String TH.TypeQ
42−
43−data IsoXmlDescPreAttribute = IsoXmlDescPreAttribute String TH.TypeQ
44−
45−data IsoXmlDescPreContent = IsoXmlDescPreContent String TH.TypeQ
46−
47−data IsoXmlDescField = IsoXmlDescField XmlFieldPlural String TH.TypeQ
48−
49−data IsoXmlDescAttribute = IsoXmlDescAttribute XmlAttributePlural String TH.TypeQ
50−
51−data IsoXmlDescContent = IsoXmlDescContent String TH.TypeQ
52−
53−data IsoXmlDescRecordPart
54− = IsoXmlDescRecordField IsoXmlDescField
55− | IsoXmlDescRecordAttribute IsoXmlDescAttribute
56− | IsoXmlDescRecordContent IsoXmlDescContent
57−
58−data IsoXmlDescRecord = IsoXmlDescRecord GenType [IsoXmlDescRecordPart]
59−
60−makePrisms ''IsoXmlDescRecord
61−
62−data ExhaustivenessName = ExhaustivenessName String Exhaustiveness
63−
64−newtype IsoXmlDescEnumCon
65− = IsoXmlDescEnumCon { unIsoXmlDescEnumCon :: String }
66−
67−instance IsString IsoXmlDescEnumCon where
68− fromString = IsoXmlDescEnumCon
69−
70−data IsoXmlDescEnum = IsoXmlDescEnum GenType [IsoXmlDescEnumCon]
71−
72−makePrisms ''IsoXmlDescEnum
73−
74−appendField
75− :: XmlFieldPlural
76− -> IsoXmlDescRecord
77− -> IsoXmlDescPreField
78− -> IsoXmlDescRecord
79−appendField plural (IsoXmlDescRecord genType fields) (IsoXmlDescPreField name ty) =
80− let xfield = IsoXmlDescRecordField $ IsoXmlDescField plural name ty
81− in IsoXmlDescRecord genType (xfield:fields)
82−
83−appendAttribute
84− :: XmlAttributePlural
85− -> IsoXmlDescRecord
86− -> IsoXmlDescPreAttribute
87− -> IsoXmlDescRecord
88−appendAttribute plural (IsoXmlDescRecord genType fields) (IsoXmlDescPreAttribute name ty) =
89− let xattribute = IsoXmlDescRecordAttribute $ IsoXmlDescAttribute plural name ty
90− in IsoXmlDescRecord genType (xattribute:fields)
91−
92−appendContent
93− :: IsoXmlDescRecord
94− -> IsoXmlDescPreContent
95− -> IsoXmlDescRecord
96−appendContent (IsoXmlDescRecord genType fields) (IsoXmlDescPreContent name ty) =
97− let xcontent = IsoXmlDescRecordContent $ IsoXmlDescContent name ty
98− in IsoXmlDescRecord genType (xcontent:fields)
99−
100−(!), (?), (*), (+) :: IsoXmlDescRecord -> IsoXmlDescPreField -> IsoXmlDescRecord
101−(!) = appendField XmlFieldPluralMandatory
102−(?) = appendField XmlFieldPluralOptional
103−(*) = appendField XmlFieldPluralRepeated
104−(+) = appendField XmlFieldPluralMultiplied
105−
106−(!%), (?%) :: IsoXmlDescRecord -> IsoXmlDescPreAttribute -> IsoXmlDescRecord
107−(!%) = appendAttribute XmlAttributePluralMandatory
108−(?%) = appendAttribute XmlAttributePluralOptional
109−
110−(^) :: IsoXmlDescRecord -> IsoXmlDescPreContent -> IsoXmlDescRecord
111−(^) = appendContent
112−
113−infixl 2 !
114−infixl 2 ?
115−infixl 2 *
116−infixl 2 +
117−infixl 2 !%
118−infixl 2 ?%
119−infixl 2 ^
120−
121−appendEnumCon :: IsoXmlDescEnum -> IsoXmlDescEnumCon -> IsoXmlDescEnum
122−appendEnumCon (IsoXmlDescEnum genType enumCons) xenumcon =
123− IsoXmlDescEnum genType (xenumcon:enumCons)
124−
125−(&) :: IsoXmlDescEnum -> IsoXmlDescEnumCon -> IsoXmlDescEnum
126−(&) = appendEnumCon
127−
128−infixl 2 &
129−
130−class Description name desc | desc -> name where
131− (=:=) :: name -> desc -> TH.DecsQ
132−
133−infix 0 =:=
134−
135−instance Description PrefixName IsoXmlDescRecord where
136− prefixName =:= (IsoXmlDescRecord genType descRecordParts) =
137− isoXmlGenerateDatatype genType prefixName (reverse descRecordParts)
138−
139−record :: GenType -> IsoXmlDescRecord
140−record gt = IsoXmlDescRecord gt []
141−
142−enum :: GenType -> IsoXmlDescEnum
143−enum gt = IsoXmlDescEnum gt []
144−
145−instance Description ExhaustivenessName IsoXmlDescEnum where
146− exhaustivenessName =:= (IsoXmlDescEnum genType descEnumCons) =
147− isoXmlGenerateEnum genType exhaustivenessName (reverse descEnumCons)
148−
149−instance IsString (TH.TypeQ -> IsoXmlDescPreField) where
150− fromString = IsoXmlDescPreField
151−
152−instance IsString IsoXmlDescPreField where
153− fromString name = IsoXmlDescPreField name ty
154− where
155− ty = (TH.conT . TH.mkName) ("Xml" ++ over _head C.toUpper (xmlLocalName name))
156−
157−instance IsString (TH.TypeQ -> IsoXmlDescPreAttribute) where
158− fromString = IsoXmlDescPreAttribute
159−
160−instance IsString (TH.TypeQ -> IsoXmlDescPreContent) where
161− fromString = IsoXmlDescPreContent
162−
163−instance IsString IsoXmlDescPreAttribute where
164− fromString name = IsoXmlDescPreAttribute name ty
165− where
166− ty = (TH.conT . TH.mkName) ("Xml" ++ over _head C.toUpper name)
167−
168−instance IsString IsoXmlDescPreContent where
169− fromString name = IsoXmlDescPreContent name ty
170− where
171− ty = (TH.conT . TH.mkName) ("Xml" ++ over _head C.toUpper name)
172−
173−instance s ~ String => IsString (s -> PrefixName) where
174− fromString = PrefixName
175−
176−instance IsString PrefixName where
177− fromString strName = PrefixName strName (makeNamePrefix strName)
178−
179−instance e ~ Exhaustiveness => IsString (e -> ExhaustivenessName) where
180− fromString = ExhaustivenessName
181−
182−instance IsString ExhaustivenessName where
183− fromString strName = ExhaustivenessName strName NonExhaustive
184−
185−makeNamePrefix :: String -> String
186−makeNamePrefix = map C.toLower . filter C.isUpper
187−
188−funSimple :: TH.Name -> TH.ExpQ -> TH.DecQ
189−funSimple name body = TH.funD name [ TH.clause [] (TH.normalB body) [] ]
190−
191−isoXmlGenerateEnum
192− :: GenType
193− -> ExhaustivenessName
194− -> [IsoXmlDescEnumCon]
195− -> TH.DecsQ
196−isoXmlGenerateEnum genType (ExhaustivenessName strName' exh) enumCons = do
197− let
198− strName = "Xml" ++ strName'
199− strVals = map unIsoXmlDescEnumCon enumCons
200− enumDesc = EnumDesc exh strName strVals
201− name = TH.mkName strName
202− enumDecls <- enumGenerate enumDesc
203− let
204− genToXmlInst = do
205− TH.instanceD
206− (return [])
207− [t|XW.ToXML $(TH.conT name)|]
208− [funSimple 'XW.toXML [e|XW.toXML . T.pack . show|]]
209− genToXmlAttributeInst = do
210− TH.instanceD
211− (return [])
212− [t|ToXmlAttribute $(TH.conT name)|]
213− [funSimple 'toXmlAttribute [e|T.pack . show|]]
214− genFromDomInst = do
215− TH.instanceD
216− (return [])
217− [t|FromDom $(TH.conT name)|]
218− [funSimple 'fromDom [e|parseContent readContent|]]
219− genFromAttributeInst = do
220− TH.instanceD
221− (return [])
222− [t|FromAttribute $(TH.conT name)|]
223− [funSimple 'fromAttribute [e|readContent|]]
224− case genType of
225− Generator -> do
226− toXmlInst <- genToXmlInst
227− toXmlAttributeInst <- genToXmlAttributeInst
228− return $ enumDecls ++ [toXmlInst, toXmlAttributeInst]
229− Parser -> do
230− fromDomInst <- genFromDomInst
231− fromAttributeInst <- genFromAttributeInst
232− return $ enumDecls ++ [fromDomInst, fromAttributeInst]
233− Both -> do
234− toXmlInst <- genToXmlInst
235− toXmlAttributeInst <- genToXmlAttributeInst
236− fromDomInst <- genFromDomInst
237− fromAttributeInst <- genFromAttributeInst
238− return $ enumDecls ++ [toXmlInst, toXmlAttributeInst,
239− fromDomInst, fromAttributeInst]
240−
241−isoXmlGenerateDatatype :: GenType -> PrefixName -> [IsoXmlDescRecordPart] -> TH.DecsQ
242−isoXmlGenerateDatatype genType (PrefixName strName' strPrefix') descRecordParts = do
243− let
244− isNewtype = length descRecordParts == 1
245− strName = "Xml" ++ strName'
246− strPrefix = "x" ++ strPrefix'
247− name = TH.mkName strName
248− fieldName str = "_" ++ strPrefix ++ over _head C.toUpper str
249− termDecl <- do
250− let
251− fields = do
252− descRecordPart <- descRecordParts
253− return $ case descRecordPart of
254− IsoXmlDescRecordField descField ->
255− let
256− IsoXmlDescField fieldPlural rawName fieldType = descField
257− fieldStrName = xmlLocalName rawName
258− fName = TH.mkName (fieldName fieldStrName)
259− fType = case fieldPlural of
260− XmlFieldPluralMandatory -> fieldType
261− XmlFieldPluralOptional -> [t| Maybe $fieldType |]
262− XmlFieldPluralRepeated -> [t| [$fieldType] |]
263− XmlFieldPluralMultiplied -> [t| NonEmpty $fieldType |]
264− in if isNewtype
265− then THC.varStrictType fName (THC.nonStrictType fType)
266− else THC.varStrictType fName (THC.strictType fType)
267− IsoXmlDescRecordAttribute descAttribute ->
268− let
269− IsoXmlDescAttribute
270− attributePlural attributeStrName attributeType = descAttribute
271− fName = TH.mkName (fieldName attributeStrName)
272− fType = case attributePlural of
273− XmlAttributePluralMandatory -> attributeType
274− XmlAttributePluralOptional -> [t| Maybe $attributeType |]
275− in if isNewtype
276− then THC.varStrictType fName (THC.nonStrictType fType)
277− else THC.varStrictType fName (THC.strictType fType)
278− IsoXmlDescRecordContent descContent ->
279− let
280− IsoXmlDescContent contentStrName contentType = descContent
281− fName = TH.mkName (fieldName contentStrName)
282− fType = contentType
283− in if isNewtype
284− then THC.varStrictType fName (THC.nonStrictType fType)
285− else THC.varStrictType fName (THC.strictType fType)
286− if isNewtype
287− -- generate a newtype instead to do less allocations later
288− then THC.newtypeD name (TH.recC name fields) [''Eq, ''Show, ''Generic]
289− else THC.dataD name [TH.recC name fields] [''Eq, ''Show, ''Generic]
290− lensDecls <- makeFieldOpticsForDec lensRules termDecl
291− nfDataInst <- do
292− TH.instanceD
293− (return [])
294− [t|NFData $(TH.conT name)|]
295− [ ]
296−
297− let
298− genFromDomInst = do
299− let
300− exprHeader = [e|pure $(TH.conE name)|]
301− exprRecordParts = do
302− descRecordPart <- descRecordParts
303− return $ case descRecordPart of
304− IsoXmlDescRecordField descField ->
305− let
306− IsoXmlDescField fieldPlural rawName _ = descField
307− fieldStrName = xmlLocalName rawName
308− exprFieldStrName = TH.litE (TH.stringL fieldStrName)
309− fieldParse = case fieldPlural of
310− XmlFieldPluralMandatory -> [e|inElem|]
311− _ -> [e|inElemTrav|]
312− in
313− [e|$fieldParse $exprFieldStrName fromDom|]
314− IsoXmlDescRecordAttribute descAttribute ->
315− let
316− IsoXmlDescAttribute attributePlural attributeStrName _ = descAttribute
317− exprAttributeStrName = TH.litE (TH.stringL attributeStrName)
318− attributeParse = case attributePlural of
319− XmlAttributePluralMandatory -> [e|parseAttribute|]
320− XmlAttributePluralOptional -> [e|parseAttributeMaybe|]
321− in
322− [e|$attributeParse $exprAttributeStrName fromAttribute|]
323− IsoXmlDescRecordContent _ -> [e|parseContent fromAttribute|]
324− fromDomExpr = foldl (\e fe -> [e| $e <*> $fe |]) exprHeader exprRecordParts
325− TH.instanceD
326− (return [])
327− [t|FromDom $(TH.conT name)|]
328− [ funSimple 'fromDom fromDomExpr ]
329−
330− genToXmlInst = do
331− objName <- TH.newName strPrefix
332− let
333− exprFields = do
334− descRecordPart <- descRecordParts
335− case descRecordPart of
336− IsoXmlDescRecordField (IsoXmlDescField fieldPlural rawName _) -> do
337− let
338− fieldStrName = xmlLocalName rawName
339− fName = TH.mkName (fieldName fieldStrName)
340− exprFieldStrName = TH.litE (TH.stringL rawName)
341− exprForField = case fieldPlural of
342− XmlFieldPluralMandatory -> [e|id|]
343− _ -> [e|traverse|]
344− exprFieldValue = [e|$(TH.varE fName) $(TH.varE objName)|]
345− exprFieldRender =
346− [e|(\a ->
347− XW.elementA $exprFieldStrName (toXmlParentAttributes a) a)|]
348− return [e|$exprForField $exprFieldRender $exprFieldValue|]
349− IsoXmlDescRecordContent (IsoXmlDescContent rawName _) -> do
350− let
351− fieldStrName = xmlLocalName rawName
352− fName = TH.mkName (fieldName fieldStrName)
353− exprFieldValue = [e|$(TH.varE fName) $(TH.varE objName)|]
354− return [e|XW.content . toXmlAttribute $ $exprFieldValue|]
355− _ -> []
356− toXmlExpr
357− = TH.lamE [if null exprFields then TH.wildP else TH.varP objName]
358− $ foldr (\fe e -> [e|$fe *> $e|]) [e|return ()|] exprFields
359− TH.instanceD
360− (return [])
361− [t|XW.ToXML $(TH.conT name)|]
362− [funSimple 'XW.toXML toXmlExpr]
363− genToXmlParentAttributeInst = do
364− objName <- TH.newName strPrefix
365− let
366− exprAttributes = do
367− descRecordPart <- descRecordParts
368− IsoXmlDescAttribute attributePlural attributeStrName _ <-
369− maybeToList $ case descRecordPart of
370− IsoXmlDescRecordAttribute descAttribute -> Just descAttribute
371− _ -> Nothing
372− let
373− fName = TH.mkName (fieldName attributeStrName)
374− exprAttrStrName = TH.litE (TH.stringL attributeStrName)
375− exprAttrValue = [e|$(TH.varE fName) $(TH.varE objName)|]
376− exprAttrWrap = case attributePlural of
377− XmlAttributePluralMandatory -> [e|Just . toXmlAttribute|]
378− XmlAttributePluralOptional -> [e|fmap toXmlAttribute|]
379− return [e|($exprAttrStrName, $exprAttrWrap $exprAttrValue)|]
380− toXmlParentAttributesExpr
381− = TH.lamE [if null exprAttributes then TH.wildP else TH.varP objName]
382− $ [e|mapMaybe distribPair $(TH.listE exprAttributes)|]
383−#if __GLASGOW_HASKELL__ < 800
384− TH.instanceD
385−#else
386− TH.instanceWithOverlapD (Just TH.Overlapping)
387−#endif
388− (return [])
389− [t|ToXmlParentAttributes $(TH.conT name)|]
390− [funSimple 'toXmlParentAttributes toXmlParentAttributesExpr]
391−
392− case genType of
393− Generator -> do
394− toXmlInst <- genToXmlInst
395− toXmlParentAttributesInst <- genToXmlParentAttributeInst
396− return $ [termDecl] ++ lensDecls ++
397− [toXmlInst, toXmlParentAttributesInst, nfDataInst]
398− Parser -> do
399− fromDomInst <- genFromDomInst
400− return $ [termDecl] ++ lensDecls ++ [fromDomInst, nfDataInst]
401− Both -> do
402− toXmlInst <- genToXmlInst
403− toXmlParentAttributesInst <- genToXmlParentAttributeInst
404− fromDomInst <- genFromDomInst
405− return $ [termDecl] ++ lensDecls ++
406− [fromDomInst, toXmlInst, toXmlParentAttributesInst, nfDataInst]
407−
408−distribPair :: Functor f => (a, f b) -> f (a, b)
409−distribPair (a, fb) = (a,) <$> fb
410−
411−-- | Get a local part of (possibly) fully qualified 'X.Name':
412−--
413−-- >>> xmlLocalName "{http://example.com/ns/my-namespace}my-name"
414−-- "my-name"
415−xmlLocalName :: String -> String
416−xmlLocalName = T.unpack . X.nameLocalName . fromString
src/Text/XML/DOM/Parser/Internal/Content.hsdeleted−28
@@ -1,28 +0,0 @@
1−{-# LANGUAGE OverloadedStrings #-}
2−module Text.XML.DOM.Parser.Internal.Content where
3−import Control.Lens
4−import Control.Monad.Reader
5−import Data.Text as T
6−import Text.XML
7−import Text.XML.DOM.Parser hiding (getCurrentContent, parseContent)
8−
9−-- Get current content. If there are more than one content
10−-- they are intercalate with space like in xpath `text()`
11−--
12−getCurrentContent :: (Monad m) => DomParserT Identity m Text
13−getCurrentContent
14− = T.intercalate " "
15− . (\e -> [T.strip cs| (NodeContent cs) <- e])
16− . elementNodes
17− . runIdentity
18− . _pdElements
19− <$> ask
20−
21−parseContent
22− :: (Monad m)
23− => (Text -> Either Text a)
24− -- ^ Content parser, return error msg if value is not parsed
25− -> DomParserT Identity m a
26−parseContent parse
27− = getCurrentContent
28− >>= either (throwParserError . PEContentWrongFormat) return . parse
stack.yaml+5−1
@@ -1,4 +1,8 @@
1−resolver: lts-10.0
1+resolver: lts-6.35
2+
3+packages:
4+- .
5+
26extra-deps:
37- dom-parser-3.0.0
48- generic-arbitrary-0.1.0
test/TestDefs.hs+6−7
@@ -4,16 +4,16 @@
44
55module TestDefs where
66
7−-- import Data.List.NonEmpty
7+import Data.List.NonEmpty
88import Data.THGen.XML
99import Test.QuickCheck.Arbitrary.Generic
1010import Test.QuickCheck.Instances ()
1111
12−"Bar" =:= enum Both
12+"Bar" =:= enum
1313 & "baroque"
1414 & "bartender"
1515
16−"Quux" Exhaustive =:= enum Both
16+"Quux" Exhaustive =:= enum
1717 & "ALL"
1818 & "YOUR"
1919 & "BASE"
@@ -22,15 +22,14 @@ import Test.QuickCheck.Instances ()
2222 & "TO"
2323 & "US"
2424
25−"Foo" =:= record Both
26− ^ "Shmuux" [t|XmlQuux|]
25+"Foo" =:= record
2726 + "Bar"
2827 ? "Baz" [t|Text|]
2928 !% "Quux"
3029 ?% "Muux" [t|XmlQuux|]
3130
32−"Root" =:= record Both
33− ! "{http://example.com/ns/my-namespace}Foo"
31+"Root" =:= record
32+ ! "Foo"
3433
3534instance Arbitrary XmlRoot where
3635 arbitrary = genericArbitrary
xml-isogen.cabal+2−5
@@ -1,5 +1,5 @@
11name: xml-isogen
2−version: 0.3.1
2+version: 0.2.1
33synopsis: Generate XML-isomorphic types
44description:
55 TemplateHaskell generators for XML-isomorphic data types, including
@@ -18,8 +18,7 @@ cabal-version: >=1.22
1818homepage: https://github.com/typeable/xml-isogen
1919tested-with: GHC == 7.10.3
2020 , GHC == 8.0.2
21− , GHC == 8.2.2
22− , GHC == 8.4.1
21+ , GHC == 8.2.1
2322extra-source-files: CHANGELOG.md
2423 , README.md
2524
@@ -30,10 +29,8 @@ source-repository head
3029library
3130 exposed-modules: Data.THGen.Enum
3231 , Data.THGen.XML
33− , Data.THGen.XML.Internal
3432 , Text.XML.ParentAttributes
3533 other-modules: Data.THGen.Compat
36− , Text.XML.DOM.Parser.Internal.Content
3734 build-depends: QuickCheck >= 2.8
3835 , base >=4.8 && <5
3936 , dom-parser >= 2.0.0