/*============================================================================= Copyright (c) 2001, Daniel C. Nuffer http://spirit.sourceforge.net/ Distributed under 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) =============================================================================*/ #ifndef BOOST_SPIRIT_ITERATOR_MULTI_PASS_HPP #define BOOST_SPIRIT_ITERATOR_MULTI_PASS_HPP #include #include #include #include #include #include // for std::swap #include // for std::exception #include #include #include #include // for BOOST_SPIRIT_ASSERT #include #include // for boost::detail::iterator_traits #include namespace boost { namespace spirit { BOOST_SPIRIT_CLASSIC_NAMESPACE_BEGIN namespace impl { template inline void mp_swap(T& t1, T& t2); } namespace multi_pass_policies { /////////////////////////////////////////////////////////////////////////////// // class ref_counted // Implementation of an OwnershipPolicy used by multi_pass. // // Implementation modified from RefCounted class from the Loki library by // Andrei Alexandrescu /////////////////////////////////////////////////////////////////////////////// class ref_counted { protected: ref_counted() : count(new std::size_t(1)) {} ref_counted(ref_counted const& x) : count(x.count) {} // clone is called when a copy of the iterator is made, so increment // the ref-count. void clone() { ++*count; } // called when a copy is deleted. Decrement the ref-count. Return // value of true indicates that the last copy has been released. bool release() { if (!--*count) { delete count; count = 0; return true; } return false; } void swap(ref_counted& x) { impl::mp_swap(count, x.count); } public: // returns true if there is only one iterator in existence. // std_deque StoragePolicy will free it's buffered data if this // returns true. bool unique() const { return *count == 1; } private: std::size_t* count; }; /////////////////////////////////////////////////////////////////////////////// // class first_owner // Implementation of an OwnershipPolicy used by multi_pass // This ownership policy dictates that the first iterator created will // determine the lifespan of the shared components. This works well for // spirit, since no dynamic allocation of iterators is done, and all copies // are make on the stack. // // There is a caveat about using this policy together with the std_deque // StoragePolicy. Since first_owner always returns false from unique(), // std_deque will only release the queued data if clear_queue() is called. /////////////////////////////////////////////////////////////////////////////// class first_owner { protected: first_owner() : first(true) {} first_owner(first_owner const&) : first(false) {} void clone() { } // return true to indicate deletion of resources bool release() { return first; } void swap(first_owner&) { // if we're the first, we still remain the first, even if assigned // to, so don't swap first_. swap is only called from operator= } public: bool unique() const { return false; // no way to know, so always return false } private: bool first; }; /////////////////////////////////////////////////////////////////////////////// // class illegal_backtracking // thrown by buf_id_check CheckingPolicy if an instance of an iterator is // used after another one has invalidated the queue /////////////////////////////////////////////////////////////////////////////// class illegal_backtracking : public std::exception { public: illegal_backtracking() throw() {} ~illegal_backtracking() throw() {} virtual const char* what() const throw() { return "BOOST_SPIRIT_CLASSIC_NS::illegal_backtracking"; } }; /////////////////////////////////////////////////////////////////////////////// // class buf_id_check // Implementation of the CheckingPolicy used by multi_pass // This policy is most effective when used together with the std_deque // StoragePolicy. // If used with the fixed_size_queue StoragePolicy, it will not detect // iterator derefereces that are out of the range of the queue. /////////////////////////////////////////////////////////////////////////////// class buf_id_check { protected: buf_id_check() : shared_buf_id(new unsigned long(0)) , buf_id(0) {} buf_id_check(buf_id_check const& x) : shared_buf_id(x.shared_buf_id) , buf_id(x.buf_id) {} // will be called from the destructor of the last iterator. void destroy() { delete shared_buf_id; shared_buf_id = 0; } void swap(buf_id_check& x) { impl::mp_swap(shared_buf_id, x.shared_buf_id); impl::mp_swap(buf_id, x.buf_id); } // called to verify that everything is okay. void check() const { if (buf_id != *shared_buf_id) { boost::throw_exception(illegal_backtracking()); } } // called from multi_pass::clear_queue, so we can increment the count void clear_queue() { ++*shared_buf_id; ++buf_id; } private: unsigned long* shared_buf_id; unsigned long buf_id; }; /////////////////////////////////////////////////////////////////////////////// // class no_check // Implementation of the CheckingPolicy used by multi_pass // It does not do anything :-) /////////////////////////////////////////////////////////////////////////////// class no_check { protected: no_check() {} no_check(no_check const&) {} void destroy() {} void swap(no_check&) {} void check() const {} void clear_queue() {} }; /////////////////////////////////////////////////////////////////////////////// // class std_deque // Implementation of the StoragePolicy used by multi_pass // This stores all data in a std::deque, and keeps an offset to the current // position. It stores all the data unless there is only one // iterator using the queue. // Note: a position is used instead of an iterator, because a push_back on // a deque can invalidate any iterators. /////////////////////////////////////////////////////////////////////////////// class std_deque { public: template class inner { private: typedef std::deque queue_type; queue_type* queuedElements; mutable typename queue_type::size_type queuePosition; protected: inner() : queuedElements(new queue_type) , queuePosition(0) {} inner(inner const& x) : queuedElements(x.queuedElements) , queuePosition(x.queuePosition) {} // will be called from the destructor of the last iterator. void destroy() { BOOST_SPIRIT_ASSERT(NULL != queuedElements); delete queuedElements; queuedElements = 0; } void swap(inner& x) { impl::mp_swap(queuedElements, x.queuedElements); impl::mp_swap(queuePosition, x.queuePosition); } // This is called when the iterator is dereferenced. It's a template // method so we can recover the type of the multi_pass iterator // and call unique and access the m_input data member. template static typename MultiPassT::reference dereference(MultiPassT const& mp) { if (mp.queuePosition == mp.queuedElements->size()) { // check if this is the only iterator if (mp.unique()) { // free up the memory used by the queue. if (mp.queuedElements->size() > 0) { mp.queuedElements->clear(); mp.queuePosition = 0; } } return mp.get_input(); } else { return (*mp.queuedElements)[mp.queuePosition]; } } // This is called when the iterator is incremented. It's a template // method so we can recover the type of the multi_pass iterator // and call unique and access the m_input data member. template static void increment(MultiPassT& mp) { if (mp.queuePosition == mp.queuedElements->size()) { // check if this is the only iterator if (mp.unique()) { // free up the memory used by the queue. if (mp.queuedElements->size() > 0) { mp.queuedElements->clear(); mp.queuePosition = 0; } } else { mp.queuedElements->push_back(mp.get_input()); ++mp.queuePosition; } mp.advance_input(); } else { ++mp.queuePosition; } } // called to forcibly clear the queue void clear_queue() { queuedElements->clear(); queuePosition = 0; } // called to determine whether the iterator is an eof iterator template static bool is_eof(MultiPassT const& mp) { return mp.queuePosition == mp.queuedElements->size() && mp.input_at_eof(); } // called by operator== bool equal_to(inner const& x) const { return queuePosition == x.queuePosition; } // called by operator< bool less_than(inner const& x) const { return queuePosition < x.queuePosition; } }; // class inner }; // class std_deque /////////////////////////////////////////////////////////////////////////////// // class fixed_size_queue // Implementation of the StoragePolicy used by multi_pass // fixed_size_queue keeps a circular buffer (implemented by // BOOST_SPIRIT_CLASSIC_NS::fixed_size_queue class) that is size N+1 and stores N elements. // It is up to the user to ensure that there is enough look ahead for their // grammar. Currently there is no way to tell if an iterator is pointing // to forgotten data. The leading iterator will put an item in the queue // and remove one when it is incremented. No dynamic allocation is done, // except on creation of the queue (fixed_size_queue constructor). /////////////////////////////////////////////////////////////////////////////// template < std::size_t N> class fixed_size_queue { public: template class inner { private: typedef BOOST_SPIRIT_CLASSIC_NS::fixed_size_queue queue_type; queue_type * queuedElements; mutable typename queue_type::iterator queuePosition; protected: inner() : queuedElements(new queue_type) , queuePosition(queuedElements->begin()) {} inner(inner const& x) : queuedElements(x.queuedElements) , queuePosition(x.queuePosition) {} // will be called from the destructor of the last iterator. void destroy() { BOOST_SPIRIT_ASSERT(NULL != queuedElements); delete queuedElements; queuedElements = 0; } void swap(inner& x) { impl::mp_swap(queuedElements, x.queuedElements); impl::mp_swap(queuePosition, x.queuePosition); } // This is called when the iterator is dereferenced. It's a template // method so we can recover the type of the multi_pass iterator // and access the m_input data member. template static typename MultiPassT::reference dereference(MultiPassT const& mp) { if (mp.queuePosition == mp.queuedElements->end()) { return mp.get_input(); } else { return *mp.queuePosition; } } // This is called when the iterator is incremented. It's a template // method so we can recover the type of the multi_pass iterator // and access the m_input data member. template static void increment(MultiPassT& mp) { if (mp.queuePosition == mp.queuedElements->end()) { // don't let the queue get larger than N if (mp.queuedElements->size() >= N) mp.queuedElements->pop_front(); mp.queuedElements->push_back(mp.get_input()); mp.advance_input(); } ++mp.queuePosition; } // no-op void clear_queue() {} // called to determine whether the iterator is an eof iterator template static bool is_eof(MultiPassT const& mp) { return mp.queuePosition == mp.queuedElements->end() && mp.input_at_eof(); } // called by operator== bool equal_to(inner const& x) const { return queuePosition == x.queuePosition; } // called by operator< bool less_than(inner const& x) const { return queuePosition < x.queuePosition; } }; // class inner }; // class fixed_size_queue /////////////////////////////////////////////////////////////////////////////// // class input_iterator // Implementation of the InputPolicy used by multi_pass // input_iterator encapsulates an input iterator of type InputT /////////////////////////////////////////////////////////////////////////////// class input_iterator { public: template class inner { private: typedef typename boost::detail::iterator_traits::value_type result_type; public: typedef result_type value_type; private: struct Data { Data(InputT const &input_) : input(input_), was_initialized(false) {} InputT input; value_type curtok; bool was_initialized; }; // Needed by compilers not implementing the resolution to DR45. For // reference, see // http://www.open-std.org/JTC1/SC22/WG21/docs/cwg_defects.html#45. friend struct Data; public: typedef typename boost::detail::iterator_traits::difference_type difference_type; typedef typename boost::detail::iterator_traits::pointer pointer; typedef typename boost::detail::iterator_traits::reference reference; protected: inner() : data(0) {} inner(InputT x) : data(new Data(x)) {} inner(inner const& x) : data(x.data) {} void destroy() { delete data; data = 0; } bool same_input(inner const& x) const { return data == x.data; } typedef typename boost::detail::iterator_traits::value_type value_t; void swap(inner& x) { impl::mp_swap(data, x.data); } void ensure_initialized() const { if (data && !data->was_initialized) { data->curtok = *data->input; // get the first token data->was_initialized = true; } } public: reference get_input() const { BOOST_SPIRIT_ASSERT(NULL != data); ensure_initialized(); return data->curtok; } void advance_input() { BOOST_SPIRIT_ASSERT(NULL != data); data->was_initialized = false; // should get the next token ++data->input; } bool input_at_eof() const { return !data || data->input == InputT(); } private: Data *data; }; }; /////////////////////////////////////////////////////////////////////////////// // class lex_input // Implementation of the InputPolicy used by multi_pass // lex_input gets tokens (ints) from yylex() /////////////////////////////////////////////////////////////////////////////// class lex_input { public: template class inner { public: typedef int value_type; typedef std::ptrdiff_t difference_type; typedef int* pointer; typedef int& reference; protected: inner() : curtok(new int(0)) {} inner(InputT x) : curtok(new int(x)) {} inner(inner const& x) : curtok(x.curtok) {} void destroy() { delete curtok; curtok = 0; } bool same_input(inner const& x) const { return curtok == x.curtok; } void swap(inner& x) { impl::mp_swap(curtok, x.curtok); } public: reference get_input() const { return *curtok; } void advance_input() { extern int yylex(); *curtok = yylex(); } bool input_at_eof() const { return *curtok == 0; } private: int* curtok; }; }; /////////////////////////////////////////////////////////////////////////////// // class functor_input // Implementation of the InputPolicy used by multi_pass // functor_input gets tokens from a functor // Note: the functor must have a typedef for result_type // It also must have a static variable of type result_type defined to // represent eof that is called eof. /////////////////////////////////////////////////////////////////////////////// class functor_input { public: template class inner { typedef typename FunctorT::result_type result_type; public: typedef result_type value_type; typedef std::ptrdiff_t difference_type; typedef result_type* pointer; typedef result_type& reference; protected: inner() : ftor(0) , curtok(0) {} inner(FunctorT const& x) : ftor(new FunctorT(x)) , curtok(new result_type((*ftor)())) {} inner(inner const& x) : ftor(x.ftor) , curtok(x.curtok) {} void destroy() { delete ftor; ftor = 0; delete curtok; curtok = 0; } bool same_input(inner const& x) const { return ftor == x.ftor; } void swap(inner& x) { impl::mp_swap(curtok, x.curtok); impl::mp_swap(ftor, x.ftor); } public: reference get_input() const { return *curtok; } void advance_input() { if (curtok) { *curtok = (*ftor)(); } } bool input_at_eof() const { return !curtok || *curtok == ftor->eof; } FunctorT& get_functor() const { return *ftor; } private: FunctorT* ftor; result_type* curtok; }; }; } // namespace multi_pass_policies /////////////////////////////////////////////////////////////////////////////// // iterator_base_creator /////////////////////////////////////////////////////////////////////////////// namespace iterator_ { namespace impl { // Meta-function to generate a std::iterator<> base class for multi_pass. This // is used mainly to improve conformance of compilers not supporting PTS // and thus relying on inheritance to recognize an iterator. // We are using boost::iterator<> because it offers an automatic workaround // for broken std::iterator<> implementations. template struct iterator_base_creator { typedef typename InputPolicyT::BOOST_NESTED_TEMPLATE inner input_t; typedef boost::iterator < std::forward_iterator_tag, typename input_t::value_type, typename input_t::difference_type, typename input_t::pointer, typename input_t::reference > type; }; }} /////////////////////////////////////////////////////////////////////////////// // class template multi_pass /////////////////////////////////////////////////////////////////////////////// // The default multi_pass instantiation uses a ref-counted std_deque scheme. template < typename InputT, typename InputPolicy, typename OwnershipPolicy, typename CheckingPolicy, typename StoragePolicy > class multi_pass : public OwnershipPolicy , public CheckingPolicy , public StoragePolicy::template inner< typename InputPolicy::template inner::value_type> , public InputPolicy::template inner , public iterator_::impl::iterator_base_creator::type { typedef OwnershipPolicy OP; typedef CheckingPolicy CHP; typedef typename StoragePolicy::template inner< typename InputPolicy::template inner::value_type> SP; typedef typename InputPolicy::template inner IP; typedef typename iterator_::impl::iterator_base_creator::type IB; public: typedef typename IB::value_type value_type; typedef typename IB::difference_type difference_type; typedef typename IB::reference reference; typedef typename IB::pointer pointer; typedef InputT iterator_type; multi_pass(); explicit multi_pass(InputT input); #if BOOST_WORKAROUND(__GLIBCPP__, == 20020514) multi_pass(int); #endif // BOOST_WORKAROUND(__GLIBCPP__, == 20020514) ~multi_pass(); multi_pass(multi_pass const&); multi_pass& operator=(multi_pass const&); void swap(multi_pass& x); reference operator*() const; pointer operator->() const; multi_pass& operator++(); multi_pass operator++(int); void clear_queue(); bool operator==(const multi_pass& y) const; bool operator<(const multi_pass& y) const; private: // helper functions bool is_eof() const; }; template < typename InputT, typename InputPolicy, typename OwnershipPolicy, typename CheckingPolicy, typename StoragePolicy > inline multi_pass:: multi_pass() : OP() , CHP() , SP() , IP() { } template < typename InputT, typename InputPolicy, typename OwnershipPolicy, typename CheckingPolicy, typename StoragePolicy > inline multi_pass:: multi_pass(InputT input) : OP() , CHP() , SP() , IP(input) { } #if BOOST_WORKAROUND(__GLIBCPP__, == 20020514) // The standard library shipped with gcc-3.1 has a bug in // bits/basic_string.tcc. It tries to use iter::iter(0) to // construct an iterator. Ironically, this happens in sanity // checking code that isn't required by the standard. // The workaround is to provide an additional constructor that // ignores its int argument and behaves like the default constructor. template < typename InputT, typename InputPolicy, typename OwnershipPolicy, typename CheckingPolicy, typename StoragePolicy > inline multi_pass:: multi_pass(int) : OP() , CHP() , SP() , IP() { } #endif // BOOST_WORKAROUND(__GLIBCPP__, == 20020514) template < typename InputT, typename InputPolicy, typename OwnershipPolicy, typename CheckingPolicy, typename StoragePolicy > inline multi_pass:: ~multi_pass() { if (OP::release()) { CHP::destroy(); SP::destroy(); IP::destroy(); } } template < typename InputT, typename InputPolicy, typename OwnershipPolicy, typename CheckingPolicy, typename StoragePolicy > inline multi_pass:: multi_pass( multi_pass const& x) : OP(x) , CHP(x) , SP(x) , IP(x) { OP::clone(); } template < typename InputT, typename InputPolicy, typename OwnershipPolicy, typename CheckingPolicy, typename StoragePolicy > inline multi_pass& multi_pass:: operator=( multi_pass const& x) { multi_pass temp(x); temp.swap(*this); return *this; } template < typename InputT, typename InputPolicy, typename OwnershipPolicy, typename CheckingPolicy, typename StoragePolicy > inline void multi_pass:: swap(multi_pass& x) { OP::swap(x); CHP::swap(x); SP::swap(x); IP::swap(x); } template < typename InputT, typename InputPolicy, typename OwnershipPolicy, typename CheckingPolicy, typename StoragePolicy > inline typename multi_pass:: reference multi_pass:: operator*() const { CHP::check(); return SP::dereference(*this); } template < typename InputT, typename InputPolicy, typename OwnershipPolicy, typename CheckingPolicy, typename StoragePolicy > inline typename multi_pass:: pointer multi_pass:: operator->() const { return &(operator*()); } template < typename InputT, typename InputPolicy, typename OwnershipPolicy, typename CheckingPolicy, typename StoragePolicy > inline multi_pass& multi_pass:: operator++() { CHP::check(); SP::increment(*this); return *this; } template < typename InputT, typename InputPolicy, typename OwnershipPolicy, typename CheckingPolicy, typename StoragePolicy > inline multi_pass multi_pass:: operator++(int) { multi_pass < InputT, InputPolicy, OwnershipPolicy, CheckingPolicy, StoragePolicy > tmp(*this); ++*this; return tmp; } template < typename InputT, typename InputPolicy, typename OwnershipPolicy, typename CheckingPolicy, typename StoragePolicy > inline void multi_pass:: clear_queue() { SP::clear_queue(); CHP::clear_queue(); } template < typename InputT, typename InputPolicy, typename OwnershipPolicy, typename CheckingPolicy, typename StoragePolicy > inline bool multi_pass:: is_eof() const { return SP::is_eof(*this); } ///// Comparisons template < typename InputT, typename InputPolicy, typename OwnershipPolicy, typename CheckingPolicy, typename StoragePolicy > inline bool multi_pass:: operator==(const multi_pass& y) const { bool is_eof_ = SP::is_eof(*this); bool y_is_eof_ = SP::is_eof(y); if (is_eof_ && y_is_eof_) { return true; // both are EOF } else if (is_eof_ ^ y_is_eof_) { return false; // one is EOF, one isn't } else if (!IP::same_input(y)) { return false; } else { return SP::equal_to(y); } } template < typename InputT, typename InputPolicy, typename OwnershipPolicy, typename CheckingPolicy, typename StoragePolicy > inline bool multi_pass:: operator<(const multi_pass& y) const { return SP::less_than(y); } template < typename InputT, typename InputPolicy, typename OwnershipPolicy, typename CheckingPolicy, typename StoragePolicy > inline bool operator!=( const multi_pass& x, const multi_pass& y) { return !(x == y); } template < typename InputT, typename InputPolicy, typename OwnershipPolicy, typename CheckingPolicy, typename StoragePolicy > inline bool operator>( const multi_pass& x, const multi_pass& y) { return y < x; } template < typename InputT, typename InputPolicy, typename OwnershipPolicy, typename CheckingPolicy, typename StoragePolicy > inline bool operator>=( const multi_pass& x, const multi_pass& y) { return !(x < y); } template < typename InputT, typename InputPolicy, typename OwnershipPolicy, typename CheckingPolicy, typename StoragePolicy > inline bool operator<=( const multi_pass& x, const multi_pass& y) { return !(y < x); } ///// Generator function template inline multi_pass make_multi_pass(InputT i) { return multi_pass(i); } // this could be a template typedef, since such a thing doesn't // exist in C++, we'll use inheritance to accomplish the same thing. template class look_ahead : public multi_pass< InputT, multi_pass_policies::input_iterator, multi_pass_policies::first_owner, multi_pass_policies::no_check, multi_pass_policies::fixed_size_queue > { typedef multi_pass< InputT, multi_pass_policies::input_iterator, multi_pass_policies::first_owner, multi_pass_policies::no_check, multi_pass_policies::fixed_size_queue > base_t; public: look_ahead() : base_t() {} explicit look_ahead(InputT x) : base_t(x) {} look_ahead(look_ahead const& x) : base_t(x) {} #if BOOST_WORKAROUND(__GLIBCPP__, == 20020514) look_ahead(int) // workaround for a bug in the library : base_t() {} // shipped with gcc 3.1 #endif // BOOST_WORKAROUND(__GLIBCPP__, == 20020514) // default generated operators destructor and assignment operator are okay. }; template < typename InputT, typename InputPolicy, typename OwnershipPolicy, typename CheckingPolicy, typename StoragePolicy > void swap( multi_pass< InputT, InputPolicy, OwnershipPolicy, CheckingPolicy, StoragePolicy > &x, multi_pass< InputT, InputPolicy, OwnershipPolicy, CheckingPolicy, StoragePolicy > &y) { x.swap(y); } namespace impl { template inline void mp_swap(T& t1, T& t2) { using std::swap; using BOOST_SPIRIT_CLASSIC_NS::swap; swap(t1, t2); } } BOOST_SPIRIT_CLASSIC_NAMESPACE_END }} // namespace BOOST_SPIRIT_CLASSIC_NS #endif // BOOST_SPIRIT_ITERATOR_MULTI_PASS_HPP