Boost logo

Boost-Commit :

Subject: [Boost-commit] svn:boost r68882 - in branches/quickbook-filenames/tools/quickbook: src test test/unit
From: dnljms_at_[hidden]
Date: 2011-02-14 19:20:14


Author: danieljames
Date: 2011-02-14 19:20:10 EST (Mon, 14 Feb 2011)
New Revision: 68882
URL: http://svn.boost.org/trac/boost/changeset/68882

Log:
A value type for quickbook data.
Added:
   branches/quickbook-filenames/tools/quickbook/src/value_tags.hpp (contents, props changed)
   branches/quickbook-filenames/tools/quickbook/src/values.cpp (contents, props changed)
   branches/quickbook-filenames/tools/quickbook/src/values.hpp (contents, props changed)
   branches/quickbook-filenames/tools/quickbook/test/unit/
   branches/quickbook-filenames/tools/quickbook/test/unit/Jamfile.v2 (contents, props changed)
   branches/quickbook-filenames/tools/quickbook/test/unit/values_test.cpp (contents, props changed)
Text files modified:
   branches/quickbook-filenames/tools/quickbook/src/Jamfile.v2 | 1 +
   branches/quickbook-filenames/tools/quickbook/test/Jamfile.v2 | 1 +
   2 files changed, 2 insertions(+), 0 deletions(-)

Modified: branches/quickbook-filenames/tools/quickbook/src/Jamfile.v2
==============================================================================
--- branches/quickbook-filenames/tools/quickbook/src/Jamfile.v2 (original)
+++ branches/quickbook-filenames/tools/quickbook/src/Jamfile.v2 2011-02-14 19:20:10 EST (Mon, 14 Feb 2011)
@@ -31,6 +31,7 @@
     actions_class.cpp
     utils.cpp
     input_path.cpp
+ values.cpp
     post_process.cpp
     collector.cpp
     template_stack.cpp

Added: branches/quickbook-filenames/tools/quickbook/src/value_tags.hpp
==============================================================================
--- (empty file)
+++ branches/quickbook-filenames/tools/quickbook/src/value_tags.hpp 2011-02-14 19:20:10 EST (Mon, 14 Feb 2011)
@@ -0,0 +1,73 @@
+/*=============================================================================
+ Copyright (c) 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)
+=============================================================================*/
+
+#if !defined(BOOST_SPIRIT_QUICKBOOK_VALUES_TAGS_HPP)
+#define BOOST_SPIRIT_QUICKBOOK_VALUES_TAGS_HPP
+
+#include <boost/preprocessor/stringize.hpp>
+#include <boost/preprocessor/seq/enum.hpp>
+#include <boost/preprocessor/seq/for_each.hpp>
+#include <boost/preprocessor/seq/elem.hpp>
+#include <boost/range/irange.hpp>
+#include <cassert>
+
+#define QUICKBOOK_VALUE_TAGS(tags_name, start_index, values) \
+ struct tags_name { \
+ enum { \
+ previous_index = start_index - 1, \
+ BOOST_PP_SEQ_ENUM(values), \
+ end_index \
+ }; \
+ \
+ static char const* name(int value) { \
+ switch(value) {\
+ case 0: \
+ return "null"; \
+ BOOST_PP_SEQ_FOR_EACH(QUICKBOOK_VALUE_CASE, _, values) \
+ default: \
+ assert(false); \
+ }; \
+ } \
+ \
+ typedef boost::integer_range<int> range_type; \
+ static range_type tags() { return boost::irange(start_index, (int) end_index); } \
+ };
+
+#define QUICKBOOK_VALUE_CASE(r, _, value) \
+ case value: return BOOST_PP_STRINGIZE(value);
+
+#define QUICKBOOK_VALUE_NAMED_TAGS(tags_name, start_index, values) \
+ struct tags_name { \
+ enum { \
+ previous_index = start_index - 1 \
+ BOOST_PP_SEQ_FOR_EACH(QUICKBOOK_VALUE_NAMED_ENUM, _, values), \
+ end_index \
+ }; \
+ \
+ static char const* name(int value) { \
+ switch(value) {\
+ case 0: \
+ return "null"; \
+ BOOST_PP_SEQ_FOR_EACH(QUICKBOOK_VALUE_NAMED_CASE, _, values) \
+ default: \
+ assert(false); \
+ }; \
+ } \
+ \
+ typedef boost::integer_range<int> range_type; \
+ static range_type tags() { return boost::irange(start_index, (int) end_index); } \
+ };
+
+#define QUICKBOOK_VALUE_NAMED_ENUM(r, _, value) \
+ , BOOST_PP_SEQ_ELEM(0, value)
+
+#define QUICKBOOK_VALUE_NAMED_CASE(r, _, value) \
+ case BOOST_PP_SEQ_ELEM(0, value): return BOOST_PP_SEQ_ELEM(1, value);
+
+
+#endif

Added: branches/quickbook-filenames/tools/quickbook/src/values.cpp
==============================================================================
--- (empty file)
+++ branches/quickbook-filenames/tools/quickbook/src/values.cpp 2011-02-14 19:20:10 EST (Mon, 14 Feb 2011)
@@ -0,0 +1,783 @@
+/*=============================================================================
+ 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 <boost/intrusive_ptr.hpp>
+
+namespace quickbook
+{
+ ////////////////////////////////////////////////////////////////////////////
+ // Node
+
+ namespace detail
+ {
+ value_node::value_node(tag_type t)
+ : ref_count_(0), tag_(t), next_() {
+ }
+
+ value_node::~value_node() {
+ }
+
+ value_node* value_node::store() { return this; }
+
+ file_position value_node::get_position() const { assert(false); }
+ std::string value_node::get_quickbook() const { assert(false); }
+ std::string value_node::get_boostbook() const { assert(false); }
+ value_node* value_node::get_list() const { assert(false); }
+
+ bool value_node::is_empty() const { return false; }
+ bool value_node::is_list() const { return false; }
+ bool value_node::is_string() const { return false; }
+ }
+
+ ////////////////////////////////////////////////////////////////////////////
+ // 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 value_empty_impl : public value_node
+ {
+ static value_node* new_(value::tag_type t);
+
+ protected:
+ explicit value_empty_impl(value::tag_type t)
+ : value_node(t) {}
+
+ private:
+ virtual value_node* clone() const
+ { return new value_empty_impl(tag_); }
+
+ virtual bool is_empty() const
+ { return true; }
+
+ friend value quickbook::empty_value(value::tag_type);
+ };
+
+ struct value_nil_impl : public value_empty_impl
+ {
+ static value_nil_impl instance;
+ private:
+ value_nil_impl()
+ : value_empty_impl(value::default_tag)
+ {
+ intrusive_ptr_add_ref(&instance);
+ }
+ };
+
+ value_nil_impl value_nil_impl::instance;
+
+ value_node* value_empty_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 value_empty_impl(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::operator=(value_counted x)
+ {
+ swap(x);
+ return *this;
+ }
+
+ value_counted::~value_counted()
+ {
+ intrusive_ptr_release(value_);
+ }
+
+ void value_counted::store()
+ {
+ value_node* new_value = value_->store();
+ intrusive_ptr_add_ref(new_value);
+ intrusive_ptr_release(value_);
+ value_ = new_value;
+ }
+ }
+
+ ////////////////////////////////////////////////////////////////////////////
+ // value
+
+ value::value()
+ : detail::value_counted()
+ {
+ }
+
+ value::value(value const& x)
+ : detail::value_counted(x)
+ {
+ }
+
+ value::value(detail::value_ref x)
+ : detail::value_counted(x)
+ {
+ }
+
+ value::value(detail::value_node* x)
+ : detail::value_counted(x)
+ {
+ }
+
+ ////////////////////////////////////////////////////////////////////////////
+ // Iterator
+
+ namespace detail
+ {
+ value::iterator::iterator()
+ : ptr_(&value_nil_impl::instance) {}
+ }
+
+ value empty_value(value::tag_type t)
+ {
+ return value(detail::value_empty_impl::new_(t));
+ }
+
+ ////////////////////////////////////////////////////////////////////////////
+ // Strings
+
+ namespace detail
+ {
+ struct value_string_impl : public value_node
+ {
+ public:
+ explicit value_string_impl(std::string const&, value::tag_type);
+ private:
+ virtual ~value_string_impl();
+ virtual value_node* clone() const;
+ virtual std::string get_boostbook() const;
+ virtual bool is_string() const;
+ virtual bool is_empty() const;
+
+ std::string value_;
+ };
+
+ struct value_qbk_string_impl : public value_node
+ {
+ public:
+ explicit value_qbk_string_impl(
+ std::string const&, file_position, value::tag_type);
+ explicit value_qbk_string_impl(
+ quickbook::iterator begin, quickbook::iterator end,
+ value::tag_type);
+ private:
+ virtual ~value_qbk_string_impl();
+ virtual value_node* clone() const;
+ virtual file_position get_position() const;
+ virtual std::string get_quickbook() const;
+ virtual bool is_string() const;
+ virtual bool is_empty() const;
+
+ std::string value_;
+ file_position position_;
+ };
+
+ struct value_qbk_ref_impl : public value_node
+ {
+ public:
+ explicit value_qbk_ref_impl(quickbook::iterator begin, quickbook::iterator end, value::tag_type);
+ private:
+ virtual ~value_qbk_ref_impl();
+ virtual value_node* clone() const;
+ virtual value_node* store();
+ virtual file_position get_position() const;
+ virtual std::string get_quickbook() const;
+ virtual bool is_string() const;
+ virtual bool is_empty() const;
+
+ quickbook::iterator begin_;
+ quickbook::iterator end_;
+ };
+
+ struct value_qbk_bbk_impl : public value_node
+ {
+ private:
+ value_qbk_bbk_impl(
+ std::string const& qbk, std::string const& bbk,
+ file_position const&, value::tag_type);
+ value_qbk_bbk_impl(std::string const&, value::tag_type);
+ value_qbk_bbk_impl(
+ quickbook::iterator, quickbook::iterator,
+ std::string const&, value::tag_type);
+
+ virtual ~value_qbk_bbk_impl();
+ virtual value_node* clone() const;
+ virtual file_position get_position() const;
+ virtual std::string get_quickbook() const;
+ virtual std::string get_boostbook() const;
+ virtual bool is_string() const;
+ virtual bool is_empty() const;
+
+ std::string qbk_value_;
+ std::string bbk_value_;
+ file_position position_;
+
+ friend quickbook::value quickbook::qbk_bbk_value(
+ std::string const&, quickbook::value::tag_type);
+ friend quickbook::value quickbook::qbk_bbk_value(
+ quickbook::iterator, quickbook::iterator,
+ std::string const&, quickbook::value::tag_type);
+ };
+
+ // value_string_impl
+
+ value_string_impl::value_string_impl(
+ std::string const& val,
+ value::tag_type tag
+ )
+ : value_node(tag), value_(val)
+ {
+ }
+
+ value_string_impl::~value_string_impl()
+ {
+ }
+
+ value_node* value_string_impl::clone() const
+ {
+ return new value_string_impl(value_, tag_);
+ }
+
+ std::string value_string_impl::get_boostbook() const
+ { return value_; }
+
+ bool value_string_impl::is_string() const
+ { return true; }
+
+ bool value_string_impl::is_empty() const
+ { return value_.empty(); }
+
+ // value_qbk_string_impl
+
+ value_qbk_string_impl::value_qbk_string_impl(
+ std::string const& v,
+ file_position p,
+ value::tag_type tag)
+ : value_node(tag)
+ , value_(v)
+ , position_(p)
+ {}
+
+ value_qbk_string_impl::value_qbk_string_impl(
+ quickbook::iterator begin, quickbook::iterator end,
+ value::tag_type tag)
+ : value_node(tag)
+ , value_(begin, end)
+ , position_(begin.get_position())
+ {}
+
+ value_qbk_string_impl::~value_qbk_string_impl()
+ {}
+
+ value_node* value_qbk_string_impl::clone() const
+ {
+ return new value_qbk_string_impl(value_, position_, tag_);
+ }
+
+ file_position value_qbk_string_impl::get_position() const
+ { return position_; }
+
+ std::string value_qbk_string_impl::get_quickbook() const
+ { return value_; }
+
+ bool value_qbk_string_impl::is_string() const
+ { return true; }
+
+ bool value_qbk_string_impl::is_empty() const
+ { return value_.empty(); }
+
+ // value_qbk_ref_impl
+
+ value_qbk_ref_impl::value_qbk_ref_impl(
+ quickbook::iterator begin, quickbook::iterator end,
+ value::tag_type tag
+ ) : value_node(tag), begin_(begin), end_(end)
+ {
+ }
+
+ value_qbk_ref_impl::~value_qbk_ref_impl()
+ {
+ }
+
+ value_node* value_qbk_ref_impl::clone() const
+ {
+ return new value_qbk_ref_impl(begin_, end_, tag_);
+ }
+
+ value_node* value_qbk_ref_impl::store()
+ {
+ return new value_qbk_string_impl(begin_, end_, tag_);
+ }
+
+ file_position value_qbk_ref_impl::get_position() const
+ { return begin_.get_position(); }
+
+ std::string value_qbk_ref_impl::get_quickbook() const
+ { return std::string(begin_.base(), end_.base()); }
+
+ bool value_qbk_ref_impl::is_string() const
+ { return true; }
+
+ bool value_qbk_ref_impl::is_empty() const
+ { return begin_ == end_; }
+
+ // value_qbk_bbk_impl
+
+ value_qbk_bbk_impl::value_qbk_bbk_impl(
+ std::string const& qbk,
+ std::string const& bbk,
+ file_position const& pos,
+ value::tag_type tag)
+ : value_node(tag)
+ , qbk_value_(qbk)
+ , bbk_value_(bbk)
+ , position_(pos)
+
+ {
+ }
+
+ value_qbk_bbk_impl::value_qbk_bbk_impl(
+ quickbook::iterator begin,
+ quickbook::iterator end,
+ std::string const& bbk,
+ value::tag_type tag)
+ : value_node(tag)
+ , qbk_value_(begin.base(), end.base())
+ , bbk_value_(bbk)
+ , position_(begin.get_position())
+
+ {
+ }
+
+ value_qbk_bbk_impl::value_qbk_bbk_impl(
+ std::string const& val,
+ value::tag_type tag)
+ : value_node(tag)
+ , qbk_value_(val)
+ , bbk_value_(val)
+ , position_()
+ {
+ }
+
+ value_qbk_bbk_impl::~value_qbk_bbk_impl()
+ {
+ }
+
+ value_node* value_qbk_bbk_impl::clone() const
+ {
+ return new value_qbk_bbk_impl(
+ qbk_value_, bbk_value_, position_, tag_);
+ }
+
+ file_position value_qbk_bbk_impl::get_position() const
+ { return position_; }
+
+ std::string value_qbk_bbk_impl::get_quickbook() const
+ { return qbk_value_; }
+
+ std::string value_qbk_bbk_impl::get_boostbook() const
+ { return bbk_value_; }
+
+ bool value_qbk_bbk_impl::is_string() const
+ { return true; }
+
+ // Should this test the quickbook, the boostbook or both?
+ bool value_qbk_bbk_impl::is_empty() const
+ { return bbk_value_.empty(); }
+ }
+
+ value qbk_value(iterator x, iterator y, value::tag_type t)
+ {
+ return value(new detail::value_qbk_ref_impl(x, y, t));
+ }
+
+ value qbk_value(std::string const& x, file_position pos, value::tag_type t)
+ {
+ return value(new detail::value_qbk_string_impl(x, pos, t));
+ }
+
+ value bbk_value(std::string const& x, value::tag_type t)
+ {
+ return value(new detail::value_string_impl(x, t));
+ }
+
+ value qbk_bbk_value(std::string const& x, value::tag_type t)
+ {
+ return value(new detail::value_qbk_bbk_impl(x,t));
+ }
+
+ value qbk_bbk_value(
+ iterator x, iterator y,
+ std::string const& z, value::tag_type t)
+ {
+ return value(new detail::value_qbk_bbk_impl(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_adjacent_ranges(
+ value_node**, value_node**, value_node**);
+ void swap_adjacent_ranges(value_node**, value_node**, value_node**);
+
+ value_node** list_ref_back(value_node** back)
+ {
+ while(*back != &value_nil_impl::instance) {
+ intrusive_ptr_add_ref(*back);
+ back = &(*back)->next_;
+ }
+
+ return back;
+ }
+
+ void list_ref(value_node* ptr)
+ {
+ while(ptr != &value_nil_impl::instance) {
+ intrusive_ptr_add_ref(ptr);
+ ptr = ptr->next_;
+ }
+ }
+
+ void list_unref(value_node* ptr)
+ {
+ while(ptr != &value_nil_impl::instance) {
+ value_node* next = ptr->next_;
+ intrusive_ptr_release(ptr);
+ ptr = next;
+ }
+ }
+
+ value_node** merge_sort(value_node** l)
+ {
+ if(*l == &value_nil_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_nil_impl::instance;
+ ++count)
+ {
+ p = merge_adjacent_ranges(l, p, merge_sort(p, count));
+ }
+ return p;
+ }
+
+ value_node** merge_adjacent_ranges(
+ 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_;
+ }
+
+ swap_adjacent_ranges(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_;
+ }
+
+ swap_adjacent_ranges(first, third, second);
+ first = &(*first)->next_;
+ }
+ }
+
+ void swap_adjacent_ranges(
+ value_node** first, value_node** second, value_node** third)
+ {
+ value_node* tmp = *first;
+ *first = *second;
+ *second = *third;
+ *third = tmp;
+ //if(*second != &value_nil_impl::instance) back = second;
+ }
+ }
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // Lists
+
+ namespace detail
+ {
+ struct value_list_impl : public value_node
+ {
+ value_list_impl(value::tag_type);
+ value_list_impl(value_node*, value::tag_type);
+ private:
+ virtual ~value_list_impl();
+ virtual value_node* clone() const;
+ virtual value_node* store();
+ virtual bool is_empty() 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_nil_impl::instance)
+ {}
+
+ value_list_impl::value_list_impl(value_node* ptr, value::tag_type tag)
+ : value_node(tag), head_(ptr)
+ {
+ list_ref(head_);
+ }
+
+ value_list_impl::~value_list_impl()
+ {
+ list_unref(head_);
+ }
+
+ value_node* value_list_impl::clone() const
+ {
+ return new value_list_impl(head_, tag_);
+ }
+
+ // TODO: Could reuse nodes is any node has a reference count of 1.
+ value_node* value_list_impl::store()
+ {
+ value_node* pos = head_;
+ boost::intrusive_ptr<value_node> new_node;
+
+ while(true) {
+ if(pos == &value_nil_impl::instance)
+ return this;
+ new_node = pos->store();
+ if(new_node.get() != pos) break;
+ pos = pos->next_;
+ }
+
+ value_list_builder build;
+
+ for(value_node* pos2 = head_;
+ pos2 != &value_nil_impl::instance;
+ pos2 = pos2->next_)
+ {
+ if(pos2 == pos)
+ build.append(new_node.get());
+ else
+ build.append(pos2);
+ }
+
+ return new value_list_impl(build.get(), tag_);
+ }
+
+
+ bool value_list_impl::is_empty() const
+ {
+ return head_ == &value_nil_impl::instance;
+ }
+
+ bool value_list_impl::is_list() const
+ {
+ return true;
+ }
+
+ value_node* value_list_impl::get_list() const
+ {
+ return head_;
+ }
+ }
+
+ value list_value(value::tag_type t)
+ {
+ return value(new detail::value_list_impl(t));
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // List builder
+
+ namespace detail
+ {
+ // value_list_builder
+
+ value_list_builder::value_list_builder()
+ : head_(&value_nil_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_;
+ }
+
+ // TODO: Multiple list refs are incompatible with 'store'
+ value_node* value_list_builder::get() const {
+ return head_;
+ }
+
+ 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_nil_impl::instance);
+ }
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // Value builder
+
+ value_builder::value_builder()
+ : current()
+ , list_tag(value::no_tag)
+ , next_tag(value::default_tag)
+ , saved()
+ {
+ }
+
+ void value_builder::swap(value_builder& other) {
+ current.swap(other.current);
+ std::swap(list_tag, other.list_tag);
+ std::swap(next_tag, other.next_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::get() {
+ return value(new detail::value_list_impl(current.get(), list_tag));
+ }
+
+ void value_builder::reset() {
+ detail::value_list_builder new_builder;
+ current.swap(new_builder);
+ list_tag = value::no_tag;
+ next_tag = value::default_tag;
+ }
+
+ void value_builder::set_tag(value::tag_type tag) {
+ next_tag = tag;
+ }
+
+ value::tag_type value_builder::release_tag(value::tag_type t) {
+ value::tag_type r = t != value::no_tag ? t : next_tag;
+ next_tag = value::default_tag;
+ return r;
+ }
+
+ void value_builder::insert(value const& item) {
+ current.append(item.value_);
+ }
+
+ void value_builder::start_list(value::tag_type tag) {
+ value::tag_type saved_tag = release_tag(tag);
+ save();
+ list_tag = saved_tag;
+ }
+
+ void value_builder::finish_list() {
+ value list = get();
+ restore();
+ insert(list);
+ }
+
+ void value_builder::clear_list() {
+ restore();
+ }
+
+ void value_builder::sort_list()
+ {
+ current.sort();
+ }
+}

Added: branches/quickbook-filenames/tools/quickbook/src/values.hpp
==============================================================================
--- (empty file)
+++ branches/quickbook-filenames/tools/quickbook/src/values.hpp 2011-02-14 19:20:10 EST (Mon, 14 Feb 2011)
@@ -0,0 +1,313 @@
+/*=============================================================================
+ 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)
+=============================================================================*/
+
+// An easy way to store data parsed for quickbook.
+
+#if !defined(BOOST_SPIRIT_QUICKBOOK_VALUES_HPP)
+#define BOOST_SPIRIT_QUICKBOOK_VALUES_HPP
+
+#include <utility>
+#include <string>
+#include <cassert>
+#include <boost/scoped_ptr.hpp>
+#include "fwd.hpp"
+
+namespace quickbook
+{
+ class value;
+ class value_builder;
+
+ namespace detail
+ {
+ ////////////////////////////////////////////////////////////////////////
+ // Node
+
+ class value_node
+ {
+ private:
+ value_node(value_node const&);
+ value_node& operator=(value_node const&);
+
+ public:
+ typedef int tag_type;
+
+ protected:
+ explicit value_node(tag_type);
+ virtual ~value_node();
+
+ public:
+ virtual value_node* clone() const = 0;
+ virtual value_node* store();
+
+ virtual file_position get_position() const;
+ virtual std::string get_quickbook() const;
+ virtual std::string get_boostbook() const;
+
+ virtual bool is_empty() const;
+ virtual bool is_list() const;
+ virtual bool is_string() const;
+
+ virtual value_node* get_list() const;
+
+ int ref_count_;
+ const tag_type tag_;
+ value_node* next_;
+
+ friend void intrusive_ptr_add_ref(value_node* ptr)
+ { ++ptr->ref_count_; }
+ friend void intrusive_ptr_release(value_node* ptr)
+ { if(--ptr->ref_count_ == 0) delete ptr; }
+ };
+
+ ////////////////////////////////////////////////////////////////////////
+ // Value base
+ //
+ // This defines most of the public methods for value.
+ // 'begin' and 'end' are defined with the iterators later.
+
+ class value_base
+ {
+ public:
+ class iterator;
+
+ typedef iterator const_iterator;
+ typedef value_node::tag_type tag_type;
+ enum { no_tag = -1, default_tag = 0 };
+
+ protected:
+ explicit value_base(value_node* base)
+ : value_(base)
+ {
+ assert(value_);
+ }
+
+ ~value_base() {}
+
+ public:
+ void swap(value_base& x) { std::swap(value_, x.value_); }
+
+ bool is_empty() const { return value_->is_empty(); }
+ bool is_list() const { return value_->is_list(); }
+ bool is_string() const { return value_->is_string(); }
+
+ iterator begin() const;
+ iterator end() const;
+
+ // Item accessors
+ int get_tag() const { return value_->tag_; }
+ file_position get_position() const
+ { return value_->get_position(); }
+ std::string get_quickbook() const
+ { return value_->get_quickbook(); }
+ std::string get_boostbook() const
+ { return value_->get_boostbook(); }
+
+ protected:
+ value_node* value_;
+
+ // value_builder needs to access 'value_' to get the node
+ // from a value.
+ friend class quickbook::value_builder;
+ };
+
+ ////////////////////////////////////////////////////////////////////////
+ // Reference and proxy values for use in iterators
+
+ class value_ref : public value_base
+ {
+ public:
+ explicit value_ref(value_node* base) : value_base(base) {}
+ };
+
+ class value_proxy : public value_base
+ {
+ public:
+ explicit value_proxy(value_node* base) : value_base(base) {}
+ value_proxy* operator->() { return this; }
+ };
+
+ ////////////////////////////////////////////////////////////////////////
+ // Iterators
+
+ class value_base::iterator
+ : public boost::forward_iterator_helper<
+ iterator, value, int, value_proxy, value_ref>
+ {
+ public:
+ iterator();
+ explicit iterator(value_node* p) : ptr_(p) {}
+ friend bool operator==(iterator x, iterator y)
+ { return x.ptr_ == y.ptr_; }
+ iterator& operator++() { ptr_ = ptr_->next_; return *this; }
+ value_ref operator*() const { return value_ref(ptr_); }
+ value_proxy operator->() const { return value_proxy(ptr_); }
+ private:
+ value_node* ptr_;
+ };
+
+ inline value_base::iterator value_base::begin() const
+ {
+ return iterator(value_->get_list());
+ }
+
+ inline value_base::iterator value_base::end() const
+ {
+ return iterator();
+ }
+
+ ////////////////////////////////////////////////////////////////////////
+ // Reference counting for values
+
+ class value_counted : public value_base
+ {
+ public:
+ value_counted();
+ value_counted(value_counted const&);
+ value_counted(value_base const&);
+ value_counted(value_node*);
+ value_counted& operator=(value_counted);
+ ~value_counted();
+
+ void store();
+ };
+
+ ////////////////////////////////////////////////////////////////////////
+ // List builder
+ //
+ // Values are immutable, so this class is used to build a list of
+ // value nodes before constructing the value.
+
+ class value_list_builder {
+ value_list_builder(value_list_builder const&);
+ value_list_builder& operator=(value_list_builder const&);
+ public:
+ value_list_builder();
+ value_list_builder(value_node*);
+ ~value_list_builder();
+ void swap(value_list_builder& b);
+ value_node* get() const;
+
+ void append(value_node*);
+ void sort();
+ private:
+ value_node* head_;
+ value_node** back_;
+ };
+ }
+
+ ////////////////////////////////////////////////////////////////////////////
+ // Value
+ //
+ // Most of the methods are in value_base.
+
+ class value : public detail::value_counted
+ {
+ public:
+ value();
+ value(value const&);
+ value(detail::value_ref);
+ explicit value(detail::value_node*);
+ };
+
+ value empty_value(value::tag_type = value::default_tag);
+ value list_value(value::tag_type = value::default_tag);
+ value qbk_value(iterator, iterator, value::tag_type = value::default_tag);
+ value qbk_value(std::string const&,
+ file_position = file_position(),
+ value::tag_type = value::default_tag);
+ value bbk_value(std::string const&, value::tag_type = value::default_tag);
+ value qbk_bbk_value(std::string const&,
+ value::tag_type = value::default_tag);
+ value qbk_bbk_value(iterator, iterator, std::string const&,
+ value::tag_type = value::default_tag);
+
+ ////////////////////////////////////////////////////////////////////////////
+ // Value Builder
+ //
+ // Used to incrementally build a valueeter tree.
+
+ class value_builder {
+ public:
+ value_builder();
+ void swap(value_builder& b);
+
+ void save();
+ void restore();
+
+ value get();
+
+ void reset();
+ void set_tag(value::tag_type);
+ value::tag_type release_tag(value::tag_type = value::no_tag);
+ void insert(value const&);
+
+ void start_list(value::tag_type);
+ void finish_list();
+ void clear_list();
+ void sort_list();
+
+ private:
+ detail::value_list_builder current;
+ value::tag_type list_tag, next_tag;
+ boost::scoped_ptr<value_builder> saved;
+ };
+
+ ////////////////////////////////////////////////////////////////////////////
+ // Value Consumer
+ //
+ // Convenience class for unpacking value values.
+
+ class value_consumer {
+ public:
+ typedef value::iterator iterator;
+ typedef value::iterator const_iterator;
+ typedef iterator::reference reference;
+
+ value_consumer(value const& x)
+ : list_(x)
+ , pos_(x.begin())
+ , end_(x.end())
+ {}
+
+ value_consumer(reference x)
+ : list_(x)
+ , pos_(x.begin())
+ , end_(x.end())
+ {}
+
+ reference consume(value::tag_type t = value::no_tag)
+ {
+ assert(is(t));
+ return *pos_++;
+ }
+
+ value optional_consume(value::tag_type t = value::no_tag)
+ {
+ if(is(t)) {
+ return *pos_++;
+ }
+ else {
+ return value();
+ }
+ }
+
+ bool is(value::tag_type t = value::no_tag)
+ {
+ return (pos_ != end_ &&
+ (t == value::no_tag || t == pos_->get_tag()));
+ }
+
+ iterator begin() const { return pos_; }
+ iterator end() const { return end_; }
+ private:
+ value list_;
+ iterator pos_, end_;
+ };
+}
+
+#endif

Modified: branches/quickbook-filenames/tools/quickbook/test/Jamfile.v2
==============================================================================
--- branches/quickbook-filenames/tools/quickbook/test/Jamfile.v2 (original)
+++ branches/quickbook-filenames/tools/quickbook/test/Jamfile.v2 2011-02-14 19:20:10 EST (Mon, 14 Feb 2011)
@@ -9,6 +9,7 @@
 project test : requirements <debug-symbols>off ;
 
 build-project doc-info ;
+build-project unit ;
 
 import quickbook-testing : quickbook-test quickbook-fail-test ;
 

Added: branches/quickbook-filenames/tools/quickbook/test/unit/Jamfile.v2
==============================================================================
--- (empty file)
+++ branches/quickbook-filenames/tools/quickbook/test/unit/Jamfile.v2 2011-02-14 19:20:10 EST (Mon, 14 Feb 2011)
@@ -0,0 +1,7 @@
+import testing ;
+
+project quickbook-unit-tests
+ : requirements <include>../../src <warnings>all
+ ;
+
+run values_test.cpp ../../src/values.cpp ;

Added: branches/quickbook-filenames/tools/quickbook/test/unit/values_test.cpp
==============================================================================
--- (empty file)
+++ branches/quickbook-filenames/tools/quickbook/test/unit/values_test.cpp 2011-02-14 19:20:10 EST (Mon, 14 Feb 2011)
@@ -0,0 +1,142 @@
+/*=============================================================================
+ 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)
+=============================================================================*/
+
+// Some very light testing for quickbook::value and friends.
+// Just for a few issues that came up during development.
+
+#include <boost/detail/lightweight_test.hpp>
+#include <boost/range/algorithm/equal.hpp>
+#include "values.hpp"
+
+void empty_tests()
+{
+ quickbook::value q;
+ BOOST_TEST(q.is_empty());
+ BOOST_TEST(!q.is_list());
+ BOOST_TEST(!q.is_string());
+}
+
+void qbk_tests()
+{
+ std::string src = "Source";
+ quickbook::value q = quickbook::qbk_value(
+ quickbook::iterator(src.begin()),
+ quickbook::iterator(src.end()));
+ BOOST_TEST_EQ(q.get_quickbook(), src);
+}
+
+void sort_test()
+{
+ quickbook::value_builder b;
+ b.insert(quickbook::bbk_value("a", 10));
+ b.insert(quickbook::bbk_value("b", 2));
+ b.insert(quickbook::bbk_value("c", 5));
+ b.insert(quickbook::bbk_value("d", 8));
+ b.sort_list();
+
+ quickbook::value_consumer c = b.get();
+ BOOST_TEST(c.is(2)); BOOST_TEST_EQ(c.consume(2).get_boostbook(), "b");
+ BOOST_TEST(c.is(5)); c.consume(5);
+ BOOST_TEST(c.is(8)); c.consume(8);
+ BOOST_TEST(c.is(10)); c.consume(10);
+ BOOST_TEST(!c.is());
+}
+
+void multiple_list_test()
+{
+ quickbook::value_builder list1;
+ quickbook::value_builder list2;
+
+ list1.insert(quickbook::bbk_value("b", 10));
+
+ {
+ quickbook::value p1 = quickbook::bbk_value("a", 5);
+ list1.insert(p1);
+ list2.insert(p1);
+ }
+
+ list2.insert(quickbook::bbk_value("c", 3));
+
+ quickbook::value_consumer l1 = list1.get(); list1.reset();
+ quickbook::value_consumer l2 = list2.get(); list2.reset();
+
+ BOOST_TEST(l1.is(10));
+ BOOST_TEST_EQ(l1.consume(10).get_boostbook(), "b");
+ BOOST_TEST(l1.is(5));
+ BOOST_TEST_EQ(l1.consume(5).get_boostbook(), "a");
+ BOOST_TEST(!l1.is());
+
+ BOOST_TEST(l2.is(5));
+ BOOST_TEST_EQ(l2.consume(5).get_boostbook(), "a");
+ BOOST_TEST(l2.is(3));
+ BOOST_TEST_EQ(l2.consume(3).get_boostbook(), "c");
+ BOOST_TEST(!l2.is());
+}
+
+void store_test1()
+{
+ quickbook::value q;
+
+ {
+ std::string src = "Hello";
+ q = quickbook::qbk_value(
+ quickbook::iterator(src.begin()),
+ quickbook::iterator(src.end()),
+ 5);
+
+ BOOST_TEST_EQ(q.get_quickbook(), "Hello");
+ q.store();
+ BOOST_TEST_EQ(q.get_quickbook(), "Hello");
+ }
+
+ BOOST_TEST_EQ(q.get_quickbook(), "Hello");
+}
+
+void store_test2_check(quickbook::value const& q)
+{
+ quickbook::value_consumer l1 = q;
+ BOOST_TEST(l1.is(5));
+ BOOST_TEST_EQ(l1.consume(5).get_quickbook(), "Hello");
+ BOOST_TEST(l1.is(10));
+ BOOST_TEST_EQ(l1.consume(10).get_boostbook(), "World");
+ BOOST_TEST(!l1.is());
+}
+
+void store_test2()
+{
+ quickbook::value q;
+
+ {
+ quickbook::value_builder list1;
+ std::string src = "Hello";
+ list1.insert(quickbook::qbk_value(
+ quickbook::iterator(src.begin()),
+ quickbook::iterator(src.end()),
+ 5));
+ list1.insert(quickbook::bbk_value("World", 10));
+
+ q = list1.get();
+
+ store_test2_check(q);
+ q.store();
+ store_test2_check(q);
+ }
+ store_test2_check(q);
+}
+
+int main()
+{
+ empty_tests();
+ qbk_tests();
+ sort_test();
+ multiple_list_test();
+ store_test1();
+ store_test2();
+
+ return boost::report_errors();
+}


Boost-Commit list run by bdawes at acm.org, david.abrahams at rcn.com, gregod at cs.rpi.edu, cpdaniel at pacbell.net, john at johnmaddock.co.uk