@@ -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,27 +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−-}
32−data GenType = Parser | Generator | Both
30+data GenType
31+ = Parser
32+ | Generator
33+ | ParserAndGenerator
34+ | LenientParser
35+ | LenientParserAndGenerator
3336
34−{- | Marks the style of code generation regarding records fields
35−@LensRenaming@ - fields get renamed by prefixing the field name with
36−'_' and lowercased capital letter used in the type name.
37−e.g. for
38−
39−> "MyNewDataType" =:= record genType LensRenaming
40−> * "field1"
41−
42−field will be renamed to `_mndtField1` and lens `mndtField1` will be created.
43−
44−@NoLens@ - no field renaming and no lens generation.
45−-}
46−data GenerateLens = LensRenaming | NoLens
37+isLenientType :: GenType -> Bool
38+isLenientType = \case
39+ LenientParser -> True
40+ LenientParserAndGenerator -> True
41+ _ -> False
4742
4843data XmlFieldPlural
4944 = XmlFieldPluralMandatory -- Occurs exactly 1 time (Identity)
@@ -74,7 +69,7 @@ data IsoXmlDescRecordPart
7469 | IsoXmlDescRecordAttribute IsoXmlDescAttribute
7570 | IsoXmlDescRecordContent IsoXmlDescContent
7671
77−data IsoXmlDescRecord = IsoXmlDescRecord GenType GenerateLens [IsoXmlDescRecordPart]
72+data IsoXmlDescRecord = IsoXmlDescRecord GenType [IsoXmlDescRecordPart]
7873
7974makePrisms ''IsoXmlDescRecord
8075
@@ -95,34 +90,26 @@ appendField
9590 -> IsoXmlDescRecord
9691 -> IsoXmlDescPreField
9792 -> IsoXmlDescRecord
98−appendField
99− plural
100− (IsoXmlDescRecord genType generateLens fields)
101− (IsoXmlDescPreField name ty) =
102− let xfield = IsoXmlDescRecordField $ IsoXmlDescField plural name ty
103− 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)
10496
10597appendAttribute
10698 :: XmlAttributePlural
10799 -> IsoXmlDescRecord
108100 -> IsoXmlDescPreAttribute
109101 -> IsoXmlDescRecord
110−appendAttribute
111− plural
112− (IsoXmlDescRecord genType generateLens fields)
113− (IsoXmlDescPreAttribute name ty) =
114− let xattribute = IsoXmlDescRecordAttribute $ IsoXmlDescAttribute plural name ty
115− 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)
116105
117106appendContent
118107 :: IsoXmlDescRecord
119108 -> IsoXmlDescPreContent
120109 -> IsoXmlDescRecord
121−appendContent
122− (IsoXmlDescRecord genType generateLens fields)
123− (IsoXmlDescPreContent name ty) =
124− let xcontent = IsoXmlDescRecordContent $ IsoXmlDescContent name ty
125− 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)
126113
127114(!), (?), (*), (+) :: IsoXmlDescRecord -> IsoXmlDescPreField -> IsoXmlDescRecord
128115(!) = appendField XmlFieldPluralMandatory
@@ -160,11 +147,11 @@ class Description name desc | desc -> name where
160147infix 0 =:=
161148
162149instance Description PrefixName IsoXmlDescRecord where
163− prefixName =:= (IsoXmlDescRecord genType generateLens descRecordParts) =
164− isoXmlGenerateDatatype genType generateLens prefixName (reverse descRecordParts)
150+ prefixName =:= (IsoXmlDescRecord genType descRecordParts) =
151+ isoXmlGenerateDatatype genType prefixName (reverse descRecordParts)
165152
166−record :: GenType -> GenerateLens -> IsoXmlDescRecord
167−record gt gl = IsoXmlDescRecord gt gl []
153+record :: GenType -> IsoXmlDescRecord
154+record gt = IsoXmlDescRecord gt []
168155
169156enum :: GenType -> IsoXmlDescEnum
170157enum gt = IsoXmlDescEnum gt []
@@ -173,7 +160,7 @@ instance Description ExhaustivenessName IsoXmlDescEnum where
173160 exhaustivenessName =:= (IsoXmlDescEnum genType descEnumCons) =
174161 isoXmlGenerateEnum genType exhaustivenessName (reverse descEnumCons)
175162
176−instance IsString (TH.TypeQ -> IsoXmlDescPreField) where
163+instance (m ~ Q) => IsString (m TH.Type -> IsoXmlDescPreField) where
177164 fromString = IsoXmlDescPreField
178165
179166instance IsString IsoXmlDescPreField where
@@ -181,10 +168,10 @@ instance IsString IsoXmlDescPreField where
181168 where
182169 ty = (TH.conT . TH.mkName) ("Xml" ++ over _head C.toUpper (xmlLocalName name))
183170
184−instance IsString (TH.TypeQ -> IsoXmlDescPreAttribute) where
171+instance (m ~ Q) => IsString (m TH.Type -> IsoXmlDescPreAttribute) where
185172 fromString = IsoXmlDescPreAttribute
186173
187−instance IsString (TH.TypeQ -> IsoXmlDescPreContent) where
174+instance (m ~ Q) => IsString (m Type -> IsoXmlDescPreContent) where
188175 fromString = IsoXmlDescPreContent
189176
190177instance IsString IsoXmlDescPreAttribute where
@@ -210,7 +197,7 @@ instance IsString ExhaustivenessName where
210197 fromString strName = ExhaustivenessName strName NonExhaustive
211198
212199makeNamePrefix :: String -> String
213−makeNamePrefix = map C.toLower . filter C.isUpper
200+makeNamePrefix = map C.toLower . filter (\c -> C.isUpper c || C.isDigit c)
214201
215202funSimple :: TH.Name -> TH.ExpQ -> TH.DecQ
216203funSimple name body = TH.funD name [ TH.clause [] (TH.normalB body) [] ]
@@ -238,11 +225,14 @@ isoXmlGenerateEnum genType (ExhaustivenessName strName' exh) enumCons = do
238225 (return [])
239226 [t|ToXmlAttribute $(TH.conT name)|]
240227 [funSimple 'toXmlAttribute [e|T.pack . show|]]
241− genFromDomInst = do
228+ genFromDomInst mode = do
242229 TH.instanceD
243230 (return [])
244231 [t|FromDom $(TH.conT name)|]
245− [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+ ]
246236 genFromAttributeInst = do
247237 TH.instanceD
248238 (return [])
@@ -254,36 +244,36 @@ isoXmlGenerateEnum genType (ExhaustivenessName strName' exh) enumCons = do
254244 toXmlAttributeInst <- genToXmlAttributeInst
255245 return $ enumDecls ++ [toXmlInst, toXmlAttributeInst]
256246 Parser -> do
257− fromDomInst <- genFromDomInst
247+ fromDomInst <- genFromDomInst Strict
258248 fromAttributeInst <- genFromAttributeInst
259249 return $ enumDecls ++ [fromDomInst, fromAttributeInst]
260− Both -> do
250+ LenientParser -> do
251+ fromDomInst <- genFromDomInst Lenient
252+ fromAttributeInst <- genFromAttributeInst
253+ return $ enumDecls ++ [fromDomInst, fromAttributeInst]
254+ ParserAndGenerator -> do
261255 toXmlInst <- genToXmlInst
262256 toXmlAttributeInst <- genToXmlAttributeInst
263− 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
264265 fromAttributeInst <- genFromAttributeInst
265266 return $ enumDecls ++ [toXmlInst, toXmlAttributeInst,
266267 fromDomInst, fromAttributeInst]
267268
268−isoXmlGenerateDatatype
269− :: GenType
270− -> GenerateLens
271− -> PrefixName
272− -> [IsoXmlDescRecordPart]
273− -> TH.DecsQ
274−isoXmlGenerateDatatype
275− genType
276− generateLens
277− (PrefixName strName' strPrefix')
278− descRecordParts = do
269+isoXmlGenerateDatatype :: GenType -> PrefixName -> [IsoXmlDescRecordPart] -> TH.DecsQ
270+isoXmlGenerateDatatype genType (PrefixName strName' strPrefix') descRecordParts = do
279271 let
280272 isNewtype = length descRecordParts == 1
281273 strName = "Xml" ++ strName'
282274 strPrefix = "x" ++ strPrefix'
283275 name = TH.mkName strName
284− fieldName str = case generateLens of
285− LensRenaming -> "_" ++ strPrefix ++ over _head C.toUpper str
286− NoLens -> str
276+ fieldName str = "_" ++ strPrefix ++ over _head C.toUpper str
287277 termDecl <- do
288278 let
289279 fields = do
@@ -294,11 +284,14 @@ isoXmlGenerateDatatype
294284 IsoXmlDescField fieldPlural rawName fieldType = descField
295285 fieldStrName = xmlLocalName rawName
296286 fName = TH.mkName (fieldName fieldStrName)
297− fType = case fieldPlural of
287+ fType' = case fieldPlural of
298288 XmlFieldPluralMandatory -> fieldType
299289 XmlFieldPluralOptional -> [t| Maybe $fieldType |]
300290 XmlFieldPluralRepeated -> [t| [$fieldType] |]
301291 XmlFieldPluralMultiplied -> [t| NonEmpty $fieldType |]
292+ fType = if isLenientType genType
293+ then [t| Maybe $fType' |]
294+ else fType'
302295 in if isNewtype
303296 then THC.varStrictType fName (THC.nonStrictType fType)
304297 else THC.varStrictType fName (THC.strictType fType)
@@ -325,9 +318,7 @@ isoXmlGenerateDatatype
325318 -- generate a newtype instead to do less allocations later
326319 then THC.newtypeD name (TH.recC name fields) [''Eq, ''Show, ''Generic]
327320 else THC.dataD name [TH.recC name fields] [''Eq, ''Show, ''Generic]
328− lensDecls <- case generateLens of
329− NoLens -> pure []
330− LensRenaming -> makeFieldOpticsForDec lensRules termDecl
321+ lensDecls <- makeFieldOpticsForDec lensRules termDecl
331322 nfDataInst <- do
332323 TH.instanceD
333324 (return [])
@@ -335,7 +326,7 @@ isoXmlGenerateDatatype
335326 [ ]
336327
337328 let
338− genFromDomInst = do
329+ genFromDomInst mode = do
339330 let
340331 exprHeader = [e|pure $(TH.conE name)|]
341332 exprRecordParts = do
@@ -349,8 +340,9 @@ isoXmlGenerateDatatype
349340 fieldParse = case fieldPlural of
350341 XmlFieldPluralMandatory -> [e|inElem|]
351342 _ -> [e|inElemTrav|]
352− in
353− [e|$fieldParse $exprFieldStrName fromDom|]
343+ in case mode of
344+ Strict -> [e|$fieldParse $exprFieldStrName fromDom|]
345+ Lenient -> [e|$fieldParse $exprFieldStrName (ignoreBlank fromDom)|]
354346 IsoXmlDescRecordAttribute descAttribute ->
355347 let
356348 IsoXmlDescAttribute attributePlural attributeStrName _ = descAttribute
@@ -436,12 +428,21 @@ isoXmlGenerateDatatype
436428 return $ [termDecl] ++ lensDecls ++
437429 [toXmlInst, toXmlParentAttributesInst, nfDataInst]
438430 Parser -> do
439− fromDomInst <- genFromDomInst
431+ fromDomInst <- genFromDomInst Strict
440432 return $ [termDecl] ++ lensDecls ++ [fromDomInst, nfDataInst]
441− 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
442443 toXmlInst <- genToXmlInst
443444 toXmlParentAttributesInst <- genToXmlParentAttributeInst
444− fromDomInst <- genFromDomInst
445+ fromDomInst <- genFromDomInst Lenient
445446 return $ [termDecl] ++ lensDecls ++
446447 [fromDomInst, toXmlInst, toXmlParentAttributesInst, nfDataInst]
447448