git.iko.soy/typeable/xsd-isogen
on master help sign in
35 files changed+1682−417
since splitsnapshots
unifiedsplit
README.md+18−7 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+438−395 src/Options.hs+84 src/References.hs+80 stack.yaml+12 xsd-isogen.cabal+19−13
README.md+18−7
@@ -5,13 +5,24 @@ Tool to generate xml-isogen descriptions from xsd files
55Usage
66=====
77
8−Use `xsd-isogen in.xsd Generator > out.hs` for queries.
8+Use `xsd-isogen -i in.xsd -m Generator > out.hs` for queries.
99
10−Use `xsd-isogen in.xsd Parser > out.hs` for responses.
10+Use `xsd-isogen -i in.xsd -m Parser > out.hs` for responses.
1111
12−Use `xsd-isogen in.xsd Both > out.hs` for common definitions.
12+Use `xsd-isogen -i in.xsd -m Both > out.hs` for common definitions.
1313
14−Keep in mind that name clashes are possible and should be disambiguated
15−manually. If you have a good idea how to handle that, feel free to create an
16−issue and/or PR. Module name for all auto-generated files is set to `module
17−Dummy where`, so it should be updated appropriately as well.
14+If there are conflicting field names in some datatypes, one of the the datatype names gets consecutive numbers starting from 2 appended to it (so that the field name prefix changes) until there are no more conflicts.
15+
16+## Available options:
17+ - `--no-header`:
18+ Do not place a header at the top of the file.
19+ - `--header ARG`:
20+ A custom header to be placed at the top of the file.
21+ - `-m,--mode ARG`:
22+ Whether to generate both parsers and generators. One of: Parser, Generator, Both.
23+ - `-i,--input ARG`:
24+ Path to the input .xsd file.
25+ - `-n,--name ARG`:
26+ Specify the name of the generated module.
27+ - `-h,--help`:
28+ Show this help text
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+438−395
@@ -1,407 +1,450 @@
1−{-# LANGUAGE FlexibleInstances #-}
2−{-# LANGUAGE LambdaCase #-}
31{-# LANGUAGE OverloadedStrings #-}
42
53module Main
6− ( main
7− ) where
8−
9−import Control.Monad.RWS.CPS (RWS, execRWS)
10−import Control.Monad.Reader
11−import Control.Monad.State.Strict
12−import Control.Monad.Writer.CPS (Writer, tell, execWriter)
13−import Data.ByteString.Lazy as BL
14−import Data.Char as C
15−import Data.Default
16−import Data.Foldable as F
17−import Data.List as L
18−import Data.Map.Strict as M
19−import Data.Set as S
20−import Data.Text as T
21−import Data.Text.IO as T
22−import Data.Traversable
23−import Debug.Trace
24−import System.Environment
25−import System.IO
26−import Text.XSD
27−
28−
29−data GenType = Parser | Generator | Both
30− deriving (Read, Show)
31−
32−genTypeToText :: GenType -> Text
33−genTypeToText Parser = "Parser"
34−genTypeToText Generator = "Generator"
35−genTypeToText Both = "Both"
36−
37−type GenMonad = ReaderT DatatypeMap (Writer Text)
38−
39−class References a where
40− references :: a -> [Text]
41−
42−instance References Datatype where
43− references (TypeComplex (ComplexType _ _ _ mmgs)) =
44− case mmgs of
45− Just mgs -> case mgs of
46− CTSequence els -> F.concatMap references els
47− CTChoice els -> F.concatMap references els
48− CTAll els -> F.concatMap references els
49− Nothing -> []
50− references (TypeSimple _) = []
51−
52−instance References Element where
53− references (Element _ t _ _ _) = references t
54−
55−instance References DatatypeRef where
56− references (DatatypeRef ty) = [ty]
57− references (InlineComplex dt) = references dt
58−
59−instance References a => References [a] where
60− references = F.concatMap references
61−
62−-- | 'DatatypeMap' are the types left to process
63−-- 'S.Set Text' are the types already processed.
64−type PreMonad = StateT (DatatypeMap, S.Set Text) (Writer [(Text, Datatype)])
65−
66−type PreElemMonad = RWS DatatypeMap [Element] [Element]
67−
68−-- | Marks the given type as processed.
69−proceed :: Text -> Datatype -> PreMonad ()
70−proceed t dt = do
71− tell [(t, dt)]
72− modify $ \(f,s) -> (M.delete t f, S.insert t s)
73−
74−-- | Marks element as processed.
75−proceed' :: Element -> PreElemMonad ()
76−proceed' el = do
77− tell [el]
78− modify $ L.delete el
79−
80−untwistDeps :: [Element] -> DatatypeMap -> ([(Text, Datatype)], [Element])
81−untwistDeps e dm = (dList, e')
82− where
83− dList = nub $ execWriter $ do
84− (a, _) <- runStateT sortDatatypesByDeps (dm, S.empty)
85− return a
86− (_, e') = execRWS sortElemsByDeps (M.fromList dList) e
87−
88−sortElemsByDeps :: PreElemMonad ()
89−sortElemsByDeps = do
90− els <- get
91− if Prelude.null els then pure () else do
92− let first = Prelude.head els
93− -- traceM $ "sortElemsByDeps: " <> show first
94− case references first of
95− [] -> proceed' first >> sortElemsByDeps
96− xs -> do
97− results <- traverse memberDatatype xs
98− if L.and results
99− then proceed' first >> sortElemsByDeps
100− else do
101− let failed = fmap snd $ L.filter (not . fst) $ L.zip results xs
102− error $ "impossible to lookup types: " <> show failed
103−
104−memberDatatype :: Text -> PreElemMonad Bool
105−memberDatatype ty = do
106− -- traceM $ "memberDatatype: " <> show ty
107− dm <- ask
108− case M.lookup ty dm of
109− Just _ -> return True
110− Nothing -> return False
111−
112−sortDatatypesByDeps :: PreMonad ()
113−sortDatatypesByDeps = do
114− (dm, _) <- get
115− if M.null dm then pure () else do
116− let first = Prelude.head (M.toList dm)
117− traceM $ "sortDatatypesByDeps: " <> show first
118− case references $ snd first of
119− [] -> uncurry proceed first >> sortDatatypesByDeps
120− xs -> do
121− traverse_ lookupDatatype xs
122− uncurry proceed first
123− sortDatatypesByDeps
124−
125−lookupDatatype :: Text -> PreMonad ()
126−lookupDatatype ty = do
127− traceM $ "lookupDatatype: " <> show ty
128− (dm, s) <- get
129− case M.lookup ty dm of
130− Nothing -> if S.member ty s then pure () else
131− error $ "can't look up datatype: " <> show ty
132− Just typ -> case references typ of
133− [] -> proceed ty typ -- >> sortDatatypesByDeps -- Pretty sure this was a bug
134− xs -> traverse_ lookupDatatype xs >> proceed ty typ
135−
136−class Capitalizable t where
137− capitalize :: t -> t
138−
139−instance Capitalizable String where
140− capitalize [] = []
141− capitalize (a:as) = C.toUpper a : as
142−
143−instance Capitalizable Text where
144− capitalize t
145− | T.null t = t
146− | otherwise = T.singleton (C.toUpper (T.head t)) <> T.tail t
147−
148−data DataFlavour = QQ | Plain
149−
150−-- TODO: improve after moving some XML types to xml-isogen
151−simpleTypeToIsoType :: DataFlavour -> SimpleAtomicType -> Text
152−simpleTypeToIsoType fl = \case
153− STAnyURI -> wrap fl "Text"
154− STBase64Binary -> wrap fl "ByteString"
155− STBoolean -> wrap fl "Bool"
156− STDate -> wrap fl "Date \"%YYYYj\""
157− STDateTime -> wrap fl "Text"
158− STDateTimeStamp -> wrap fl "Text"
159− STDecimal DTDecimal -> wrap fl "Double"
160− (STDecimal _) -> wrap fl "Integer"
161− STDouble -> wrap fl "Double"
162− (STDuration _) -> wrap fl "Integer"
163− STFloat -> wrap fl "Float"
164− STGDay -> wrap fl "Day"
165− STGMonth -> wrap fl "Text"
166− STGMonthDay -> wrap fl "Text"
167− STGYear -> wrap fl "Text"
168− STGYearMonth -> wrap fl "Text"
169− STHexBinary -> wrap fl "Text"
170− STNOTATION -> wrap fl "Text"
171− STQName -> wrap fl "Text"
172− (STString _) -> wrap fl "Text"
173− STTime -> wrap fl "Text"
174− where
175− wrap QQ t = "[t|" <> t <> "|]"
176− wrap Plain t = t
177−
178−tellGen
179− :: [Text] -- generated code
180− -> Text -- comment
181− -> GenMonad ()
182−tellGen code comment =
183− tell $ "\n" <> comment <> T.unlines code
4+( main
5+)
6+where
7+
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
19+
20+import Options
21+import Gen
22+import References
23+import BuiltInTypes
18424
185−runGen :: DatatypeMap -> GenMonad a -> Text
186−runGen dm act = execWriter $ runReaderT act dm
187−
188−generateIsoXml :: GenType -> XSD -> Text
189−generateIsoXml genType (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
19041 let
191− (dList, el) = untwistDeps e dm
192− in (header genType <>) . runGen dm $ do
193− for_ dList $ \(t,dt) ->
194− generateIsoDatatype genType t dt []
195− for_ el (generateIsoRecord genType)
196−
197−toQualifier :: Int -> Int -> Text
198−toQualifier minOc maxOc
199− | minOc == 1 && maxOc == 1 = "!"
200− | minOc == 0 && maxOc == 1 = "?"
201− | minOc == 1 && maxOc > minOc = "+"
202− | minOc == 1 && maxOc < minOc = error "maxOccurs < minOccurs"
203− | otherwise = "*"
204−
205−header :: GenType -> Text
206−header genType = T.unlines $
207− [ "module Dummy where"
208− , ""
209− , "import Control.DeepSeq"
210− , "import Data.THGen.XML"
211− ] <> specImports <>
212− [ "import Prelude hiding ((*), (+))"
213− , "" ]
214− where
215− specImports = case genType of
216− Generator -> [ xmlWriter ]
217− Parser -> [ domParser ]
218− Both -> [ domParser, xmlWriter ]
219− xmlWriter, domParser :: Text
220− xmlWriter = "import Text.XML.Writer"
221− domParser = "import Text.XML.DOM.Parser.FromDom"
222−
223−quote :: Text -> Text
224−quote t = "\"" <> t <> "\""
225−
226−-- | Nothing means unknown name
227−typeName :: DatatypeRef -> Maybe Text
228−typeName (InlineComplex dt) = result
229− where
230− result = case dt of
231− TypeComplex (ComplexType n _ _ _) -> n
232− TypeSimple (STAtomic _ sat _ _) -> Just $ simpleTypeToIsoType QQ sat
233−typeName (DatatypeRef t) = Just t
234−
235−generateIsoRecord :: GenType -> Element -> GenMonad ()
236−generateIsoRecord
237− genType
238− (Element (_, laxName) elXtype _ _ elAnnotations) = do
239− written <- ask
240− case elXtype of
241− InlineComplex dt ->
242− generateIsoDatatype genType laxName dt elAnnotations
243− DatatypeRef nam -> if M.member nam written
244− then pure ()
245− else error $ "Implementation failure: "
246− <> show nam <> " type never encountered before"
247−
248−data CommentType = Haddock | Regular
249−
250−toComment :: CommentType -> [Annotation] -> Text
251−toComment ct ann = case texts of
252− [] -> ""
253− (h:tl) -> T.unlines ((prefix <> h) : fmap ("-- "<>) tl)
254− where
255− texts = stripTexts $ F.concatMap (wrap80 . fromDocumentation) ann
256− stripTexts = \case
257− [] -> []
258− l@(h:tl) -> if T.all (\x -> x == ' ' || x == '\n') h then stripTexts tl else l
259− prefix = case ct of
260− Haddock -> "-- | "
261− Regular -> "-- "
262−
263−fromDocumentation :: Annotation -> Text
264−fromDocumentation (Documentation t) = t
265−
266−wrap80 :: Text -> [Text]
267−wrap80 = L.foldl' go [] . T.split (==' ')
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
26854 where
269− go [] e = [e]
270− go l e =
271− let last' = L.last l
272− in if T.length last' + T.length e >= 73 -- 80 minus "-- | " and ' '
273− then l <> pure e
274− else L.init l <> [L.last l <> " " <> e]
275−
276−generateIsoDatatype
277− :: GenType
278− -> (Maybe Text) -- ^ Explicitly provided name, used for inline complexType's
279− -> Datatype
280− -> [Annotation]
281− -> GenMonad ()
282− -- ^ (type name for lookups, generated code)
283−generateIsoDatatype
284− genType
285− eName
286− (TypeComplex (ComplexType iName attrs annotations mGroupSchema))
287− annotationsRec = do
288− name <- case eName of
289− Just name -> pure name
290− Nothing -> case iName of
291− Just name' -> pure name'
292− Nothing -> error $ "[generateIsoDatatype] no name was provided"
293− case mGroupSchema of
294− Just groupSchema -> case groupSchema of
295− CTSequence elems -> do
296− fields <- for elems (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
29797 let
298− comment = toComment Haddock $ annotationsRec
299− <> [Documentation ""]
300− <> annotations
301− header =
302− (quote $ capitalize name)
303− <> " =:= record "
304− <> genTypeToText genType
305− tellGen (header : fields) comment
306−generateIsoDatatype
307− genType
308− eName
309− (TypeSimple (STAtomic iName sat annotations restrictions))
310− annotationsRec = do
311− name <- case eName of
312− Just name -> pure name
313− Nothing -> pure iName
314− let
315− comment =
316− toComment Haddock $ annotationsRec
317− <> [Documentation ""]
318− <> annotations
319− isEnumeration (Enumeration _) = True
320− fromEnumeration (Enumeration x) = x
321− enums = Prelude.filter isEnumeration restrictions
322− typeStr = simpleTypeToIsoType QQ sat
323− if Prelude.length enums > 0
324− then tellGen
325− (generateEnum
326− (quote $ capitalize name)
327− (F.concatMap fromEnumeration enums))
328− comment
329− else
330− tellGen (generateNewtype genType iName sat) comment
331−
332−generateNewtype :: GenType -> Text -> SimpleAtomicType -> [Text]
333−generateNewtype genType n sat =
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
334193 let
335− cons = "Xml" <> capitalize n
336− decons = "un" <> cons
337− parser =
338− [ ""
339− , "instance FromDom " <> cons <> " where"
340− , " fromDom = " <> cons <> " <$> fromDom"
341− ]
342− in
343− [ "newtype " <> cons
344− , " = " <> cons
345− , " { " <> decons <> " :: " <> simpleTypeToIsoType Plain sat
346− , " } deriving (Show, Eq, NFData" <> case genType of
347− Parser -> ")"
348− _ -> ", ToXML)"
349− ] <> case genType of
350− Generator -> []
351− _ -> parser
352−
353−generateEnum
354− :: Text -- type name
355− -> [Text] -- enums
356− -> [Text] -- generated code
357−generateEnum enumName enums =
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
358215 let
359− enumHeader = enumName <> " Exhaustive =:= enum Both"
360− eFields = ("& "<>) <$> enums
361− in pure enumHeader <> eFields
362−
363−-- | Returns Just baseTypeName if type is simple, Nothing - otherwise
364−lookupSimpleBaseType
365− :: DatatypeRef
366− -> GenMonad (Maybe SimpleAtomicType)
367−lookupSimpleBaseType (InlineComplex _) = pure Nothing
368−lookupSimpleBaseType (DatatypeRef tyName) = do
369− M.lookup tyName <$> ask >>= \case
370− Just (TypeComplex _) -> pure Nothing
371− Just (TypeSimple (STAtomic _ sat _ _)) -> pure $ Just sat
372− Nothing -> error $ "can't look up type: " <> show tyName
373−
374−lookupSimpleType
375− :: DatatypeRef
376− -> GenMonad (Maybe Text)
377−lookupSimpleType (InlineComplex _) = pure Nothing
378−lookupSimpleType (DatatypeRef tyName) =
379− asks (M.lookup tyName) >>= \case
380− Just (TypeComplex _) -> pure Nothing
381− Just (TypeSimple (STAtomic nam _ _ _)) -> pure $ Just nam
382− Nothing -> error $ "can't look up type: " <> show tyName
383−
384−generateIsoField :: GenType -> Element -> GenMonad Text
385−generateIsoField genType (Element (_, name) xtype minOc maxOc annotations) = do
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
386238 let
387− qualifier = toQualifier minOc maxOc
388− line <- lookupSimpleType xtype >>= \case
389− Just tyName -> do
390− pure $ quote name <> " [t|Xml" <> tyName <> "|]"
391− Nothing -> case xtype of
392− InlineComplex dt -> do
393− generateIsoDatatype genType (Just $ capitalize name) dt annotations
394− pure $ quote name
395− DatatypeRef ref ->
396− pure $ quote name <> " [t|Xml" <> capitalize ref <> "|]"
397− pure $ " " <> qualifier <> " " <> line
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
251+ where
252+ isEnumConstraint (Xsd.Enumeration _) = True
253+
254+genComplexType :: TypeName -> Xsd.ComplexType -> [Xsd.Annotation] -> Gen ()
255+genComplexType typeName t parentAnn = do
256+ mode <- getMode
257+ let
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)
398334
399−main :: IO ()
400−main = do
401− (inFile:mode:_) <- getArgs
402− xml <- BL.readFile inFile
403335 let
404− xsd = case parseXSD def xml of
405− Right xsd' -> xsd'
406− Left e -> error $ show e
407− T.hPutStrLn stdout $ generateIsoXml (read mode) 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.hsnew file+84
@@ -0,0 +1,84 @@
1+{-# LANGUAGE OverloadedStrings #-}
2+{-# LANGUAGE TypeApplications #-}
3+
4+module Options
5+ ( Options(..)
6+ , optionsParser
7+ , getOptions
8+ , GenType(..)
9+ , genTypeToText
10+ , ModuleName
11+ , Header
12+ ) where
13+
14+import Data.Text (Text)
15+import qualified Data.Text as T
16+import Data.Version (showVersion)
17+import Options.Applicative
18+import Paths_xsd_isogen (version)
19+import Data.List
20+
21+
22+data GenType = Parser | Generator | ParserAndGenerator
23+ deriving (Read, Show, Eq, Bounded, Enum)
24+
25+genTypeToText :: GenType -> Text
26+genTypeToText Parser = "Parser"
27+genTypeToText Generator = "Generator"
28+genTypeToText ParserAndGenerator = "ParserAndGenerator"
29+
30+type ModuleName = Text
31+type Header = Text
32+
33+data Options = Options
34+ { oHeader :: Maybe Header
35+ , oMode :: GenType
36+ , oInput :: FilePath
37+ , oModule :: ModuleName
38+ }
39+ deriving (Show)
40+
41+optionsParser :: Parser Options
42+optionsParser = Options
43+ <$> ( flag' Nothing
44+ ( long "no-header"
45+ <> help "Do not place a header at the top of the file."
46+ ) <|> Just <$> strOption
47+ ( long "header"
48+ <> help "A custom header to be placed at the top of the file."
49+ <> value ("This file was generated by xsd-isogen version "
50+ <> T.pack (showVersion version)
51+ <> "\nhttps://github.com/dredozubov/xsd-isogen")
52+ )
53+ )
54+ <*> option auto
55+ ( long "mode"
56+ <> short 'm'
57+ <> value ParserAndGenerator
58+ <> help ("Whether to generate both parsers and generators. One of: "
59+ <> (mconcat . intersperse ", " . fmap show
60+ $ [minBound @GenType .. maxBound]) <> ".")
61+ )
62+ <*> strOption
63+ ( long "input"
64+ <> short 'i'
65+ <> help "Path to the input .xsd file."
66+ )
67+ <*> strOption
68+ ( long "name"
69+ <> short 'n'
70+ <> help "Specify the name of the generated module."
71+ <> value "Dummy"
72+ )
73+
74+
75+getOptions :: IO Options
76+getOptions = execParser $ info (optionsParser <**> helper)
77+ $ fullDesc
78+ <> header
79+ ( "xsd-isogen version " <> showVersion version
80+ <> " (https://github.com/dredozubov/xsd-isogen)"
81+ )
82+ <> progDesc ( "A tool for generating xml-isogen-compatible "
83+ <> "(https://github.com/typeable/xml-isogen) .hs files from .xsd files."
84+ )
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+19−13
@@ -19,27 +19,33 @@ extra-source-files: CHANGELOG.md
1919
2020executable xsd-isogen
2121 main-is: Main.hs
22− -- other-modules:
22+ other-modules: Options
23+ , Gen
24+ , BuiltInTypes
25+ , References
26+ , Paths_xsd_isogen
2327 -- other-extensions:
24− build-depends: base ^>=4.12.0.0
28+ build-depends: base >=4.12 && < 5.0
2529 , bytestring
2630 , containers
2731 , data-default
32+ , transformers
2833 , mtl
34+ , optparse-applicative
2935 , text
36+ , unordered-containers
3037 , writer-cps-mtl
3138 , xsd-parser
32− , unordered-containers
3339 hs-source-dirs: src
3440 default-language: Haskell2010
3541 ghc-options: -fwarn-unused-binds
36− , -fwarn-unused-imports
37− , -Wall
38− , -Wincomplete-uni-patterns
39− , -Wincomplete-record-updates
40− , -Wpartial-fields
41− , -Werror=missing-home-modules
42− , -Wmissing-home-modules
43− , -Widentities
44− , -Wredundant-constraints
45− , -Wmissing-export-lists
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