1 /*============================================================================= 2 Copyright (c) 2010-2011 Daniel James 3 4 Use, modification and distribution is subject to the Boost Software 5 License, Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at 6 http://www.boost.org/LICENSE_1_0.txt) 7 =============================================================================*/ 8 9 #include "values.hpp" 10 #include <boost/current_function.hpp> 11 #include <boost/lexical_cast.hpp> 12 #include "files.hpp" 13 14 #define UNDEFINED_ERROR() \ 15 throw value_undefined_method( \ 16 std::string(BOOST_CURRENT_FUNCTION) + " not defined for " + \ 17 this->type_name() + " values."); 18 19 namespace quickbook 20 { 21 //////////////////////////////////////////////////////////////////////////// 22 // Value Error 23 24 struct value_undefined_method : value_error 25 { 26 value_undefined_method(std::string const&); 27 }; 28 value_error(std::string const & x)29 value_error::value_error(std::string const& x) : std::logic_error(x) {} 30 value_undefined_method(std::string const & x)31 value_undefined_method::value_undefined_method(std::string const& x) 32 : value_error(x) 33 { 34 } 35 36 //////////////////////////////////////////////////////////////////////////// 37 // Node 38 39 namespace detail 40 { value_node(tag_type t)41 value_node::value_node(tag_type t) : ref_count_(0), tag_(t), next_() {} 42 ~value_node()43 value_node::~value_node() {} 44 get_file() const45 file_ptr value_node::get_file() const { UNDEFINED_ERROR(); } get_position() const46 string_iterator value_node::get_position() const { UNDEFINED_ERROR(); } get_int() const47 int value_node::get_int() const { UNDEFINED_ERROR(); } get_quickbook() const48 quickbook::string_view value_node::get_quickbook() const 49 { 50 UNDEFINED_ERROR(); 51 } get_encoded() const52 std::string value_node::get_encoded() const { UNDEFINED_ERROR(); } get_list() const53 value_node* value_node::get_list() const { UNDEFINED_ERROR(); } 54 empty() const55 bool value_node::empty() const { return false; } check() const56 bool value_node::check() const { return true; } is_list() const57 bool value_node::is_list() const { return false; } is_encoded() const58 bool value_node::is_encoded() const { return false; } equals(value_node *) const59 bool value_node::equals(value_node*) const { UNDEFINED_ERROR(); } 60 } 61 62 //////////////////////////////////////////////////////////////////////////// 63 // List end value 64 // 65 // A special value for marking the end of lists. 66 67 namespace detail 68 { 69 struct value_list_end_impl : public value_node 70 { 71 static value_list_end_impl instance; 72 73 private: value_list_end_implquickbook::detail::value_list_end_impl74 value_list_end_impl() : value_node(value::default_tag) 75 { 76 intrusive_ptr_add_ref(&instance); 77 next_ = this; 78 } 79 type_namequickbook::detail::value_list_end_impl80 virtual char const* type_name() const { return "list end"; } clonequickbook::detail::value_list_end_impl81 virtual value_node* clone() const { UNDEFINED_ERROR(); } 82 equalsquickbook::detail::value_list_end_impl83 virtual bool equals(value_node* other) const 84 { 85 return this == other; 86 } 87 emptyquickbook::detail::value_list_end_impl88 bool empty() const { UNDEFINED_ERROR(); } checkquickbook::detail::value_list_end_impl89 bool check() const { UNDEFINED_ERROR(); } is_listquickbook::detail::value_list_end_impl90 bool is_list() const { UNDEFINED_ERROR(); } is_encodedquickbook::detail::value_list_end_impl91 bool is_encoded() const { UNDEFINED_ERROR(); } 92 }; 93 94 value_list_end_impl value_list_end_impl::instance; 95 } 96 97 //////////////////////////////////////////////////////////////////////////// 98 // Empty/nil values 99 // 100 // (nil is just a special case of empty, don't be mislead by the name 101 // the type is not important). 102 103 namespace detail 104 { 105 struct empty_value_impl : public value_node 106 { 107 static value_node* new_(value::tag_type t); 108 109 protected: empty_value_implquickbook::detail::empty_value_impl110 explicit empty_value_impl(value::tag_type t) : value_node(t) {} 111 112 private: type_namequickbook::detail::empty_value_impl113 char const* type_name() const { return "empty"; } 114 clonequickbook::detail::empty_value_impl115 virtual value_node* clone() const 116 { 117 return new empty_value_impl(tag_); 118 } 119 emptyquickbook::detail::empty_value_impl120 virtual bool empty() const { return true; } 121 checkquickbook::detail::empty_value_impl122 virtual bool check() const { return false; } 123 equalsquickbook::detail::empty_value_impl124 virtual bool equals(value_node* other) const 125 { 126 return !other->check(); 127 } 128 129 friend value quickbook::empty_value(value::tag_type); 130 }; 131 132 struct value_nil_impl : public empty_value_impl 133 { 134 static value_nil_impl instance; 135 136 private: value_nil_implquickbook::detail::value_nil_impl137 value_nil_impl() : empty_value_impl(value::default_tag) 138 { 139 intrusive_ptr_add_ref(&instance); 140 next_ = &value_list_end_impl::instance; 141 } 142 }; 143 144 value_nil_impl value_nil_impl::instance; 145 new_(value::tag_type t)146 value_node* empty_value_impl::new_(value::tag_type t) 147 { 148 // The return value from this function is always placed in an 149 // intrusive_ptr which will manage the memory correctly. 150 // Note that value_nil_impl increments its reference count 151 // in its constructor, so that it will never be deleted by the 152 // intrusive pointer. 153 154 if (t == value::default_tag) 155 return &value_nil_impl::instance; 156 else 157 return new empty_value_impl(t); 158 } 159 } 160 empty_value(value::tag_type t)161 value empty_value(value::tag_type t) 162 { 163 return value(detail::empty_value_impl::new_(t)); 164 } 165 166 //////////////////////////////////////////////////////////////////////////// 167 // value_counted 168 169 namespace detail 170 { value_counted()171 value_counted::value_counted() : value_base(&value_nil_impl::instance) 172 { 173 // Even though empty is not on the heap, its reference 174 // counter needs to be incremented so that the destructor 175 // doesn't try to delete it. 176 177 intrusive_ptr_add_ref(value_); 178 } 179 value_counted(value_counted const & x)180 value_counted::value_counted(value_counted const& x) : value_base(x) 181 { 182 intrusive_ptr_add_ref(value_); 183 } 184 value_counted(value_base const & x)185 value_counted::value_counted(value_base const& x) : value_base(x) 186 { 187 intrusive_ptr_add_ref(value_); 188 } 189 value_counted(value_node * x)190 value_counted::value_counted(value_node* x) : value_base(x) 191 { 192 intrusive_ptr_add_ref(value_); 193 } 194 ~value_counted()195 value_counted::~value_counted() { intrusive_ptr_release(value_); } 196 } 197 198 //////////////////////////////////////////////////////////////////////////// 199 // value 200 value()201 value::value() : detail::value_counted() {} 202 value(value const & x)203 value::value(value const& x) : detail::value_counted(x) {} 204 value(detail::value_base const & x)205 value::value(detail::value_base const& x) : detail::value_counted(x) {} 206 value(detail::value_node * x)207 value::value(detail::value_node* x) : detail::value_counted(x) {} 208 operator =(value x)209 value& value::operator=(value x) 210 { 211 swap(x); 212 return *this; 213 } 214 215 //////////////////////////////////////////////////////////////////////////// 216 // Integers 217 218 namespace detail 219 { 220 struct int_value_impl : public value_node 221 { 222 public: 223 explicit int_value_impl(int, value::tag_type); 224 225 private: type_namequickbook::detail::int_value_impl226 char const* type_name() const { return "integer"; } 227 virtual value_node* clone() const; 228 virtual int get_int() const; 229 virtual std::string get_encoded() const; 230 virtual bool empty() const; 231 virtual bool is_encoded() const; 232 virtual bool equals(value_node*) const; 233 234 int value_; 235 }; 236 int_value_impl(int v,value::tag_type t)237 int_value_impl::int_value_impl(int v, value::tag_type t) 238 : value_node(t), value_(v) 239 { 240 } 241 clone() const242 value_node* int_value_impl::clone() const 243 { 244 return new int_value_impl(value_, tag_); 245 } 246 get_int() const247 int int_value_impl::get_int() const { return value_; } 248 get_encoded() const249 std::string int_value_impl::get_encoded() const 250 { 251 return boost::lexical_cast<std::string>(value_); 252 } 253 empty() const254 bool int_value_impl::empty() const { return false; } 255 is_encoded() const256 bool int_value_impl::is_encoded() const { return true; } 257 equals(value_node * other) const258 bool int_value_impl::equals(value_node* other) const 259 { 260 try { 261 return value_ == other->get_int(); 262 } catch (value_undefined_method&) { 263 return false; 264 } 265 } 266 } 267 int_value(int v,value::tag_type t)268 value int_value(int v, value::tag_type t) 269 { 270 return value(new detail::int_value_impl(v, t)); 271 } 272 273 //////////////////////////////////////////////////////////////////////////// 274 // Strings 275 276 namespace detail 277 { 278 struct encoded_value_impl : public value_node 279 { 280 public: 281 explicit encoded_value_impl(std::string const&, value::tag_type); 282 283 private: type_namequickbook::detail::encoded_value_impl284 char const* type_name() const { return "encoded text"; } 285 286 virtual ~encoded_value_impl(); 287 virtual value_node* clone() const; 288 virtual std::string get_encoded() const; 289 virtual bool empty() const; 290 virtual bool is_encoded() const; 291 virtual bool equals(value_node*) const; 292 293 std::string value_; 294 }; 295 296 struct qbk_value_impl : public value_node 297 { 298 public: 299 explicit qbk_value_impl( 300 file_ptr const&, 301 string_iterator begin, 302 string_iterator end, 303 value::tag_type); 304 305 private: type_namequickbook::detail::qbk_value_impl306 char const* type_name() const { return "quickbook"; } 307 308 virtual ~qbk_value_impl(); 309 virtual value_node* clone() const; 310 virtual file_ptr get_file() const; 311 virtual string_iterator get_position() const; 312 virtual quickbook::string_view get_quickbook() const; 313 virtual bool empty() const; 314 virtual bool equals(value_node*) const; 315 316 file_ptr file_; 317 string_iterator begin_; 318 string_iterator end_; 319 }; 320 321 struct encoded_qbk_value_impl : public value_node 322 { 323 private: type_namequickbook::detail::encoded_qbk_value_impl324 char const* type_name() const 325 { 326 return "encoded text with quickbook reference"; 327 } 328 329 encoded_qbk_value_impl( 330 file_ptr const&, 331 string_iterator, 332 string_iterator, 333 std::string const&, 334 value::tag_type); 335 336 virtual ~encoded_qbk_value_impl(); 337 virtual value_node* clone() const; 338 virtual file_ptr get_file() const; 339 virtual string_iterator get_position() const; 340 virtual quickbook::string_view get_quickbook() const; 341 virtual std::string get_encoded() const; 342 virtual bool empty() const; 343 virtual bool is_encoded() const; 344 virtual bool equals(value_node*) const; 345 346 file_ptr file_; 347 string_iterator begin_; 348 string_iterator end_; 349 std::string encoded_value_; 350 351 friend quickbook::value quickbook::encoded_qbk_value( 352 file_ptr const&, 353 string_iterator, 354 string_iterator, 355 std::string const&, 356 quickbook::value::tag_type); 357 }; 358 359 // encoded_value_impl 360 encoded_value_impl(std::string const & val,value::tag_type tag)361 encoded_value_impl::encoded_value_impl( 362 std::string const& val, value::tag_type tag) 363 : value_node(tag), value_(val) 364 { 365 } 366 ~encoded_value_impl()367 encoded_value_impl::~encoded_value_impl() {} 368 clone() const369 value_node* encoded_value_impl::clone() const 370 { 371 return new encoded_value_impl(value_, tag_); 372 } 373 get_encoded() const374 std::string encoded_value_impl::get_encoded() const { return value_; } 375 empty() const376 bool encoded_value_impl::empty() const { return value_.empty(); } 377 is_encoded() const378 bool encoded_value_impl::is_encoded() const { return true; } 379 equals(value_node * other) const380 bool encoded_value_impl::equals(value_node* other) const 381 { 382 try { 383 return value_ == other->get_encoded(); 384 } catch (value_undefined_method&) { 385 return false; 386 } 387 } 388 389 // qbk_value_impl 390 qbk_value_impl(file_ptr const & f,string_iterator begin,string_iterator end,value::tag_type tag)391 qbk_value_impl::qbk_value_impl( 392 file_ptr const& f, 393 string_iterator begin, 394 string_iterator end, 395 value::tag_type tag) 396 : value_node(tag), file_(f), begin_(begin), end_(end) 397 { 398 } 399 ~qbk_value_impl()400 qbk_value_impl::~qbk_value_impl() {} 401 clone() const402 value_node* qbk_value_impl::clone() const 403 { 404 return new qbk_value_impl(file_, begin_, end_, tag_); 405 } 406 get_file() const407 file_ptr qbk_value_impl::get_file() const { return file_; } 408 get_position() const409 string_iterator qbk_value_impl::get_position() const { return begin_; } 410 get_quickbook() const411 quickbook::string_view qbk_value_impl::get_quickbook() const 412 { 413 return quickbook::string_view(begin_, end_ - begin_); 414 } 415 empty() const416 bool qbk_value_impl::empty() const { return begin_ == end_; } 417 equals(value_node * other) const418 bool qbk_value_impl::equals(value_node* other) const 419 { 420 try { 421 return this->get_quickbook() == other->get_quickbook(); 422 } catch (value_undefined_method&) { 423 return false; 424 } 425 } 426 427 // encoded_qbk_value_impl 428 encoded_qbk_value_impl(file_ptr const & f,string_iterator begin,string_iterator end,std::string const & encoded,value::tag_type tag)429 encoded_qbk_value_impl::encoded_qbk_value_impl( 430 file_ptr const& f, 431 string_iterator begin, 432 string_iterator end, 433 std::string const& encoded, 434 value::tag_type tag) 435 : value_node(tag) 436 , file_(f) 437 , begin_(begin) 438 , end_(end) 439 , encoded_value_(encoded) 440 441 { 442 } 443 ~encoded_qbk_value_impl()444 encoded_qbk_value_impl::~encoded_qbk_value_impl() {} 445 clone() const446 value_node* encoded_qbk_value_impl::clone() const 447 { 448 return new encoded_qbk_value_impl( 449 file_, begin_, end_, encoded_value_, tag_); 450 } 451 get_file() const452 file_ptr encoded_qbk_value_impl::get_file() const { return file_; } 453 get_position() const454 string_iterator encoded_qbk_value_impl::get_position() const 455 { 456 return begin_; 457 } 458 get_quickbook() const459 quickbook::string_view encoded_qbk_value_impl::get_quickbook() const 460 { 461 return quickbook::string_view(begin_, end_ - begin_); 462 } 463 get_encoded() const464 std::string encoded_qbk_value_impl::get_encoded() const 465 { 466 return encoded_value_; 467 } 468 469 // Should this test the quickbook, the boostbook or both? empty() const470 bool encoded_qbk_value_impl::empty() const 471 { 472 return encoded_value_.empty(); 473 } 474 is_encoded() const475 bool encoded_qbk_value_impl::is_encoded() const { return true; } 476 equals(value_node * other) const477 bool encoded_qbk_value_impl::equals(value_node* other) const 478 { 479 try { 480 return this->get_quickbook() == other->get_quickbook(); 481 } catch (value_undefined_method&) { 482 } 483 484 try { 485 return this->get_encoded() == other->get_encoded(); 486 } catch (value_undefined_method&) { 487 } 488 489 return false; 490 } 491 } 492 qbk_value(file_ptr const & f,string_iterator x,string_iterator y,value::tag_type t)493 value qbk_value( 494 file_ptr const& f, 495 string_iterator x, 496 string_iterator y, 497 value::tag_type t) 498 { 499 return value(new detail::qbk_value_impl(f, x, y, t)); 500 } 501 encoded_value(std::string const & x,value::tag_type t)502 value encoded_value(std::string const& x, value::tag_type t) 503 { 504 return value(new detail::encoded_value_impl(x, t)); 505 } 506 encoded_qbk_value(file_ptr const & f,string_iterator x,string_iterator y,std::string const & z,value::tag_type t)507 value encoded_qbk_value( 508 file_ptr const& f, 509 string_iterator x, 510 string_iterator y, 511 std::string const& z, 512 value::tag_type t) 513 { 514 return value(new detail::encoded_qbk_value_impl(f, x, y, z, t)); 515 } 516 517 ////////////////////////////////////////////////////////////////////////// 518 // List methods 519 520 namespace detail 521 { 522 namespace 523 { 524 value_node** list_ref_back(value_node**); 525 void list_ref(value_node*); 526 void list_unref(value_node*); 527 value_node** merge_sort(value_node**); 528 value_node** merge_sort(value_node**, int); 529 value_node** merge(value_node**, value_node**, value_node**); 530 void rotate(value_node**, value_node**, value_node**); 531 list_ref_back(value_node ** back)532 value_node** list_ref_back(value_node** back) 533 { 534 while (*back != &value_list_end_impl::instance) { 535 intrusive_ptr_add_ref(*back); 536 back = &(*back)->next_; 537 } 538 539 return back; 540 } 541 list_ref(value_node * ptr)542 void list_ref(value_node* ptr) 543 { 544 while (ptr != &value_list_end_impl::instance) { 545 intrusive_ptr_add_ref(ptr); 546 ptr = ptr->next_; 547 } 548 } 549 list_unref(value_node * ptr)550 void list_unref(value_node* ptr) 551 { 552 while (ptr != &value_list_end_impl::instance) { 553 value_node* next = ptr->next_; 554 intrusive_ptr_release(ptr); 555 ptr = next; 556 } 557 } 558 merge_sort(value_node ** l)559 value_node** merge_sort(value_node** l) 560 { 561 if (*l == &value_list_end_impl::instance) 562 return l; 563 else 564 return merge_sort(l, 9999); 565 } 566 merge_sort(value_node ** l,int recurse_limit)567 value_node** merge_sort(value_node** l, int recurse_limit) 568 { 569 value_node** p = &(*l)->next_; 570 for (int count = 0; count < recurse_limit && 571 *p != &value_list_end_impl::instance; 572 ++count) { 573 p = merge(l, p, merge_sort(p, count)); 574 } 575 return p; 576 } 577 merge(value_node ** first,value_node ** second,value_node ** third)578 value_node** merge( 579 value_node** first, value_node** second, value_node** third) 580 { 581 for (;;) { 582 for (;;) { 583 if (first == second) return third; 584 if ((*second)->tag_ < (*first)->tag_) break; 585 first = &(*first)->next_; 586 } 587 588 rotate(first, second, third); 589 first = &(*first)->next_; 590 591 // Since the two ranges were just swapped, the order is now: 592 // first...third...second 593 // 594 // Also, that since the second section of the list was 595 // originally before the first, if the heads are equal 596 // we need to swap to maintain the order. 597 598 for (;;) { 599 if (first == third) return second; 600 if ((*third)->tag_ <= (*first)->tag_) break; 601 first = &(*first)->next_; 602 } 603 604 rotate(first, third, second); 605 first = &(*first)->next_; 606 } 607 } 608 rotate(value_node ** first,value_node ** second,value_node ** third)609 void rotate( 610 value_node** first, value_node** second, value_node** third) 611 { 612 value_node* tmp = *first; 613 *first = *second; 614 *second = *third; 615 *third = tmp; 616 // if(*second != &value_list_end_impl::instance) back = second; 617 } 618 } 619 } 620 621 ////////////////////////////////////////////////////////////////////////// 622 // Lists 623 624 namespace detail 625 { 626 struct value_list_impl : public value_node 627 { 628 value_list_impl(value::tag_type); 629 value_list_impl(value_list_builder&, value::tag_type); 630 631 private: 632 value_list_impl(value_list_impl const&); 633 type_namequickbook::detail::value_list_impl634 char const* type_name() const { return "list"; } 635 636 virtual ~value_list_impl(); 637 virtual value_node* clone() const; 638 virtual bool empty() const; 639 virtual bool equals(value_node*) const; 640 641 virtual bool is_list() const; 642 virtual value_node* get_list() const; 643 644 value_node* head_; 645 }; 646 value_list_impl(value::tag_type tag)647 value_list_impl::value_list_impl(value::tag_type tag) 648 : value_node(tag), head_(&value_list_end_impl::instance) 649 { 650 } 651 value_list_impl(value_list_builder & builder,value::tag_type tag)652 value_list_impl::value_list_impl( 653 value_list_builder& builder, value::tag_type tag) 654 : value_node(tag), head_(builder.release()) 655 { 656 } 657 value_list_impl(value_list_impl const & x)658 value_list_impl::value_list_impl(value_list_impl const& x) 659 : value_node(x.tag_), head_(x.head_) 660 { 661 list_ref(head_); 662 } 663 ~value_list_impl()664 value_list_impl::~value_list_impl() { list_unref(head_); } 665 clone() const666 value_node* value_list_impl::clone() const 667 { 668 return new value_list_impl(*this); 669 } 670 empty() const671 bool value_list_impl::empty() const 672 { 673 return head_ == &value_list_end_impl::instance; 674 } 675 is_list() const676 bool value_list_impl::is_list() const { return true; } 677 get_list() const678 value_node* value_list_impl::get_list() const { return head_; } 679 equals(value_node * other) const680 bool value_list_impl::equals(value_node* other) const 681 { 682 value_node* x1; 683 684 try { 685 x1 = other->get_list(); 686 } catch (value_undefined_method&) { 687 return false; 688 } 689 690 for (value_node *x2 = head_; x1 != x2; 691 x1 = x1->next_, x2 = x2->next_) { 692 if (x2 == &value_list_end_impl::instance || !x1->equals(x2)) 693 return false; 694 } 695 696 return true; 697 } 698 } 699 700 ////////////////////////////////////////////////////////////////////////// 701 // List builder 702 703 namespace detail 704 { 705 // value_list_builder 706 value_list_builder()707 value_list_builder::value_list_builder() 708 : head_(&value_list_end_impl::instance), back_(&head_) 709 { 710 } 711 value_list_builder(value_node * ptr)712 value_list_builder::value_list_builder(value_node* ptr) 713 : head_(ptr), back_(list_ref_back(&head_)) 714 { 715 } 716 ~value_list_builder()717 value_list_builder::~value_list_builder() { list_unref(head_); } 718 swap(value_list_builder & other)719 void value_list_builder::swap(value_list_builder& other) 720 { 721 std::swap(head_, other.head_); 722 std::swap(back_, other.back_); 723 if (back_ == &other.head_) back_ = &head_; 724 if (other.back_ == &head_) other.back_ = &other.head_; 725 } 726 release()727 value_node* value_list_builder::release() 728 { 729 value_node* r = head_; 730 head_ = &value_list_end_impl::instance; 731 back_ = &head_; 732 return r; 733 } 734 append(value_node * item)735 void value_list_builder::append(value_node* item) 736 { 737 if (item->next_) item = item->clone(); 738 intrusive_ptr_add_ref(item); 739 item->next_ = *back_; 740 *back_ = item; 741 back_ = &item->next_; 742 } 743 sort()744 void value_list_builder::sort() 745 { 746 back_ = merge_sort(&head_); 747 assert(*back_ == &value_list_end_impl::instance); 748 } 749 empty() const750 bool value_list_builder::empty() const 751 { 752 return head_ == &value_list_end_impl::instance; 753 } 754 } 755 756 ////////////////////////////////////////////////////////////////////////// 757 // Value builder 758 value_builder()759 value_builder::value_builder() 760 : current(), list_tag(value::default_tag), saved() 761 { 762 } 763 swap(value_builder & other)764 void value_builder::swap(value_builder& other) 765 { 766 current.swap(other.current); 767 std::swap(list_tag, other.list_tag); 768 saved.swap(other.saved); 769 } 770 save()771 void value_builder::save() 772 { 773 boost::scoped_ptr<value_builder> store(new value_builder); 774 swap(*store); 775 saved.swap(store); 776 } 777 restore()778 void value_builder::restore() 779 { 780 boost::scoped_ptr<value_builder> store; 781 store.swap(saved); 782 swap(*store); 783 } 784 release()785 value value_builder::release() 786 { 787 return value(new detail::value_list_impl(current, list_tag)); 788 } 789 insert(value const & item)790 void value_builder::insert(value const& item) 791 { 792 current.append(item.value_); 793 } 794 extend(value const & list)795 void value_builder::extend(value const& list) 796 { 797 for (value::iterator begin = list.begin(), end = list.end(); 798 begin != end; ++begin) { 799 insert(*begin); 800 } 801 } 802 start_list(value::tag_type tag)803 void value_builder::start_list(value::tag_type tag) 804 { 805 save(); 806 list_tag = tag; 807 } 808 finish_list()809 void value_builder::finish_list() 810 { 811 value list = release(); 812 restore(); 813 insert(list); 814 } 815 clear_list()816 void value_builder::clear_list() { restore(); } 817 sort_list()818 void value_builder::sort_list() { current.sort(); } 819 empty() const820 bool value_builder::empty() const { return current.empty(); } 821 822 //////////////////////////////////////////////////////////////////////////// 823 // Iterator 824 825 namespace detail 826 { iterator()827 value::iterator::iterator() : ptr_(&value_list_end_impl::instance) {} 828 } 829 } 830