aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--tests/buildfile2
-rw-r--r--tests/web/buildfile7
-rw-r--r--tests/web/xhtml/buildfile8
-rw-r--r--tests/web/xhtml/driver.cxx44
-rw-r--r--tests/web/xhtml/test.out15
-rw-r--r--web/xhtml255
6 files changed, 330 insertions, 1 deletions
diff --git a/tests/buildfile b/tests/buildfile
index 41a9bcc..ed48d00 100644
--- a/tests/buildfile
+++ b/tests/buildfile
@@ -2,6 +2,6 @@
# copyright : Copyright (c) 2014-2015 Code Synthesis Ltd
# license : MIT; see accompanying LICENSE file
-d = loader/
+d = loader/ web/
.: $d
include $d
diff --git a/tests/web/buildfile b/tests/web/buildfile
new file mode 100644
index 0000000..0077eae
--- /dev/null
+++ b/tests/web/buildfile
@@ -0,0 +1,7 @@
+# file : tests/web/buildfile
+# copyright : Copyright (c) 2014-2015 Code Synthesis Ltd
+# license : MIT; see accompanying LICENSE file
+
+d = xhtml/
+.: $d
+include $d
diff --git a/tests/web/xhtml/buildfile b/tests/web/xhtml/buildfile
new file mode 100644
index 0000000..308a104
--- /dev/null
+++ b/tests/web/xhtml/buildfile
@@ -0,0 +1,8 @@
+# file : tests/web/xhtml/buildfile
+# copyright : Copyright (c) 2014-2015 Code Synthesis Ltd
+# license : MIT; see accompanying LICENSE file
+
+import libs = libstudxml%lib{studxml}
+
+exe{driver}: cxx{driver} $libs
+exe{driver}: test.output = test.out
diff --git a/tests/web/xhtml/driver.cxx b/tests/web/xhtml/driver.cxx
new file mode 100644
index 0000000..23531f7
--- /dev/null
+++ b/tests/web/xhtml/driver.cxx
@@ -0,0 +1,44 @@
+// file : tests/web/xhtml/driver.cxx -*- C++ -*-
+// copyright : Copyright (c) 2014-2015 Code Synthesis Ltd
+// license : MIT; see accompanying LICENSE file
+
+#include <iostream>
+
+#include <xml/serializer>
+
+#include <web/xhtml>
+
+using namespace std;
+using namespace xml;
+
+int
+main ()
+{
+ using namespace web::xhtml;
+
+ serializer s (cout, "output");
+
+ s << HTML
+ << HEAD
+ << TITLE << "Example XHTML5 document" << ~TITLE
+ << ~HEAD
+ << BODY
+ //
+ // Inline elements (no indentation).
+ //
+ << P << "Here be " << B << "Dragons!" << ~B << *BR
+ << "and a newline" << ~P
+ //
+ // Various ways to specify attributes.
+ //
+ << P(ID=123, CLASS="cool") << "Text" << ~P
+ << P << (ID=123) << (CLASS="cool") << "Text" << ~P
+ << P << ID(123) << CLASS("cool") << "Text" << ~P
+ << P << ID << 123 << ~ID << CLASS << "cool" << ~CLASS << "Text" << ~P
+ //
+ // Empty element with attributes.
+ //
+ << P << "Text" << *BR(CLASS="double") << ~P
+ << ~BODY
+ << ~HTML;
+}
diff --git a/tests/web/xhtml/test.out b/tests/web/xhtml/test.out
new file mode 100644
index 0000000..ffa6df6
--- /dev/null
+++ b/tests/web/xhtml/test.out
@@ -0,0 +1,15 @@
+<!DOCTYPE html>
+<html xmlns="http://www.w3.org/1999/xhtml">
+ <head>
+ <meta charset="UTF-8"/>
+ <title>Example XHTML5 document</title>
+ </head>
+ <body>
+ <p>Here be <b>Dragons!</b><br/>and a newline</p>
+ <p id="123" class="cool">Text</p>
+ <p id="123" class="cool">Text</p>
+ <p id="123" class="cool">Text</p>
+ <p id="123" class="cool">Text</p>
+ <p>Text<br class="double"/></p>
+ </body>
+</html>
diff --git a/web/xhtml b/web/xhtml
new file mode 100644
index 0000000..b8d6ce0
--- /dev/null
+++ b/web/xhtml
@@ -0,0 +1,255 @@
+// file : web/xhtml -*- C++ -*-
+// copyright : Copyright (c) 2014-2015 Code Synthesis Ltd
+// license : MIT; see accompanying LICENSE file
+
+#ifndef WEB_XHTML
+#define WEB_XHTML
+
+#include <xml/serializer>
+
+namespace web
+{
+ // "Canonical" XHTML5 vocabulary.
+ //
+ // One-letter tag names and local variable clash problem:
+ //
+ // a at|an|an anc anch
+ // b bt|bo|bl bld bold
+ // i it|it|it itl ital
+ // p pt|pr|pr par para
+ // q qt|qu|qt quo quot
+ // s st|st|st stk strk
+ // u ut|un|un unl undr
+ //
+ // Other options:
+ // - _a, a_, xa
+ // - A, I
+ // - x::i
+ //
+ // Things can actually get much worse, consider:
+ //
+ // int i;
+ // s << i << "text" << ~i;
+ //
+ // So perhaps this is the situation where the explicit namespace
+ // qualification (e.g., x::p) is the only robust option?
+ //
+ namespace xhtml
+ {
+ const char* xmlns = "http://www.w3.org/1999/xhtml";
+
+ using serializer_func = void (*) (xml::serializer&);
+
+ struct attr_value_base
+ {
+ const char* name;
+ mutable const attr_value_base* next;
+
+ virtual void
+ operator() (xml::serializer& s) const = 0;
+
+ protected:
+ explicit
+ attr_value_base (const char* n): name (n), next (nullptr) {}
+ };
+
+ template <typename T>
+ struct attr_value: attr_value_base
+ {
+ const T* val;
+
+ attr_value (const char* n, const T& v): attr_value_base (n), val (&v) {}
+
+ virtual void
+ operator() (xml::serializer& s) const
+ {
+ s.attribute (name, *val);
+ if (next != nullptr)
+ s << *next;
+ }
+ };
+
+ struct element_base;
+
+ // Element without any content, e.g., *BR.
+ //
+ struct empty_element
+ {
+ const element_base* e;
+
+ void
+ operator() (xml::serializer& s) const;
+ };
+
+ struct element_base
+ {
+ virtual void
+ operator() (xml::serializer& s) const = 0;
+
+ virtual serializer_func
+ operator~ () const = 0;
+
+ empty_element
+ operator* () const {return empty_element {this};}
+ };
+
+ inline void empty_element::
+ operator() (xml::serializer& s) const {s << *e << ~*e;}
+
+ // Element with an attribute chain, e.g., P(ID = 123, CLASS = "abc").
+ //
+ struct attr_element: element_base
+ {
+ const element_base* e;
+ const attr_value_base* a;
+
+ attr_element (const element_base& e, const attr_value_base& a)
+ : e (&e), a (&a) {}
+
+ virtual void
+ operator() (xml::serializer& s) const {s << *e << *a;}
+
+ virtual serializer_func
+ operator~ () const {return ~*e;}
+ };
+
+ struct element: element_base
+ {
+ const char* name;
+
+ explicit
+ element (const char* n): name (n) {}
+
+ virtual void
+ operator() (xml::serializer& s) const {s.start_element (xmlns, name);}
+
+ virtual serializer_func
+ operator~ () const {return [](xml::serializer& s) {s.end_element ();};}
+
+ // s << elem(attr1 = 123, attr2 = "abc");
+ //
+ template <typename T1>
+ attr_element
+ operator () (const attr_value<T1>& a1) const
+ {
+ return attr_element (*this, a1);
+ }
+
+ template <typename T1, typename... TN>
+ attr_element
+ operator () (const attr_value<T1>& a1, const attr_value<TN>&... an) const
+ {
+ a1.next = operator() (an...).a;
+ return attr_element (*this, a1);
+ }
+
+ protected:
+ element () = default;
+ };
+
+ struct inline_element: element
+ {
+ using element::element;
+ using element::operator();
+
+ virtual void
+ operator() (xml::serializer& s) const
+ {
+ s.suspend_indentation ();
+ element::operator() (s);
+ }
+
+ virtual serializer_func
+ operator~ () const
+ {
+ return [](xml::serializer& s)
+ {
+ s.end_element (); s.resume_indentation ();
+ };
+ }
+ };
+
+ struct attribute
+ {
+ const char* name;
+
+ explicit
+ attribute (const char* n): name (n) {}
+
+ // s << (attr1 = 123) << (attr2 = "abc");
+ //
+ template <typename T>
+ attr_value<T>
+ operator= (const T& v) const {return attr_value<T> (name, v);}
+
+ // s << attr1 (123) << attr2 ("abc");
+ //
+ template <typename T>
+ attr_value<T>
+ operator() (const T& v) const {return attr_value<T> (name, v);}
+
+ // s << attr1 << 123 << ~attr1 << attr2 << "abc" << ~attr2;
+ //
+ void
+ operator() (xml::serializer& s) const {s.start_attribute (name);}
+
+ virtual serializer_func
+ operator~ () const {return [](xml::serializer& s) {s.end_attribute ();};}
+ };
+
+ // Elements.
+ //
+ // Note that they are all declared static which means we may end
+ // up with multiple identical copies if this header get included
+ // into multiple translation units. The hope here is that the
+ // compiler will "see-through" and eliminate all of them.
+ //
+ struct html_element: element
+ {
+ html_element () {} // Uninitialized const static.
+
+ virtual void
+ operator() (xml::serializer& s) const
+ {
+ s.doctype_decl ("html");
+ s.start_element (xmlns, "html");
+ s.namespace_decl (xmlns, "");
+ }
+ };
+ static const html_element HTML;
+
+ struct head_element: element
+ {
+ head_element () {} // Uninitialized const static.
+
+ virtual void
+ operator() (xml::serializer& s) const
+ {
+ s.start_element (xmlns, "head");
+ s.start_element (xmlns, "meta");
+ s.attribute ("charset", "UTF-8");
+ s.end_element ();
+ }
+ };
+ static const head_element HEAD;
+
+ static const element TITLE ("title");
+ static const element BODY ("body");
+ static const element P ("p");
+
+ static const inline_element B ("b");
+ static const inline_element I ("i");
+ static const inline_element U ("u");
+
+ static const inline_element EM ("em");
+ static const inline_element BR ("br");
+
+ // Attributes.
+ //
+ static const attribute ID ("id");
+ static const attribute CLASS ("class");
+ static const attribute STYLE ("style");
+ }
+}
+
+#endif // WEB_XHTML