diff options
author | Boris Kolpackov <boris@codesynthesis.com> | 2015-08-10 18:28:53 +0200 |
---|---|---|
committer | Boris Kolpackov <boris@codesynthesis.com> | 2015-08-10 18:28:53 +0200 |
commit | 63aaad529c9ae9358f22fd9563aeabc679f4defd (patch) | |
tree | 67adfffcb14250f083d86b568577a8a2eb7bdf9f | |
parent | 8e866579cb459c5104c532d5e41d562d45236ea5 (diff) |
Add support for generating XHTML5
-rw-r--r-- | tests/buildfile | 2 | ||||
-rw-r--r-- | tests/web/buildfile | 7 | ||||
-rw-r--r-- | tests/web/xhtml/buildfile | 8 | ||||
-rw-r--r-- | tests/web/xhtml/driver.cxx | 44 | ||||
-rw-r--r-- | tests/web/xhtml/test.out | 15 | ||||
-rw-r--r-- | web/xhtml | 255 |
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 |