blob: ce55838e8eef61984461abe3e32ba1bd45f7ad7e [file] [log] [blame]
/*=============================================================================
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>
#include <boost/current_function.hpp>
#include <boost/lexical_cast.hpp>
#define UNDEFINED_ERROR() \
throw value_undefined_method( \
std::string(BOOST_CURRENT_FUNCTION) +\
" not defined for " + \
this->type_name() + \
" values." \
);
namespace quickbook
{
////////////////////////////////////////////////////////////////////////////
// Value Error
struct value_undefined_method : value_error
{
value_undefined_method(std::string const&);
};
value_error::value_error(std::string const& x)
: std::logic_error(x) {}
value_undefined_method::value_undefined_method(std::string const& x)
: value_error(x) {}
////////////////////////////////////////////////////////////////////////////
// Node
namespace detail
{
value_node::value_node(tag_type t)
: ref_count_(0), tag_(t), next_() {
}
value_node::~value_node() {
}
value_node* value_node::store() { return this; }
file_position value_node::get_position() const { UNDEFINED_ERROR(); }
int value_node::get_int() const { UNDEFINED_ERROR(); }
std::string value_node::get_quickbook() const { UNDEFINED_ERROR(); }
value_node::qbk_range value_node::get_quickbook_range() const { UNDEFINED_ERROR(); }
std::string value_node::get_boostbook() const { UNDEFINED_ERROR(); }
value_node* value_node::get_list() const { UNDEFINED_ERROR(); }
bool value_node::empty() const { return false; }
bool value_node::check() const { return true; }
bool value_node::is_list() const { return false; }
bool value_node::is_string() const { return false; }
bool value_node::equals(value_node*) const { UNDEFINED_ERROR(); }
}
////////////////////////////////////////////////////////////////////////////
// List end value
//
// A special value for marking the end of lists.
namespace detail
{
struct value_list_end_impl : public value_node
{
static value_list_end_impl instance;
private:
value_list_end_impl()
: value_node(value::default_tag)
{
intrusive_ptr_add_ref(&instance);
next_ = this;
}
virtual char const* type_name() const { return "list end"; }
virtual value_node* clone() const { UNDEFINED_ERROR(); }
virtual bool equals(value_node* other) const
{ return this == other; }
bool empty() const { UNDEFINED_ERROR(); }
bool check() const { UNDEFINED_ERROR(); }
bool is_list() const { UNDEFINED_ERROR(); }
bool is_string() const { UNDEFINED_ERROR(); }
};
value_list_end_impl value_list_end_impl::instance;
}
////////////////////////////////////////////////////////////////////////////
// Empty/nil values
//
// (nil is just a special case of empty, don't be mislead by the name
// the type is not important).
namespace detail
{
struct 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:
char const* type_name() const { return "empty"; }
virtual value_node* clone() const
{ return new value_empty_impl(tag_); }
virtual bool empty() const
{ return true; }
virtual bool check() const
{ return false; }
virtual bool equals(value_node* other) const
{ return !other->check(); }
friend value quickbook::empty_value(value::tag_type);
};
struct value_nil_impl : public value_empty_impl
{
static value_nil_impl instance;
private:
value_nil_impl()
: value_empty_impl(value::default_tag)
{
intrusive_ptr_add_ref(&instance);
next_ = &value_list_end_impl::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 empty_value(value::tag_type t)
{
return value(detail::value_empty_impl::new_(t));
}
////////////////////////////////////////////////////////////////////////////
// value_counted
namespace detail
{
value_counted::value_counted()
: value_base(&value_nil_impl::instance)
{
// Even though empty is not on the heap, its reference
// counter needs to be incremented so that the destructor
// doesn't try to delete it.
intrusive_ptr_add_ref(value_);
}
value_counted::value_counted(value_counted const& x)
: value_base(x)
{
intrusive_ptr_add_ref(value_);
}
value_counted::value_counted(value_base const& x)
: value_base(x)
{
intrusive_ptr_add_ref(value_);
}
value_counted::value_counted(value_node* x)
: value_base(x)
{
intrusive_ptr_add_ref(value_);
}
value_counted::~value_counted()
{
intrusive_ptr_release(value_);
}
}
////////////////////////////////////////////////////////////////////////////
// value
value::value()
: detail::value_counted()
{
}
value::value(value const& x)
: detail::value_counted(x)
{
}
value::value(detail::value_base const& x)
: detail::value_counted(x)
{
}
value::value(detail::value_node* x)
: detail::value_counted(x)
{
}
value& value::operator=(value x)
{
swap(x);
return *this;
}
////////////////////////////////////////////////////////////////////////////
// stored_value
stored_value::stored_value()
: detail::value_counted()
{
}
stored_value::stored_value(stored_value const& x)
: detail::value_counted(x)
{
}
stored_value::stored_value(detail::value_base const& x)
: detail::value_counted(x.value_->store())
{
}
stored_value& stored_value::operator=(stored_value x)
{
swap(x);
return *this;
}
////////////////////////////////////////////////////////////////////////////
// Integers
namespace detail
{
struct value_int_impl : public value_node
{
public:
explicit value_int_impl(int, value::tag_type);
private:
char const* type_name() const { return "integer"; }
virtual value_node* clone() const;
virtual int get_int() const;
virtual std::string get_quickbook() const;
virtual std::string get_boostbook() const;
virtual bool empty() const;
virtual bool equals(value_node*) const;
int value_;
};
value_int_impl::value_int_impl(int v, value::tag_type t)
: value_node(t)
, value_(v)
{}
value_node* value_int_impl::clone() const
{
return new value_int_impl(value_, tag_);
}
int value_int_impl::get_int() const
{
return value_;
}
std::string value_int_impl::get_quickbook() const
{
return boost::lexical_cast<std::string>(value_);
}
std::string value_int_impl::get_boostbook() const
{
return boost::lexical_cast<std::string>(value_);
}
bool value_int_impl::empty() const
{
return false;
}
bool value_int_impl::equals(value_node* other) const {
try {
return value_ == other->get_int();
}
catch(value_undefined_method&) {
return false;
}
}
}
value int_value(int v, value::tag_type t)
{
return value(new detail::value_int_impl(v, t));
}
////////////////////////////////////////////////////////////////////////////
// Strings
namespace detail
{
struct value_string_impl : public value_node
{
public:
explicit value_string_impl(std::string const&, value::tag_type);
private:
char const* type_name() const { return "boostbook"; }
virtual ~value_string_impl();
virtual value_node* clone() const;
virtual std::string get_boostbook() const;
virtual bool is_string() const;
virtual bool empty() const;
virtual bool equals(value_node*) 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:
char const* type_name() const { return "quickbook"; }
virtual ~value_qbk_string_impl();
virtual value_node* clone() const;
virtual file_position get_position() const;
virtual std::string get_quickbook() const;
qbk_range get_quickbook_range() const;
virtual bool is_string() const;
virtual bool empty() const;
virtual bool equals(value_node*) 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:
char const* type_name() const { return "quickbook"; }
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;
qbk_range get_quickbook_range() const;
virtual bool is_string() const;
virtual bool empty() const;
virtual bool equals(value_node*) const;
quickbook::iterator begin_;
quickbook::iterator end_;
};
struct value_qbk_bbk_impl : public value_node
{
private:
char const* type_name() const { return "quickbook/boostbook"; }
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;
qbk_range get_quickbook_range() const;
virtual std::string get_boostbook() const;
virtual bool is_string() const;
virtual bool empty() const;
virtual bool equals(value_node*) 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::empty() const
{ return value_.empty(); }
bool value_string_impl::equals(value_node* other) const {
try {
return value_ == other->get_boostbook();
}
catch(value_undefined_method&) {
return false;
}
}
// 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_; }
value::qbk_range value_qbk_string_impl::get_quickbook_range() const
{ return qbk_range(
iterator(value_.begin(), position_),
iterator(value_.end())); }
bool value_qbk_string_impl::is_string() const
{ return true; }
bool value_qbk_string_impl::empty() const
{ return value_.empty(); }
bool value_qbk_string_impl::equals(value_node* other) const {
try {
return value_ == other->get_quickbook();
}
catch(value_undefined_method&) {
return false;
}
}
// 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()); }
value::qbk_range value_qbk_ref_impl::get_quickbook_range() const
{ return qbk_range(begin_, end_); }
bool value_qbk_ref_impl::is_string() const
{ return true; }
bool value_qbk_ref_impl::empty() const
{ return begin_ == end_; }
bool value_qbk_ref_impl::equals(value_node* other) const {
try {
return this->get_quickbook() == other->get_quickbook();
}
catch(value_undefined_method&) {
return false;
}
}
// 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_; }
value::qbk_range value_qbk_bbk_impl::get_quickbook_range() const
{ return qbk_range(
iterator(qbk_value_.begin(), position_),
iterator(qbk_value_.end())); }
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::empty() const
{ return bbk_value_.empty(); }
bool value_qbk_bbk_impl::equals(value_node* other) const {
try {
return this->get_quickbook() == other->get_quickbook();
}
catch(value_undefined_method&) {}
try {
return this->get_boostbook() == other->get_boostbook();
}
catch(value_undefined_method&) {}
return false;
}
}
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(value_node**, value_node**, value_node**);
void rotate(value_node**, value_node**, value_node**);
value_node** list_ref_back(value_node** back)
{
while(*back != &value_list_end_impl::instance) {
intrusive_ptr_add_ref(*back);
back = &(*back)->next_;
}
return back;
}
void list_ref(value_node* ptr)
{
while(ptr != &value_list_end_impl::instance) {
intrusive_ptr_add_ref(ptr);
ptr = ptr->next_;
}
}
void list_unref(value_node* ptr)
{
while(ptr != &value_list_end_impl::instance) {
value_node* next = ptr->next_;
intrusive_ptr_release(ptr);
ptr = next;
}
}
value_node** merge_sort(value_node** l)
{
if(*l == &value_list_end_impl::instance)
return l;
else
return merge_sort(l, 9999);
}
value_node** merge_sort(value_node** l, int recurse_limit)
{
value_node** p = &(*l)->next_;
for(int count = 0;
count < recurse_limit && *p != &value_list_end_impl::instance;
++count)
{
p = merge(l, p, merge_sort(p, count));
}
return p;
}
value_node** merge(
value_node** first, value_node** second, value_node** third)
{
for(;;) {
for(;;) {
if(first == second) return third;
if((*second)->tag_ < (*first)->tag_) break;
first = &(*first)->next_;
}
rotate(first, second, third);
first = &(*first)->next_;
// Since the two ranges were just swapped, the order is now:
// first...third...second
//
// Also, that since the second section of the list was
// originally before the first, if the heads are equal
// we need to swap to maintain the order.
for(;;) {
if(first == third) return second;
if((*third)->tag_ <= (*first)->tag_) break;
first = &(*first)->next_;
}
rotate(first, third, second);
first = &(*first)->next_;
}
}
void rotate(value_node** first, value_node** second, value_node** third)
{
value_node* tmp = *first;
*first = *second;
*second = *third;
*third = tmp;
//if(*second != &value_list_end_impl::instance) back = second;
}
}
}
//////////////////////////////////////////////////////////////////////////
// Lists
namespace detail
{
struct value_list_impl : public value_node
{
value_list_impl(value::tag_type);
value_list_impl(value_list_builder&, value::tag_type);
private:
value_list_impl(value_list_impl const&);
char const* type_name() const { return "list"; }
virtual ~value_list_impl();
virtual value_node* clone() const;
virtual value_node* store();
virtual bool empty() const;
virtual bool equals(value_node*) const;
virtual bool is_list() const;
virtual value_node* get_list() const;
value_node* head_;
};
value_list_impl::value_list_impl(value::tag_type tag)
: value_node(tag), head_(&value_list_end_impl::instance)
{}
value_list_impl::value_list_impl(value_list_builder& builder,
value::tag_type tag)
: value_node(tag), head_(builder.release())
{
}
value_list_impl::value_list_impl(value_list_impl const& x)
: value_node(x.tag_), head_(x.head_)
{
list_ref(head_);
}
value_list_impl::~value_list_impl()
{
list_unref(head_);
}
value_node* value_list_impl::clone() const
{
return new value_list_impl(*this);
}
value_node* value_list_impl::store()
{
value_node* pos = head_;
boost::intrusive_ptr<value_node> new_node;
for(;;) {
if(pos == &value_list_end_impl::instance)
return this;
new_node = pos->store();
if(new_node.get() != pos) break;
pos = pos->next_;
}
value_list_builder build;
value_node* pos2 = head_;
for(;pos2 != pos; pos2 = pos2->next_)
build.append(pos2);
build.append(new_node.get());
pos2 = pos2->next_;
for(;pos2 != &value_list_end_impl::instance; pos2 = pos2->next_)
build.append(pos2->store());
return new value_list_impl(build, tag_);
}
bool value_list_impl::empty() const
{
return head_ == &value_list_end_impl::instance;
}
bool value_list_impl::is_list() const
{
return true;
}
value_node* value_list_impl::get_list() const
{
return head_;
}
bool value_list_impl::equals(value_node* other) const {
value_node* x1;
try {
x1 = other->get_list();
}
catch(value_undefined_method&) {
return false;
}
for(value_node *x2 = head_; x1 != x2; x1 = x1->next_, x2 = x2->next_)
{
if (x2 == &value_list_end_impl::instance ||
!x1->equals(x2)) return false;
}
return true;
}
}
//////////////////////////////////////////////////////////////////////////
// List builder
namespace detail
{
// value_list_builder
value_list_builder::value_list_builder()
: head_(&value_list_end_impl::instance)
, back_(&head_)
{}
value_list_builder::value_list_builder(value_node* ptr)
: head_(ptr)
, back_(list_ref_back(&head_))
{}
value_list_builder::~value_list_builder()
{
list_unref(head_);
}
void value_list_builder::swap(value_list_builder& other) {
std::swap(head_, other.head_);
std::swap(back_, other.back_);
if(back_ == &other.head_) back_ = &head_;
if(other.back_ == &head_) other.back_ = &other.head_;
}
value_node* value_list_builder::release() {
value_node* r = head_;
head_ = &value_list_end_impl::instance;
back_ = &head_;
return r;
}
void value_list_builder::append(value_node* item)
{
if(item->next_) item = item->clone();
intrusive_ptr_add_ref(item);
item->next_ = *back_;
*back_ = item;
back_ = &item->next_;
}
void value_list_builder::sort()
{
back_ = merge_sort(&head_);
assert(*back_ == &value_list_end_impl::instance);
}
}
//////////////////////////////////////////////////////////////////////////
// Value builder
value_builder::value_builder()
: current()
, list_tag(value::default_tag)
, saved()
{
}
void value_builder::swap(value_builder& other) {
current.swap(other.current);
std::swap(list_tag, other.list_tag);
saved.swap(other.saved);
}
void value_builder::save() {
boost::scoped_ptr<value_builder> store(new value_builder);
swap(*store);
saved.swap(store);
}
void value_builder::restore() {
boost::scoped_ptr<value_builder> store;
store.swap(saved);
swap(*store);
}
value value_builder::release() {
return value(new detail::value_list_impl(current, list_tag));
}
void value_builder::reset() {
detail::value_list_builder new_builder;
current.swap(new_builder);
list_tag = value::default_tag;
}
void value_builder::insert(value const& item) {
current.append(item.value_);
}
void value_builder::extend(value const& list) {
for (value::iterator begin = list.begin(), end = list.end();
begin != end; ++begin)
{
insert(*begin);
}
}
void value_builder::start_list(value::tag_type tag) {
value::tag_type saved_tag = tag;
save();
list_tag = saved_tag;
}
void value_builder::finish_list() {
value list = release();
restore();
insert(list);
}
void value_builder::clear_list() {
restore();
}
void value_builder::sort_list()
{
current.sort();
}
////////////////////////////////////////////////////////////////////////////
// Iterator
namespace detail
{
value::iterator::iterator()
: ptr_(&value_list_end_impl::instance) {}
}
}