A value type for quickbook data.
[SVN r68882]
This commit is contained in:
parent
79afa364c4
commit
d92609023f
@ -31,6 +31,7 @@ exe quickbook
|
||||
actions_class.cpp
|
||||
utils.cpp
|
||||
input_path.cpp
|
||||
values.cpp
|
||||
post_process.cpp
|
||||
collector.cpp
|
||||
template_stack.cpp
|
||||
|
73
src/value_tags.hpp
Normal file
73
src/value_tags.hpp
Normal file
@ -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
|
783
src/values.cpp
Normal file
783
src/values.cpp
Normal file
@ -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();
|
||||
}
|
||||
}
|
313
src/values.hpp
Normal file
313
src/values.hpp
Normal file
@ -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
|
@ -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 ;
|
||||
|
||||
|
7
test/unit/Jamfile.v2
Normal file
7
test/unit/Jamfile.v2
Normal file
@ -0,0 +1,7 @@
|
||||
import testing ;
|
||||
|
||||
project quickbook-unit-tests
|
||||
: requirements <include>../../src <warnings>all
|
||||
;
|
||||
|
||||
run values_test.cpp ../../src/values.cpp ;
|
142
test/unit/values_test.cpp
Normal file
142
test/unit/values_test.cpp
Normal file
@ -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();
|
||||
}
|
Loading…
Reference in New Issue
Block a user