aboutsummaryrefslogtreecommitdiff
path: root/boost/format/parsing.hpp
diff options
context:
space:
mode:
Diffstat (limited to 'boost/format/parsing.hpp')
-rw-r--r--boost/format/parsing.hpp457
1 files changed, 457 insertions, 0 deletions
diff --git a/boost/format/parsing.hpp b/boost/format/parsing.hpp
new file mode 100644
index 000000000..a461314f9
--- /dev/null
+++ b/boost/format/parsing.hpp
@@ -0,0 +1,457 @@
+// -*- C++ -*-
+// Boost general library 'format' ---------------------------
+// See http://www.boost.org for updates, documentation, and revision history.
+
+// (C) Samuel Krempp 2001
+// krempp@crans.ens-cachan.fr
+// Permission to copy, use, modify, sell and
+// distribute this software is granted provided this copyright notice appears
+// in all copies. This software is provided "as is" without express or implied
+// warranty, and with no claim as to its suitability for any purpose.
+
+// ideas taken from Rudiger Loos's format class
+// and Karl Nelson's ofstream (also took its parsing code as basis for printf parsing)
+
+// ------------------------------------------------------------------------------
+// parsing.hpp : implementation of the parsing member functions
+// ( parse, parse_printf_directive)
+// ------------------------------------------------------------------------------
+
+
+#ifndef BOOST_FORMAT_PARSING_HPP
+#define BOOST_FORMAT_PARSING_HPP
+
+
+#include <boost/format/format_class.hpp>
+//#include <boost/throw_exception.hpp>
+//#include <boost/assert.hpp>
+
+
+namespace boost {
+namespace io {
+namespace detail {
+
+ template<class Ch, class Stream> inline
+ bool wrap_isdigit(Ch c, Stream &os)
+ {
+#ifndef BOOST_NO_LOCALE_ISIDIGIT
+ return std::isdigit(c, os.rdbuf()->getloc() );
+# else
+ using namespace std;
+ return isdigit(c);
+#endif
+ } //end- wrap_isdigit(..)
+
+ template<class Res, class Ch, class Tr> inline
+ Res str2int(const std::basic_string<Ch, Tr>& s,
+ typename std::basic_string<Ch, Tr>::size_type start,
+ BOOST_IO_STD basic_ios<Ch,Tr> &os,
+ const Res = Res(0) )
+ // Input : char string, with starting index
+ // a basic_ios& merely to call its widen/narrow member function in the desired locale.
+ // Effects : reads s[start:] and converts digits into an integral n, of type Res
+ // Returns : n
+ {
+ Res n = 0;
+ while(start<s.size() && wrap_isdigit(s[start], os) ) {
+ char cur_ch = os.narrow( s[start], 0);
+ BOOST_ASSERT(cur_ch != 0 ); // since we called isdigit, this should not happen.
+ n *= 10;
+ n += cur_ch - '0'; // 22.2.1.1.2 of the C++ standard
+ ++start;
+ }
+ return n;
+ }
+
+ template<class Ch, class Tr>
+ void skip_asterisk(const std::basic_string<Ch,Tr> & buf,
+ typename std::basic_string<Ch,Tr>::size_type * pos_p,
+ BOOST_IO_STD basic_ios<Ch, Tr> &os)
+ // skip printf's "asterisk-fields" directives in the format-string buf
+ // Input : char string, with starting index *pos_p
+ // a basic_ios& merely to call its widen/narrow member function in the desired locale.
+ // Effects : advance *pos_p by skipping printf's asterisk fields.
+ // Returns : nothing
+ {
+ using namespace std;
+ BOOST_ASSERT( pos_p != 0);
+ if(*pos_p >= buf.size() ) return;
+ if(buf[ *pos_p]==os.widen('*')) {
+ ++ (*pos_p);
+ while (*pos_p < buf.size() && wrap_isdigit(buf[*pos_p],os)) ++(*pos_p);
+ if(buf[*pos_p]==os.widen('$')) ++(*pos_p);
+ }
+ }
+
+
+ inline void maybe_throw_exception( unsigned char exceptions)
+ // auxiliary func called by parse_printf_directive
+ // for centralising error handling
+ // it either throws if user sets the corresponding flag, or does nothing.
+ {
+ if(exceptions & io::bad_format_string_bit)
+ boost::throw_exception(io::bad_format_string());
+ }
+
+
+
+ template<class Ch, class Tr>
+ bool parse_printf_directive(const std::basic_string<Ch, Tr> & buf,
+ typename std::basic_string<Ch, Tr>::size_type * pos_p,
+ detail::format_item<Ch, Tr> * fpar,
+ BOOST_IO_STD basic_ios<Ch,Tr> &os,
+ unsigned char exceptions)
+ // Input : a 'printf-directive' in the format-string, starting at buf[ *pos_p ]
+ // a basic_ios& merely to call its widen/narrow member function in the desired locale.
+ // a bitset'excpetions' telling whether to throw exceptions on errors.
+ // Returns : true if parse somehow succeeded (possibly ignoring errors if exceptions disabled)
+ // false if it failed so bad that the directive should be printed verbatim
+ // Effects : - *pos_p is incremented so that buf[*pos_p] is the first char after the directive
+ // - *fpar is set with the parameters read in the directive
+ {
+ typedef format_item<Ch, Tr> format_item_t;
+ BOOST_ASSERT( pos_p != 0);
+ typename std::basic_string<Ch, Tr>::size_type &i1 = *pos_p,
+ i0;
+ fpar->argN_ = format_item_t::argN_no_posit; // if no positional-directive
+
+ bool in_brackets=false;
+ if(buf[i1]==os.widen('|'))
+ {
+ in_brackets=true;
+ if( ++i1 >= buf.size() ) {
+ maybe_throw_exception(exceptions);
+ return false;
+ }
+ }
+
+ // the flag '0' would be picked as a digit for argument order, but here it's a flag :
+ if(buf[i1]==os.widen('0'))
+ goto parse_flags;
+
+ // handle argument order (%2$d) or possibly width specification: %2d
+ i0 = i1; // save position before digits
+ while (i1 < buf.size() && wrap_isdigit(buf[i1], os))
+ ++i1;
+ if (i1!=i0)
+ {
+ if( i1 >= buf.size() ) {
+ maybe_throw_exception(exceptions);
+ return false;
+ }
+ int n=str2int(buf,i0, os, int(0) );
+
+ // %N% case : this is already the end of the directive
+ if( buf[i1] == os.widen('%') )
+ {
+ fpar->argN_ = n-1;
+ ++i1;
+ if( in_brackets)
+ maybe_throw_exception(exceptions);
+ // but don't return. maybe "%" was used in lieu of '$', so we go on.
+ else return true;
+ }
+
+ if ( buf[i1]==os.widen('$') )
+ {
+ fpar->argN_ = n-1;
+ ++i1;
+ }
+ else
+ {
+ // non-positionnal directive
+ fpar->ref_state_.width_ = n;
+ fpar->argN_ = format_item_t::argN_no_posit;
+ goto parse_precision;
+ }
+ }
+
+ parse_flags:
+ // handle flags
+ while ( i1 <buf.size()) // as long as char is one of + - = # 0 l h or ' '
+ {
+ // misc switches
+ switch (os.narrow(buf[i1], 0))
+ {
+ case '\'' : break; // no effect yet. (painful to implement)
+ case 'l':
+ case 'h': // short/long modifier : for printf-comaptibility (no action needed)
+ break;
+ case '-':
+ fpar->ref_state_.flags_ |= std::ios_base::left;
+ break;
+ case '=':
+ fpar->pad_scheme_ |= format_item_t::centered;
+ break;
+ case ' ':
+ fpar->pad_scheme_ |= format_item_t::spacepad;
+ break;
+ case '+':
+ fpar->ref_state_.flags_ |= std::ios_base::showpos;
+ break;
+ case '0':
+ fpar->pad_scheme_ |= format_item_t::zeropad;
+ // need to know alignment before really setting flags,
+ // so just add 'zeropad' flag for now, it will be processed later.
+ break;
+ case '#':
+ fpar->ref_state_.flags_ |= std::ios_base::showpoint | std::ios_base::showbase;
+ break;
+ default:
+ goto parse_width;
+ }
+ ++i1;
+ } // loop on flag.
+ if( i1>=buf.size()) {
+ maybe_throw_exception(exceptions);
+ return true;
+ }
+
+ parse_width:
+ // handle width spec
+ skip_asterisk(buf, &i1, os); // skips 'asterisk fields' : *, or *N$
+ i0 = i1; // save position before digits
+ while (i1<buf.size() && wrap_isdigit(buf[i1], os))
+ i1++;
+
+ if (i1!=i0)
+ { fpar->ref_state_.width_ = str2int( buf,i0, os, std::streamsize(0) ); }
+
+ parse_precision:
+ if( i1>=buf.size()) {
+ maybe_throw_exception(exceptions);
+ return true;
+ }
+ // handle precision spec
+ if (buf[i1]==os.widen('.'))
+ {
+ ++i1;
+ skip_asterisk(buf, &i1, os);
+ i0 = i1; // save position before digits
+ while (i1<buf.size() && wrap_isdigit(buf[i1], os))
+ ++i1;
+
+ if(i1==i0)
+ fpar->ref_state_.precision_ = 0;
+ else
+ fpar->ref_state_.precision_ = str2int(buf,i0, os, std::streamsize(0) );
+ }
+
+ // handle formatting-type flags :
+ while( i1<buf.size() &&
+ ( buf[i1]==os.widen('l') || buf[i1]==os.widen('L') || buf[i1]==os.widen('h')) )
+ ++i1;
+ if( i1>=buf.size()) {
+ maybe_throw_exception(exceptions);
+ return true;
+ }
+
+ if( in_brackets && buf[i1]==os.widen('|') )
+ {
+ ++i1;
+ return true;
+ }
+ switch (os.narrow(buf[i1], 0) )
+ {
+ case 'X':
+ fpar->ref_state_.flags_ |= std::ios_base::uppercase;
+ case 'p': // pointer => set hex.
+ case 'x':
+ fpar->ref_state_.flags_ &= ~std::ios_base::basefield;
+ fpar->ref_state_.flags_ |= std::ios_base::hex;
+ break;
+
+ case 'o':
+ fpar->ref_state_.flags_ &= ~std::ios_base::basefield;
+ fpar->ref_state_.flags_ |= std::ios_base::oct;
+ break;
+
+ case 'E':
+ fpar->ref_state_.flags_ |= std::ios_base::uppercase;
+ case 'e':
+ fpar->ref_state_.flags_ &= ~std::ios_base::floatfield;
+ fpar->ref_state_.flags_ |= std::ios_base::scientific;
+
+ fpar->ref_state_.flags_ &= ~std::ios_base::basefield;
+ fpar->ref_state_.flags_ |= std::ios_base::dec;
+ break;
+
+ case 'f':
+ fpar->ref_state_.flags_ &= ~std::ios_base::floatfield;
+ fpar->ref_state_.flags_ |= std::ios_base::fixed;
+ case 'u':
+ case 'd':
+ case 'i':
+ fpar->ref_state_.flags_ &= ~std::ios_base::basefield;
+ fpar->ref_state_.flags_ |= std::ios_base::dec;
+ break;
+
+ case 'T':
+ ++i1;
+ if( i1 >= buf.size())
+ maybe_throw_exception(exceptions);
+ else
+ fpar->ref_state_.fill_ = buf[i1];
+ fpar->pad_scheme_ |= format_item_t::tabulation;
+ fpar->argN_ = format_item_t::argN_tabulation;
+ break;
+ case 't':
+ fpar->ref_state_.fill_ = os.widen(' ');
+ fpar->pad_scheme_ |= format_item_t::tabulation;
+ fpar->argN_ = format_item_t::argN_tabulation;
+ break;
+
+ case 'G':
+ fpar->ref_state_.flags_ |= std::ios_base::uppercase;
+ break;
+ case 'g': // 'g' conversion is default for floats.
+ fpar->ref_state_.flags_ &= ~std::ios_base::basefield;
+ fpar->ref_state_.flags_ |= std::ios_base::dec;
+
+ // CLEAR all floatield flags, so stream will CHOOSE
+ fpar->ref_state_.flags_ &= ~std::ios_base::floatfield;
+ break;
+
+ case 'C':
+ case 'c':
+ fpar->truncate_ = 1;
+ break;
+ case 'S':
+ case 's':
+ fpar->truncate_ = fpar->ref_state_.precision_;
+ fpar->ref_state_.precision_ = -1;
+ break;
+ case 'n' :
+ fpar->argN_ = format_item_t::argN_ignored;
+ break;
+ default:
+ maybe_throw_exception(exceptions);
+ }
+ ++i1;
+
+ if( in_brackets )
+ {
+ if( i1<buf.size() && buf[i1]==os.widen('|') )
+ {
+ ++i1;
+ return true;
+ }
+ else maybe_throw_exception(exceptions);
+ }
+ return true;
+ }
+
+} // detail namespace
+} // io namespace
+
+
+// -----------------------------------------------
+// format :: parse(..)
+
+template<class Ch, class Traits>
+void basic_format<Ch, Traits> ::parse(const string_t & buf)
+ // parse the format-string
+{
+ using namespace std;
+ const Ch arg_mark = oss_.widen('%');
+ bool ordered_args=true;
+ int max_argN=-1;
+ typename string_t::size_type i1=0;
+ int num_items=0;
+
+ // A: find upper_bound on num_items and allocates arrays
+ i1=0;
+ while( (i1=buf.find(arg_mark,i1)) != string::npos )
+ {
+ if( i1+1 >= buf.size() ) {
+ if(exceptions() & io::bad_format_string_bit)
+ boost::throw_exception(io::bad_format_string()); // must not end in "bla bla %"
+ else break; // stop there, ignore last '%'
+ }
+ if(buf[i1+1] == buf[i1] ) { i1+=2; continue; } // escaped "%%" / "##"
+ ++i1;
+
+ // in case of %N% directives, dont count it double (wastes allocations..) :
+ while(i1 < buf.size() && io::detail::wrap_isdigit(buf[i1],oss_)) ++i1;
+ if( i1 < buf.size() && buf[i1] == arg_mark ) ++ i1;
+
+ ++num_items;
+ }
+ items_.assign( num_items, format_item_t() );
+
+ // B: Now the real parsing of the format string :
+ num_items=0;
+ i1 = 0;
+ typename string_t::size_type i0 = i1;
+ bool special_things=false;
+ int cur_it=0;
+ while( (i1=buf.find(arg_mark,i1)) != string::npos )
+ {
+ string_t & piece = (cur_it==0) ? prefix_ : items_[cur_it-1].appendix_;
+
+ if( buf[i1+1] == buf[i1] ) // escaped mark, '%%'
+ {
+ piece += buf.substr(i0, i1-i0) + buf[i1];
+ i1+=2; i0=i1;
+ continue;
+ }
+ BOOST_ASSERT( static_cast<unsigned int>(cur_it) < items_.size() || cur_it==0);
+
+ if(i1!=i0) piece += buf.substr(i0, i1-i0);
+ ++i1;
+
+ bool parse_ok;
+ parse_ok = io::detail::parse_printf_directive(buf, &i1, &items_[cur_it], oss_, exceptions());
+ if( ! parse_ok ) continue; // the directive will be printed verbatim
+
+ i0=i1;
+ items_[cur_it].compute_states(); // process complex options, like zeropad, into stream params.
+
+ int argN=items_[cur_it].argN_;
+ if(argN == format_item_t::argN_ignored)
+ continue;
+ if(argN ==format_item_t::argN_no_posit)
+ ordered_args=false;
+ else if(argN == format_item_t::argN_tabulation) special_things=true;
+ else if(argN > max_argN) max_argN = argN;
+ ++num_items;
+ ++cur_it;
+ } // loop on %'s
+ BOOST_ASSERT(cur_it == num_items);
+
+ // store the final piece of string
+ string_t & piece = (cur_it==0) ? prefix_ : items_[cur_it-1].appendix_;
+ piece += buf.substr(i0);
+
+ if( !ordered_args)
+ {
+ if(max_argN >= 0 ) // dont mix positional with non-positionnal directives
+ {
+ if(exceptions() & io::bad_format_string_bit)
+ boost::throw_exception(io::bad_format_string());
+ // else do nothing. => positionnal arguments are processed as non-positionnal
+ }
+ // set things like it would have been with positional directives :
+ int non_ordered_items = 0;
+ for(int i=0; i< num_items; ++i)
+ if(items_[i].argN_ == format_item_t::argN_no_posit)
+ {
+ items_[i].argN_ = non_ordered_items;
+ ++non_ordered_items;
+ }
+ max_argN = non_ordered_items-1;
+ }
+
+ // C: set some member data :
+ items_.resize(num_items);
+
+ if(special_things) style_ |= special_needs;
+ num_args_ = max_argN + 1;
+ if(ordered_args) style_ |= ordered;
+ else style_ &= ~ordered;
+}
+
+} // namespace boost
+
+
+#endif // BOOST_FORMAT_PARSING_HPP