Skip to content

Instantly share code, notes, and snippets.

@moriyoshi
Created April 22, 2009 13:27
Show Gist options
  • Save moriyoshi/99792 to your computer and use it in GitHub Desktop.
Save moriyoshi/99792 to your computer and use it in GitHub Desktop.
#include <iostream>
#include <vector>
#include <typeinfo>
#include <utility>
#include <boost/shared_ptr.hpp>
#include <boost/variant.hpp>
#include <boost/foreach.hpp>
#include <boost/assert.hpp>
namespace xml_builder {
namespace detail {
namespace tokens {
struct start_tag;
struct end_tag;
struct cdata;
} // namespace tokens
typedef boost::variant<
tokens::start_tag,
tokens::end_tag,
tokens::cdata> token;
typedef std::vector<token> token_vector;
typedef boost::shared_ptr<token_vector> token_vector_handle;
} // namespace detail
struct xml_string
{
xml_string(char const* _data): data(_data) {}
char const *data;
};
struct xml
{
xml(detail::token_vector_handle hdl): hdl_(hdl) {}
template<typename T1_, typename T2_>
void dump(std::basic_ostream<T1_, T2_>&) const;
private:
detail::token_vector_handle hdl_;
};
namespace detail {
namespace tokens {
struct start_tag
{
typedef std::vector<std::pair<char const*, char const*> > attr_list_type;
start_tag(): name_(0), attr_list_() {}
start_tag(char const* name): name_(name) {}
char const* name() const
{
return name_;
}
attr_list_type& attributes()
{
if (!attr_list_)
attr_list_.reset(new attr_list_type());
return *attr_list_;
}
attr_list_type const& attributes() const
{
return const_cast<start_tag*>(this)->attributes();
}
private:
char const* name_;
boost::shared_ptr<attr_list_type> attr_list_;
};
struct end_tag
{
end_tag(): name_(0) {}
end_tag(char const* name): name_(name) {}
char const* name() const
{
return name_;
}
private:
char const* name_;
};
struct cdata
{
cdata(): data_(0) {}
cdata(char const *data): data_(data) {}
char const* data() const
{
return data_;
}
private:
char const *data_;
};
} // namespace tokens
struct expect_attr_name_or_close;
struct empty {};
struct expect_open
{
expect_open(token_vector_handle const& hdl): hdl_(hdl) {}
token_vector_handle handle() const
{
return hdl_;
}
operator xml() { return xml(hdl_); }
private:
token_vector_handle hdl_;
};
struct expect_close
{
expect_close(token_vector_handle const& hdl): hdl_(hdl) {}
token_vector_handle handle() const
{
return hdl_;
}
private:
const token_vector_handle hdl_;
};
struct expect_attr_name_or_close
{
expect_attr_name_or_close(token_vector_handle const& hdl): hdl_(hdl) {}
token_vector_handle handle() const
{
return hdl_;
}
private:
const token_vector_handle hdl_;
};
struct expect_attr_value
{
expect_attr_value(token_vector_handle const& hdl): hdl_(hdl) {}
token_vector_handle handle() const
{
return hdl_;
}
private:
const token_vector_handle hdl_;
};
struct attribute_pair
{
attribute_pair(char const* name): name_(name), value_(0) {}
attribute_pair& operator=(char const* v)
{
value_ = v;
return *this;
}
char const* name() const
{
return name_;
}
char const* value() const
{
return value_;
}
private:
char const* name_;
char const* value_;
};
template<typename Tstrm_>
struct token_printer: public boost::static_visitor<>
{
token_printer(Tstrm_& out): out_(out) {}
void operator()(tokens::start_tag const& tag) const
{
out_ << "<" << tag.name();
BOOST_FOREACH (typename tokens::start_tag::attr_list_type::value_type i,
tag.attributes() )
{
out_ << " " << i.first << "=\"" << i.second << "\"";
}
out_ << ">" << std::endl;
}
void operator()(tokens::end_tag const& tag) const
{
out_ << "</" << tag.name() << ">" << std::endl;
}
void operator()(tokens::cdata const& cdata) const
{
out_ << cdata.data() << std::endl;
}
private:
Tstrm_& out_;
};
template<typename T1_, typename T2_>
inline token_printer<std::basic_ostream<T1_, T2_> >
make_token_printer(std::basic_ostream<T1_, T2_>& out)
{
return token_printer<std::basic_ostream<T1_, T2_> >(out);
}
} // namespace detail
inline detail::expect_open operator>(
detail::expect_attr_name_or_close const&lhs, xml_string const& text)
{
detail::token_vector_handle hdl = lhs.handle();
hdl->push_back(detail::tokens::cdata(text.data));
return detail::expect_open(hdl);
}
inline detail::expect_open operator>=(
detail::expect_attr_name_or_close const& lhs, xml_string const &text)
{
detail::token_vector_handle hdl = lhs.handle();
hdl->push_back(detail::tokens::cdata(text.data));
return detail::expect_open(hdl);
}
inline detail::expect_open operator>(
detail::expect_attr_name_or_close const& lhs, detail::empty const&)
{
return detail::expect_open(lhs.handle());
}
inline detail::expect_open operator>=(
detail::expect_attr_name_or_close const& lhs, detail::empty const&)
{
return detail::expect_open(lhs.handle());
}
inline detail::expect_open operator>(
detail::expect_close const& lhs, xml_string const& text)
{
detail::token_vector_handle hdl = lhs.handle();
hdl->push_back(detail::tokens::cdata(text.data));
return detail::expect_open(hdl);
}
inline detail::expect_open operator>(
detail::expect_close const& lhs, detail::empty const&)
{
return detail::expect_open(lhs.handle());
}
inline detail::expect_close operator<=(
detail::expect_open const& lhs, xml_string const& tagname)
{
detail::token_vector_handle hdl = lhs.handle();
hdl->push_back(detail::tokens::end_tag(tagname.data));
return detail::expect_close(hdl);
}
inline detail::expect_attr_name_or_close operator<(
detail::empty const&, xml_string const& tagname)
{
detail::token_vector_handle hdl(new detail::token_vector());
hdl->push_back(detail::tokens::start_tag(tagname.data));
return detail::expect_attr_name_or_close(hdl);
}
inline detail::expect_attr_name_or_close operator<(
detail::expect_open const& lhs, xml_string const& tagname)
{
detail::token_vector_handle hdl = lhs.handle();
hdl->push_back(detail::tokens::start_tag(tagname.data));
return detail::expect_attr_name_or_close(hdl);
}
inline detail::expect_attr_name_or_close operator>=(detail::expect_attr_name_or_close const& lhs, detail::attribute_pair const& pair)
{
detail::token_vector_handle hdl = lhs.handle();
BOOST_ASSERT(hdl->back().which() == 0);
boost::get<detail::tokens::start_tag&>(hdl->back()).attributes().push_back(
std::make_pair(pair.name(), pair.value()));
return detail::expect_attr_name_or_close(hdl);
}
template<typename T1_, typename T2_>
inline void xml::dump(std::basic_ostream<T1_, T2_>& out) const
{
for (typename detail::token_vector::const_iterator i(hdl_->begin()), e(hdl_->end());
i != e; ++i) {
boost::apply_visitor(detail::make_token_printer(out), *i);
}
}
static const detail::empty $ = detail::empty();
}
#define $$(kv) >=((detail::attribute_pair)kv)
int main(int, char **)
{
using namespace xml_builder;
xml($
<"html">$
<"head">$
<"meta" $$("http-equiv"="Content-Type")
$$("content"="text/html") >=$
<="head">$
<"body">$
<"h1">"Hello, World!"<="h1">
"test"
<="body">$
<="html">$
).dump(std::cout);
return 0;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment