diff options
Diffstat (limited to 'tools/quickbook/src/values.cpp')
-rw-r--r-- | tools/quickbook/src/values.cpp | 853 |
1 files changed, 853 insertions, 0 deletions
diff --git a/tools/quickbook/src/values.cpp b/tools/quickbook/src/values.cpp new file mode 100644 index 0000000000..7c3521d50b --- /dev/null +++ b/tools/quickbook/src/values.cpp @@ -0,0 +1,853 @@ +/*============================================================================= + Copyright (c) 2010-2011 Daniel James + + Use, modification and distribution is subject to the Boost Software + License, Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at + http://www.boost.org/LICENSE_1_0.txt) +=============================================================================*/ + +#include "values.hpp" +#include "files.hpp" +#include <boost/current_function.hpp> +#include <boost/lexical_cast.hpp> + +#define UNDEFINED_ERROR() \ + throw value_undefined_method( \ + std::string(BOOST_CURRENT_FUNCTION) +\ + " not defined for " + \ + this->type_name() + \ + " values." \ + ); + +namespace quickbook +{ + //////////////////////////////////////////////////////////////////////////// + // Value Error + + struct value_undefined_method : value_error + { + value_undefined_method(std::string const&); + }; + + value_error::value_error(std::string const& x) + : std::logic_error(x) {} + + value_undefined_method::value_undefined_method(std::string const& x) + : value_error(x) {} + + //////////////////////////////////////////////////////////////////////////// + // Node + + namespace detail + { + value_node::value_node(tag_type t) + : ref_count_(0), tag_(t), next_() { + } + + value_node::~value_node() { + } + + file_ptr value_node::get_file() const { UNDEFINED_ERROR(); } + string_iterator value_node::get_position() const { UNDEFINED_ERROR(); } + int value_node::get_int() const { UNDEFINED_ERROR(); } + string_ref value_node::get_quickbook() const { UNDEFINED_ERROR(); } + std::string value_node::get_encoded() const { UNDEFINED_ERROR(); } + value_node* value_node::get_list() const { UNDEFINED_ERROR(); } + + bool value_node::empty() const { return false; } + bool value_node::check() const { return true; } + bool value_node::is_list() const { return false; } + bool value_node::is_encoded() const { return false; } + bool value_node::equals(value_node*) const { UNDEFINED_ERROR(); } + } + + //////////////////////////////////////////////////////////////////////////// + // List end value + // + // A special value for marking the end of lists. + + namespace detail + { + struct value_list_end_impl : public value_node + { + static value_list_end_impl instance; + private: + value_list_end_impl() + : value_node(value::default_tag) + { + intrusive_ptr_add_ref(&instance); + next_ = this; + } + + virtual char const* type_name() const { return "list end"; } + virtual value_node* clone() const { UNDEFINED_ERROR(); } + + virtual bool equals(value_node* other) const + { return this == other; } + + bool empty() const { UNDEFINED_ERROR(); } + bool check() const { UNDEFINED_ERROR(); } + bool is_list() const { UNDEFINED_ERROR(); } + bool is_encoded() const { UNDEFINED_ERROR(); } + }; + + value_list_end_impl value_list_end_impl::instance; + } + + //////////////////////////////////////////////////////////////////////////// + // Empty/nil values + // + // (nil is just a special case of empty, don't be mislead by the name + // the type is not important). + + namespace detail + { + struct empty_value_impl : public value_node + { + static value_node* new_(value::tag_type t); + + protected: + explicit empty_value_impl(value::tag_type t) + : value_node(t) {} + + private: + char const* type_name() const { return "empty"; } + + virtual value_node* clone() const + { return new empty_value_impl(tag_); } + + virtual bool empty() const + { return true; } + + virtual bool check() const + { return false; } + + virtual bool equals(value_node* other) const + { return !other->check(); } + + friend value quickbook::empty_value(value::tag_type); + }; + + struct value_nil_impl : public empty_value_impl + { + static value_nil_impl instance; + private: + value_nil_impl() + : empty_value_impl(value::default_tag) + { + intrusive_ptr_add_ref(&instance); + next_ = &value_list_end_impl::instance; + } + }; + + value_nil_impl value_nil_impl::instance; + + value_node* empty_value_impl::new_(value::tag_type t) { + // The return value from this function is always placed in an + // intrusive_ptr which will manage the memory correctly. + // Note that value_nil_impl increments its reference count + // in its constructor, so that it will never be deleted by the + // intrusive pointer. + + if (t == value::default_tag) + return &value_nil_impl::instance; + else + return new empty_value_impl(t); + } + } + + value empty_value(value::tag_type t) + { + return value(detail::empty_value_impl::new_(t)); + } + + //////////////////////////////////////////////////////////////////////////// + // value_counted + + namespace detail + { + value_counted::value_counted() + : value_base(&value_nil_impl::instance) + { + // Even though empty is not on the heap, its reference + // counter needs to be incremented so that the destructor + // doesn't try to delete it. + + intrusive_ptr_add_ref(value_); + } + + value_counted::value_counted(value_counted const& x) + : value_base(x) + { + intrusive_ptr_add_ref(value_); + } + + value_counted::value_counted(value_base const& x) + : value_base(x) + { + intrusive_ptr_add_ref(value_); + } + + value_counted::value_counted(value_node* x) + : value_base(x) + { + intrusive_ptr_add_ref(value_); + } + + value_counted::~value_counted() + { + intrusive_ptr_release(value_); + } + } + + //////////////////////////////////////////////////////////////////////////// + // value + + value::value() + : detail::value_counted() + { + } + + value::value(value const& x) + : detail::value_counted(x) + { + } + + value::value(detail::value_base const& x) + : detail::value_counted(x) + { + } + + value::value(detail::value_node* x) + : detail::value_counted(x) + { + } + + value& value::operator=(value x) + { + swap(x); + return *this; + } + + //////////////////////////////////////////////////////////////////////////// + // Integers + + namespace detail + { + struct int_value_impl : public value_node + { + public: + explicit int_value_impl(int, value::tag_type); + private: + char const* type_name() const { return "integer"; } + virtual value_node* clone() const; + virtual int get_int() const; + virtual std::string get_encoded() const; + virtual bool empty() const; + virtual bool is_encoded() const; + virtual bool equals(value_node*) const; + + int value_; + }; + + int_value_impl::int_value_impl(int v, value::tag_type t) + : value_node(t) + , value_(v) + {} + + value_node* int_value_impl::clone() const + { + return new int_value_impl(value_, tag_); + } + + int int_value_impl::get_int() const + { + return value_; + } + + std::string int_value_impl::get_encoded() const + { + return boost::lexical_cast<std::string>(value_); + } + + bool int_value_impl::empty() const + { + return false; + } + + bool int_value_impl::is_encoded() const + { + return true; + } + + bool int_value_impl::equals(value_node* other) const { + try { + return value_ == other->get_int(); + } + catch(value_undefined_method&) { + return false; + } + } + } + + value int_value(int v, value::tag_type t) + { + return value(new detail::int_value_impl(v, t)); + } + + //////////////////////////////////////////////////////////////////////////// + // Strings + + namespace detail + { + struct encoded_value_impl : public value_node + { + public: + explicit encoded_value_impl(std::string const&, value::tag_type); + private: + char const* type_name() const { return "encoded text"; } + + virtual ~encoded_value_impl(); + virtual value_node* clone() const; + virtual std::string get_encoded() const; + virtual bool empty() const; + virtual bool is_encoded() const; + virtual bool equals(value_node*) const; + + std::string value_; + }; + + struct qbk_value_impl : public value_node + { + public: + explicit qbk_value_impl( + file_ptr const&, + string_iterator begin, + string_iterator end, + value::tag_type); + private: + char const* type_name() const { return "quickbook"; } + + virtual ~qbk_value_impl(); + virtual value_node* clone() const; + virtual file_ptr get_file() const; + virtual string_iterator get_position() const; + virtual string_ref get_quickbook() const; + virtual bool empty() const; + virtual bool equals(value_node*) const; + + file_ptr file_; + string_iterator begin_; + string_iterator end_; + }; + + struct encoded_qbk_value_impl : public value_node + { + private: + char const* type_name() const { return "encoded text with quickbook reference"; } + + encoded_qbk_value_impl(file_ptr const&, + string_iterator, string_iterator, + std::string const&, value::tag_type); + + virtual ~encoded_qbk_value_impl(); + virtual value_node* clone() const; + virtual file_ptr get_file() const; + virtual string_iterator get_position() const; + virtual string_ref get_quickbook() const; + virtual std::string get_encoded() const; + virtual bool empty() const; + virtual bool is_encoded() const; + virtual bool equals(value_node*) const; + + file_ptr file_; + string_iterator begin_; + string_iterator end_; + std::string encoded_value_; + + friend quickbook::value quickbook::encoded_qbk_value( + file_ptr const&, string_iterator, string_iterator, + std::string const&, quickbook::value::tag_type); + }; + + // encoded_value_impl + + encoded_value_impl::encoded_value_impl( + std::string const& val, + value::tag_type tag + ) + : value_node(tag), value_(val) + { + } + + encoded_value_impl::~encoded_value_impl() + { + } + + value_node* encoded_value_impl::clone() const + { + return new encoded_value_impl(value_, tag_); + } + + std::string encoded_value_impl::get_encoded() const + { return value_; } + + bool encoded_value_impl::empty() const + { return value_.empty(); } + + bool encoded_value_impl::is_encoded() const + { return true; } + + bool encoded_value_impl::equals(value_node* other) const { + try { + return value_ == other->get_encoded(); + } + catch(value_undefined_method&) { + return false; + } + } + + // qbk_value_impl + + qbk_value_impl::qbk_value_impl( + file_ptr const& f, + string_iterator begin, + string_iterator end, + value::tag_type tag + ) : value_node(tag), file_(f), begin_(begin), end_(end) + { + } + + qbk_value_impl::~qbk_value_impl() + { + } + + value_node* qbk_value_impl::clone() const + { + return new qbk_value_impl(file_, begin_, end_, tag_); + } + + file_ptr qbk_value_impl::get_file() const + { return file_; } + + string_iterator qbk_value_impl::get_position() const + { return begin_; } + + string_ref qbk_value_impl::get_quickbook() const + { return string_ref(begin_, end_); } + + bool qbk_value_impl::empty() const + { return begin_ == end_; } + + bool qbk_value_impl::equals(value_node* other) const { + try { + return this->get_quickbook() == other->get_quickbook(); + } + catch(value_undefined_method&) { + return false; + } + } + + // encoded_qbk_value_impl + + encoded_qbk_value_impl::encoded_qbk_value_impl( + file_ptr const& f, + string_iterator begin, + string_iterator end, + std::string const& encoded, + value::tag_type tag) + : value_node(tag) + , file_(f) + , begin_(begin) + , end_(end) + , encoded_value_(encoded) + + { + } + + encoded_qbk_value_impl::~encoded_qbk_value_impl() + { + } + + value_node* encoded_qbk_value_impl::clone() const + { + return new encoded_qbk_value_impl( + file_, begin_, end_, encoded_value_, tag_); + } + + file_ptr encoded_qbk_value_impl::get_file() const + { return file_; } + + string_iterator encoded_qbk_value_impl::get_position() const + { return begin_; } + + string_ref encoded_qbk_value_impl::get_quickbook() const + { return string_ref(begin_, end_); } + + std::string encoded_qbk_value_impl::get_encoded() const + { return encoded_value_; } + + // Should this test the quickbook, the boostbook or both? + bool encoded_qbk_value_impl::empty() const + { return encoded_value_.empty(); } + + bool encoded_qbk_value_impl::is_encoded() const + { return true; } + + bool encoded_qbk_value_impl::equals(value_node* other) const { + try { + return this->get_quickbook() == other->get_quickbook(); + } + catch(value_undefined_method&) {} + + try { + return this->get_encoded() == other->get_encoded(); + } + catch(value_undefined_method&) {} + + return false; + } + } + + value qbk_value(file_ptr const& f, string_iterator x, string_iterator y, value::tag_type t) + { + return value(new detail::qbk_value_impl(f, x, y, t)); + } + + value encoded_value(std::string const& x, value::tag_type t) + { + return value(new detail::encoded_value_impl(x, t)); + } + + value encoded_qbk_value( + file_ptr const& f, string_iterator x, string_iterator y, + std::string const& z, value::tag_type t) + { + return value(new detail::encoded_qbk_value_impl(f,x,y,z,t)); + } + + ////////////////////////////////////////////////////////////////////////// + // List methods + + namespace detail + { + namespace { + value_node** list_ref_back(value_node**); + void list_ref(value_node*); + void list_unref(value_node*); + value_node** merge_sort(value_node**); + value_node** merge_sort(value_node**, int); + value_node** merge(value_node**, value_node**, value_node**); + void rotate(value_node**, value_node**, value_node**); + + value_node** list_ref_back(value_node** back) + { + while(*back != &value_list_end_impl::instance) { + intrusive_ptr_add_ref(*back); + back = &(*back)->next_; + } + + return back; + } + + void list_ref(value_node* ptr) + { + while(ptr != &value_list_end_impl::instance) { + intrusive_ptr_add_ref(ptr); + ptr = ptr->next_; + } + } + + void list_unref(value_node* ptr) + { + while(ptr != &value_list_end_impl::instance) { + value_node* next = ptr->next_; + intrusive_ptr_release(ptr); + ptr = next; + } + } + + value_node** merge_sort(value_node** l) + { + if(*l == &value_list_end_impl::instance) + return l; + else + return merge_sort(l, 9999); + } + + value_node** merge_sort(value_node** l, int recurse_limit) + { + value_node** p = &(*l)->next_; + for(int count = 0; + count < recurse_limit && *p != &value_list_end_impl::instance; + ++count) + { + p = merge(l, p, merge_sort(p, count)); + } + return p; + } + + value_node** merge( + value_node** first, value_node** second, value_node** third) + { + for(;;) { + for(;;) { + if(first == second) return third; + if((*second)->tag_ < (*first)->tag_) break; + first = &(*first)->next_; + } + + rotate(first, second, third); + first = &(*first)->next_; + + // Since the two ranges were just swapped, the order is now: + // first...third...second + // + // Also, that since the second section of the list was + // originally before the first, if the heads are equal + // we need to swap to maintain the order. + + for(;;) { + if(first == third) return second; + if((*third)->tag_ <= (*first)->tag_) break; + first = &(*first)->next_; + } + + rotate(first, third, second); + first = &(*first)->next_; + } + } + + void rotate(value_node** first, value_node** second, value_node** third) + { + value_node* tmp = *first; + *first = *second; + *second = *third; + *third = tmp; + //if(*second != &value_list_end_impl::instance) back = second; + } + } + } + + ////////////////////////////////////////////////////////////////////////// + // Lists + + namespace detail + { + struct value_list_impl : public value_node + { + value_list_impl(value::tag_type); + value_list_impl(value_list_builder&, value::tag_type); + private: + value_list_impl(value_list_impl const&); + + char const* type_name() const { return "list"; } + + virtual ~value_list_impl(); + virtual value_node* clone() const; + virtual bool empty() const; + virtual bool equals(value_node*) const; + + virtual bool is_list() const; + virtual value_node* get_list() const; + + value_node* head_; + }; + + value_list_impl::value_list_impl(value::tag_type tag) + : value_node(tag), head_(&value_list_end_impl::instance) + {} + + value_list_impl::value_list_impl(value_list_builder& builder, + value::tag_type tag) + : value_node(tag), head_(builder.release()) + { + } + + value_list_impl::value_list_impl(value_list_impl const& x) + : value_node(x.tag_), head_(x.head_) + { + list_ref(head_); + } + + value_list_impl::~value_list_impl() + { + list_unref(head_); + } + + value_node* value_list_impl::clone() const + { + return new value_list_impl(*this); + } + + bool value_list_impl::empty() const + { + return head_ == &value_list_end_impl::instance; + } + + bool value_list_impl::is_list() const + { + return true; + } + + value_node* value_list_impl::get_list() const + { + return head_; + } + + bool value_list_impl::equals(value_node* other) const { + value_node* x1; + + try { + x1 = other->get_list(); + } + catch(value_undefined_method&) { + return false; + } + + for(value_node *x2 = head_; x1 != x2; x1 = x1->next_, x2 = x2->next_) + { + if (x2 == &value_list_end_impl::instance || + !x1->equals(x2)) return false; + } + + return true; + } + } + + ////////////////////////////////////////////////////////////////////////// + // List builder + + namespace detail + { + // value_list_builder + + value_list_builder::value_list_builder() + : head_(&value_list_end_impl::instance) + , back_(&head_) + {} + + value_list_builder::value_list_builder(value_node* ptr) + : head_(ptr) + , back_(list_ref_back(&head_)) + {} + + value_list_builder::~value_list_builder() + { + list_unref(head_); + } + + void value_list_builder::swap(value_list_builder& other) { + std::swap(head_, other.head_); + std::swap(back_, other.back_); + if(back_ == &other.head_) back_ = &head_; + if(other.back_ == &head_) other.back_ = &other.head_; + } + + value_node* value_list_builder::release() { + value_node* r = head_; + head_ = &value_list_end_impl::instance; + back_ = &head_; + return r; + } + + void value_list_builder::append(value_node* item) + { + if(item->next_) item = item->clone(); + intrusive_ptr_add_ref(item); + item->next_ = *back_; + *back_ = item; + back_ = &item->next_; + } + + void value_list_builder::sort() + { + back_ = merge_sort(&head_); + assert(*back_ == &value_list_end_impl::instance); + } + } + + ////////////////////////////////////////////////////////////////////////// + // Value builder + + value_builder::value_builder() + : current() + , list_tag(value::default_tag) + , saved() + { + } + + void value_builder::swap(value_builder& other) { + current.swap(other.current); + std::swap(list_tag, other.list_tag); + saved.swap(other.saved); + } + + void value_builder::save() { + boost::scoped_ptr<value_builder> store(new value_builder); + swap(*store); + saved.swap(store); + } + + void value_builder::restore() { + boost::scoped_ptr<value_builder> store; + store.swap(saved); + swap(*store); + } + + value value_builder::release() { + return value(new detail::value_list_impl(current, list_tag)); + } + + void value_builder::reset() { + detail::value_list_builder new_builder; + current.swap(new_builder); + list_tag = value::default_tag; + } + + void value_builder::set_tag(value::tag_type tag) { + list_tag = tag; + } + + void value_builder::insert(value const& item) { + current.append(item.value_); + } + + void value_builder::extend(value const& list) { + for (value::iterator begin = list.begin(), end = list.end(); + begin != end; ++begin) + { + insert(*begin); + } + } + + void value_builder::start_list(value::tag_type tag) { + value::tag_type saved_tag = tag; + save(); + list_tag = saved_tag; + } + + void value_builder::finish_list() { + value list = release(); + restore(); + insert(list); + } + + void value_builder::clear_list() { + restore(); + } + + void value_builder::sort_list() + { + current.sort(); + } + + //////////////////////////////////////////////////////////////////////////// + // Iterator + + namespace detail + { + value::iterator::iterator() + : ptr_(&value_list_end_impl::instance) {} + } +} |