A value type for quickbook data.

[SVN r68882]
This commit is contained in:
Daniel James 2011-02-15 00:20:10 +00:00
parent 79afa364c4
commit d92609023f
7 changed files with 1320 additions and 0 deletions

View File

@ -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
View 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
View 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
View 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

View File

@ -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
View 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
View 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();
}