Bug fix: check if vm exists
[avr-sim.git] / Format.cpp
blob8ae4214cb99a138d16dc89426409fe8a390ec100
1 #include "Format.h"
2 #include <ctype.h>
3 #include <iostream>
5 namespace util {
6 void empty_buf(std::ostringstream & os) {
7 static const std::string emptyStr;
8 os.str(emptyStr);
11 // applies centered / left / right padding to the string s.
12 // Effects : string s is padded.
13 void do_pad(std::string & s, std::streamsize w, const char c,
14 std::ios_base::fmtflags f, bool center) {
16 std::streamsize n=w-s.size();
17 if(n<=0)
18 return;
20 if( center ){
21 s.reserve(w); // allocate once for the 2 inserts
22 const std::streamsize n1 = n /2, n0 = n - n1;
23 s.insert(s.begin(), n0, c);
24 s.append(n1, c);
25 } else {
26 if(f & std::ios_base::left)
27 s.append(n, c);
28 else
29 s.insert(s.begin(), n, c);
33 format::format(const char *str)
34 : style(0), cur_arg(0), num_args(0), dumped(false),
35 items(), oss(), exceptions_flag(all_error_bits) {
37 state0.set_by_stream(oss);
39 std::string emptyStr;
40 if( str == 0 )
41 str = emptyStr.c_str();
43 parse( str );
46 format::format(const std::string & s)
47 : style(0), cur_arg(0), num_args(0), dumped(false),
48 items(), oss(), exceptions_flag(all_error_bits) {
50 state0.set_by_stream(oss);
51 parse(s);
54 format::format(const format & x)
55 : style(x.style), cur_arg(x.cur_arg), num_args(x.num_args), dumped(false),
56 items(x.items), prefix(x.prefix), bound(x.bound),
57 oss(), // <- we obviously can't copy x.oss
58 state0(x.state0), exceptions_flag(x.exceptions_flag){
60 state0.apply_on(oss);
63 format & format::operator =(const format & x) {
64 if( this == &x )
65 return *this;
67 state0 = x.state0;
68 state0.apply_on(oss);
70 // plus all the other (trivial) assignments :
71 exceptions_flag = x.exceptions_flag;
72 items = x.items;
73 prefix = x.prefix;
74 bound=x.bound;
75 style=x.style;
76 cur_arg=x.cur_arg;
77 num_args=x.num_args;
78 dumped=x.dumped;
79 return *this;
82 // empty the string buffers (except bound arguments, see clear_binds() )
83 // and make the format object ready for formatting a new set of arguments
84 format & format::clear() {
85 for(unsigned long i=0; i<items.size(); ++i) {
86 items[i].state = items[i].ref_state;
88 // clear converted strings only if the corresponding argument is not bound :
89 if( bound.size()==0 || !bound[ items[i].argN ] ) items[i].res.resize(0);
92 cur_arg=0; dumped=false;
94 // maybe first arg is bound:
95 if(bound.size() != 0)
96 while( (cur_arg < num_args) && bound[cur_arg] ) ++cur_arg;
98 return *this;
101 // cancel all bindings, and clear()
102 format & format::clear_binds() {
103 bound.resize(0);
104 clear();
106 return *this;
109 // cancel the binding of ONE argument, and clear()
110 format & format::clear_bind(int argN) {
111 if(argN<1 || argN > num_args || bound.size()==0 || !bound[argN-1] ) {
112 if( exceptions() & out_of_range_bit )
113 throw out_of_range(); // arg not in range.
114 else
115 return *this;
118 bound[argN-1]=false;
119 clear();
121 return *this;
124 std::string format::str() const {
125 dumped=true;
127 if( items.size()==0 )
128 return prefix;
130 if( cur_arg < num_args )
131 if( exceptions() & too_few_args_bit )
132 throw too_few_args(); // not enough variables have been supplied !
134 unsigned long sz = prefix.size();
135 unsigned long i;
136 for(i=0; i < items.size(); ++i)
137 sz += items[i].res.size() + items[i].appendix.size();
139 std::string res;
140 res.reserve(sz);
141 res += prefix;
143 for(i=0; i < items.size(); ++i) {
144 const format_item & item = items[i];
145 res += item.res;
146 if( item.argN == format_item::argN_tabulation) {
147 std::streamsize n = item.state.width - res.size();
149 if( n > 0 )
150 res.append( n, item.state.fill );
153 res += item.appendix;
156 return res;
159 // Input : char string, with starting index
160 // a basic_ios& merely to call its widen/narrow member function in the desired locale.
161 // Effects : reads s[start:] and converts digits into an integral n, of type Res
162 // Returns : n
163 template<class Res>
164 inline Res str2int(const std::string & s,
165 std::string::size_type start,
166 std::ios & os, const Res = Res(0) ) {
167 Res n = 0;
169 while( start<s.size() && isdigit(s[start]) ) {
170 char cur_ch = os.narrow( s[start], 0);
171 n *= 10;
172 n += cur_ch - '0'; // 22.2.1.1.2 of the C++ standard
173 ++start;
176 return n;
179 // skip printf's "asterisk-fields" directives in the format-string buf
180 // Input : char string, with starting index *pos_p
181 // a basic_ios& merely to call its widen/narrow member function in the desired locale.
182 // Effects : advance *pos_p by skipping printf's asterisk fields.
183 // Returns : nothing
184 static void skip_asterisk(const std::string & buf,
185 std::string::size_type *pos_p,
186 std::ios &os) {
187 using namespace std;
188 if(*pos_p >= buf.size() ) return;
189 if(buf[ *pos_p]==os.widen('*')) {
190 ++ (*pos_p);
191 while (*pos_p < buf.size() && isdigit(buf[*pos_p])) ++(*pos_p);
192 if(buf[*pos_p]==os.widen('$')) ++(*pos_p);
196 // Input : a 'printf-directive' in the format-string, starting at buf[ *pos_p ]
197 // a basic_ios& merely to call its widen/narrow member function in the desired locale.
198 // a bitset'exceptions' telling whether to throw exceptions on errors.
199 // Returns : true if parse somehow succeeded (possibly ignoring errors if exceptions disabled)
200 // false if it failed so bad that the directive should be printed verbatim
201 // Effects : - *pos_p is incremented so that buf[*pos_p] is the first char after the directive
202 // - *fpar is set with the parameters read in the directive
203 bool parse_printf_directive(const std::string & buf,
204 std::string::size_type *pos_p,
205 format::format_item *fpar, std::ios & os,
206 unsigned char exceptions) {
207 std::string::size_type & i1 = *pos_p, i0;
208 fpar->argN = format::format_item::argN_no_posit; // if no positional-directive
210 bool in_brackets=false;
211 if(buf[i1]==os.widen('|')) {
212 in_brackets=true;
213 if( ++i1 >= buf.size() ) {
214 if(exceptions & format::bad_format_string_bit)
215 throw bad_format_string();
217 return false;
221 // the flag '0' would be picked as a digit for argument order, but here it's a flag :
222 if(buf[i1]==os.widen('0'))
223 goto parse_flags;
225 // handle argument order (%2$d) or possibly width specification: %2d
226 i0 = i1; // save position before digits
227 while( i1 < buf.size() && isdigit(buf[i1]) )
228 ++i1;
230 if( i1 != i0 ) {
231 if( i1 >= buf.size() ) {
232 if( exceptions & format::bad_format_string_bit )
233 throw bad_format_string();
235 return false;
238 int n=str2int(buf,i0, os, int(0) );
239 // %N% case : this is already the end of the directive
241 if( buf[i1] == os.widen('%') ) {
242 fpar->argN = n-1;
243 ++i1;
245 if( in_brackets && (exceptions & format::bad_format_string_bit) )
246 throw bad_format_string();
248 // but don't return. maybe "%" was used in lieu of '$', so we go on.
249 else return true;
253 if ( buf[i1]==os.widen('$') ) {
254 fpar->argN = n-1;
255 ++i1;
256 } else {
257 // non-positionnal directive
258 fpar->ref_state.width = n;
259 fpar->argN = format::format_item::argN_no_posit;
260 goto parse_precision;
264 parse_flags:
265 // handle flags
267 while ( i1 <buf.size() ) {
268 // as long as char is one of + - = # 0 l h or ' '
269 // misc switches
271 switch( os.narrow(buf[i1], 0) ) {
272 case '\'' : break; // no effect yet. (painful to implement)
273 case 'l':
274 case 'h': // short/long modifier : for printf-comaptibility (no action needed)
275 break;
277 case '-':
278 fpar->ref_state.flags |= std::ios_base::left;
279 break;
281 case '=':
282 fpar->pad_scheme |= format::format_item::centered;
283 break;
285 case ' ':
286 fpar->pad_scheme |= format::format_item::spacepad;
287 break;
289 case '+':
290 fpar->ref_state.flags |= std::ios_base::showpos;
291 break;
293 case '0':
294 fpar->pad_scheme |= format::format_item::zeropad;
295 // need to know alignment before really setting flags,
296 // so just add 'zeropad' flag for now, it will be processed later.
297 break;
299 case '#':
300 fpar->ref_state.flags |= std::ios_base::showpoint | std::ios_base::showbase;
301 break;
303 default:
304 goto parse_width;
307 ++i1;
308 } // loop on flag.
310 if( i1>=buf.size()) {
311 if( exceptions & format::bad_format_string_bit )
312 throw bad_format_string();
314 return true;
317 parse_width:
318 // handle width spec
319 skip_asterisk(buf, &i1, os); // skips 'asterisk fields' : *, or *N$
320 i0 = i1; // save position before digits
321 while (i1<buf.size() && isdigit(buf[i1]))
322 i1++;
324 if( i1!=i0 ) {
325 fpar->ref_state.width = str2int( buf,i0, os, std::streamsize(0) );
328 parse_precision:
329 if( i1>=buf.size()) {
330 if( exceptions & format::bad_format_string_bit )
331 throw bad_format_string();
332 return true;
335 // handle precision spec
336 if (buf[i1]==os.widen('.')) {
337 ++i1;
338 skip_asterisk(buf, &i1, os);
339 i0 = i1; // save position before digits
340 while (i1<buf.size() && isdigit(buf[i1]))
341 ++i1;
343 if(i1==i0)
344 fpar->ref_state.precision = 0;
345 else
346 fpar->ref_state.precision = str2int(buf,i0, os, std::streamsize(0) );
349 // handle formatting-type flags :
351 while( i1<buf.size() &&
352 ( buf[i1]==os.widen('l') || buf[i1]==os.widen('L') || buf[i1]==os.widen('h')) )
353 ++i1;
355 if( i1>=buf.size()) {
356 if( exceptions & format::bad_format_string_bit )
357 throw bad_format_string();
359 return true;
362 if( in_brackets && buf[i1]==os.widen('|') ) {
363 ++i1;
364 return true;
367 switch (os.narrow(buf[i1], 0) ) {
368 case 'X':
369 fpar->ref_state.flags |= std::ios_base::uppercase;
371 case 'p': // pointer => set hex.
372 case 'x':
373 fpar->ref_state.flags &= ~std::ios_base::basefield;
374 fpar->ref_state.flags |= std::ios_base::hex;
375 break;
377 case 'o':
378 fpar->ref_state.flags &= ~std::ios_base::basefield;
379 fpar->ref_state.flags |= std::ios_base::oct;
380 break;
382 case 'E':
383 fpar->ref_state.flags |= std::ios_base::uppercase;
384 case 'e':
385 fpar->ref_state.flags &= ~std::ios_base::floatfield;
386 fpar->ref_state.flags |= std::ios_base::scientific;
387 fpar->ref_state.flags &= ~std::ios_base::basefield;
388 fpar->ref_state.flags |= std::ios_base::dec;
389 break;
391 case 'f':
392 fpar->ref_state.flags &= ~std::ios_base::floatfield;
393 fpar->ref_state.flags |= std::ios_base::fixed;
394 case 'u':
395 case 'd':
396 case 'i':
397 fpar->ref_state.flags &= ~std::ios_base::basefield;
398 fpar->ref_state.flags |= std::ios_base::dec;
399 break;
401 case 'T':
402 ++i1;
403 if( i1 >= buf.size())
404 if( exceptions & format::bad_format_string_bit )
405 throw bad_format_string();
406 else
407 fpar->ref_state.fill = buf[i1];
409 fpar->pad_scheme |= format::format_item::tabulation;
410 fpar->argN = format::format_item::argN_tabulation;
411 break;
413 case 't':
414 fpar->ref_state.fill = os.widen(' ');
415 fpar->pad_scheme |= format::format_item::tabulation;
416 fpar->argN = format::format_item::argN_tabulation;
417 break;
419 case 'G':
420 fpar->ref_state.flags |= std::ios_base::uppercase;
421 break;
423 case 'g': // 'g' conversion is default for floats.
424 fpar->ref_state.flags &= ~std::ios_base::basefield;
425 fpar->ref_state.flags |= std::ios_base::dec;
426 // CLEAR all floatield flags, so stream will CHOOSE
427 fpar->ref_state.flags &= ~std::ios_base::floatfield;
428 break;
430 case 'C':
431 case 'c':
432 fpar->truncate = 1;
433 break;
435 case 'S':
436 case 's':
437 fpar->truncate = fpar->ref_state.precision;
438 fpar->ref_state.precision = -1;
439 break;
441 case 'n' :
442 fpar->argN = format::format_item::argN_ignored;
443 break;
445 default:
446 if( exceptions & format::bad_format_string_bit )
447 throw bad_format_string();
450 ++i1;
452 if( in_brackets ) {
453 if( i1<buf.size() && buf[i1]==os.widen('|') ) {
454 ++i1;
455 return true;
456 } else if( exceptions & format::bad_format_string_bit )
457 throw bad_format_string();
460 return true;
463 // parse the format-string
464 void format::parse(const std::string & buf) {
465 using namespace std;
466 const char arg_mark = oss.widen('%');
467 bool ordered_args=true;
468 int max_argN=-1;
469 string::size_type i1=0;
470 int num_items=0;
472 // A: find upper_bound on num_items and allocates arrays
473 i1=0;
474 while( (i1=buf.find(arg_mark,i1)) != string::npos ) {
475 if( i1+1 >= buf.size() ) {
476 if( exceptions() & bad_format_string_bit)
477 throw bad_format_string(); // must not end in "bla bla %"
479 else break; // stop there, ignore last '%'
482 if(buf[i1+1] == buf[i1] ) { i1+=2; continue; } // escaped "%%" / "##"
483 ++i1;
485 // in case of %N% directives, dont count it double (wastes allocations..) :
486 while(i1 < buf.size() && isdigit(buf[i1])) ++i1;
487 if( i1 < buf.size() && buf[i1] == arg_mark ) ++ i1;
489 ++num_items;
493 items.assign( num_items, format_item() );
495 // B: Now the real parsing of the format string :
496 num_items=0;
497 i1 = 0;
498 string::size_type i0 = i1;
499 bool special_things=false;
500 int cur_it=0;
501 while( (i1=buf.find(arg_mark,i1)) != string::npos ) {
502 string & piece = (cur_it==0) ? prefix : items[cur_it-1].appendix;
504 // escaped mark, '%%'
505 if( buf[i1+1] == buf[i1] ) {
506 piece += buf.substr(i0, i1-i0) + buf[i1];
507 i1+=2; i0=i1;
508 continue;
511 if(i1!=i0) piece += buf.substr(i0, i1-i0);
512 ++i1;
514 bool parse_ok;
515 parse_ok = parse_printf_directive(buf, &i1, &items[cur_it], oss, exceptions());
516 if( ! parse_ok ) continue; // the directive will be printed verbatim
518 i0=i1;
519 items[cur_it].compute_states(); // process complex options, like zeropad, into stream params.
521 int argN=items[cur_it].argN;
523 if( argN == format_item::argN_ignored )
524 continue;
526 if(argN ==format_item::argN_no_posit)
527 ordered_args=false;
528 else if(argN == format_item::argN_tabulation)
529 special_things=true;
530 else if(argN > max_argN)
531 max_argN = argN;
533 ++num_items;
534 ++cur_it;
535 } // loop on %'s
537 // store the final piece of string
538 string & piece = (cur_it==0) ? prefix : items[cur_it-1].appendix;
539 piece += buf.substr(i0);
541 if( !ordered_args) {
542 // dont mix positional with non-positionnal directives
543 if(max_argN >= 0 ) {
544 if( exceptions() & bad_format_string_bit )
545 throw bad_format_string();
546 // else do nothing. => positional arguments are processed as non-positional
549 // set things like it would have been with positional directives :
550 int non_ordered_items = 0;
551 for(int i=0; i< num_items; ++i)
552 if(items[i].argN == format_item::argN_no_posit) {
553 items[i].argN = non_ordered_items;
554 ++non_ordered_items;
556 max_argN = non_ordered_items-1;
559 // C: set some member data :
560 items.resize(num_items);
562 if(special_things)
563 style |= special_needs;
565 num_args = max_argN + 1;
567 if(ordered_args)
568 style |= ordered;
569 else
570 style &= ~ordered;
573 // effect: "return os << str(f);" but we can try to do it faster
574 std::ostream & operator <<(std::ostream & os, const format & f) {
575 if( f.items.size() == 0 ) {
576 os << f.prefix;
577 } else {
578 if( f.cur_arg < f.num_args )
579 if( f.exceptions() & format::too_few_args_bit )
580 throw too_few_args(); // not enough variables have been supplied !
582 if( f.style & format::special_needs) {
583 os << f.str();
584 } else {
585 // else we dont have to count chars output, so we dump directly to os :
586 os << f.prefix;
587 for(unsigned long i=0; i<f.items.size(); ++i) {
589 const format::format_item & item = f.items[i];
590 os << item.res;
591 os << item.appendix;
596 f.dumped=true;
597 return os;