aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBoris Kolpackov <boris@codesynthesis.com>2010-04-15 13:47:31 +0200
committerBoris Kolpackov <boris@codesynthesis.com>2010-04-15 13:47:31 +0200
commit4c174428379af308926ec70bc5b58539a1863abf (patch)
tree0f2aeaa68b28e0c86925fd4ae0e2d893d020efdc
parent8b5538ed49925aaf9fa22e961bd632bed8458184 (diff)
Implement enum synthesis from union
A union which has enumerations with a common base as members can be transformed to an equivalent enumeration.
-rw-r--r--tests/dump/driver.cxx12
-rw-r--r--tests/schema/enumeration/makefile35
-rw-r--r--tests/schema/enumeration/test-000.std62
-rw-r--r--tests/schema/enumeration/test-000.xsd72
-rw-r--r--tests/schema/makefile9
-rw-r--r--xsd-frontend/makefile1
-rw-r--r--xsd-frontend/semantic-graph/elements.hxx2
-rw-r--r--xsd-frontend/transformations/enum-synthesis.cxx249
-rw-r--r--xsd-frontend/transformations/enum-synthesis.hxx33
9 files changed, 474 insertions, 1 deletions
diff --git a/tests/dump/driver.cxx b/tests/dump/driver.cxx
index 973a30a..2e55e97 100644
--- a/tests/dump/driver.cxx
+++ b/tests/dump/driver.cxx
@@ -7,6 +7,7 @@
#include <xsd-frontend/parser.hxx>
#include <xsd-frontend/transformations/anonymous.hxx>
+#include <xsd-frontend/transformations/enum-synthesis.hxx>
#include <xsd-frontend/semantic-graph.hxx>
#include <xsd-frontend/traversal.hxx>
@@ -571,11 +572,14 @@ main (Int argc, Char* argv[])
//
Int i (1);
Boolean anon (false);
+ Boolean enum_synth (false);
for (; i < argc; ++i)
{
if (argv[i] == NarrowString ("--anonymous"))
anon = true;
+ else if (argv[i] == NarrowString ("--enum-synthesis"))
+ enum_synth = true;
else
break;
}
@@ -607,6 +611,14 @@ main (Int argc, Char* argv[])
//
//
+ if (enum_synth)
+ {
+ Transformations::EnumSynthesis transf;
+ transf.transform (*tu, path);
+ }
+
+ //
+ //
Schema schema;
Uses uses;
diff --git a/tests/schema/enumeration/makefile b/tests/schema/enumeration/makefile
new file mode 100644
index 0000000..d958178
--- /dev/null
+++ b/tests/schema/enumeration/makefile
@@ -0,0 +1,35 @@
+# file : tests/schema/enumeration/makefile
+# author : Boris Kolpackov <boris@codesynthesis.com>
+# copyright : Copyright (c) 2006-2010 Code Synthesis Tools CC
+# license : GNU GPL v2 + exceptions; see accompanying LICENSE file
+
+include $(dir $(lastword $(MAKEFILE_LIST)))../../../build/bootstrap.make
+
+tests := 000
+
+driver := $(out_root)/tests/dump/driver
+test := $(out_base)/.test
+clean := $(out_base)/.clean
+
+# Convenience alias for default target.
+#
+$(out_base)/: $(driver)
+
+# Test.
+#
+test_targets := $(addprefix $(out_base)/.test-,$(tests))
+
+$(test): $(test_targets)
+$(test_targets): driver := $(driver)
+
+.PHONY: $(out_base)/.test-%
+$(out_base)/.test-%: $(driver) $(src_base)/test-%.xsd $(src_base)/test-%.std
+ $(call message,test $(out_base)/$*,$(driver) --enum-synthesis $(src_base)/test-$*.xsd | diff -u $(src_base)/test-$*.std -)
+
+# Clean.
+#
+$(clean):
+
+# Dependencies.
+#
+$(call import,$(src_root)/tests/dump/makefile)
diff --git a/tests/schema/enumeration/test-000.std b/tests/schema/enumeration/test-000.std
new file mode 100644
index 0000000..64e3065
--- /dev/null
+++ b/tests/schema/enumeration/test-000.std
@@ -0,0 +1,62 @@
+primary
+{
+ namespace test
+ {
+ complex common-base: http://www.w3.org/2001/XMLSchema#string
+ {
+ }
+ complex base: test#common-base
+ {
+ }
+ enumeration one: test#base
+ {
+ <romance documentation>
+ enumerator romance
+ enumerator fiction
+ enumerator horror
+ }
+ enumeration two: test#common-base
+ {
+ enumerator horror
+ enumerator history
+ enumerator philosophy
+ }
+ enumeration three: http://www.w3.org/2001/XMLSchema#anyURI
+ {
+ enumerator foo
+ enumerator bar
+ }
+ enumeration union0: test#common-base
+ {
+ <romance documentation>
+ enumerator romance
+ enumerator fiction
+ enumerator horror
+ enumerator history
+ enumerator philosophy
+ }
+ <union1 documentation>
+ enumeration union1: test#common-base
+ {
+ <romance documentation>
+ enumerator romance
+ enumerator fiction
+ enumerator horror
+ enumerator history
+ enumerator philosophy
+ }
+ union union2 test#one test#union1 test#common-base
+ union union3 test#one test#three
+ complex complex1
+ {
+ element a
+ [1, 1] sequence
+ {
+ [1, 1] element a test#union1
+ }
+ }
+ complex complex2: test#union1
+ {
+ }
+ }
+}
diff --git a/tests/schema/enumeration/test-000.xsd b/tests/schema/enumeration/test-000.xsd
new file mode 100644
index 0000000..08539bf
--- /dev/null
+++ b/tests/schema/enumeration/test-000.xsd
@@ -0,0 +1,72 @@
+<?xml version="1.0"?>
+<schema xmlns="http://www.w3.org/2001/XMLSchema" xmlns:t="test" targetNamespace="test">
+
+ <!-- Enumeration synthesis -->
+
+ <simpleType name="common-base">
+ <restriction base="string"/>
+ </simpleType>
+
+ <simpleType name="base">
+ <restriction base="t:common-base"/>
+ </simpleType>
+
+ <simpleType name="one">
+ <restriction base="t:base">
+ <enumeration value="romance">
+ <annotation>
+ <documentation>romance documentation</documentation>
+ </annotation>
+ </enumeration>
+ <enumeration value="fiction"/>
+ <enumeration value="horror"/>
+ </restriction>
+ </simpleType>
+
+ <simpleType name="two">
+ <restriction base="t:common-base">
+ <enumeration value="horror"/>
+ <enumeration value="history"/>
+ <enumeration value="philosophy"/>
+ </restriction>
+ </simpleType>
+
+ <simpleType name="three">
+ <restriction base="anyURI">
+ <enumeration value="foo"/>
+ <enumeration value="bar"/>
+ </restriction>
+ </simpleType>
+
+ <simpleType name="union0">
+ <union memberTypes="t:one t:two t:union1"/>
+ </simpleType>
+
+ <simpleType name="union1">
+ <annotation>
+ <documentation>union1 documentation</documentation>
+ </annotation>
+ <union memberTypes="t:one t:two"/>
+ </simpleType>
+
+ <simpleType name="union2">
+ <union memberTypes="t:one t:union1 t:common-base"/>
+ </simpleType>
+
+ <simpleType name="union3">
+ <union memberTypes="t:one t:three"/>
+ </simpleType>
+
+ <complexType name="complex1">
+ <sequence>
+ <element name="a" type="t:union1"/>
+ </sequence>
+ </complexType>
+
+ <complexType name="complex2">
+ <simpleContent>
+ <extension base="t:union1"/>
+ </simpleContent>
+ </complexType>
+
+</schema>
diff --git a/tests/schema/makefile b/tests/schema/makefile
index b0fab92..fde5482 100644
--- a/tests/schema/makefile
+++ b/tests/schema/makefile
@@ -5,7 +5,14 @@
include $(dir $(lastword $(MAKEFILE_LIST)))../../build/bootstrap.make
-tests := annotation anonymous attribute-group element-group default union
+tests := \
+annotation \
+anonymous \
+attribute-group \
+default \
+element-group \
+enumeration \
+union
default := $(out_base)/
test := $(out_base)/.test
diff --git a/xsd-frontend/makefile b/xsd-frontend/makefile
index e0526ab..abf6565 100644
--- a/xsd-frontend/makefile
+++ b/xsd-frontend/makefile
@@ -39,6 +39,7 @@ cxx_tun += traversal/attribute.cxx \
traversal/union.cxx
cxx_tun += transformations/anonymous.cxx \
+ transformations/enum-synthesis.cxx \
transformations/restriction.cxx \
transformations/schema-per-type.cxx \
transformations/simplifier.cxx
diff --git a/xsd-frontend/semantic-graph/elements.hxx b/xsd-frontend/semantic-graph/elements.hxx
index ec425f6..f59961b 100644
--- a/xsd-frontend/semantic-graph/elements.hxx
+++ b/xsd-frontend/semantic-graph/elements.hxx
@@ -850,6 +850,8 @@ namespace XSDFrontend
}
protected:
+ friend class Bits::Graph<Node, Edge>;
+
Inherits ()
{
}
diff --git a/xsd-frontend/transformations/enum-synthesis.cxx b/xsd-frontend/transformations/enum-synthesis.cxx
new file mode 100644
index 0000000..4a5156f
--- /dev/null
+++ b/xsd-frontend/transformations/enum-synthesis.cxx
@@ -0,0 +1,249 @@
+// file : xsd-frontend/transformations/enum-synthesis.cxx
+// author : Boris Kolpackov <boris@codesynthesis.com>
+// copyright : Copyright (c) 2006-2010 Code Synthesis Tools CC
+// license : GNU GPL v2 + exceptions; see accompanying LICENSE file
+
+#include <xsd-frontend/transformations/enum-synthesis.hxx>
+
+#include <xsd-frontend/semantic-graph.hxx>
+#include <xsd-frontend/traversal.hxx>
+
+#include <cult/containers/set.hxx>
+
+namespace XSDFrontend
+{
+ using namespace Cult;
+ typedef WideString String;
+
+ namespace
+ {
+ typedef Cult::Containers::Set<String> Enumerators;
+
+ struct Enumerator: Traversal::Enumerator
+ {
+ Enumerator (SemanticGraph::Schema& s,
+ SemanticGraph::Enumeration& e,
+ Enumerators& enumerators)
+ : schema_ (s), enum_ (e), enumerators_ (enumerators)
+ {
+ }
+
+ virtual Void
+ traverse (Type& e)
+ {
+ String const& name (e.name ());
+
+ if (enumerators_.find (name) == enumerators_.end ())
+ {
+ enumerators_.insert (name);
+
+ // Clone the enumerator and add it to enum_.
+ //
+ Type& c (schema_.new_node<Type> (e.file (), e.line (), e.column ()));
+
+ schema_.new_edge<SemanticGraph::Names> (enum_, c, name);
+ schema_.new_edge<SemanticGraph::Belongs> (c, enum_);
+
+ if (e.annotated_p ())
+ schema_.new_edge<SemanticGraph::Annotates> (e.annotation (), c);
+ }
+ }
+
+ private:
+ SemanticGraph::Schema& schema_;
+ SemanticGraph::Enumeration& enum_;
+ Enumerators& enumerators_;
+ };
+
+ //
+ //
+ struct Union: Traversal::Union
+ {
+ Union (SemanticGraph::Schema& schema)
+ : schema_ (schema)
+ {
+ }
+
+ virtual Void
+ traverse (Type& u)
+ {
+ using SemanticGraph::Enumeration;
+
+ SemanticGraph::Context& uc (u.context ());
+
+ if (uc.count ("xsd-frontend-enum-synthesis-processed"))
+ return;
+
+ uc.set ("xsd-frontend-enum-synthesis-processed", true);
+
+ // First see if this union is suitable for synthesis.
+ //
+ SemanticGraph::Type* base (0);
+
+ for (Type::ArgumentedIterator i (u.argumented_begin ());
+ i != u.argumented_end (); ++i)
+ {
+ if (i->type ().is_a<SemanticGraph::Union> ())
+ {
+ // See if we can synthesize an enum for this union. This
+ // call can change the value i->type() returns.
+ //
+ dispatch (i->type ());
+ }
+
+ SemanticGraph::Type& t (i->type ());
+
+ if (!t.is_a<Enumeration> ())
+ return;
+
+ // Make sure all the enums have a common base.
+ //
+ if (base == 0)
+ base = &t;
+ else
+ {
+ // Traverse the inheritance hierarchy until we fine a
+ // common base.
+ //
+ while (base != 0)
+ {
+ SemanticGraph::Type* b (&t);
+
+ for (; b != base && b->inherits_p ();
+ b = &b->inherits ().base ()) ;
+
+ if (base == b)
+ break;
+
+ // Could not find any match on this level. Go one step
+ // lower and try again.
+ //
+ base = base->inherits_p () ? &base->inherits ().base () : 0;
+ }
+
+ if (base == 0)
+ return; // No common base.
+ }
+ }
+
+ if (base == 0)
+ return; // Empty union.
+
+ // So this union is suitable for synthesis. Base variable points
+ // to the "most-derived" common base type.
+ //
+ Enumeration& e (schema_.new_node<Enumeration> (
+ u.file (), u.line (), u.column ()));
+
+ schema_.new_edge<SemanticGraph::Restricts> (e, *base);
+
+ // Copy enumerators from the member enums.
+ //
+ {
+ Enumerators set;
+ Traversal::Enumeration en;
+ Traversal::Names names;
+ Enumerator er (schema_, e, set);
+ en >> names >> er;
+
+ for (Type::ArgumentedIterator i (u.argumented_begin ());
+ i != u.argumented_end (); ++i)
+ {
+ en.dispatch (i->type ());
+ }
+ }
+
+ // Reset edges pointing to union to point to enum.
+ //
+ if (u.annotated_p ())
+ {
+ schema_.reset_right_node (u.annotated (), e);
+ schema_.add_edge_right (e, u.annotated ());
+ }
+
+ schema_.reset_right_node (u.named_ (), e);
+ schema_.add_edge_right (e, u.named_ ());
+
+ for (Type::ClassifiesIterator i (u.classifies_begin ()),
+ end (u.classifies_end ()); i != end; ++i)
+ {
+ schema_.reset_right_node (*i, e);
+ schema_.add_edge_right (e, *i);
+ }
+
+ for (Type::BegetsIterator i (u.begets_begin ()),
+ end (u.begets_end ()); i != end; ++i)
+ {
+ schema_.reset_right_node (*i, e);
+ schema_.add_edge_right (e, *i);
+ }
+
+ for (Type::ArgumentsIterator i (u.arguments_begin ()),
+ end (u.arguments_end ()); i != end; ++i)
+ {
+ schema_.reset_left_node (*i, e);
+ schema_.add_edge_left (e, *i);
+ }
+
+ // Remove Arguments edges pointing to the union.
+ //
+ while (u.argumented_begin () != u.argumented_end ())
+ {
+ SemanticGraph::Arguments& a (*u.argumented_begin ());
+ schema_.delete_edge (a.type (), a.specialization (), a);
+ }
+
+ // Copy context and delete the union node.
+ //
+ e.context ().swap (uc);
+ schema_.delete_node (u);
+ }
+
+ private:
+ SemanticGraph::Schema& schema_;
+ };
+
+ // Go into implied/included/imported schemas while making sure
+ // we don't process the same stuff more than once.
+ //
+ struct Uses: Traversal::Uses
+ {
+ virtual Void
+ traverse (Type& u)
+ {
+ SemanticGraph::Schema& s (u.schema ());
+
+ if (!s.context ().count ("xsd-frontend-enum-synthesis-seen"))
+ {
+ s.context ().set ("xsd-frontend-enum-synthesis-seen", true);
+ Traversal::Uses::traverse (u);
+ }
+ }
+ };
+ }
+
+ namespace Transformations
+ {
+ Void EnumSynthesis::
+ transform (SemanticGraph::Schema& s, SemanticGraph::Path const&)
+ {
+ Traversal::Schema schema;
+ Uses uses;
+
+ schema >> uses >> schema;
+
+ Traversal::Names schema_names;
+ Traversal::Namespace ns;
+ Traversal::Names ns_names;
+ Union u (s);
+
+ schema >> schema_names >> ns >> ns_names >> u;
+
+ // Some twisted schemas do recusive inclusions.
+ //
+ s.context ().set ("xsd-frontend-enum-synthesis-seen", true);
+
+ schema.dispatch (s);
+ }
+ }
+}
diff --git a/xsd-frontend/transformations/enum-synthesis.hxx b/xsd-frontend/transformations/enum-synthesis.hxx
new file mode 100644
index 0000000..e3c38c7
--- /dev/null
+++ b/xsd-frontend/transformations/enum-synthesis.hxx
@@ -0,0 +1,33 @@
+// file : xsd-frontend/transformations/enum-synthesis.hxx
+// author : Boris Kolpackov <boris@codesynthesis.com>
+// copyright : Copyright (c) 2006-2010 Code Synthesis Tools CC
+// license : GNU GPL v2 + exceptions; see accompanying LICENSE file
+
+#ifndef XSD_FRONTEND_TRANSFORMATIONS_ENUM_SYNTHESIS_HXX
+#define XSD_FRONTEND_TRANSFORMATIONS_ENUM_SYNTHESIS_HXX
+
+#include <cult/types.hxx>
+
+#include <xsd-frontend/semantic-graph/elements.hxx> // Path
+#include <xsd-frontend/semantic-graph/schema.hxx>
+
+namespace XSDFrontend
+{
+ namespace Transformations
+ {
+ using namespace Cult::Types;
+
+ // This transformation replaces unions of one or more enumerations
+ // with the same base with an equivalent synthesized enumeration.
+ // This transformation assumes that there are no anonymous types.
+ //
+ class EnumSynthesis
+ {
+ public:
+ Void
+ transform (SemanticGraph::Schema&, SemanticGraph::Path const&);
+ };
+ }
+}
+
+#endif // XSD_FRONTEND_TRANSFORMATIONS_ENUM_SYNTHESIS_HXX