@@ -5,7 +5,7 @@
55module Data.THGen.XML.Internal where
66
77import Control.DeepSeq
8−import Control.Lens hiding (repeated, enum, (&))
8+import Control.Lens hiding (repeated, enum, (&), Strict)
99import Control.Lens.Internal.FieldTH (makeFieldOpticsForDec)
1010import qualified Data.Char as C
1111import Data.List.NonEmpty (NonEmpty)
@@ -15,7 +15,7 @@ import Data.THGen.Compat as THC
1515import Data.THGen.Enum
1616import qualified Data.Text as T
1717import GHC.Generics (Generic)
18−import Language.Haskell.TH as TH
18+import Language.Haskell.TH as TH hiding (Strict)
1919import Prelude hiding ((+), (*), (^))
2020import qualified Text.XML as X
2121import Text.XML.DOM.Parser hiding (parseContent)
@@ -23,26 +23,22 @@ import Text.XML.DOM.Parser.Internal.Content
2323import Text.XML.ParentAttributes
2424import qualified Text.XML.Writer as XW
2525
26+data ParserMode
27+ = Strict
28+ | Lenient
2629
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
31+ = Parser
32+ | Generator
33+ | ParserAndGenerator
34+ | LenientParser
35+ | LenientParserAndGenerator
3236
33−{- | Marks the style of code generation regarding records fields
34−@LensRenaming@ - fields get renamed by prefixing the field name with
35−'_' and lowercased capital letter used in the type name.
36−e.g. for
37−
38−> "MyNewDataType" =:= record genType LensRenaming
39−> * "field1"
40−
41−field will be renamed to `_mndtField1` and lens `mndtField1` will be created.
42−
43−@NoLens@ - no field renaming and no lens generation.
44−-}
45−data GenerateLens = LensRenaming | NoLens
37+isLenientType :: GenType -> Bool
38+isLenientType = \case
39+ LenientParser -> True
40+ LenientParserAndGenerator -> True
41+ _ -> False
4642
4743data XmlFieldPlural
4844 = XmlFieldPluralMandatory -- Occurs exactly 1 time (Identity)
@@ -73,7 +69,7 @@ data IsoXmlDescRecordPart
7369 | IsoXmlDescRecordAttribute IsoXmlDescAttribute
7470 | IsoXmlDescRecordContent IsoXmlDescContent
7571
76−data IsoXmlDescRecord = IsoXmlDescRecord GenType GenerateLens [IsoXmlDescRecordPart]
72+data IsoXmlDescRecord = IsoXmlDescRecord GenType [IsoXmlDescRecordPart]
7773
7874makePrisms ''IsoXmlDescRecord
7975
@@ -94,34 +90,26 @@ appendField
9490 -> IsoXmlDescRecord
9591 -> IsoXmlDescPreField
9692 -> IsoXmlDescRecord
97−appendField
98− plural
99− (IsoXmlDescRecord genType generateLens fields)
100− (IsoXmlDescPreField name ty) =
101− let xfield = IsoXmlDescRecordField $ IsoXmlDescField plural name ty
102− in IsoXmlDescRecord genType generateLens (xfield:fields)
93+appendField plural (IsoXmlDescRecord genType fields) (IsoXmlDescPreField name ty) =
94+ let xfield = IsoXmlDescRecordField $ IsoXmlDescField plural name ty
95+ in IsoXmlDescRecord genType (xfield:fields)
10396
10497appendAttribute
10598 :: XmlAttributePlural
10699 -> IsoXmlDescRecord
107100 -> IsoXmlDescPreAttribute
108101 -> IsoXmlDescRecord
109−appendAttribute
110− plural
111− (IsoXmlDescRecord genType generateLens fields)
112− (IsoXmlDescPreAttribute name ty) =
113− let xattribute = IsoXmlDescRecordAttribute $ IsoXmlDescAttribute plural name ty
114− in IsoXmlDescRecord genType generateLens (xattribute:fields)
102+appendAttribute plural (IsoXmlDescRecord genType fields) (IsoXmlDescPreAttribute name ty) =
103+ let xattribute = IsoXmlDescRecordAttribute $ IsoXmlDescAttribute plural name ty
104+ in IsoXmlDescRecord genType (xattribute:fields)
115105
116106appendContent
117107 :: IsoXmlDescRecord
118108 -> IsoXmlDescPreContent
119109 -> IsoXmlDescRecord
120−appendContent
121− (IsoXmlDescRecord genType generateLens fields)
122− (IsoXmlDescPreContent name ty) =
123− let xcontent = IsoXmlDescRecordContent $ IsoXmlDescContent name ty
124− in IsoXmlDescRecord genType generateLens (xcontent:fields)
110+appendContent (IsoXmlDescRecord genType fields) (IsoXmlDescPreContent name ty) =
111+ let xcontent = IsoXmlDescRecordContent $ IsoXmlDescContent name ty
112+ in IsoXmlDescRecord genType (xcontent:fields)
125113
126114(!), (?), (*), (+) :: IsoXmlDescRecord -> IsoXmlDescPreField -> IsoXmlDescRecord
127115(!) = appendField XmlFieldPluralMandatory
@@ -159,11 +147,11 @@ class Description name desc | desc -> name where
159147infix 0 =:=
160148
161149instance Description PrefixName IsoXmlDescRecord where
162− prefixName =:= (IsoXmlDescRecord genType generateLens descRecordParts) =
163− isoXmlGenerateDatatype genType generateLens prefixName (reverse descRecordParts)
150+ prefixName =:= (IsoXmlDescRecord genType descRecordParts) =
151+ isoXmlGenerateDatatype genType prefixName (reverse descRecordParts)
164152
165−record :: GenType -> GenerateLens -> IsoXmlDescRecord
166−record gt gl = IsoXmlDescRecord gt gl []
153+record :: GenType -> IsoXmlDescRecord
154+record gt = IsoXmlDescRecord gt []
167155
168156enum :: GenType -> IsoXmlDescEnum
169157enum gt = IsoXmlDescEnum gt []
@@ -172,7 +160,7 @@ instance Description ExhaustivenessName IsoXmlDescEnum where
172160 exhaustivenessName =:= (IsoXmlDescEnum genType descEnumCons) =
173161 isoXmlGenerateEnum genType exhaustivenessName (reverse descEnumCons)
174162
175−instance IsString (TH.TypeQ -> IsoXmlDescPreField) where
163+instance (m ~ Q) => IsString (m TH.Type -> IsoXmlDescPreField) where
176164 fromString = IsoXmlDescPreField
177165
178166instance IsString IsoXmlDescPreField where
@@ -180,10 +168,10 @@ instance IsString IsoXmlDescPreField where
180168 where
181169 ty = (TH.conT . TH.mkName) ("Xml" ++ over _head C.toUpper (xmlLocalName name))
182170
183−instance IsString (TH.TypeQ -> IsoXmlDescPreAttribute) where
171+instance (m ~ Q) => IsString (m TH.Type -> IsoXmlDescPreAttribute) where
184172 fromString = IsoXmlDescPreAttribute
185173
186−instance IsString (TH.TypeQ -> IsoXmlDescPreContent) where
174+instance (m ~ Q) => IsString (m Type -> IsoXmlDescPreContent) where
187175 fromString = IsoXmlDescPreContent
188176
189177instance IsString IsoXmlDescPreAttribute where
@@ -209,7 +197,7 @@ instance IsString ExhaustivenessName where
209197 fromString strName = ExhaustivenessName strName NonExhaustive
210198
211199makeNamePrefix :: String -> String
212−makeNamePrefix = map C.toLower . filter C.isUpper
200+makeNamePrefix = map C.toLower . filter (\c -> C.isUpper c || C.isDigit c)
213201
214202funSimple :: TH.Name -> TH.ExpQ -> TH.DecQ
215203funSimple name body = TH.funD name [ TH.clause [] (TH.normalB body) [] ]
@@ -237,11 +225,14 @@ isoXmlGenerateEnum genType (ExhaustivenessName strName' exh) enumCons = do
237225 (return [])
238226 [t|ToXmlAttribute $(TH.conT name)|]
239227 [funSimple 'toXmlAttribute [e|T.pack . show|]]
240− genFromDomInst = do
228+ genFromDomInst mode = do
241229 TH.instanceD
242230 (return [])
243231 [t|FromDom $(TH.conT name)|]
244− [funSimple 'fromDom [e|parseContent readContent|]]
232+ [case mode of
233+ Strict -> funSimple 'fromDom [e|parseContent readContent|]
234+ Lenient -> funSimple 'fromDom [e|ignoreBlank $ parseContent readContent|]
235+ ]
245236 genFromAttributeInst = do
246237 TH.instanceD
247238 (return [])
@@ -253,36 +244,36 @@ isoXmlGenerateEnum genType (ExhaustivenessName strName' exh) enumCons = do
253244 toXmlAttributeInst <- genToXmlAttributeInst
254245 return $ enumDecls ++ [toXmlInst, toXmlAttributeInst]
255246 Parser -> do
256− fromDomInst <- genFromDomInst
247+ fromDomInst <- genFromDomInst Strict
257248 fromAttributeInst <- genFromAttributeInst
258249 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
260255 toXmlInst <- genToXmlInst
261256 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
263265 fromAttributeInst <- genFromAttributeInst
264266 return $ enumDecls ++ [toXmlInst, toXmlAttributeInst,
265267 fromDomInst, fromAttributeInst]
266268
267−isoXmlGenerateDatatype
268− :: GenType
269− -> GenerateLens
270− -> PrefixName
271− -> [IsoXmlDescRecordPart]
272− -> TH.DecsQ
273−isoXmlGenerateDatatype
274− genType
275− generateLens
276− (PrefixName strName' strPrefix')
277− descRecordParts = do
269+isoXmlGenerateDatatype :: GenType -> PrefixName -> [IsoXmlDescRecordPart] -> TH.DecsQ
270+isoXmlGenerateDatatype genType (PrefixName strName' strPrefix') descRecordParts = do
278271 let
279272 isNewtype = length descRecordParts == 1
280273 strName = "Xml" ++ strName'
281274 strPrefix = "x" ++ strPrefix'
282275 name = TH.mkName strName
283− fieldName str = case generateLens of
284− LensRenaming -> "_" ++ strPrefix ++ over _head C.toUpper str
285− NoLens -> str
276+ fieldName str = "_" ++ strPrefix ++ over _head C.toUpper str
286277 termDecl <- do
287278 let
288279 fields = do
@@ -293,11 +284,14 @@ isoXmlGenerateDatatype
293284 IsoXmlDescField fieldPlural rawName fieldType = descField
294285 fieldStrName = xmlLocalName rawName
295286 fName = TH.mkName (fieldName fieldStrName)
296− fType = case fieldPlural of
287+ fType' = case fieldPlural of
297288 XmlFieldPluralMandatory -> fieldType
298289 XmlFieldPluralOptional -> [t| Maybe $fieldType |]
299290 XmlFieldPluralRepeated -> [t| [$fieldType] |]
300291 XmlFieldPluralMultiplied -> [t| NonEmpty $fieldType |]
292+ fType = if isLenientType genType
293+ then [t| Maybe $fType' |]
294+ else fType'
301295 in if isNewtype
302296 then THC.varStrictType fName (THC.nonStrictType fType)
303297 else THC.varStrictType fName (THC.strictType fType)
@@ -332,7 +326,7 @@ isoXmlGenerateDatatype
332326 [ ]
333327
334328 let
335− genFromDomInst = do
329+ genFromDomInst mode = do
336330 let
337331 exprHeader = [e|pure $(TH.conE name)|]
338332 exprRecordParts = do
@@ -346,8 +340,9 @@ isoXmlGenerateDatatype
346340 fieldParse = case fieldPlural of
347341 XmlFieldPluralMandatory -> [e|inElem|]
348342 _ -> [e|inElemTrav|]
349− in
350− [e|$fieldParse $exprFieldStrName fromDom|]
343+ in case mode of
344+ Strict -> [e|$fieldParse $exprFieldStrName fromDom|]
345+ Lenient -> [e|$fieldParse $exprFieldStrName (ignoreBlank fromDom)|]
351346 IsoXmlDescRecordAttribute descAttribute ->
352347 let
353348 IsoXmlDescAttribute attributePlural attributeStrName _ = descAttribute
@@ -433,12 +428,21 @@ isoXmlGenerateDatatype
433428 return $ [termDecl] ++ lensDecls ++
434429 [toXmlInst, toXmlParentAttributesInst, nfDataInst]
435430 Parser -> do
436− fromDomInst <- genFromDomInst
431+ fromDomInst <- genFromDomInst Strict
437432 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
439443 toXmlInst <- genToXmlInst
440444 toXmlParentAttributesInst <- genToXmlParentAttributeInst
441− fromDomInst <- genFromDomInst
445+ fromDomInst <- genFromDomInst Lenient
442446 return $ [termDecl] ++ lensDecls ++
443447 [fromDomInst, toXmlInst, toXmlParentAttributesInst, nfDataInst]
444448