2 * avr-sim: An atmel AVR simulator
3 * Copyright (C) 2008 Tom Haber
5 * This program is free software: you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation, either version 3 of the License, or
8 * (at your option) any later version.
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
15 * You should have received a copy of the GNU General Public License
16 * along with this program. If not, see <http://www.gnu.org/licenses/>.
24 void empty_buf(std::ostringstream
& os
) {
25 static const std::string emptyStr
;
29 // applies centered / left / right padding to the string s.
30 // Effects : string s is padded.
31 void do_pad(std::string
& s
, std::streamsize w
, const char c
,
32 std::ios_base::fmtflags f
, bool center
) {
34 std::streamsize n
=w
-s
.size();
39 s
.reserve(w
); // allocate once for the 2 inserts
40 const std::streamsize n1
= n
/2, n0
= n
- n1
;
41 s
.insert(s
.begin(), n0
, c
);
44 if(f
& std::ios_base::left
)
47 s
.insert(s
.begin(), n
, c
);
51 format::format(const char *str
)
52 : style(0), cur_arg(0), num_args(0), dumped(false),
53 items(), oss(), exceptions_flag(all_error_bits
) {
55 state0
.set_by_stream(oss
);
59 str
= emptyStr
.c_str();
64 format::format(const std::string
& s
)
65 : style(0), cur_arg(0), num_args(0), dumped(false),
66 items(), oss(), exceptions_flag(all_error_bits
) {
68 state0
.set_by_stream(oss
);
72 format::format(const format
& x
)
73 : style(x
.style
), cur_arg(x
.cur_arg
), num_args(x
.num_args
), dumped(false),
74 items(x
.items
), prefix(x
.prefix
), bound(x
.bound
),
75 oss(), // <- we obviously can't copy x.oss
76 state0(x
.state0
), exceptions_flag(x
.exceptions_flag
){
81 format
& format::operator =(const format
& x
) {
88 // plus all the other (trivial) assignments :
89 exceptions_flag
= x
.exceptions_flag
;
100 // empty the string buffers (except bound arguments, see clear_binds() )
101 // and make the format object ready for formatting a new set of arguments
102 format
& format::clear() {
103 for(unsigned long i
=0; i
<items
.size(); ++i
) {
104 items
[i
].state
= items
[i
].ref_state
;
106 // clear converted strings only if the corresponding argument is not bound :
107 if( bound
.size()==0 || !bound
[ items
[i
].argN
] ) items
[i
].res
.resize(0);
110 cur_arg
=0; dumped
=false;
112 // maybe first arg is bound:
113 if(bound
.size() != 0)
114 while( (cur_arg
< num_args
) && bound
[cur_arg
] ) ++cur_arg
;
119 // cancel all bindings, and clear()
120 format
& format::clear_binds() {
127 // cancel the binding of ONE argument, and clear()
128 format
& format::clear_bind(int argN
) {
129 if(argN
<1 || argN
> num_args
|| bound
.size()==0 || !bound
[argN
-1] ) {
130 if( exceptions() & out_of_range_bit
)
131 throw out_of_range(); // arg not in range.
142 std::string
format::str() const {
145 if( items
.size()==0 )
148 if( cur_arg
< num_args
)
149 if( exceptions() & too_few_args_bit
)
150 throw too_few_args(); // not enough variables have been supplied !
152 unsigned long sz
= prefix
.size();
154 for(i
=0; i
< items
.size(); ++i
)
155 sz
+= items
[i
].res
.size() + items
[i
].appendix
.size();
161 for(i
=0; i
< items
.size(); ++i
) {
162 const format_item
& item
= items
[i
];
164 if( item
.argN
== format_item::argN_tabulation
) {
165 std::streamsize n
= item
.state
.width
- res
.size();
168 res
.append( n
, item
.state
.fill
);
171 res
+= item
.appendix
;
177 // Input : char string, with starting index
178 // a basic_ios& merely to call its widen/narrow member function in the desired locale.
179 // Effects : reads s[start:] and converts digits into an integral n, of type Res
182 inline Res
str2int(const std::string
& s
,
183 std::string::size_type start
,
184 std::ios
& os
, const Res
= Res(0) ) {
187 while( start
<s
.size() && isdigit(s
[start
]) ) {
188 char cur_ch
= os
.narrow( s
[start
], 0);
190 n
+= cur_ch
- '0'; // 22.2.1.1.2 of the C++ standard
197 // skip printf's "asterisk-fields" directives in the format-string buf
198 // Input : char string, with starting index *pos_p
199 // a basic_ios& merely to call its widen/narrow member function in the desired locale.
200 // Effects : advance *pos_p by skipping printf's asterisk fields.
202 static void skip_asterisk(const std::string
& buf
,
203 std::string::size_type
*pos_p
,
206 if(*pos_p
>= buf
.size() ) return;
207 if(buf
[ *pos_p
]==os
.widen('*')) {
209 while (*pos_p
< buf
.size() && isdigit(buf
[*pos_p
])) ++(*pos_p
);
210 if(buf
[*pos_p
]==os
.widen('$')) ++(*pos_p
);
214 // Input : a 'printf-directive' in the format-string, starting at buf[ *pos_p ]
215 // a basic_ios& merely to call its widen/narrow member function in the desired locale.
216 // a bitset'exceptions' telling whether to throw exceptions on errors.
217 // Returns : true if parse somehow succeeded (possibly ignoring errors if exceptions disabled)
218 // false if it failed so bad that the directive should be printed verbatim
219 // Effects : - *pos_p is incremented so that buf[*pos_p] is the first char after the directive
220 // - *fpar is set with the parameters read in the directive
221 bool parse_printf_directive(const std::string
& buf
,
222 std::string::size_type
*pos_p
,
223 format::format_item
*fpar
, std::ios
& os
,
224 unsigned char exceptions
) {
225 std::string::size_type
& i1
= *pos_p
, i0
;
226 fpar
->argN
= format::format_item::argN_no_posit
; // if no positional-directive
228 bool in_brackets
=false;
229 if(buf
[i1
]==os
.widen('|')) {
231 if( ++i1
>= buf
.size() ) {
232 if(exceptions
& format::bad_format_string_bit
)
233 throw bad_format_string();
239 // the flag '0' would be picked as a digit for argument order, but here it's a flag :
240 if(buf
[i1
]==os
.widen('0'))
243 // handle argument order (%2$d) or possibly width specification: %2d
244 i0
= i1
; // save position before digits
245 while( i1
< buf
.size() && isdigit(buf
[i1
]) )
249 if( i1
>= buf
.size() ) {
250 if( exceptions
& format::bad_format_string_bit
)
251 throw bad_format_string();
256 int n
=str2int(buf
,i0
, os
, int(0) );
257 // %N% case : this is already the end of the directive
259 if( buf
[i1
] == os
.widen('%') ) {
263 if( in_brackets
&& (exceptions
& format::bad_format_string_bit
) )
264 throw bad_format_string();
266 // but don't return. maybe "%" was used in lieu of '$', so we go on.
271 if ( buf
[i1
]==os
.widen('$') ) {
275 // non-positionnal directive
276 fpar
->ref_state
.width
= n
;
277 fpar
->argN
= format::format_item::argN_no_posit
;
278 goto parse_precision
;
285 while ( i1
<buf
.size() ) {
286 // as long as char is one of + - = # 0 l h or ' '
289 switch( os
.narrow(buf
[i1
], 0) ) {
290 case '\'' : break; // no effect yet. (painful to implement)
292 case 'h': // short/long modifier : for printf-comaptibility (no action needed)
296 fpar
->ref_state
.flags
|= std::ios_base::left
;
300 fpar
->pad_scheme
|= format::format_item::centered
;
304 fpar
->pad_scheme
|= format::format_item::spacepad
;
308 fpar
->ref_state
.flags
|= std::ios_base::showpos
;
312 fpar
->pad_scheme
|= format::format_item::zeropad
;
313 // need to know alignment before really setting flags,
314 // so just add 'zeropad' flag for now, it will be processed later.
318 fpar
->ref_state
.flags
|= std::ios_base::showpoint
| std::ios_base::showbase
;
328 if( i1
>=buf
.size()) {
329 if( exceptions
& format::bad_format_string_bit
)
330 throw bad_format_string();
337 skip_asterisk(buf
, &i1
, os
); // skips 'asterisk fields' : *, or *N$
338 i0
= i1
; // save position before digits
339 while (i1
<buf
.size() && isdigit(buf
[i1
]))
343 fpar
->ref_state
.width
= str2int( buf
,i0
, os
, std::streamsize(0) );
347 if( i1
>=buf
.size()) {
348 if( exceptions
& format::bad_format_string_bit
)
349 throw bad_format_string();
353 // handle precision spec
354 if (buf
[i1
]==os
.widen('.')) {
356 skip_asterisk(buf
, &i1
, os
);
357 i0
= i1
; // save position before digits
358 while (i1
<buf
.size() && isdigit(buf
[i1
]))
362 fpar
->ref_state
.precision
= 0;
364 fpar
->ref_state
.precision
= str2int(buf
,i0
, os
, std::streamsize(0) );
367 // handle formatting-type flags :
369 while( i1
<buf
.size() &&
370 ( buf
[i1
]==os
.widen('l') || buf
[i1
]==os
.widen('L') || buf
[i1
]==os
.widen('h')) )
373 if( i1
>=buf
.size()) {
374 if( exceptions
& format::bad_format_string_bit
)
375 throw bad_format_string();
380 if( in_brackets
&& buf
[i1
]==os
.widen('|') ) {
385 switch (os
.narrow(buf
[i1
], 0) ) {
387 fpar
->ref_state
.flags
|= std::ios_base::uppercase
;
389 case 'p': // pointer => set hex.
391 fpar
->ref_state
.flags
&= ~std::ios_base::basefield
;
392 fpar
->ref_state
.flags
|= std::ios_base::hex
;
396 fpar
->ref_state
.flags
&= ~std::ios_base::basefield
;
397 fpar
->ref_state
.flags
|= std::ios_base::oct
;
401 fpar
->ref_state
.flags
|= std::ios_base::uppercase
;
403 fpar
->ref_state
.flags
&= ~std::ios_base::floatfield
;
404 fpar
->ref_state
.flags
|= std::ios_base::scientific
;
405 fpar
->ref_state
.flags
&= ~std::ios_base::basefield
;
406 fpar
->ref_state
.flags
|= std::ios_base::dec
;
410 fpar
->ref_state
.flags
&= ~std::ios_base::floatfield
;
411 fpar
->ref_state
.flags
|= std::ios_base::fixed
;
415 fpar
->ref_state
.flags
&= ~std::ios_base::basefield
;
416 fpar
->ref_state
.flags
|= std::ios_base::dec
;
421 if( i1
>= buf
.size()) {
422 if( exceptions
& format::bad_format_string_bit
)
423 throw bad_format_string();
425 fpar
->ref_state
.fill
= buf
[i1
];
428 fpar
->pad_scheme
|= format::format_item::tabulation
;
429 fpar
->argN
= format::format_item::argN_tabulation
;
433 fpar
->ref_state
.fill
= os
.widen(' ');
434 fpar
->pad_scheme
|= format::format_item::tabulation
;
435 fpar
->argN
= format::format_item::argN_tabulation
;
439 fpar
->ref_state
.flags
|= std::ios_base::uppercase
;
442 case 'g': // 'g' conversion is default for floats.
443 fpar
->ref_state
.flags
&= ~std::ios_base::basefield
;
444 fpar
->ref_state
.flags
|= std::ios_base::dec
;
445 // CLEAR all floatield flags, so stream will CHOOSE
446 fpar
->ref_state
.flags
&= ~std::ios_base::floatfield
;
456 fpar
->truncate
= fpar
->ref_state
.precision
;
457 fpar
->ref_state
.precision
= -1;
461 fpar
->argN
= format::format_item::argN_ignored
;
465 if( exceptions
& format::bad_format_string_bit
)
466 throw bad_format_string();
472 if( i1
<buf
.size() && buf
[i1
]==os
.widen('|') ) {
475 } else if( exceptions
& format::bad_format_string_bit
)
476 throw bad_format_string();
482 // parse the format-string
483 void format::parse(const std::string
& buf
) {
485 const char arg_mark
= oss
.widen('%');
486 bool ordered_args
=true;
488 string::size_type i1
=0;
491 // A: find upper_bound on num_items and allocates arrays
493 while( (i1
=buf
.find(arg_mark
,i1
)) != string::npos
) {
494 if( i1
+1 >= buf
.size() ) {
495 if( exceptions() & bad_format_string_bit
)
496 throw bad_format_string(); // must not end in "bla bla %"
498 else break; // stop there, ignore last '%'
501 if(buf
[i1
+1] == buf
[i1
] ) { i1
+=2; continue; } // escaped "%%" / "##"
504 // in case of %N% directives, dont count it double (wastes allocations..) :
505 while(i1
< buf
.size() && isdigit(buf
[i1
])) ++i1
;
506 if( i1
< buf
.size() && buf
[i1
] == arg_mark
) ++ i1
;
512 items
.assign( num_items
, format_item() );
514 // B: Now the real parsing of the format string :
517 string::size_type i0
= i1
;
518 bool special_things
=false;
520 while( (i1
=buf
.find(arg_mark
,i1
)) != string::npos
) {
521 string
& piece
= (cur_it
==0) ? prefix
: items
[cur_it
-1].appendix
;
523 // escaped mark, '%%'
524 if( buf
[i1
+1] == buf
[i1
] ) {
525 piece
+= buf
.substr(i0
, i1
-i0
) + buf
[i1
];
530 if(i1
!=i0
) piece
+= buf
.substr(i0
, i1
-i0
);
534 parse_ok
= parse_printf_directive(buf
, &i1
, &items
[cur_it
], oss
, exceptions());
535 if( ! parse_ok
) continue; // the directive will be printed verbatim
538 items
[cur_it
].compute_states(); // process complex options, like zeropad, into stream params.
540 int argN
=items
[cur_it
].argN
;
542 if( argN
== format_item::argN_ignored
)
545 if(argN
==format_item::argN_no_posit
)
547 else if(argN
== format_item::argN_tabulation
)
549 else if(argN
> max_argN
)
556 // store the final piece of string
557 string
& piece
= (cur_it
==0) ? prefix
: items
[cur_it
-1].appendix
;
558 piece
+= buf
.substr(i0
);
561 // dont mix positional with non-positionnal directives
563 if( exceptions() & bad_format_string_bit
)
564 throw bad_format_string();
565 // else do nothing. => positional arguments are processed as non-positional
568 // set things like it would have been with positional directives :
569 int non_ordered_items
= 0;
570 for(int i
=0; i
< num_items
; ++i
)
571 if(items
[i
].argN
== format_item::argN_no_posit
) {
572 items
[i
].argN
= non_ordered_items
;
575 max_argN
= non_ordered_items
-1;
578 // C: set some member data :
579 items
.resize(num_items
);
582 style
|= special_needs
;
584 num_args
= max_argN
+ 1;
592 // effect: "return os << str(f);" but we can try to do it faster
593 std::ostream
& operator <<(std::ostream
& os
, const format
& f
) {
594 if( f
.items
.size() == 0 ) {
597 if( f
.cur_arg
< f
.num_args
)
598 if( f
.exceptions() & format::too_few_args_bit
)
599 throw too_few_args(); // not enough variables have been supplied !
601 if( f
.style
& format::special_needs
) {
604 // else we dont have to count chars output, so we dump directly to os :
606 for(unsigned long i
=0; i
<f
.items
.size(); ++i
) {
608 const format::format_item
& item
= f
.items
[i
];