git.iko.soy/typeable/xsd-isogen
on master help sign in
34 files changed+1584−462
since splitsnapshots
unifiedsplit
cabal.project+2−2 golden/README+3 golden/ambig.xsd+20 golden/ambig.xsd.hs+39 golden/ambig1.xsd+16 golden/ambig1.xsd.hs+39 golden/enum.xsd+12 golden/enum.xsd.hs+25 golden/extension.xsd+36 golden/extension.xsd.hs+83 golden/extension_dep.xsd+31 golden/extension_dep.xsd.hs+101 golden/import.xsd+16 golden/import.xsd.hs+33 golden/import_ambig.xsd+26 golden/import_ambig.xsd.hs+50 golden/imported.xsd+9 golden/imported.xsd.hs+27 golden/include.xsd+5 golden/include.xsd.hs+27 golden/included.xsd+6 golden/included.xsd.hs+27 golden/seq.xsd+14 golden/seq.xsd.hs+34 golden/simple.xsd+25 golden/simple.xsd.hs+40 golden/test.sh+5 src/BuiltInTypes.hs+67 src/Gen.hs+213 src/Main.hs+436−440 src/Options.hs+10−6 src/References.hs+80 stack.yaml+12 xsd-isogen.cabal+15−14
cabal.project+2−2
@@ -2,5 +2,5 @@ packages: .
22
33source-repository-package
44 type: git
5− location: https://github.com/dredozubov/xsd-parser.git
6− tag: f9cbe65a9b9c37e9015a73707f1cd4f00e79c96e
5+ location: https://github.com/typeable/xsd-parser.git
6+ tag: e08a37cf08674a492407b9b7f4a61e8faedb1590
golden/READMEnew file+3
@@ -0,0 +1,3 @@
1+To use the golden tests, cd to this folder and run test.sh. It will run
2+xsd-isogen on each xsd file here. Then use git diff to check the
3+difference.
golden/ambig.xsdnew file+20
@@ -0,0 +1,20 @@
1+<?xml version="1.0"?>
2+<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
3+ <xs:simpleType name="stringtype">
4+ <xs:restriction base="xs:string"/>
5+ </xs:simpleType>
6+ <xs:element name="Aa">
7+ <xs:complexType>
8+ <xs:sequence>
9+ <xs:element name="firstName" type="stringtype"/>
10+ </xs:sequence>
11+ </xs:complexType>
12+ </xs:element>
13+ <xs:element name="Ab">
14+ <xs:complexType>
15+ <xs:sequence>
16+ <xs:element name="firstName" type="stringtype"/>
17+ </xs:sequence>
18+ </xs:complexType>
19+ </xs:element>
20+</xs:schema>
golden/ambig.xsd.hsnew file+39
@@ -0,0 +1,39 @@
1+-- This file was generated by xsd-isogen version 0.1.0.0
2+-- https://github.com/dredozubov/xsd-isogen
3+
4+{-# OPTIONS_GHC -Wno-unused-imports #-}
5+module Dummy where
6+
7+import Control.DeepSeq
8+import Data.Scientific
9+import Data.THGen.XML
10+import Text.XML.DOM.Parser
11+import Text.XML.Writer
12+import Text.XML.ParentAttributes
13+import Prelude hiding ((*), (+))
14+
15+
16+{-|
17+Generated from: stringtype
18+-}
19+newtype XmlStringtype
20+ = XmlStringtype
21+ { unXmlStringtype :: Text
22+ } deriving (Show, Eq, NFData, ToXML)
23+
24+instance FromDom XmlStringtype where
25+ fromDom = XmlStringtype <$> fromDom
26+
27+{-|
28+Generated from: Aa
29+-}
30+"Aa" =:= record ParserAndGenerator
31+ ! "firstName" [t|XmlStringtype|]
32+
33+{-|
34+Generated from: Ab
35+-}
36+"Ab1" =:= record ParserAndGenerator
37+ ! "firstName" [t|XmlStringtype|]
38+
39+
golden/ambig1.xsdnew file+16
@@ -0,0 +1,16 @@
1+<?xml version="1.0"?>
2+<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
3+ <xs:simpleType name="stringtype">
4+ <xs:restriction base="xs:string"/>
5+ </xs:simpleType>
6+ <xs:complexType name="Aa">
7+ <xs:sequence>
8+ <xs:element name="firstName" type="stringtype"/>
9+ </xs:sequence>
10+ </xs:complexType>
11+ <xs:complexType name="Ab">
12+ <xs:sequence>
13+ <xs:element name="firstName" type="stringtype"/>
14+ </xs:sequence>
15+ </xs:complexType>
16+</xs:schema>
golden/ambig1.xsd.hsnew file+39
@@ -0,0 +1,39 @@
1+-- This file was generated by xsd-isogen version 0.1.0.0
2+-- https://github.com/dredozubov/xsd-isogen
3+
4+{-# OPTIONS_GHC -Wno-unused-imports #-}
5+module Dummy where
6+
7+import Control.DeepSeq
8+import Data.Scientific
9+import Data.THGen.XML
10+import Text.XML.DOM.Parser
11+import Text.XML.Writer
12+import Text.XML.ParentAttributes
13+import Prelude hiding ((*), (+))
14+
15+
16+{-|
17+Generated from: stringtype
18+-}
19+newtype XmlStringtype
20+ = XmlStringtype
21+ { unXmlStringtype :: Text
22+ } deriving (Show, Eq, NFData, ToXML)
23+
24+instance FromDom XmlStringtype where
25+ fromDom = XmlStringtype <$> fromDom
26+
27+{-|
28+Generated from: Aa
29+-}
30+"Aa" =:= record ParserAndGenerator
31+ ! "firstName" [t|XmlStringtype|]
32+
33+{-|
34+Generated from: Ab
35+-}
36+"Ab1" =:= record ParserAndGenerator
37+ ! "firstName" [t|XmlStringtype|]
38+
39+
golden/enum.xsdnew file+12
@@ -0,0 +1,12 @@
1+<?xml version="1.0"?>
2+<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
3+ <xs:simpleType name="stringtype">
4+ <xs:annotation>
5+ <xs:documentation>Blah</xs:documentation>
6+ </xs:annotation>
7+ <xs:restriction base="xs:string">
8+ <xs:enumeration value="day"/>
9+ <xs:enumeration value="night"/>
10+ </xs:restriction>
11+ </xs:simpleType>
12+</xs:schema>
golden/enum.xsd.hsnew file+25
@@ -0,0 +1,25 @@
1+-- This file was generated by xsd-isogen version 0.1.0.0
2+-- https://github.com/dredozubov/xsd-isogen
3+
4+{-# OPTIONS_GHC -Wno-unused-imports #-}
5+module Dummy where
6+
7+import Control.DeepSeq
8+import Data.Scientific
9+import Data.THGen.XML
10+import Text.XML.DOM.Parser
11+import Text.XML.Writer
12+import Text.XML.ParentAttributes
13+import Prelude hiding ((*), (+))
14+
15+
16+{-|
17+Generated from: stringtype
18+
19+Blah
20+-}
21+"Stringtype" Exhaustive =:= enum ParserAndGenerator
22+ & "Day"
23+ & "Night"
24+
25+
golden/extension.xsdnew file+36
@@ -0,0 +1,36 @@
1+<?xml version="1.0"?>
2+<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
3+ <xs:complexType name="some">
4+ <xs:sequence>
5+ <xs:element name="a" type="xs:string"/>
6+ </xs:sequence>
7+ </xs:complexType>
8+ <xs:complexType name="basetype">
9+ <xs:sequence>
10+ <xs:element name="a" type="xs:string"/>
11+ </xs:sequence>
12+ </xs:complexType>
13+ <xs:complexType name="ext1">
14+ <xs:complexContent>
15+ <xs:extension base="basetype">
16+ <xs:sequence>
17+ <xs:element name="b" type="xs:int"/>
18+ </xs:sequence>
19+ </xs:extension>
20+ </xs:complexContent>
21+ </xs:complexType>
22+ <xs:complexType name="ext2">
23+ <xs:complexContent>
24+ <xs:extension base="basetype">
25+ <xs:sequence>
26+ <xs:element name="c" type="xs:int"/>
27+ </xs:sequence>
28+ </xs:extension>
29+ </xs:complexContent>
30+ </xs:complexType>
31+ <xs:complexType name="other">
32+ <xs:sequence>
33+ <xs:element name="a" type="basetype"/>
34+ </xs:sequence>
35+ </xs:complexType>
36+</xs:schema>
golden/extension.xsd.hsnew file+83
@@ -0,0 +1,83 @@
1+-- This file was generated by xsd-isogen version 0.1.0.0
2+-- https://github.com/dredozubov/xsd-isogen
3+
4+{-# OPTIONS_GHC -Wno-unused-imports #-}
5+module Dummy where
6+
7+import Control.DeepSeq
8+import Data.Scientific
9+import Data.THGen.XML
10+import Text.XML.DOM.Parser
11+import Text.XML.Writer
12+import Text.XML.ParentAttributes
13+import Prelude hiding ((*), (+))
14+
15+
16+{-|
17+Generated from: basetype
18+-}
19+"Basetype" =:= record ParserAndGenerator
20+ ! "a" [t|Text|]
21+
22+{-|
23+Generated from: ext2
24+-}
25+"Ext2" =:= record ParserAndGenerator
26+ ! "a" [t|Text|]
27+ ! "c" [t|Integer|]
28+
29+{-|
30+Generated from: ext1
31+-}
32+"Ext1" =:= record ParserAndGenerator
33+ ! "a" [t|Text|]
34+ ! "b" [t|Integer|]
35+
36+data AnyXmlBasetype
37+ = TheXmlBasetype !XmlBasetype
38+ | TheXmlExt2 !XmlExt2
39+ | TheXmlExt1 !XmlExt1
40+ deriving (Eq, Show)
41+
42+instance NFData AnyXmlBasetype where
43+ rnf = rwhnf
44+
45+instance FromDom AnyXmlBasetype where
46+ fromDom = do
47+ tp <- parseAttribute
48+ (matchName "{http://www.w3.org/2001/XMLSchema-instance}type")
49+ Right
50+ case tp of
51+ "Basetype" -> TheXmlBasetype <$> fromDom
52+ "Ext2" -> TheXmlExt2 <$> fromDom
53+ "Ext1" -> TheXmlExt1 <$> fromDom
54+ _ -> throwParserError (PEOther "Unexpected type")
55+
56+instance ToXML AnyXmlBasetype where
57+ toXML (TheXmlBasetype a) = toXML a
58+ toXML (TheXmlExt2 a) = toXML a
59+ toXML (TheXmlExt1 a) = toXML a
60+
61+instance ToXmlParentAttributes AnyXmlBasetype where
62+ toXmlParentAttributes (TheXmlBasetype a) =
63+ ("{http://www.w3.org/2001/XMLSchema-instance}type", "Basetype")
64+ : toXmlParentAttributes a
65+ toXmlParentAttributes (TheXmlExt2 a) =
66+ ("{http://www.w3.org/2001/XMLSchema-instance}type", "Ext2")
67+ : toXmlParentAttributes a
68+ toXmlParentAttributes (TheXmlExt1 a) =
69+ ("{http://www.w3.org/2001/XMLSchema-instance}type", "Ext1")
70+ : toXmlParentAttributes a
71+{-|
72+Generated from: other
73+-}
74+"Other" =:= record ParserAndGenerator
75+ ! "a" [t|AnyXmlBasetype|]
76+
77+{-|
78+Generated from: some
79+-}
80+"Some" =:= record ParserAndGenerator
81+ ! "a" [t|Text|]
82+
83+
golden/extension_dep.xsdnew file+31
@@ -0,0 +1,31 @@
1+<?xml version="1.0"?>
2+<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
3+ <xs:complexType name="X">
4+ <xs:sequence>
5+ <xs:element name="s" type="xs:string"/>
6+ </xs:sequence>
7+ </xs:complexType>
8+ <xs:complexType name="B">
9+ <xs:sequence>
10+ <xs:element name="s" type="xs:string"/>
11+ </xs:sequence>
12+ </xs:complexType>
13+ <xs:complexType name="C">
14+ <xs:complexContent>
15+ <xs:extension base="B">
16+ <xs:sequence>
17+ <xs:element name="x" type="X"/>
18+ </xs:sequence>
19+ </xs:extension>
20+ </xs:complexContent>
21+ </xs:complexType>
22+ <xs:complexType name="A">
23+ <xs:complexContent>
24+ <xs:extension base="C">
25+ <xs:sequence>
26+ <xs:element name="i" type="xs:int"/>
27+ </xs:sequence>
28+ </xs:extension>
29+ </xs:complexContent>
30+ </xs:complexType>
31+</xs:schema>
golden/extension_dep.xsd.hsnew file+101
@@ -0,0 +1,101 @@
1+-- This file was generated by xsd-isogen version 0.1.0.0
2+-- https://github.com/dredozubov/xsd-isogen
3+
4+{-# OPTIONS_GHC -Wno-unused-imports #-}
5+module Dummy where
6+
7+import Control.DeepSeq
8+import Data.Scientific
9+import Data.THGen.XML
10+import Text.XML.DOM.Parser
11+import Text.XML.Writer
12+import Text.XML.ParentAttributes
13+import Prelude hiding ((*), (+))
14+
15+
16+{-|
17+Generated from: X
18+-}
19+"X" =:= record ParserAndGenerator
20+ ! "s" [t|Text|]
21+
22+{-|
23+Generated from: A
24+-}
25+"A" =:= record ParserAndGenerator
26+ ! "s" [t|Text|]
27+ ! "x" [t|XmlX|]
28+ ! "i" [t|Integer|]
29+
30+{-|
31+Generated from: B
32+-}
33+"B" =:= record ParserAndGenerator
34+ ! "s" [t|Text|]
35+
36+{-|
37+Generated from: C
38+-}
39+"C" =:= record ParserAndGenerator
40+ ! "s" [t|Text|]
41+ ! "x" [t|XmlX|]
42+
43+data AnyXmlC
44+ = TheXmlC !XmlC
45+ | TheXmlA !XmlA
46+ deriving (Eq, Show)
47+
48+instance NFData AnyXmlC where
49+ rnf = rwhnf
50+
51+instance FromDom AnyXmlC where
52+ fromDom = do
53+ tp <- parseAttribute
54+ (matchName "{http://www.w3.org/2001/XMLSchema-instance}type")
55+ Right
56+ case tp of
57+ "C" -> TheXmlC <$> fromDom
58+ "A" -> TheXmlA <$> fromDom
59+ _ -> throwParserError (PEOther "Unexpected type")
60+
61+instance ToXML AnyXmlC where
62+ toXML (TheXmlC a) = toXML a
63+ toXML (TheXmlA a) = toXML a
64+
65+instance ToXmlParentAttributes AnyXmlC where
66+ toXmlParentAttributes (TheXmlC a) =
67+ ("{http://www.w3.org/2001/XMLSchema-instance}type", "C")
68+ : toXmlParentAttributes a
69+ toXmlParentAttributes (TheXmlA a) =
70+ ("{http://www.w3.org/2001/XMLSchema-instance}type", "A")
71+ : toXmlParentAttributes a
72+data AnyXmlB
73+ = TheXmlB !XmlB
74+ | TheAnyXmlC !AnyXmlC
75+ deriving (Eq, Show)
76+
77+instance NFData AnyXmlB where
78+ rnf = rwhnf
79+
80+instance FromDom AnyXmlB where
81+ fromDom = do
82+ tp <- parseAttribute
83+ (matchName "{http://www.w3.org/2001/XMLSchema-instance}type")
84+ Right
85+ case tp of
86+ "B" -> TheXmlB <$> fromDom
87+ "C" -> TheAnyXmlC <$> fromDom
88+ _ -> throwParserError (PEOther "Unexpected type")
89+
90+instance ToXML AnyXmlB where
91+ toXML (TheXmlB a) = toXML a
92+ toXML (TheAnyXmlC a) = toXML a
93+
94+instance ToXmlParentAttributes AnyXmlB where
95+ toXmlParentAttributes (TheXmlB a) =
96+ ("{http://www.w3.org/2001/XMLSchema-instance}type", "B")
97+ : toXmlParentAttributes a
98+ toXmlParentAttributes (TheAnyXmlC a) =
99+ ("{http://www.w3.org/2001/XMLSchema-instance}type", "C")
100+ : toXmlParentAttributes a
101+
golden/import.xsdnew file+16
@@ -0,0 +1,16 @@
1+<?xml version="1.0"?>
2+<xs:schema
3+ xmlns:xs="http://www.w3.org/2001/XMLSchema"
4+ xmlns:my="uri:my"
5+ xmlns:my1="uri:my1"
6+ targetNamespace="uri:my1"
7+ >
8+ <xs:import schemaLocation="imported.xsd" namespace="uri:my"/>
9+ <xs:element name="person">
10+ <xs:complexType>
11+ <xs:sequence>
12+ <xs:element name="name" type="my:stringtype"/>
13+ </xs:sequence>
14+ </xs:complexType>
15+ </xs:element>
16+</xs:schema>
golden/import.xsd.hsnew file+33
@@ -0,0 +1,33 @@
1+-- This file was generated by xsd-isogen version 0.1.0.0
2+-- https://github.com/dredozubov/xsd-isogen
3+
4+{-# OPTIONS_GHC -Wno-unused-imports #-}
5+module Dummy where
6+
7+import Control.DeepSeq
8+import Data.Scientific
9+import Data.THGen.XML
10+import Text.XML.DOM.Parser
11+import Text.XML.Writer
12+import Text.XML.ParentAttributes
13+import Prelude hiding ((*), (+))
14+
15+
16+{-|
17+Generated from: uri:my:stringtype
18+-}
19+newtype XmlStringtype
20+ = XmlStringtype
21+ { unXmlStringtype :: Text
22+ } deriving (Show, Eq, NFData, ToXML)
23+
24+instance FromDom XmlStringtype where
25+ fromDom = XmlStringtype <$> fromDom
26+
27+{-|
28+Generated from: uri:my1:person
29+-}
30+"Person" =:= record ParserAndGenerator
31+ ! "{uri:my1}name" [t|XmlStringtype|]
32+
33+
golden/import_ambig.xsdnew file+26
@@ -0,0 +1,26 @@
1+<?xml version="1.0"?>
2+<xs:schema
3+ xmlns:xs="http://www.w3.org/2001/XMLSchema"
4+ xmlns:my="uri:my"
5+ xmlns:my1="uri:my1"
6+ targetNamespace="uri:my1"
7+ >
8+ <xs:import schemaLocation="imported.xsd" namespace="uri:my"/>
9+ <xs:simpleType name="stringtype">
10+ <xs:restriction base="xs:string"/>
11+ </xs:simpleType>
12+ <xs:element name="AaBb">
13+ <xs:complexType>
14+ <xs:sequence>
15+ <xs:element name="name" type="my:stringtype"/>
16+ </xs:sequence>
17+ </xs:complexType>
18+ </xs:element>
19+ <xs:element name="AaBc">
20+ <xs:complexType>
21+ <xs:sequence>
22+ <xs:element name="name" type="my1:stringtype"/>
23+ </xs:sequence>
24+ </xs:complexType>
25+ </xs:element>
26+</xs:schema>
golden/import_ambig.xsd.hsnew file+50
@@ -0,0 +1,50 @@
1+-- This file was generated by xsd-isogen version 0.1.0.0
2+-- https://github.com/dredozubov/xsd-isogen
3+
4+{-# OPTIONS_GHC -Wno-unused-imports #-}
5+module Dummy where
6+
7+import Control.DeepSeq
8+import Data.Scientific
9+import Data.THGen.XML
10+import Text.XML.DOM.Parser
11+import Text.XML.Writer
12+import Text.XML.ParentAttributes
13+import Prelude hiding ((*), (+))
14+
15+
16+{-|
17+Generated from: uri:my:stringtype
18+-}
19+newtype XmlStringtype
20+ = XmlStringtype
21+ { unXmlStringtype :: Text
22+ } deriving (Show, Eq, NFData, ToXML)
23+
24+instance FromDom XmlStringtype where
25+ fromDom = XmlStringtype <$> fromDom
26+
27+{-|
28+Generated from: uri:my1:AaBb
29+-}
30+"AaBb" =:= record ParserAndGenerator
31+ ! "{uri:my1}name" [t|XmlStringtype|]
32+
33+{-|
34+Generated from: uri:my1:stringtype
35+-}
36+newtype XmlStringtype1
37+ = XmlStringtype1
38+ { unXmlStringtype1 :: Text
39+ } deriving (Show, Eq, NFData, ToXML)
40+
41+instance FromDom XmlStringtype1 where
42+ fromDom = XmlStringtype1 <$> fromDom
43+
44+{-|
45+Generated from: uri:my1:AaBc
46+-}
47+"AaBc1" =:= record ParserAndGenerator
48+ ! "{uri:my1}name" [t|XmlStringtype1|]
49+
50+
golden/imported.xsdnew file+9
@@ -0,0 +1,9 @@
1+<?xml version="1.0"?>
2+<xs:schema
3+ xmlns:xs="http://www.w3.org/2001/XMLSchema"
4+ targetNamespace="uri:my"
5+ >
6+ <xs:simpleType name="stringtype">
7+ <xs:restriction base="xs:string"/>
8+ </xs:simpleType>
9+</xs:schema>
golden/imported.xsd.hsnew file+27
@@ -0,0 +1,27 @@
1+-- This file was generated by xsd-isogen version 0.1.0.0
2+-- https://github.com/dredozubov/xsd-isogen
3+
4+{-# OPTIONS_GHC -Wno-unused-imports #-}
5+module Dummy where
6+
7+import Control.DeepSeq
8+import Data.Scientific
9+import Data.THGen.XML
10+import Text.XML.DOM.Parser
11+import Text.XML.Writer
12+import Text.XML.ParentAttributes
13+import Prelude hiding ((*), (+))
14+
15+
16+{-|
17+Generated from: uri:my:stringtype
18+-}
19+newtype XmlStringtype
20+ = XmlStringtype
21+ { unXmlStringtype :: Text
22+ } deriving (Show, Eq, NFData, ToXML)
23+
24+instance FromDom XmlStringtype where
25+ fromDom = XmlStringtype <$> fromDom
26+
27+
golden/include.xsdnew file+5
@@ -0,0 +1,5 @@
1+<?xml version="1.0"?>
2+<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
3+ <xs:include schemaLocation="included.xsd"/>
4+ <xs:element name="name" type="stringtype"/>
5+</xs:schema>
golden/include.xsd.hsnew file+27
@@ -0,0 +1,27 @@
1+-- This file was generated by xsd-isogen version 0.1.0.0
2+-- https://github.com/dredozubov/xsd-isogen
3+
4+{-# OPTIONS_GHC -Wno-unused-imports #-}
5+module Dummy where
6+
7+import Control.DeepSeq
8+import Data.Scientific
9+import Data.THGen.XML
10+import Text.XML.DOM.Parser
11+import Text.XML.Writer
12+import Text.XML.ParentAttributes
13+import Prelude hiding ((*), (+))
14+
15+
16+{-|
17+Generated from: stringtype
18+-}
19+newtype XmlStringtype
20+ = XmlStringtype
21+ { unXmlStringtype :: Text
22+ } deriving (Show, Eq, NFData, ToXML)
23+
24+instance FromDom XmlStringtype where
25+ fromDom = XmlStringtype <$> fromDom
26+
27+
golden/included.xsdnew file+6
@@ -0,0 +1,6 @@
1+<?xml version="1.0"?>
2+<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
3+ <xs:simpleType name="stringtype">
4+ <xs:restriction base="xs:string"/>
5+ </xs:simpleType>
6+</xs:schema>
golden/included.xsd.hsnew file+27
@@ -0,0 +1,27 @@
1+-- This file was generated by xsd-isogen version 0.1.0.0
2+-- https://github.com/dredozubov/xsd-isogen
3+
4+{-# OPTIONS_GHC -Wno-unused-imports #-}
5+module Dummy where
6+
7+import Control.DeepSeq
8+import Data.Scientific
9+import Data.THGen.XML
10+import Text.XML.DOM.Parser
11+import Text.XML.Writer
12+import Text.XML.ParentAttributes
13+import Prelude hiding ((*), (+))
14+
15+
16+{-|
17+Generated from: stringtype
18+-}
19+newtype XmlStringtype
20+ = XmlStringtype
21+ { unXmlStringtype :: Text
22+ } deriving (Show, Eq, NFData, ToXML)
23+
24+instance FromDom XmlStringtype where
25+ fromDom = XmlStringtype <$> fromDom
26+
27+
golden/seq.xsdnew file+14
@@ -0,0 +1,14 @@
1+<?xml version="1.0"?>
2+<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
3+ <xs:simpleType name="str">
4+ <xs:restriction base="xs:string"/>
5+ </xs:simpleType>
6+ <xs:element name="customer">
7+ <xs:complexType>
8+ <xs:sequence>
9+ <xs:element name="firstName" type="str"/>
10+ <xs:element name="lastName" type="str"/>
11+ </xs:sequence>
12+ </xs:complexType>
13+ </xs:element>
14+</xs:schema>
golden/seq.xsd.hsnew file+34
@@ -0,0 +1,34 @@
1+-- This file was generated by xsd-isogen version 0.1.0.0
2+-- https://github.com/dredozubov/xsd-isogen
3+
4+{-# OPTIONS_GHC -Wno-unused-imports #-}
5+module Dummy where
6+
7+import Control.DeepSeq
8+import Data.Scientific
9+import Data.THGen.XML
10+import Text.XML.DOM.Parser
11+import Text.XML.Writer
12+import Text.XML.ParentAttributes
13+import Prelude hiding ((*), (+))
14+
15+
16+{-|
17+Generated from: str
18+-}
19+newtype XmlStr
20+ = XmlStr
21+ { unXmlStr :: Text
22+ } deriving (Show, Eq, NFData, ToXML)
23+
24+instance FromDom XmlStr where
25+ fromDom = XmlStr <$> fromDom
26+
27+{-|
28+Generated from: customer
29+-}
30+"Customer" =:= record ParserAndGenerator
31+ ! "firstName" [t|XmlStr|]
32+ ! "lastName" [t|XmlStr|]
33+
34+
golden/simple.xsdnew file+25
@@ -0,0 +1,25 @@
1+<?xml version="1.0"?>
2+<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
3+ <xs:simpleType name="stringtype">
4+ <xs:annotation>
5+ <xs:documentation>Blah</xs:documentation>
6+ </xs:annotation>
7+ <xs:restriction base="xs:string"/>
8+ </xs:simpleType>
9+ <xs:element name="name" type="stringtype"/>
10+ <xs:element name="a">
11+ <xs:annotation>
12+ <xs:documentation>A blah</xs:documentation>
13+ </xs:annotation>
14+ <xs:complexType>
15+ <xs:sequence>
16+ <xs:element name="a" type="stringtype"/>
17+ <xs:element name="b" minOccurs="1" type="stringtype"/>
18+ <xs:element name="c" minOccurs="0" maxOccurs="1" type="stringtype"/>
19+ <xs:element name="d" minOccurs="1" maxOccurs="2" type="stringtype"/>
20+ <xs:element name="e" minOccurs="1" maxOccurs="unbounded" type="stringtype"/>
21+ <xs:element name="f" type="xs:decimal"/>
22+ </xs:sequence>
23+ </xs:complexType>
24+ </xs:element>
25+</xs:schema>
golden/simple.xsd.hsnew file+40
@@ -0,0 +1,40 @@
1+-- This file was generated by xsd-isogen version 0.1.0.0
2+-- https://github.com/dredozubov/xsd-isogen
3+
4+{-# OPTIONS_GHC -Wno-unused-imports #-}
5+module Dummy where
6+
7+import Control.DeepSeq
8+import Data.Scientific
9+import Data.THGen.XML
10+import Text.XML.DOM.Parser
11+import Text.XML.Writer
12+import Text.XML.ParentAttributes
13+import Prelude hiding ((*), (+))
14+
15+
16+{-|
17+Generated from: stringtype
18+
19+Blah
20+-}
21+newtype XmlStringtype
22+ = XmlStringtype
23+ { unXmlStringtype :: Text
24+ } deriving (Show, Eq, NFData, ToXML)
25+
26+instance FromDom XmlStringtype where
27+ fromDom = XmlStringtype <$> fromDom
28+
29+{-|
30+Generated from: a
31+-}
32+"A" =:= record ParserAndGenerator
33+ ! "a" [t|XmlStringtype|]
34+ ! "b" [t|XmlStringtype|]
35+ ? "c" [t|XmlStringtype|]
36+ + "d" [t|XmlStringtype|]
37+ + "e" [t|XmlStringtype|]
38+ ! "f" [t|Scientific|]
39+
40+
golden/test.shnew file+5
@@ -0,0 +1,5 @@
1+#!/bin/bash
2+
3+for file in *.xsd; do
4+ cabal run -v0 xsd-isogen -- -i $file > $file.hs
5+done
src/BuiltInTypes.hsnew file+67
@@ -0,0 +1,67 @@
1+{-# LANGUAGE OverloadedStrings #-}
2+
3+module BuiltInTypes
4+( builtInTypes
5+)
6+where
7+
8+import Data.Map (Map)
9+import qualified Data.Map as Map
10+
11+import qualified Xsd
12+
13+import Gen
14+
15+builtInTypes :: Map Xsd.QName TypeName
16+builtInTypes = Map.fromList . map qualify $
17+ [ ("anyURI", "Text")
18+ , ("base64Binary", "ByteString")
19+ , ("boolean", "Bool")
20+ , ("date", "Date")
21+ , ("dateTime", "Text")
22+ , ("dateTimeStamp", "Text")
23+ , ("decimal", "Scientific")
24+ , ("integer", "Integer")
25+ , ("long", "Integer")
26+ , ("int", "Integer")
27+ , ("short", "Integer")
28+ , ("byte", "Integer")
29+ , ("nonNegativeInteger", "Integer")
30+ , ("positiveInteger", "Integer")
31+ , ("unsignedLong", "Integer")
32+ , ("unsignedInt", "Integer")
33+ , ("unsignedByte", "Integer")
34+ , ("nonPositiveInteger", "Integer")
35+ , ("negativeInteger", "Integer")
36+ , ("float", "Float")
37+ , ("double", "Scientific")
38+ , ("gDay", "Day")
39+ , ("gMonth", "Text")
40+ , ("gMonthDay", "Text")
41+ , ("gYear", "Text")
42+ , ("gYearMonth", "Text")
43+ , ("hexBinary", "Text")
44+ , ("NOTATION", "Text")
45+ , ("QName", "Text")
46+ , ("string", "Text")
47+ , ("normalizedString", "Text")
48+ , ("token", "Text")
49+ , ("language", "Text")
50+ , ("NCName", "Text")
51+ , ("Entity", "Text")
52+ , ("ID", "Text")
53+ , ("IDREF", "Text")
54+ , ("NMToken", "Text")
55+ , ("time", "Text")
56+ , ("duration", "Text")
57+ ]
58+ where
59+ qualify (n, t) =
60+ let
61+ tn = TypeName
62+ { tnName = t
63+ , tnPrefixed = t
64+ , tnQName = qname
65+ }
66+ qname = Xsd.QName (Just (Xsd.Namespace Xsd.schemaNamespace)) n
67+ in (qname, tn)
src/Gen.hsnew file+213
@@ -0,0 +1,213 @@
1+{-# LANGUAGE OverloadedStrings #-}
2+{-# LANGUAGE GeneralizedNewtypeDeriving #-}
3+
4+module Gen
5+( GenState(..)
6+, Gen
7+, runGen
8+, TypeName(..)
9+, getMode
10+, writeCode
11+, warn
12+, genError
13+, resolveType
14+, resolveTypeName
15+, knownTypeName
16+, extensions
17+, makeTypeName
18+, makeFieldName
19+, makeEnumName
20+)
21+where
22+
23+import qualified Data.Char as Char
24+import Data.Text (Text)
25+import qualified Data.Text as Text
26+import Data.Maybe
27+import Data.Set (Set)
28+import qualified Data.Set as Set
29+import Data.Map (Map)
30+import qualified Data.Map as Map
31+import Control.Monad
32+import Control.Monad.Trans
33+import Control.Monad.Trans.State
34+import Control.Monad.Trans.Except
35+import qualified Xsd
36+
37+import Options (GenType(..))
38+
39+-- | Generator state
40+data GenState = GenState
41+ { gsTypes :: Map Xsd.QName Xsd.Type
42+ -- ^ All global types
43+ , gsExtensions :: Map Xsd.QName [(Xsd.QName, Xsd.Type)]
44+ -- ^ Lift of extension of the specified type
45+ , gsKnownTypes :: Map Xsd.QName TypeName
46+ -- ^ Global types that were already processed and their generated names
47+ , gsBoundTypeName :: Set Text
48+ -- ^ prefixed names that were already used
49+ , gsBoundFields :: Set Text
50+ -- ^ field names that were already used
51+ , gsMode :: GenType
52+ , gsCode :: [Text]
53+ , gsWarnings :: [Text]
54+ }
55+
56+-- | Generator monad
57+newtype Gen a = Gen
58+ { fromGen :: StateT GenState (Except String) a
59+ }
60+ deriving (Functor, Applicative, Monad)
61+
62+-- | Run generator
63+runGen :: GenState -> Gen a -> Either String GenState
64+runGen s m = runExcept $ execStateT (fromGen m) s
65+
66+getMode :: Gen GenType
67+getMode = Gen $ gets gsMode
68+
69+-- | Add lines to the output
70+writeCode :: [Text] -> Gen ()
71+writeCode code = Gen $ modify' $ \s -> s
72+ { gsCode = reverse code ++ gsCode s
73+ }
74+
75+-- | Emit warning
76+warn :: Text -> Gen ()
77+warn msg = Gen $ modify' $ \s -> s
78+ { gsWarnings = ("WARNING: " <> msg) : gsWarnings s
79+ }
80+
81+-- | Usually we prefix types with "Xml", but for records we need plain name
82+--
83+-- Note that built-in types doesn't have prefix, so prefixed name is
84+-- the same as the plain one.
85+data TypeName = TypeName
86+ { tnName :: Text
87+ -- ^ plain name
88+ , tnPrefixed :: Text
89+ -- ^ name with "Xml" prefix
90+ , tnQName :: Xsd.QName
91+ -- ^ the original name
92+ }
93+
94+-- | Allocate new type name for given qualified name
95+--
96+-- It checks for ambiguous type names and fields
97+makeTypeName :: Xsd.QName -> Xsd.Type -> Gen TypeName
98+makeTypeName name tp = go Nothing
99+ where
100+ go suffix = do
101+ let
102+ typeName = capitalize (Xsd.qnName name)
103+ <> maybe "" (Text.pack . show) suffix
104+ prefixed = "Xml" <> typeName
105+ next = case suffix of
106+ Nothing -> Just (1 :: Int)
107+ Just i -> Just (succ i)
108+ bound <- Gen $ gets (Set.member prefixed . gsBoundTypeName)
109+ duplicateFields <- hasDuplicateFields typeName tp
110+ if bound || duplicateFields
111+ then go next
112+ else do
113+ let
114+ tn = TypeName
115+ { tnName = typeName
116+ , tnPrefixed = prefixed
117+ , tnQName = name
118+ }
119+ Gen $ modify' $ \s -> s
120+ { gsKnownTypes = Map.insert name tn (gsKnownTypes s)
121+ , gsBoundTypeName = Set.insert prefixed (gsBoundTypeName s)
122+ }
123+ return tn
124+
125+makeFieldName :: TypeName -> Xsd.QName -> Gen Text
126+makeFieldName typeName name = do
127+ -- here we remember that the field name is bound, so that
128+ -- hasDuplicateFields can check for ambiguous fields
129+ let prefixed = makePrefixedField (tnName typeName) name
130+ Gen $ modify' $ \s -> s
131+ { gsBoundFields = Set.insert prefixed (gsBoundFields s)
132+ }
133+ return (qualifier <> Xsd.qnName name)
134+ where
135+ qualifier = maybe ""
136+ (\(Xsd.Namespace ns) -> "{" <> ns <> "}")
137+ (Xsd.qnNamespace name)
138+
139+-- | Make value for enum
140+--
141+-- We may need to handle duplicate enum values here
142+makeEnumName :: Text -> Gen Text
143+makeEnumName = return . capitalize
144+
145+-- | Check whether field name is ambiguous
146+hasDuplicateFields :: Text -> Xsd.Type -> Gen Bool
147+hasDuplicateFields typeName tp = do
148+ bound <- forM (allFields tp) $ \f -> do
149+ let prefixed = makePrefixedField typeName f
150+ Gen $ gets (Set.member prefixed . gsBoundFields)
151+ return (or bound)
152+
153+-- | Get all fields from the type
154+allFields :: Xsd.Type -> [Xsd.QName]
155+allFields (Xsd.TypeSimple _) = []
156+allFields (Xsd.TypeComplex t) =
157+ case Xsd.complexContent t of
158+ Xsd.ContentPlain c ->
159+ case Xsd.plainContentModel c of
160+ Just (Xsd.Sequence es) -> mapMaybe elementName es
161+ _ -> []
162+ Xsd.ContentSimple _ -> []
163+ Xsd.ContentComplex (Xsd.ComplexContentExtension e) ->
164+ case Xsd.complexExtensionModel e of
165+ Just (Xsd.Sequence es) -> mapMaybe elementName es
166+ _ -> []
167+ Xsd.ContentComplex (Xsd.ComplexContentRestriction _) -> []
168+ where
169+ elementName = Xsd.refOr
170+ (const Nothing)
171+ (Just . Xsd.elementName)
172+
173+-- | Prefix field name with based on the type name
174+makePrefixedField :: Text -> Xsd.QName -> Text
175+makePrefixedField typeName field = prefix <> Xsd.qnName field
176+ where
177+ prefix
178+ = Text.map Char.toLower
179+ . Text.filter (\c -> Char.isUpper c || Char.isDigit c)
180+ $ typeName
181+
182+-- | Get generated name for the type name
183+--
184+-- Returns Nothing if the type was not generated yet
185+knownTypeName :: Xsd.QName -> Gen (Maybe TypeName)
186+knownTypeName name = Gen $ gets (Map.lookup name . gsKnownTypes)
187+
188+extensions :: Xsd.QName -> Gen [(Xsd.QName, Xsd.Type)]
189+extensions base = Gen $ gets (Map.findWithDefault [] base . gsExtensions)
190+
191+-- | Returns type for the reference
192+resolveType :: Xsd.QName -> Gen Xsd.Type
193+resolveType name = do
194+ mt <- Gen $ gets (Map.lookup name . gsTypes)
195+ case mt of
196+ Just t -> return t
197+ Nothing -> genError ("unknown reference to " <> show name)
198+
199+-- | Returns type type for the reference
200+--
201+-- It should already be generated
202+resolveTypeName :: Xsd.QName -> Gen TypeName
203+resolveTypeName name = knownTypeName name
204+ >>= maybe (genError ("unresolved type: " <> show name)) return
205+
206+-- | Fail the generator
207+genError :: String -> Gen a
208+genError = Gen . lift . throwE
209+
210+capitalize :: Text -> Text
211+capitalize t
212+ | Text.null t = t
213+ | otherwise = Text.singleton (Char.toUpper (Text.head t)) <> Text.tail t
src/Main.hs+436−440
@@ -1,454 +1,450 @@
1−{-# LANGUAGE FlexibleInstances #-}
2−{-# LANGUAGE LambdaCase #-}
31{-# LANGUAGE OverloadedStrings #-}
4−{-# LANGUAGE TupleSections #-}
52
63module Main
7− ( main
8− ) where
9−
10−import Control.Monad.RWS.CPS (RWS, execRWS)
11−import Control.Monad.Reader
12−import Control.Monad.State.Strict
13−import Control.Monad.Writer.CPS (Writer, tell, execWriter)
14−import Data.ByteString.Lazy as BL
15−import Data.Char as C
16−import Data.Default
17−import Data.Foldable as F
18−import Data.List as L
19−import Data.Map.Strict as M
20−import Data.Set as S
21−import Data.Text as T
22−import Data.Text.IO as T
23−import Data.Traversable
24−import Debug.Trace
25−import System.Environment
26−import System.IO
27−import Text.XSD
28−import Options
29−import Data.Maybe
30−
31−
32−makeNamePrefix :: Text -> Text
33−makeNamePrefix = T.map C.toLower . T.filter (\c -> C.isUpper c || C.isDigit c)
34−
35−type GenMonad = ReaderT DatatypeMap (Writer Text)
36−
37−class Fields a where
38− fields :: a -> [Text]
39−
40−instance Fields Datatype where
41− fields (TypeComplex ty) = fields ty
42− fields (TypeSimple ty) = fields ty
43−
44−instance Fields SimpleType where
45− fields ty@(STAtomic tyName _ _ restrictions) = if shouldBeNewtype ty
46− then [getNewtypeUnconsName tyName]
47− else getEnumeratedCode restrictions
48−
49−instance Fields ComplexType where
50− fields (ComplexType _ _ _ (Just g)) = fields g
51− fields (ComplexType _ _ _ Nothing) = []
52−
53−instance Fields ModelGroupSchema where
54− fields (CTSequence els) = snd . name <$> els
55− fields (CTChoice els) = snd . name <$> els
56− fields (CTAll els) = snd . name <$> els
57−
58−class References a where
59− references :: a -> [Text]
4+( main
5+)
6+where
607
61−instance References Datatype where
62− references (TypeComplex (ComplexType _ _ _ mmgs)) =
63− case mmgs of
64− Just mgs -> case mgs of
65− CTSequence els -> F.concatMap references els
66− CTChoice els -> F.concatMap references els
67− CTAll els -> F.concatMap references els
68− Nothing -> []
69− references (TypeSimple _) = []
70−
71−instance References Element where
72− references (Element _ t _ _ _) = references t
73−
74−instance References DatatypeRef where
75− references (DatatypeRef ty) = [ty]
76− references (InlineComplex dt) = references dt
77−
78−instance References a => References [a] where
79− references = F.concatMap references
80−
81−-- | 'DatatypeMap' are the types left to process
82−-- 'S.Set Text' are the types already processed.
83−type PreMonad = StateT (DatatypeMap, S.Set Text) (Writer [(Text, Datatype)])
84−
85−type PreElemMonad = RWS DatatypeMap [Element] [Element]
86−
87−-- | Marks the given type as processed.
88−proceed :: Text -> Datatype -> PreMonad ()
89−proceed t dt = do
90− tell [(t, dt)]
91− modify $ \(f,s) -> (M.delete t f, S.insert t s)
92−
93−-- | Marks element as processed.
94−proceed' :: Element -> PreElemMonad ()
95−proceed' el = do
96− tell [el]
97− modify $ L.delete el
98−
99−untwistDeps :: [Element] -> DatatypeMap -> ([(Text, Datatype)], [Element])
100−untwistDeps e dm = (dList, e')
101− where
102− dList = disambiguateNames . nub . execWriter $ do
103− (a, _) <- runStateT sortDatatypesByDeps (dm, S.empty)
104− return a
105− (_, e') = execRWS sortElemsByDeps (M.fromList dList) e
106−
107−type Abbreviation = Text
108−type Subelement = Text
109−type DisambiguaterMonad = State (M.Map Abbreviation (S.Set Subelement))
110−
111−-- | If any two datatypes with the same name abbreviation ('makeNamePrefix')
112−-- have any fields in common, then one of them gets a \"2\" appended to it
113−-- (\"_2\" if the last character of the name was a digit).
114−-- Repeat the same process until there are no conflicts.
115−-- If we have already appended a \"2\" to a datatype and it still has conflicts,
116−-- we change the 2 to a 3, a 3 to a 4 and so on.
117−disambiguateNames :: [(Text, Datatype)] -> [(Text, Datatype)]
118−disambiguateNames = flip evalState mempty . mapM disambiguateName
119− where
120− disambiguateName :: (Text, Datatype) -> DisambiguaterMonad (Text, Datatype)
121− disambiguateName (tyName', dt) = do
122− let rNames = S.fromList $ fields dt
123− (, dt) <$> attempt tyName' rNames 1
124− where
125− attempt :: Abbreviation -> S.Set Subelement -> Int -> DisambiguaterMonad Text
126− attempt tyName fieldNames n = do
127− processedTypes <- get
128− let
129− newTyName
130− | n == 1 = tyName
131− | shouldSeparateNumber = tyName <> "_" <> T.pack (show n)
132− | otherwise = tyName <> T.pack (show n)
133− abr = makeNamePrefix newTyName
134− case M.lookup abr processedTypes of
135− Just usedNames ->
136− if S.null $ S.intersection fieldNames usedNames
137− then modify (M.adjust (S.union fieldNames) abr) >> return newTyName
138− else attempt tyName fieldNames (n + 1)
139− Nothing -> modify (M.insert abr fieldNames) >> return newTyName
140− shouldSeparateNumber = isDigit $ T.last tyName'
141−
142−sortElemsByDeps :: PreElemMonad ()
143−sortElemsByDeps = do
144− els <- get
145− if Prelude.null els then pure () else do
146− let first = Prelude.head els
147− -- traceM $ "sortElemsByDeps: " <> show first
148− case references first of
149− [] -> proceed' first >> sortElemsByDeps
150− xs -> do
151− results <- traverse memberDatatype xs
152− if L.and results
153− then proceed' first >> sortElemsByDeps
154− else do
155− let failed = fmap snd $ L.filter (not . fst) $ L.zip results xs
156− error $ "impossible to lookup types: " <> show failed
157−
158−memberDatatype :: Text -> PreElemMonad Bool
159−memberDatatype ty = do
160− -- traceM $ "memberDatatype: " <> show ty
161− dm <- ask
162− case M.lookup ty dm of
163− Just _ -> return True
164− Nothing -> return False
165−
166−sortDatatypesByDeps :: PreMonad ()
167−sortDatatypesByDeps = do
168− (dm, _) <- get
169− if M.null dm then pure () else do
170− let first = Prelude.head (M.toList dm)
171− traceM $ "sortDatatypesByDeps: " <> show first
172− case references $ snd first of
173− [] -> uncurry proceed first >> sortDatatypesByDeps
174− xs -> do
175− traverse_ lookupDatatype xs
176− uncurry proceed first
177− sortDatatypesByDeps
178−
179−lookupDatatype :: Text -> PreMonad ()
180−lookupDatatype ty = do
181− traceM $ "lookupDatatype: " <> show ty
182− (dm, s) <- get
183− case M.lookup ty dm of
184− Nothing -> if S.member ty s then pure () else
185− error $ "can't look up datatype: " <> show ty
186− Just typ -> case references typ of
187− [] -> proceed ty typ
188− xs -> traverse_ lookupDatatype xs >> proceed ty typ
189−
190−class Capitalizable t where
191− capitalize :: t -> t
192−
193−instance Capitalizable String where
194− capitalize [] = []
195− capitalize (a:as) = C.toUpper a : as
196−
197−instance Capitalizable Text where
198− capitalize t
199− | T.null t = t
200− | otherwise = T.singleton (C.toUpper (T.head t)) <> T.tail t
201−
202−data DataFlavour = QQ | Plain
203−
204−-- TODO: improve after moving some XML types to xml-isogen
205−simpleTypeToIsoType :: DataFlavour -> SimpleAtomicType -> Text
206−simpleTypeToIsoType fl = \case
207− STAnyURI -> wrap fl "Text"
208− STBase64Binary -> wrap fl "ByteString"
209− STBoolean -> wrap fl "Bool"
210− STDate -> wrap fl "Date \"%YYYYj\""
211− STDateTime -> wrap fl "Text"
212− STDateTimeStamp -> wrap fl "Text"
213− STDecimal DTDecimal -> wrap fl "Double"
214− (STDecimal _) -> wrap fl "Integer"
215− STDouble -> wrap fl "Double"
216− (STDuration _) -> wrap fl "Integer"
217− STFloat -> wrap fl "Float"
218− STGDay -> wrap fl "Day"
219− STGMonth -> wrap fl "Text"
220− STGMonthDay -> wrap fl "Text"
221− STGYear -> wrap fl "Text"
222− STGYearMonth -> wrap fl "Text"
223− STHexBinary -> wrap fl "Text"
224− STNOTATION -> wrap fl "Text"
225− STQName -> wrap fl "Text"
226− (STString _) -> wrap fl "Text"
227− STTime -> wrap fl "Text"
228− where
229− wrap QQ t = "[t|" <> t <> "|]"
230− wrap Plain t = t
231−
232−tellGen
233− :: [Text] -- generated code
234− -> Text -- comment
235− -> GenMonad ()
236−tellGen code comment =
237− tell $ "\n" <> comment <> T.unlines code
8+import Data.Text (Text)
9+import qualified Data.Text as Text
10+import qualified Data.Text.IO as Text
11+import Data.Maybe
12+import qualified Data.List as List
13+import qualified Data.Set as Set
14+import Data.Map (Map)
15+import qualified Data.Map as Map
16+import Control.Monad
17+import qualified System.IO as IO
18+import qualified Xsd
23819
239−runGen :: DatatypeMap -> GenMonad a -> Text
240−runGen dm act = execWriter $ runReaderT act dm
20+import Options
21+import Gen
22+import References
23+import BuiltInTypes
24124
242−generateIsoXml :: Maybe Header -> ModuleName -> GenType -> XSD -> Text
243−generateIsoXml mHeader name genType xsd@(XSD (e,dm)) =
25+main :: IO ()
26+main = do
27+ opts <- getOptions
28+ schema <- Xsd.getSchema (oInput opts)
29+ res <- either fail return
30+ (generate opts schema)
31+ mapM_ (Text.hPutStrLn IO.stderr) (resultWarnings res)
32+ Text.putStrLn (resultCode res)
33+
34+data Result = Result
35+ { resultCode :: Text
36+ , resultWarnings :: [Text]
37+ }
38+
39+generate :: Options -> Xsd.Schema -> Either String Result
40+generate opts schema = do
24441 let
245− (dList, el) = untwistDeps e dm
246− in (header mHeader name genType <>) . runGen dm $ do
247− for_ dList $ \(t,dt) -> do
248− generateIsoDatatype genType (Just t) dt []
249− for_ el (generateIsoRecord genType)
250−
251−toQualifier :: Int -> Int -> Text
252−toQualifier minOc maxOc
253− | minOc == 1 && maxOc == 1 = "!"
254− | minOc == 0 && maxOc == 1 = "?"
255− | minOc == 1 && maxOc > minOc = "+"
256− | minOc == 1 && maxOc < minOc = error "maxOccurs < minOccurs"
257− | otherwise = "*"
258−
259−header :: Maybe Header -> ModuleName -> GenType -> Text
260−header mHeader name genType = T.unlines $
261− maybeToList (T.unlines . fmap ("-- " <>) . T.lines <$> mHeader) <>
262− [ "module " <> name <> " where"
263− , ""
264− , "import Control.DeepSeq"
265− , "import Data.THGen.XML"
266− ] <> specImports <>
267− [ "import Prelude hiding ((*), (+))"
268− , "" ]
42+ types = Map.union (Xsd.schemaTypes schema)
43+ (elementsToTypes (Xsd.schemaElements schema))
44+ s <- runGen (initialGenState opts types) $ do
45+ genHeader opts
46+ genTypes types
47+ Right Result
48+ { resultCode = Text.unlines (reverse (gsCode s))
49+ , resultWarnings = reverse (gsWarnings s)
50+ }
51+
52+elementsToTypes :: Map Xsd.QName Xsd.Element -> Map Xsd.QName Xsd.Type
53+elementsToTypes = List.foldl' step Map.empty . Map.toList
26954 where
270− specImports = case genType of
271− Generator -> [ xmlWriter ]
272− Parser -> [ domParser ]
273− Both -> [ domParser, xmlWriter ]
274− xmlWriter, domParser :: Text
275− xmlWriter = "import Text.XML.Writer"
276− domParser = "import Text.XML.DOM.Parser.FromDom"
277−
278−quote :: Text -> Text
279−quote t = "\"" <> t <> "\""
280−
281−generateIsoRecord :: GenType -> Element -> GenMonad ()
282−generateIsoRecord
283− genType
284− (Element (_, laxName) elXtype _ _ elAnnotations) = do
285− written <- ask
286− case elXtype of
287− InlineComplex dt ->
288− generateIsoDatatype genType laxName dt elAnnotations
289− DatatypeRef nam -> if M.member nam written
290− then pure ()
291− else error $ "Implementation failure: "
292− <> show nam <> " type never encountered before"
293−
294−data CommentType = Haddock | Regular
295−
296−toComment :: CommentType -> [Annotation] -> Text
297−toComment ct ann = case texts of
298− [] -> ""
299− (h:tl) -> T.unlines ((prefix <> h) : fmap ("-- "<>) tl)
300− where
301− texts = stripTexts $ F.concatMap (wrap80 . fromDocumentation) ann
302− stripTexts = \case
303− [] -> []
304− l@(h:tl) -> if T.all (\x -> x == ' ' || x == '\n') h then stripTexts tl else l
305− prefix = case ct of
306− Haddock -> "-- | "
307− Regular -> "-- "
308−
309−fromDocumentation :: Annotation -> Text
310−fromDocumentation (Documentation t) = t
311−
312−wrap80 :: Text -> [Text]
313−wrap80 = L.foldl' go [] . T.split (==' ')
314− where
315− go [] e = [e]
316− go l e =
317− let last' = L.last l
318− in if T.length last' + T.length e >= 73 -- 80 minus "-- | " and ' '
319− then l <> pure e
320− else L.init l <> [L.last l <> " " <> e]
321−
322−generateIsoDatatype
323− :: GenType
324− -> Text
325− -> Datatype
326− -> [Annotation]
327− -> GenMonad ()
328− -- ^ (type name for lookups, generated code)
329−generateIsoDatatype
330− genType
331− eName
332− (TypeComplex (ComplexType _ _ tyAnnotations mGroupSchema))
333− annotationsRec = do
334− case mGroupSchema of
335− Just groupSchema -> case groupSchema of
336− CTSequence elements -> do
337− tyFields <- for elements (generateIsoField genType)
55+ step types (_, e) = case Xsd.elementType e of
56+ Xsd.Ref _ -> types
57+ Xsd.Inline t -> Map.insert (Xsd.elementName e) t types
58+
59+genTypes :: Map Xsd.QName Xsd.Type -> Gen ()
60+genTypes types = mapM_ (uncurry genTopType) . Map.toList $ types
61+
62+genTopType :: Xsd.QName -> Xsd.Type -> Gen ()
63+genTopType = genType []
64+
65+genType :: [Xsd.Annotation] -> Xsd.QName -> Xsd.Type -> Gen ()
66+genType annotations name tp = do
67+ known <- knownTypeName name
68+ case known of
69+ Just _ -> return () -- already generated
70+ Nothing -> do
71+ -- we should generate dependencies before the type itself to make
72+ -- sure they are in scope when xml-isogen quotes them
73+ -- (stage restriction in TH)
74+ genDependencies tp
75+ typeName <- makeTypeName name tp
76+ case tp of
77+ Xsd.TypeSimple t -> genSimpleType typeName t annotations
78+ Xsd.TypeComplex t -> genComplexType typeName t annotations
79+ genExtension name
80+
81+genExtension :: Xsd.QName -> Gen ()
82+genExtension base = do
83+ exts <- extensions base
84+ case exts of
85+ [] -> return ()
86+ types -> do
87+ mapM_ (uncurry genTopType) types
88+ baseTypeName <- tnPrefixed <$> resolveTypeName base
89+ writeCode
90+ [ "data Any" <> baseTypeName
91+ , " = The" <> baseTypeName <> " !" <> baseTypeName
92+ ]
93+ let
94+ mkConstructor (n, _) = do
95+ tn <- tnPrefixed <$> resolveTypeName n
96+ exts' <- extensions n
33897 let
339− comment = toComment Haddock $ annotationsRec
340− <> [Documentation ""]
341− <> tyAnnotations
342− tyHeader =
343− quote (capitalize eName)
344− <> " =:= record "
345− <> genTypeToText genType
346− tellGen (tyHeader : tyFields) comment
347− CTChoice _ -> error "'Choice' group not supported yet"
348− CTAll _ -> error "'All' group not supported yet"
349− Nothing -> error $ "No group schema provided for type: " <> T.unpack eName
350−generateIsoDatatype
351− genType
352− eName
353− (TypeSimple ty@(STAtomic iName sat tyAnnotations restrictions))
354− annotationsRec = do
355− let eName = eName
356− let
357− comment =
358− toComment Haddock $ annotationsRec
359− <> [Documentation ""]
360− <> tyAnnotations
361− if not (shouldBeNewtype ty)
362− then tellGen
363− (generateEnum
364− (quote $ capitalize eName)
365− (getEnumeratedCode restrictions))
366− comment
367− else
368− tellGen (generateNewtype genType iName sat) comment
369−
370−getEnumeratedCode :: [Restriction] -> [Text]
371−getEnumeratedCode = F.concatMap fromEnumeration . Prelude.filter isEnumeration
98+ n' = case exts' of
99+ [] -> tn
100+ _ -> "Any" <> tn
101+ return (" | The" <> n' <> " !" <> n')
102+ constructors <- mapM mkConstructor types
103+ writeCode constructors
104+ writeCode
105+ [ " deriving (Eq, Show)"
106+ , ""
107+ ]
108+ writeCode
109+ [ "instance NFData Any" <> baseTypeName <> " where"
110+ , " rnf = rwhnf"
111+ , " "
112+ ]
113+ mode <- getMode
114+ unless (mode == Generator) $ do
115+ writeCode
116+ [ "instance FromDom Any" <> baseTypeName <> " where"
117+ , " fromDom = do"
118+ , " tp <- parseAttribute"
119+ , " (matchName " <>
120+ "\"{http://www.w3.org/2001/XMLSchema-instance}type\")"
121+ , " Right"
122+ , " case tp of"
123+ ]
124+ let
125+ mkClause n = do
126+ tn <- resolveTypeName n
127+ exts' <- extensions n
128+ n' <- case exts' of
129+ [] -> return (tnPrefixed tn)
130+ _ | n == base -> return (tnPrefixed tn)
131+ _ -> return ("Any" <> tnPrefixed tn)
132+ return $ mconcat
133+ [ " \""
134+ , tnName tn
135+ , "\" -> The"
136+ , n'
137+ , " <$> fromDom"
138+ ]
139+ clauses <- mapM mkClause (base : map fst types)
140+ writeCode clauses
141+ writeCode
142+ [ " _ -> throwParserError (PEOther \"Unexpected type\")"
143+ , ""
144+ ]
145+ unless (mode == Parser) $ do
146+ writeCode
147+ [ "instance ToXML Any" <> baseTypeName <> " where"
148+ , " toXML (The" <> baseTypeName <> " a) = toXML a"
149+ ]
150+ let
151+ mkClause (n, _) = do
152+ tn <- tnPrefixed <$> resolveTypeName n
153+ exts' <- extensions n
154+ let
155+ n' = case exts' of
156+ [] -> tn
157+ _ -> "Any" <> tn
158+ return (" toXML (The" <> n' <> " a) = toXML a")
159+ clauses <- mapM mkClause types
160+ writeCode clauses
161+ writeCode [""]
162+ writeCode
163+ [ "instance ToXmlParentAttributes Any" <> baseTypeName <> " where"
164+ ]
165+ let
166+ toAttrLine n = do
167+ tn <- resolveTypeName n
168+ exts' <- extensions n
169+ n' <- case exts' of
170+ [] -> return (tnPrefixed tn)
171+ _ | n == base -> return (tnPrefixed tn)
172+ _ -> return ("Any" <> tnPrefixed tn)
173+ return
174+ [ " toXmlParentAttributes (The" <> n' <> " a) ="
175+ , " (\"{http://www.w3.org/2001/XMLSchema-instance}type\", \""
176+ <> tnName tn <> "\")"
177+ , " : toXmlParentAttributes a"
178+ ]
179+ attrLines <- mapM toAttrLine (base : map fst types)
180+ writeCode (concat attrLines)
181+
182+genDependencies :: References a => a -> Gen ()
183+genDependencies a = forM_ (references a) $ \name -> do
184+ known <- knownTypeName name
185+ case known of
186+ Just _ -> return () -- already generated
187+ Nothing -> do
188+ tp <- resolveType name
189+ genTopType name tp
190+
191+genSimpleType :: TypeName -> Xsd.SimpleType -> [Xsd.Annotation]-> Gen ()
192+genSimpleType tn (Xsd.AtomicType restriction ann) parentAnn = do
193+ let
194+ baseName r = case Xsd.simpleRestrictionBase r of
195+ Xsd.Ref n -> Just n
196+ Xsd.Inline (Xsd.AtomicType r' _) -> baseName r'
197+ Xsd.Inline (Xsd.ListType _ _) -> Nothing
198+ Xsd.Inline (Xsd.UnionType _ _) -> Nothing
199+ case baseName restriction of
200+ Just _
201+ | isEnum restriction
202+ -> genEnum tn
203+ (Xsd.simpleRestrictionConstraints restriction)
204+ (parentAnn <> ann)
205+ Just base ->
206+ genNewtype tn base (parentAnn <> ann)
207+ _ -> genUnsupported tn
208+genSimpleType typeName (Xsd.ListType _ _) _ = genUnsupported typeName
209+genSimpleType typeName (Xsd.UnionType _ _) _ = genUnsupported typeName
210+
211+genNewtype :: TypeName -> Xsd.QName -> [Xsd.Annotation] -> Gen ()
212+genNewtype tn base annots = do
213+ fieldTypeName <- tnPrefixed <$> resolveTypeName base
214+ mode <- getMode
215+ let
216+ typeName = tnPrefixed tn
217+ fieldName = "un" <> typeName
218+ toxml = case mode of
219+ Parser -> ""
220+ _ -> ", ToXML"
221+ writeCode $ makeComments tn annots
222+ writeCode
223+ [ "newtype " <> typeName
224+ , " = " <> typeName
225+ , " { " <> fieldName <> " :: " <> fieldTypeName
226+ , " } deriving (Show, Eq, NFData" <> toxml <> ")"
227+ , ""
228+ ]
229+ unless (mode == Generator) $ writeCode
230+ [ "instance FromDom " <> typeName <> " where"
231+ , " fromDom = " <> typeName <> " <$> fromDom"
232+ , ""
233+ ]
234+
235+genEnum :: TypeName -> [Xsd.Constraint] -> [Xsd.Annotation] -> Gen ()
236+genEnum tn constraints annots = do
237+ mode <- getMode
238+ let
239+ values = map toValue constraints
240+ toValue (Xsd.Enumeration v) = v
241+ enumLines <- forM values $ \v -> do
242+ enumName <- makeEnumName v
243+ return (" & \"" <> enumName <> "\"")
244+ writeCode $ makeComments tn annots
245+ writeCode $ "\"" <> tnName tn <> "\" Exhaustive =:= enum "
246+ <> genTypeToText mode : enumLines
247+ writeCode [""]
248+
249+isEnum :: Xsd.SimpleRestriction -> Bool
250+isEnum = any isEnumConstraint . Xsd.simpleRestrictionConstraints
372251 where
373− fromEnumeration (Enumeration x) = x
252+ isEnumConstraint (Xsd.Enumeration _) = True
374253
375−isEnumeration :: Restriction -> Bool
376−isEnumeration (Enumeration _) = True
377−
378−shouldBeNewtype :: SimpleType -> Bool
379−shouldBeNewtype (STAtomic _ _ _ restrictions) =
380− not . Prelude.any isEnumeration $ restrictions
381−
382−getNewtypeConsName :: Text -> Text
383−getNewtypeConsName = ("Xml" <>) . capitalize
384−
385−getNewtypeUnconsName :: Text -> Text
386−getNewtypeUnconsName = ("un" <>) . getNewtypeConsName
387−
388−generateNewtype :: GenType -> Text -> SimpleAtomicType -> [Text]
389−generateNewtype genType n sat =
390− let
391− constructor = getNewtypeConsName n
392− decons = getNewtypeUnconsName n
393− parser =
394− [ ""
395− , "instance FromDom " <> constructor <> " where"
396− , " fromDom = " <> constructor <> " <$> fromDom"
397− ]
398− in
399− [ "newtype " <> constructor
400− , " = " <> constructor
401− , " { " <> decons <> " :: " <> simpleTypeToIsoType Plain sat
402− , " } deriving (Show, Eq, NFData" <> case genType of
403− Parser -> ")"
404− _ -> ", ToXML)"
405− ] <> case genType of
406− Generator -> []
407− _ -> parser
408−
409−generateEnum
410− :: Text -- type name
411− -> [Text] -- enums
412− -> [Text] -- generated code
413−generateEnum enumName enums =
254+genComplexType :: TypeName -> Xsd.ComplexType -> [Xsd.Annotation] -> Gen ()
255+genComplexType typeName t parentAnn = do
256+ mode <- getMode
414257 let
415− enumHeader = enumName <> " Exhaustive =:= enum Both"
416− eFields = ("& "<>) <$> enums
417− in pure enumHeader <> eFields
418−
419−lookupSimpleType
420− :: DatatypeRef
421− -> GenMonad (Maybe Text)
422−lookupSimpleType (InlineComplex _) = pure Nothing
423−lookupSimpleType (DatatypeRef tyName) =
424− asks (M.lookup tyName) >>= \case
425− Just (TypeComplex _) -> pure Nothing
426− Just (TypeSimple (STAtomic nam _ _ _)) -> pure $ Just nam
427− Nothing -> error $ "can't look up type: " <> show tyName
428−
429−generateIsoField :: GenType -> Element -> GenMonad Text
430−generateIsoField
431− genType
432− (Element (_, fieldName) fieldXtype minOc maxOc fieldAnnotations) = do
433− let
434− qualifier = toQualifier minOc maxOc
435− line <- lookupSimpleType fieldXtype >>= \case
436− Just tyName ->
437− pure $ quote fieldName <> " [t|Xml" <> tyName <> "|]"
438− Nothing -> case fieldXtype of
439− InlineComplex dt -> do
440− generateIsoDatatype genType (capitalize fieldName) dt fieldAnnotations
441− pure $ quote fieldName
442− DatatypeRef ref ->
443− pure $ quote fieldName <> " [t|Xml" <> capitalize ref <> "|]"
444− pure $ " " <> qualifier <> " " <> line
258+ header = "\"" <> tnName typeName <> "\" =:= record "
259+ <> genTypeToText mode
260+ fields <- genFields typeName (Xsd.complexContent t)
261+ writeCode $ makeComments typeName
262+ (parentAnn <> Xsd.complexAnnotations t)
263+ writeCode [header]
264+ writeCode fields
265+ writeCode [""]
266+
267+makeComments :: TypeName -> [Xsd.Annotation] -> [Text]
268+makeComments typeName annotations = if null annotations
269+ then
270+ [ "{-|"
271+ , generated
272+ , "-}"
273+ ]
274+ else
275+ [ "{-|"
276+ , generated
277+ , ""
278+ ] <> map makeDoc annotations <>
279+ [ "-}"
280+ ]
281+ where
282+ generated = "Generated from: " <> prefix <> name
283+ qname = tnQName typeName
284+ name = Xsd.qnName qname
285+ prefix = maybe "" ((<> ":") . Xsd.fromNamespace) (Xsd.qnNamespace qname)
286+ makeDoc (Xsd.Documentation docs) = docs
287+
288+-- | Generate code for record fields
289+--
290+-- The first argument - record name
291+genFields :: TypeName -> Xsd.Content -> Gen [Text]
292+genFields typeName (Xsd.ContentPlain c) =
293+ maybe (return []) (genFieldsForModel typeName) (Xsd.plainContentModel c)
294+genFields _ (Xsd.ContentSimple _) = return []
295+genFields typeName (Xsd.ContentComplex (Xsd.ComplexContentExtension e)) = do
296+ base <- resolveType (Xsd.complexExtensionBase e)
297+ genDependencies base
298+ baseFields <- case base of
299+ Xsd.TypeComplex t -> genFields typeName (Xsd.complexContent t)
300+ _ -> return []
301+ fields <- maybe (return []) (genFieldsForModel typeName)
302+ (Xsd.complexExtensionModel e)
303+ return (baseFields ++ fields)
304+genFields _ (Xsd.ContentComplex (Xsd.ComplexContentRestriction _)) =
305+ return []
306+
307+genFieldsForModel :: TypeName -> Xsd.ModelGroup -> Gen [Text]
308+genFieldsForModel typeName (Xsd.Sequence elements) =
309+ catMaybes <$> mapM (genField typeName) elements
310+genFieldsForModel _ (Xsd.Choice _) = do
311+ warn "choice: not implemented"
312+ return []
313+genFieldsForModel _ (Xsd.All _) = do
314+ warn "all: not implemented"
315+ return []
316+
317+genField :: TypeName -> Xsd.RefOr Xsd.Element -> Gen (Maybe Text)
318+genField _ (Xsd.Ref _) = return Nothing
319+genField typeName (Xsd.Inline e) = do
320+ fieldName <- makeFieldName typeName (Xsd.elementName e)
321+
322+ fieldTypeName <- case Xsd.elementType e of
323+ Xsd.Ref name -> do
324+ tn <- tnPrefixed <$> resolveTypeName name
325+ exts <- extensions name
326+ case exts of
327+ [] -> return tn
328+ _ -> return ("Any" <> tn)
329+ Xsd.Inline t -> do
330+ genType (Xsd.elementAnnotations e) (Xsd.elementName e) t
331+ tnPrefixed <$> resolveTypeName (Xsd.elementName e)
332+
333+ qualifier <- makeQualifier (Xsd.elementOccurs e)
445334
446−main :: IO ()
447−main = do
448− Options{..} <- getOptions
449− xml <- BL.readFile oInput
450335 let
451− xsd = case parseXSD def xml of
452− Right xsd' -> xsd'
453− Left e -> error $ show e
454− T.hPutStrLn stdout $ generateIsoXml oHeader oModule oMode xsd
336+ fieldType = if Xsd.elementNillable e
337+ then "Nillable " <> fieldTypeName
338+ else fieldTypeName
339+
340+ return $ Just $ mconcat
341+ [ " "
342+ , qualifier
343+ , " \""
344+ , fieldName
345+ , "\" [t|"
346+ , fieldType
347+ , "|]"
348+ ]
349+
350+makeQualifier :: (Int, Xsd.MaxOccurs) -> Gen Text
351+makeQualifier (minOcc, maxOcc) = case maxOcc of
352+ Xsd.MaxOccursUnbound -> toQualifier minOcc maxBound
353+ Xsd.MaxOccurs v -> toQualifier minOcc v
354+
355+toQualifier :: Int -> Int -> Gen Text
356+toQualifier 1 1 = return "!"
357+toQualifier 0 1 = return "?"
358+toQualifier 1 maxOcc
359+ | maxOcc < 1
360+ = genError "maxOccurs < minOccurs"
361+toQualifier 1 maxOcc
362+ | maxOcc > 1
363+ = return "+"
364+toQualifier _ _ = return "*"
365+
366+genUnsupported :: TypeName -> Gen ()
367+genUnsupported tn = do
368+ let typeName = tnPrefixed tn
369+ warn $ "generating stub for unsupported construct in " <> typeName
370+ mode <- getMode
371+ writeCode (makeComments tn [])
372+ writeCode
373+ [ "data " <> typeName <> " = " <> typeName
374+ , " deriving (Eq, Show)"
375+ , ""
376+ , "instance NFData " <> typeName <> " where"
377+ , " rnf _ = ()"
378+ , ""
379+ ]
380+
381+ unless (mode == Parser) $ writeCode
382+ [ "instance ToXML " <> typeName <> " where"
383+ , " toXML _ = comment \"" <> typeName <> "\""
384+ , ""
385+ ]
386+
387+ unless (mode == Generator) $ writeCode
388+ [ "instance FromDom " <> typeName <> " where"
389+ , " fromDom = undefined"
390+ , ""
391+ ]
392+
393+genHeader :: Options -> Gen ()
394+genHeader opts = do
395+ case oHeader opts of
396+ Nothing -> return ()
397+ Just h -> do
398+ writeCode . fmap ("-- " <>) . Text.lines $ h
399+ writeCode [""]
400+
401+ writeCode
402+ [ "{-# OPTIONS_GHC -Wno-unused-imports #-}"
403+ , "module " <> oModule opts <> " where"
404+ , ""
405+ , "import Control.DeepSeq"
406+ , "import Data.Scientific"
407+ , "import Data.THGen.XML"
408+ ]
409+ writeCode imports
410+ writeCode
411+ [ "import Prelude hiding ((*), (+))"
412+ , ""
413+ , ""
414+ ]
415+ where
416+ imports = case oMode opts of
417+ Generator -> [ xmlWriter, parentAttr ]
418+ Parser -> [ domParser ]
419+ ParserAndGenerator -> [ domParser, xmlWriter, parentAttr ]
420+ xmlWriter = "import Text.XML.Writer"
421+ parentAttr = "import Text.XML.ParentAttributes"
422+ domParser = "import Text.XML.DOM.Parser"
423+
424+-- | We populate the initial state with all built-in types to make sure
425+-- they will be correctly resolved
426+initialGenState :: Options -> Map Xsd.QName Xsd.Type -> GenState
427+initialGenState opts types = GenState
428+ { gsTypes = types
429+ , gsExtensions = collectExtensions types
430+ , gsKnownTypes = builtInTypes
431+ , gsBoundTypeName = Set.empty
432+ , gsBoundFields = Set.empty
433+ , gsMode = oMode opts
434+ , gsCode = []
435+ , gsWarnings = []
436+ }
437+
438+collectExtensions
439+ :: Map Xsd.QName Xsd.Type
440+ -> Map Xsd.QName [(Xsd.QName, Xsd.Type)]
441+collectExtensions = List.foldl' step Map.empty . Map.toList
442+ where
443+ step exts (n, t) = case baseOf t of
444+ Nothing -> exts
445+ Just base -> Map.insertWith (++) base [(n, t)] exts
446+ baseOf (Xsd.TypeSimple _) = Nothing
447+ baseOf (Xsd.TypeComplex t) = case Xsd.complexContent t of
448+ Xsd.ContentComplex (Xsd.ComplexContentExtension e) ->
449+ Just (Xsd.complexExtensionBase e)
450+ _ -> Nothing
src/Options.hs+10−6
@@ -1,3 +1,6 @@
1+{-# LANGUAGE OverloadedStrings #-}
2+{-# LANGUAGE TypeApplications #-}
3+
14module Options
25 ( Options(..)
36 , optionsParser
@@ -16,13 +19,13 @@ import Paths_xsd_isogen (version)
1619import Data.List
1720
1821
19−data GenType = Parser | Generator | Both
20− deriving (Read, Show, Bounded, Enum)
22+data GenType = Parser | Generator | ParserAndGenerator
23+ deriving (Read, Show, Eq, Bounded, Enum)
2124
2225genTypeToText :: GenType -> Text
23−genTypeToText Parser = "Parser"
24−genTypeToText Generator = "Generator"
25−genTypeToText Both = "Both"
26+genTypeToText Parser = "Parser"
27+genTypeToText Generator = "Generator"
28+genTypeToText ParserAndGenerator = "ParserAndGenerator"
2629
2730type ModuleName = Text
2831type Header = Text
@@ -33,6 +36,7 @@ data Options = Options
3336 , oInput :: FilePath
3437 , oModule :: ModuleName
3538 }
39+ deriving (Show)
3640
3741optionsParser :: Parser Options
3842optionsParser = Options
@@ -50,7 +54,7 @@ optionsParser = Options
5054 <*> option auto
5155 ( long "mode"
5256 <> short 'm'
53− <> value Both
57+ <> value ParserAndGenerator
5458 <> help ("Whether to generate both parsers and generators. One of: "
5559 <> (mconcat . intersperse ", " . fmap show
5660 $ [minBound @GenType .. maxBound]) <> ".")
src/References.hsnew file+80
@@ -0,0 +1,80 @@
1+
2+module References
3+( References(..)
4+)
5+where
6+
7+import qualified Xsd
8+
9+class References a where
10+ references :: a -> [Xsd.QName]
11+ referencesList :: [a] -> [Xsd.QName]
12+ referencesList = concatMap references
13+
14+instance References t => References (Maybe t) where
15+ references Nothing = []
16+ references (Just t) = references t
17+
18+instance References Xsd.Type where
19+ references (Xsd.TypeSimple t) = references t
20+ references (Xsd.TypeComplex t) = references t
21+
22+instance References Xsd.SimpleType where
23+ references (Xsd.AtomicType r _) = references r
24+ references (Xsd.ListType r _) = references r
25+ references (Xsd.UnionType rs _) = referencesList rs
26+
27+instance References Xsd.SimpleRestriction where
28+ references r = references (Xsd.simpleRestrictionBase r)
29+
30+instance References Xsd.ComplexRestriction where
31+ references r = [Xsd.complexRestrictionBase r]
32+
33+instance References t => References (Xsd.RefOr t) where
34+ references (Xsd.Ref r) = [r]
35+ references (Xsd.Inline t) = references t
36+
37+instance References Xsd.ComplexType where
38+ references t = references (Xsd.complexContent t)
39+
40+instance References Xsd.Content where
41+ references (Xsd.ContentComplex c) = references c
42+ references (Xsd.ContentSimple c) = references c
43+ references (Xsd.ContentPlain c) = references c
44+
45+instance References Xsd.PlainContent where
46+ references t = referencesList (Xsd.plainContentAttributes t)
47+ ++ references (Xsd.plainContentModel t)
48+
49+instance References Xsd.SimpleContent where
50+ references _ = []
51+
52+instance References Xsd.ComplexContent where
53+ references (Xsd.ComplexContentExtension e) = references e
54+ references (Xsd.ComplexContentRestriction r) = references r
55+
56+instance References Xsd.ComplexExtension where
57+ -- Note that we don't include extension base here
58+ -- becase we don't depend on it. And actaully we generate extensions before
59+ -- the base because we need to generate the ADT with all the extensions
60+ -- at this point.
61+ references e = referencesList (Xsd.complexExtensionAttributes e)
62+ ++ references (Xsd.complexExtensionModel e)
63+
64+instance References Xsd.Attribute where
65+ references (Xsd.RefAttribute a) = references a
66+ references (Xsd.InlineAttribute a) = references a
67+
68+instance References Xsd.AttributeRef where
69+ references a = [Xsd.attributeRefRef a]
70+
71+instance References Xsd.AttributeInline where
72+ references = references . Xsd.attributeInlineType
73+
74+instance References Xsd.ModelGroup where
75+ references (Xsd.Sequence elements) = referencesList elements
76+ references (Xsd.Choice elements) = referencesList elements
77+ references (Xsd.All elements) = referencesList elements
78+
79+instance References Xsd.Element where
80+ references = references . Xsd.elementType
stack.yamlnew file+12
@@ -0,0 +1,12 @@
1+#resolver: nightly-2022-01-24
2+resolver: lts-18.23
3+
4+packages:
5+- .
6+
7+extra-deps:
8+- git: https://github.com/typeable/xsd-parser
9+ commit: 648877d94d03a58dc9114f68d4e99c674e353fca
10+
11+nix:
12+ packages: [zlib]
xsd-isogen.cabal+15−14
@@ -20,12 +20,16 @@ extra-source-files: CHANGELOG.md
2020executable xsd-isogen
2121 main-is: Main.hs
2222 other-modules: Options
23+ , Gen
24+ , BuiltInTypes
25+ , References
2326 , Paths_xsd_isogen
2427 -- other-extensions:
25− build-depends: base ^>=4.12.0.0
28+ build-depends: base >=4.12 && < 5.0
2629 , bytestring
2730 , containers
2831 , data-default
32+ , transformers
2933 , mtl
3034 , optparse-applicative
3135 , text
@@ -35,16 +39,13 @@ executable xsd-isogen
3539 hs-source-dirs: src
3640 default-language: Haskell2010
3741 ghc-options: -fwarn-unused-binds
38− , -fwarn-unused-imports
39− , -Wall
40− , -Wincomplete-uni-patterns
41− , -Wincomplete-record-updates
42− , -Wpartial-fields
43− , -Werror=missing-home-modules
44− , -Wmissing-home-modules
45− , -Widentities
46− , -Wredundant-constraints
47− , -Wmissing-export-lists
48− default-extensions: TypeApplications
49− , OverloadedStrings
50− , RecordWildCards
42+ -fwarn-unused-imports
43+ -Wall
44+ -Wincomplete-uni-patterns
45+ -Wincomplete-record-updates
46+ -Wpartial-fields
47+ -- -Werror=missing-home-modules
48+ -Wmissing-home-modules
49+ -Widentities
50+ -Wredundant-constraints
51+ -Wmissing-export-lists