@@ -5,7 +5,7 @@
5module Data.THGen.XML.Internal where
5module Data.THGen.XML.Internal where
7import Control.DeepSeq
7import Control.DeepSeq
8−import Control.Lens hiding (repeated, enum, (&))
8+import Control.Lens hiding (repeated, enum, (&), Strict)
9import Control.Lens.Internal.FieldTH (makeFieldOpticsForDec)
9import Control.Lens.Internal.FieldTH (makeFieldOpticsForDec)
10import qualified Data.Char as C
10import qualified Data.Char as C
11import Data.List.NonEmpty (NonEmpty)
11import Data.List.NonEmpty (NonEmpty)
@@ -15,7 +15,7 @@ import Data.THGen.Compat as THC
15import Data.THGen.Enum
15import Data.THGen.Enum
16import qualified Data.Text as T
16import qualified Data.Text as T
17import GHC.Generics (Generic)
17import GHC.Generics (Generic)
18−import Language.Haskell.TH as TH
18+import Language.Haskell.TH as TH hiding (Strict)
19import Prelude hiding ((+), (*), (^))
19import Prelude hiding ((+), (*), (^))
20import qualified Text.XML as X
20import qualified Text.XML as X
21import Text.XML.DOM.Parser hiding (parseContent)
21import Text.XML.DOM.Parser hiding (parseContent)
@@ -23,26 +23,22 @@ import Text.XML.DOM.Parser.Internal.Content
23import Text.XML.ParentAttributes
23import Text.XML.ParentAttributes
24import qualified Text.XML.Writer as XW
24import qualified Text.XML.Writer as XW
27−-- | Marks the type of code generation:
28−-- Parser - generate only instances for xml parsing (@FromDom@)
29−-- Generator - generate only @ToXML@ instances
30−-- Both - generate @FromDom@ and @ToXML@ instances
31−data GenType = Parser | Generator | Both
30+data GenType
35+ | LenientParserAndGenerator
33−{- | Marks the style of code generation regarding records fields
37+isLenientType :: GenType -> Bool
34−@LensRenaming@ - fields get renamed by prefixing the field name with
38+isLenientType = \case
35−'_' and lowercased capital letter used in the type name.
39+ LenientParser -> True
36−e.g. for
40+ LenientParserAndGenerator -> True
38−> "MyNewDataType" =:= record genType LensRenaming
41−field will be renamed to `_mndtField1` and lens `mndtField1` will be created.
43−@NoLens@ - no field renaming and no lens generation.
45−data GenerateLens = LensRenaming | NoLens
47data XmlFieldPlural
43data XmlFieldPlural
48 = XmlFieldPluralMandatory -- Occurs exactly 1 time (Identity)
44 = XmlFieldPluralMandatory -- Occurs exactly 1 time (Identity)
@@ -73,7 +69,7 @@ data IsoXmlDescRecordPart
73 | IsoXmlDescRecordAttribute IsoXmlDescAttribute
69 | IsoXmlDescRecordAttribute IsoXmlDescAttribute
74 | IsoXmlDescRecordContent IsoXmlDescContent
70 | IsoXmlDescRecordContent IsoXmlDescContent
76−data IsoXmlDescRecord = IsoXmlDescRecord GenType GenerateLens [IsoXmlDescRecordPart]
72+data IsoXmlDescRecord = IsoXmlDescRecord GenType [IsoXmlDescRecordPart]
78makePrisms ''IsoXmlDescRecord
74makePrisms ''IsoXmlDescRecord
@@ -94,34 +90,26 @@ appendField
94 -> IsoXmlDescRecord
90 -> IsoXmlDescRecord
95 -> IsoXmlDescPreField
91 -> IsoXmlDescPreField
96 -> IsoXmlDescRecord
92 -> IsoXmlDescRecord
99− (IsoXmlDescRecord genType generateLens fields)
93+appendField plural (IsoXmlDescRecord genType fields) (IsoXmlDescPreField name ty) =
100− (IsoXmlDescPreField name ty) =
101− let xfield = IsoXmlDescRecordField $ IsoXmlDescField plural name ty
94+ let xfield = IsoXmlDescRecordField $ IsoXmlDescField plural name ty
102− in IsoXmlDescRecord genType generateLens (xfield:fields)
95+ in IsoXmlDescRecord genType (xfield:fields)
104appendAttribute
97appendAttribute
105 :: XmlAttributePlural
98 :: XmlAttributePlural
106 -> IsoXmlDescRecord
99 -> IsoXmlDescRecord
107 -> IsoXmlDescPreAttribute
100 -> IsoXmlDescPreAttribute
108 -> IsoXmlDescRecord
101 -> IsoXmlDescRecord
111− (IsoXmlDescRecord genType generateLens fields)
112− (IsoXmlDescPreAttribute name ty) =
102+appendAttribute plural (IsoXmlDescRecord genType fields) (IsoXmlDescPreAttribute name ty) =
113− let xattribute = IsoXmlDescRecordAttribute $ IsoXmlDescAttribute plural name ty
103+ let xattribute = IsoXmlDescRecordAttribute $ IsoXmlDescAttribute plural name ty
114− in IsoXmlDescRecord genType generateLens (xattribute:fields)
104+ in IsoXmlDescRecord genType (xattribute:fields)
116appendContent
106appendContent
117 :: IsoXmlDescRecord
107 :: IsoXmlDescRecord
118 -> IsoXmlDescPreContent
108 -> IsoXmlDescPreContent
119 -> IsoXmlDescRecord
109 -> IsoXmlDescRecord
121− (IsoXmlDescRecord genType generateLens fields)
122− (IsoXmlDescPreContent name ty) =
110+appendContent (IsoXmlDescRecord genType fields) (IsoXmlDescPreContent name ty) =
123− let xcontent = IsoXmlDescRecordContent $ IsoXmlDescContent name ty
111+ let xcontent = IsoXmlDescRecordContent $ IsoXmlDescContent name ty
124− in IsoXmlDescRecord genType generateLens (xcontent:fields)
112+ in IsoXmlDescRecord genType (xcontent:fields)
126(!), (?), (*), (+) :: IsoXmlDescRecord -> IsoXmlDescPreField -> IsoXmlDescRecord
114(!), (?), (*), (+) :: IsoXmlDescRecord -> IsoXmlDescPreField -> IsoXmlDescRecord
127(!) = appendField XmlFieldPluralMandatory
115(!) = appendField XmlFieldPluralMandatory
@@ -159,11 +147,11 @@ class Description name desc | desc -> name where
159infix 0 =:=
147infix 0 =:=
161instance Description PrefixName IsoXmlDescRecord where
149instance Description PrefixName IsoXmlDescRecord where
162− prefixName =:= (IsoXmlDescRecord genType generateLens descRecordParts) =
150+ prefixName =:= (IsoXmlDescRecord genType descRecordParts) =
163− isoXmlGenerateDatatype genType generateLens prefixName (reverse descRecordParts)
151+ isoXmlGenerateDatatype genType prefixName (reverse descRecordParts)
165−record :: GenType -> GenerateLens -> IsoXmlDescRecord
153+record :: GenType -> IsoXmlDescRecord
166−record gt gl = IsoXmlDescRecord gt gl []
154+record gt = IsoXmlDescRecord gt []
168enum :: GenType -> IsoXmlDescEnum
156enum :: GenType -> IsoXmlDescEnum
169enum gt = IsoXmlDescEnum gt []
157enum gt = IsoXmlDescEnum gt []
@@ -172,7 +160,7 @@ instance Description ExhaustivenessName IsoXmlDescEnum where
172 exhaustivenessName =:= (IsoXmlDescEnum genType descEnumCons) =
160 exhaustivenessName =:= (IsoXmlDescEnum genType descEnumCons) =
173 isoXmlGenerateEnum genType exhaustivenessName (reverse descEnumCons)
161 isoXmlGenerateEnum genType exhaustivenessName (reverse descEnumCons)
175−instance IsString (TH.TypeQ -> IsoXmlDescPreField) where
163+instance (m ~ Q) => IsString (m TH.Type -> IsoXmlDescPreField) where
176 fromString = IsoXmlDescPreField
164 fromString = IsoXmlDescPreField
178instance IsString IsoXmlDescPreField where
166instance IsString IsoXmlDescPreField where
@@ -180,10 +168,10 @@ instance IsString IsoXmlDescPreField where
181 ty = (TH.conT . TH.mkName) ("Xml" ++ over _head C.toUpper (xmlLocalName name))
169 ty = (TH.conT . TH.mkName) ("Xml" ++ over _head C.toUpper (xmlLocalName name))
183−instance IsString (TH.TypeQ -> IsoXmlDescPreAttribute) where
171+instance (m ~ Q) => IsString (m TH.Type -> IsoXmlDescPreAttribute) where
184 fromString = IsoXmlDescPreAttribute
172 fromString = IsoXmlDescPreAttribute
186−instance IsString (TH.TypeQ -> IsoXmlDescPreContent) where
174+instance (m ~ Q) => IsString (m Type -> IsoXmlDescPreContent) where
187 fromString = IsoXmlDescPreContent
175 fromString = IsoXmlDescPreContent
189instance IsString IsoXmlDescPreAttribute where
177instance IsString IsoXmlDescPreAttribute where
@@ -209,7 +197,7 @@ instance IsString ExhaustivenessName where
209 fromString strName = ExhaustivenessName strName NonExhaustive
197 fromString strName = ExhaustivenessName strName NonExhaustive
211makeNamePrefix :: String -> String
199makeNamePrefix :: String -> String
212−makeNamePrefix = map C.toLower . filter C.isUpper
200+makeNamePrefix = map C.toLower . filter (\c -> C.isUpper c || C.isDigit c)
214funSimple :: TH.Name -> TH.ExpQ -> TH.DecQ
202funSimple :: TH.Name -> TH.ExpQ -> TH.DecQ
215funSimple name body = TH.funD name [ TH.clause [] (TH.normalB body) [] ]
203funSimple name body = TH.funD name [ TH.clause [] (TH.normalB body) [] ]
@@ -237,11 +225,14 @@ isoXmlGenerateEnum genType (ExhaustivenessName strName' exh) enumCons = do
237 (return [])
225 (return [])
238 [t|ToXmlAttribute $(TH.conT name)|]
226 [t|ToXmlAttribute $(TH.conT name)|]
239 [funSimple 'toXmlAttribute [e|T.pack . show|]]
227 [funSimple 'toXmlAttribute [e|T.pack . show|]]
240− genFromDomInst = do
228+ genFromDomInst mode = do
241 TH.instanceD
229 TH.instanceD
242 (return [])
230 (return [])
243 [t|FromDom $(TH.conT name)|]
231 [t|FromDom $(TH.conT name)|]
233+ Strict -> funSimple 'fromDom [e|parseContent readContent|]
244− [funSimple 'fromDom [e|parseContent readContent|]]
234+ Lenient -> funSimple 'fromDom [e|ignoreBlank $ parseContent readContent|]
245 genFromAttributeInst = do
236 genFromAttributeInst = do
246 TH.instanceD
237 TH.instanceD
247 (return [])
238 (return [])
@@ -253,36 +244,36 @@ isoXmlGenerateEnum genType (ExhaustivenessName strName' exh) enumCons = do
253 toXmlAttributeInst <- genToXmlAttributeInst
244 toXmlAttributeInst <- genToXmlAttributeInst
254 return $ enumDecls ++ [toXmlInst, toXmlAttributeInst]
245 return $ enumDecls ++ [toXmlInst, toXmlAttributeInst]
255 Parser -> do
246 Parser -> do
256− fromDomInst <- genFromDomInst
247+ fromDomInst <- genFromDomInst Strict
257 fromAttributeInst <- genFromAttributeInst
248 fromAttributeInst <- genFromAttributeInst
258 return $ enumDecls ++ [fromDomInst, fromAttributeInst]
249 return $ enumDecls ++ [fromDomInst, fromAttributeInst]
259− Both -> do
250+ LenientParser -> do
251+ fromDomInst <- genFromDomInst Lenient
252+ fromAttributeInst <- genFromAttributeInst
253+ return $ enumDecls ++ [fromDomInst, fromAttributeInst]
254+ ParserAndGenerator -> do
260 toXmlInst <- genToXmlInst
255 toXmlInst <- genToXmlInst
261 toXmlAttributeInst <- genToXmlAttributeInst
256 toXmlAttributeInst <- genToXmlAttributeInst
262− fromDomInst <- genFromDomInst
257+ fromDomInst <- genFromDomInst Strict
258+ fromAttributeInst <- genFromAttributeInst
259+ return $ enumDecls ++ [toXmlInst, toXmlAttributeInst,
260+ fromDomInst, fromAttributeInst]
261+ LenientParserAndGenerator -> do
262+ toXmlInst <- genToXmlInst
263+ toXmlAttributeInst <- genToXmlAttributeInst
264+ fromDomInst <- genFromDomInst Lenient
263 fromAttributeInst <- genFromAttributeInst
265 fromAttributeInst <- genFromAttributeInst
264 return $ enumDecls ++ [toXmlInst, toXmlAttributeInst,
266 return $ enumDecls ++ [toXmlInst, toXmlAttributeInst,
265 fromDomInst, fromAttributeInst]
267 fromDomInst, fromAttributeInst]
267−isoXmlGenerateDatatype
269+isoXmlGenerateDatatype :: GenType -> PrefixName -> [IsoXmlDescRecordPart] -> TH.DecsQ
271− -> [IsoXmlDescRecordPart]
273−isoXmlGenerateDatatype
276− (PrefixName strName' strPrefix')
270+isoXmlGenerateDatatype genType (PrefixName strName' strPrefix') descRecordParts = do
277− descRecordParts = do
279 isNewtype = length descRecordParts == 1
272 isNewtype = length descRecordParts == 1
280 strName = "Xml" ++ strName'
273 strName = "Xml" ++ strName'
281 strPrefix = "x" ++ strPrefix'
274 strPrefix = "x" ++ strPrefix'
282 name = TH.mkName strName
275 name = TH.mkName strName
283− fieldName str = case generateLens of
284− LensRenaming -> "_" ++ strPrefix ++ over _head C.toUpper str
276+ fieldName str = "_" ++ strPrefix ++ over _head C.toUpper str
286 termDecl <- do
277 termDecl <- do
288 fields = do
279 fields = do
@@ -293,11 +284,14 @@ isoXmlGenerateDatatype
293 IsoXmlDescField fieldPlural rawName fieldType = descField
284 IsoXmlDescField fieldPlural rawName fieldType = descField
294 fieldStrName = xmlLocalName rawName
285 fieldStrName = xmlLocalName rawName
295 fName = TH.mkName (fieldName fieldStrName)
286 fName = TH.mkName (fieldName fieldStrName)
296− fType = case fieldPlural of
287+ fType' = case fieldPlural of
297 XmlFieldPluralMandatory -> fieldType
288 XmlFieldPluralMandatory -> fieldType
298 XmlFieldPluralOptional -> [t| Maybe $fieldType |]
289 XmlFieldPluralOptional -> [t| Maybe $fieldType |]
299 XmlFieldPluralRepeated -> [t| [$fieldType] |]
290 XmlFieldPluralRepeated -> [t| [$fieldType] |]
300 XmlFieldPluralMultiplied -> [t| NonEmpty $fieldType |]
291 XmlFieldPluralMultiplied -> [t| NonEmpty $fieldType |]
292+ fType = if isLenientType genType
293+ then [t| Maybe $fType' |]
301 in if isNewtype
295 in if isNewtype
302 then THC.varStrictType fName (THC.nonStrictType fType)
296 then THC.varStrictType fName (THC.nonStrictType fType)
303 else THC.varStrictType fName (THC.strictType fType)
297 else THC.varStrictType fName (THC.strictType fType)
@@ -332,7 +326,7 @@ isoXmlGenerateDatatype
335− genFromDomInst = do
329+ genFromDomInst mode = do
337 exprHeader = [e|pure $(TH.conE name)|]
331 exprHeader = [e|pure $(TH.conE name)|]
338 exprRecordParts = do
332 exprRecordParts = do
@@ -346,8 +340,9 @@ isoXmlGenerateDatatype
346 fieldParse = case fieldPlural of
340 fieldParse = case fieldPlural of
347 XmlFieldPluralMandatory -> [e|inElem|]
341 XmlFieldPluralMandatory -> [e|inElem|]
348 _ -> [e|inElemTrav|]
342 _ -> [e|inElemTrav|]
349− in
343+ in case mode of
344+ Strict -> [e|$fieldParse $exprFieldStrName fromDom|]
350− [e|$fieldParse $exprFieldStrName fromDom|]
345+ Lenient -> [e|$fieldParse $exprFieldStrName (ignoreBlank fromDom)|]
351 IsoXmlDescRecordAttribute descAttribute ->
346 IsoXmlDescRecordAttribute descAttribute ->
353 IsoXmlDescAttribute attributePlural attributeStrName _ = descAttribute
348 IsoXmlDescAttribute attributePlural attributeStrName _ = descAttribute
@@ -433,12 +428,21 @@ isoXmlGenerateDatatype
433 return $ [termDecl] ++ lensDecls ++
428 return $ [termDecl] ++ lensDecls ++
434 [toXmlInst, toXmlParentAttributesInst, nfDataInst]
429 [toXmlInst, toXmlParentAttributesInst, nfDataInst]
435 Parser -> do
430 Parser -> do
436− fromDomInst <- genFromDomInst
431+ fromDomInst <- genFromDomInst Strict
437 return $ [termDecl] ++ lensDecls ++ [fromDomInst, nfDataInst]
432 return $ [termDecl] ++ lensDecls ++ [fromDomInst, nfDataInst]
438− Both -> do
433+ LenientParser -> do
434+ fromDomInst <- genFromDomInst Lenient
435+ return $ [termDecl] ++ lensDecls ++ [fromDomInst, nfDataInst]
436+ ParserAndGenerator -> do
437+ toXmlInst <- genToXmlInst
438+ toXmlParentAttributesInst <- genToXmlParentAttributeInst
439+ fromDomInst <- genFromDomInst Strict
440+ return $ [termDecl] ++ lensDecls ++
441+ [fromDomInst, toXmlInst, toXmlParentAttributesInst, nfDataInst]
442+ LenientParserAndGenerator -> do
439 toXmlInst <- genToXmlInst
443 toXmlInst <- genToXmlInst
440 toXmlParentAttributesInst <- genToXmlParentAttributeInst
444 toXmlParentAttributesInst <- genToXmlParentAttributeInst
441− fromDomInst <- genFromDomInst
445+ fromDomInst <- genFromDomInst Lenient
442 return $ [termDecl] ++ lensDecls ++
446 return $ [termDecl] ++ lensDecls ++
443 [fromDomInst, toXmlInst, toXmlParentAttributesInst, nfDataInst]
447 [fromDomInst, toXmlInst, toXmlParentAttributesInst, nfDataInst]