ALib C++ Framework
by
Library Version: 2605 R0
Documentation generated by doxygen
Loading...
Searching...
No Matches
formatterpythonstyle.cpp
1using namespace alib::strings;
2
3namespace alib::format {
4
5
7: FormatterStdImpl( A_CHAR( "FormatterPythonStyle" ) )
9 // set number format to python defaults
10 DefaultNumberFormat.Flags-= NumberFormatFlags::ForceDecimalPoint;
11 DefaultNumberFormat.Flags+= NumberFormatFlags::WriteExponentPlusSign;
12}
13
14
16 // create a clone
17 SPFormatter clone;
19
20 // create a clone of Next, in the case that next is derived from std base class
21 if( Next )
22 clone->Next= Next->Clone();
23
24 // copy my settings, that's it
25 clone->CloneSettings( *this );
26 return clone;
27}
28
30 // first invoke parent's setting...
32
33 // ...then make some "python like" adjustments
34 placeholderPS.Conversion = nullptr;
35 placeholderPS.ConversionPos = -1;
36
37 placeholder.NF.Flags -= NumberFormatFlags::ForceDecimalPoint;
38 placeholder.NF.ExponentSeparator = AlternativeNumberFormat.ExponentSeparator; // choose lower case as default
39 placeholder.NF.INFLiteral = AlternativeNumberFormat.INFLiteral; // choose lower case as default
40 placeholder.NF.NANLiteral = AlternativeNumberFormat.NANLiteral; // choose lower case as default
41 placeholder.NF.Flags += NumberFormatFlags::OmitTrailingFractionalZeros;
42
43 placeholderPS.Precision = -1;
44 placeholderPS.PrecisionPos = -1;
45 placeholderPS.DefaultPrecision = 6;
46}
47
48
50 integer idx= 0;
51 while( (idx= parser.IndexOf('{', idx )) >= 0
52 && parser.CharAt( idx + 1 ) == '{' )
53 {
54 // double ESC found (escaped {{)
55 idx+= + 2;
56 }
57 return idx;
58}
59
60
62 // read position
63 if( isdigit( parser.CharAtStart() ) ) {
64 int argNo;
65 parser.ConsumeDecDigits( argNo );
66 setArgument( argNo );
67 }
68
69 // read conversion
70 if( parser.CharAtStart() == '!' ) {
71 placeholderPS.ConversionPos = int( formatString.Length()
72 - parser.Length() - 1 );
73 integer endConversion= parser.IndexOfAny<lang::Inclusion::Include>( A_CHAR( ":}" ) );
74 if( endConversion < 0 )
76 formatString, placeholderPS.ConversionPos );
77
78 parser.ConsumeChars( endConversion, &placeholderPS.Conversion );
79 }
80
81 // read colon and format spec
82 if( parser.CharAtStart() == ':' ) {
83 parser.ConsumeChars(1);
84
85 // find end of format spec: allow "\}", "\{" and nested "{..}" in format string
86 Substring subParser= parser;
87 int depth= 0;
88 while( subParser.IsNotEmpty() ) {
89 if( subParser.CharAtStart() == '}' ) {
90 if(depth == 0 )
91 break;
92
93 --depth;
94 subParser.ConsumeChars<NC>(1);
95 continue;
96 }
97
98 if( subParser.CharAtStart() == '\\' ) { subParser.ConsumeChars(2); continue; }
99 if( subParser.CharAtStart() == '{' ) ++depth;
100 subParser.ConsumeChars<NC>(1);
101 }
102
103 if ( subParser.IsEmpty() )
106 formatString.Length() );
107
108 // extract format spec to separate substring
109 parser.ConsumeChars( parser.Length() - subParser.Length(), &(placeholder.FormatSpec) ) ;
110 }
111
112 // check for closing brace
113 if( parser.CharAtStart() != '}' )
116 formatString.Length() - parser.Length() );
117
118 parser.ConsumeChars(1);
119 return true;
120}
121
122
123
125 // parse alignment and fill character. This is a bit tricky to shorten the code.
126 // The alignment token <,>,^ or = can be on first or second position. If second, then the
127 // first was a fill character. Therefore , we loop from 0 to 1....
128 integer charsToConsume= 1;
129 placeholder.AlignmentSpecified= true;
130 Substring& formatSpec= placeholder.FormatSpec;
131
132 int cNo= 0;
133 do switch ( formatSpec.CharAt( cNo++ ) ) {
134 case '<': placeholder.ValueAlignment= lang::Alignment::Left; break;
135 case '>': placeholder.ValueAlignment= lang::Alignment::Right; break;
136 case '^': placeholder.ValueAlignment= lang::Alignment::Center; break;
137 case '=': placeholder.ValueAlignment= lang::Alignment::Right;
138 placeholder.SignPaddingMode= true; break;
139
140 default:
141 if(cNo == 1 ) {
142 charsToConsume= 2;
143 placeholder.FillChar= formatSpec.CharAtStart();
144 } else {
145 charsToConsume= 0;
146 placeholder.FillChar= ' ';
147 placeholder.AlignmentSpecified= false;
148 }
149 break;
150 }
151 while( cNo < 2 && charsToConsume != 1 );
152 formatSpec.ConsumeChars( charsToConsume );
153
154 // other things to parse
155 character actChar;
156 while( (actChar= formatSpec.CharAtStart()) != '\0' ) {
157 // width
158 if( isdigit( actChar ) ) {
159 // Python doc says:
160 // When no explicit alignment is given, preceding the width field by a zero ('0') character
161 // enables sign-aware zero-padding for numeric types.
162 // This is equivalent to a fill character of '0' with an alignment type of '='.
163 if ( actChar == '0' )
164 placeholder.SignPaddingMode= true;
165 formatSpec.ConsumeDecDigits( placeholder.Width );
166
167 continue; // not consume below
168 }
169
170 // precision. Python doc says:
171 // "The precision is a decimal number indicating how many digits should be displayed
172 // after the decimal point for a floating point value formatted with 'f' and 'F', or
173 // before and after the decimal point for a floating point value formatted with 'g' or 'G'.
174 // For non-number types the field indicates the maximum field size - in other words,
175 // how many characters will be used from the field content. The precision is not
176 // allowed for integral values."
177 else if( actChar == '.' ) {
178 placeholderPS.PrecisionPos= int( formatString.Length()
179 - parser.Length()
180 - formatSpec.Length() -1 );
181 formatSpec.ConsumeChars( 1 );
182 if ( ! formatSpec.ConsumeDecDigits( placeholderPS.Precision ) )
185 formatString.Length()
186 - parser.Length()
187 - formatSpec.Length() - 1 );
188 continue;
189 }
190
191
192 //------------------------------------------- Types ------------------------------------------
193 else if (String(A_CHAR( "sdcboxXeEfFngGhHB%" )).IndexOf( actChar ) >= 0 ) {
194 if ( placeholder.TypeCode != '\0' )
196 actChar, placeholder.TypeCode,
198 formatString.Length() - parser.Length()
199 - formatSpec.Length() - 1
200 #if ALIB_DEBUG
201 , A_CHAR("\nDbgInfo: Native argument type: <{}>")
202 , placeholder.Arg->TypeID()
203 #endif
204 );
205
206 placeholder.TypeCode= actChar;
207 placeholder.TypeCodePosition= int( formatString.Length()
208 - parser.Length()
209 - formatSpec.Length() - 1 );
210
211 if( String(A_CHAR( "EGF" )).IndexOf( actChar ) >= 0 ) {
212 placeholder.NF.ExponentSeparator= DefaultNumberFormat.ExponentSeparator;
213 placeholder.NF.INFLiteral= DefaultNumberFormat.INFLiteral;
214 placeholder.NF.NANLiteral= DefaultNumberFormat.NANLiteral;
215 }
216
217 switch( actChar ) {
218 case 's': placeholder.Type= PHTypes::String; break;
219 case 'd': placeholder.Type= PHTypes::IntBase10; break;
220 case 'c': placeholder.Type= PHTypes::Character; break;
221 case 'b': placeholder.Type= PHTypes::IntBinary; break;
222 case 'o': placeholder.Type= PHTypes::IntOctal; break;
223
224 case 'X': placeholder.NF.Flags-= NumberFormatFlags::HexLowerCase; ALIB_FALLTHROUGH
225 case 'x': placeholder.Type= PHTypes::IntHex; break;
226
227 case 'H': placeholder.NF.Flags-= NumberFormatFlags::HexLowerCase; ALIB_FALLTHROUGH
228 case 'h': placeholder.Type= PHTypes::HashCode; break;
229 case 'B': placeholder.Type= PHTypes::Bool; break;
230
231 // float types
232 case 'E':
233 case 'e': placeholder.Type= PHTypes::Float;
234 placeholder.NF.Flags+= NumberFormatFlags::ForceScientific; break;
235
236 case '%': placeholder.IsPercentage= true; ALIB_FALLTHROUGH
237 case 'f':
238 case 'F': placeholder.NF.Flags-= NumberFormatFlags::OmitTrailingFractionalZeros;
239 placeholder.Type= PHTypes::Float; break;
240
241 case 'n': placeholder.NF.DecimalPointChar= AlternativeNumberFormat.DecimalPointChar;
242 placeholder.NF.ThousandsGroupChar= AlternativeNumberFormat.ThousandsGroupChar;
244 case 'G': ALIB_FALLTHROUGH
245 case 'g': placeholder.Type= PHTypes::Float;
246 placeholderPS.DefaultPrecision= -1;
247 break;
248
249 default: ALIB_ERROR( "FORMAT", "Unhandled character in choices string above" )
250 return false;
251
252 }//switch
253 }
254
255
256 // others
257 else switch( actChar ) {
258 // sign
259 case '+': placeholder.NF.PlusSign= '+' ; break;
260 case '-': placeholder.NF.PlusSign= '\0'; break;
261 case ' ': placeholder.NF.PlusSign= ' ' ; break;
262
263 // alternate version ('#"')"
264 case '#': placeholder.WriteBinOctHexPrefix= true;
265 placeholder.NF.Flags+= NumberFormatFlags::ForceDecimalPoint;
266 placeholder.NF.Flags-= NumberFormatFlags::OmitTrailingFractionalZeros;
267 break;
268
269 // Enable grouping
270 case ',': placeholder.NF.Flags+= NumberFormatFlags::WriteGroupChars;
271 break;
272
273
274 default:
276 actChar,
278 formatString.Length()
279 - parser.Length()
280 - formatSpec.Length() - 1
281 #if ALIB_DEBUG
282 , A_CHAR("\nDbgInfo: Native argument type: <{}>")
283 , placeholder.Arg->TypeID()
284 #endif
285 );
286 }
287
288 formatSpec.ConsumeChars(1);
289 }
290
291 return true;
292}
293
294
296 if( length == 0)
297 return;
298
299 targetString->EnsureRemainingCapacity( length );
300 auto* src = parser.Buffer();
301 auto* dest= targetString->VBuffer() + targetString->Length();
302 parser.ConsumeChars( length );
303
304 character c1;
305 character c2= *src;
306 while( length > 1 ) {
307 c1= c2;
308 c2= *++src;
309
310 if( c1 == '\n' ) // reset auto-sizes when act ual character is '\n'
311 Sizes->Restart();
312
313 else if( ( c1 == '{' && c2 =='{')
314 || ( c1 == '}' && c2 =='}')
315 || c1 == '\\' )
316 {
317 if( c1 == '\\' )
318 switch(c2) {
319 case 'r': c1= '\r' ; break;
320 case 'n': c1= '\n' ;
321 // reset auto-sizes with \n
322 targetStringStartLength= dest - targetString->VBuffer() + 1;
323 Sizes->Restart();
324 break;
325 case 't': c1= '\t' ; break;
326 case 'a': c1= '\a' ; break;
327 case 'b': c1= '\b' ; break;
328 case 'v': c1= '\v' ; break;
329 case 'f': c1= '\f' ; break;
330 case '"': c1= '"' ; break;
331 case '\\': c1= '\\' ; break;
332 default: c1= '?' ; break;
333 }
334
335 c2= *++src;
336 --length;
337 }
338
339 *dest++= c1;
340 --length;
341 }
342
343 // copy the last character and adjust target string length:
344 // Note: length usually is 1. Only if last character is an escape sequence, it is 0.
345 if( length == 1) {
346 *dest= *src;
347 // reset auto-sizes when last character is '\n'
348 if( *dest == '\n' )
349 Sizes->Restart();
350 }
351 targetString->SetLength( dest - targetString->VBuffer() + length);
352}
353
354
356 bool isPreProcess= startIdx < 0;
357 bool isPostProcess= startIdx>= 0 && target == nullptr;
358 Substring conversion= placeholderPS.Conversion;
359 ++placeholderPS.ConversionPos;
360
361 while( conversion.IsNotEmpty() ) {
362 if( !conversion.ConsumeChar('!') )
364 formatString, placeholderPS.ConversionPos
365 + placeholderPS.Conversion.Length()
366 - conversion.Length()
367 #if ALIB_DEBUG
368 , A_CHAR("\nDbgInfo: Native argument type: <{}>")
369 , placeholder.Arg->TypeID()
370 #endif
371 );
372
373 if( conversion.ConsumePartOf( A_CHAR( "Xtinguish" ) ) > 0 ) { return false; }
374 if( conversion.ConsumePartOf( A_CHAR( "Upper" ) ) > 0 ) { if (isPostProcess) targetString->ToUpper( startIdx ); }
375 else if( conversion.ConsumePartOf( A_CHAR( "Lower" ) ) > 0 ) { if (isPostProcess) targetString->ToLower( startIdx ); }
376 else if( conversion.ConsumePartOf( A_CHAR( "str" ) ) > 0
377 ||conversion.ConsumePartOf( A_CHAR( "Quote" ) ) > 0 )
378 {
379 String8 open = A_CHAR( '"' );
380 String8 close= A_CHAR( '"' );
381 if (conversion.IsNotEmpty() && conversion.CharAtStart() != A_CHAR('!')) {
382 open .Reset( conversion.ConsumeChar() );
383 close.Reset( conversion.IsNotEmpty() && conversion.CharAtStart() != A_CHAR('!')
384 ? conversion.ConsumeChar()
385 : open.CharAtStart() );
386 }
387
388 if (isPostProcess)
389 targetString->InsertAt<NC>( open, startIdx ) .Append<NC>( close );
390 }
391
392 else if( conversion.ConsumePartOf( A_CHAR( "Fill" ) ) > 0 ) {
394 placeholder.FillChar= conversion.ConsumeChar<lang::Case::Ignore>('C' ) && conversion.Length() > 0
395 ? conversion.ConsumeChar<NC>()
396 : ' ';
397
398 }
399
400 else if( conversion.ConsumePartOf( A_CHAR( "Tab" ) ) > 0 ) {
401 character tabChar= conversion.ConsumeChar<lang::Case::Ignore>('C') && conversion.Length() > 0
402 ? conversion.ConsumeChar<NC>()
403 : ' ';
404 int tabSize;
405 if( !conversion.ConsumeDecDigits<int>( tabSize ) )
406 tabSize= 8;
407
408 if( isPreProcess )
409 targetString->_<NC>( strings::TTab<character>( tabSize, -1, 1, tabChar ) );
410
411 }
412
413 else if( conversion.ConsumePartOf( A_CHAR( "ATab" ), 2 ) > 0 ) {
414
415 if( conversion.ConsumePartOf( A_CHAR( "Reset") ) > 0 ) {
416 if( isPreProcess )
417 Sizes->Reset();
418 } else {
419 character tabChar= conversion.ConsumeChar<lang::Case::Ignore>('C' ) && conversion.Length() > 0
420 ? conversion.ConsumeChar<NC>()
421 : ' ';
422
423 int growth;
424 if( !conversion.ConsumeDecDigits<int>( growth ) )
425 growth= 3;
426
427 if( isPreProcess ) {
428 integer actPos= targetString->Length() - targetStringStartLength;
429 integer tabStop= Sizes->Next( AutoSizes::Types::Tabstop, actPos , growth );
430 targetString->InsertChars<NC>( tabChar, tabStop - actPos );
431 } } }
432
433 else if( conversion.ConsumePartOf( A_CHAR( "AWidth" ), 2 ) > 0 ) {
434 if( conversion.ConsumePartOf( A_CHAR( "Reset" ) ) > 0 ) {
435 if( isPreProcess )
436 Sizes->Reset();
437 } else {
438 int extraPadding;
439 conversion.ConsumeDecDigits<int>( extraPadding );
440
441 if( isPreProcess )
442 placeholder.Width= int( Sizes->Actual( AutoSizes::Types::Field, 0, extraPadding ) );
443 else if ( isPostProcess )
444 Sizes->Next( AutoSizes::Types::Field, targetString->Length() - startIdx, extraPadding );
445 } }
446
447 else if( conversion.ConsumePartOf( A_CHAR( "Esc" ) ) > 0
448 || conversion.ConsumePartOf( A_CHAR( "A" ) ) > 0 )
449 {
451 conversion.ConsumeChar('<');
452 if(conversion.ConsumeChar('>') )
453 toESC= lang::Switch::Off;
454
455 if( isPostProcess )
456 targetString->_<NC>( typename strings::TEscape<character>( toESC, startIdx ) );
457 }
458
459 else if( conversion.ConsumePartOf( A_CHAR( "Replace" ), 2 ) > 0 ) {
460 String search = conversion.ConsumeField( '<', '>' );
461 String replace= conversion.ConsumeField( '<', '>' );
462 if( search.IsNull() || replace.IsNull() )
464 formatString, placeholderPS.ConversionPos
465 + placeholderPS.Conversion.Length()
466 - conversion.Length()
467 #if ALIB_DEBUG
468 , A_CHAR("\nDbgInfo: Native argument type: <{}>")
469 , placeholder.Arg->TypeID()
470 #endif
471 );
472
473 if( target != nullptr ) {
474 // special case: fill empty fields
475 if( search.IsEmpty() && target->Length() - startIdx == 0 ) {
476 *target << replace;
477 }
478 else
479 target->SearchAndReplace( search, replace, startIdx );
480 } }
481
482 // error (not recognized)
483 else
485 conversion,
486 formatString, placeholderPS.ConversionPos
487 + placeholderPS.Conversion.Length()
488 - conversion.Length()
489 #if ALIB_DEBUG
490 , A_CHAR("\nDbgInfo: Native argument type: <{}>")
491 , placeholder.Arg->TypeID()
492 #endif
493 );
494 }
495
496 return true;
497}
498
499
501 bool wasFloat= placeholder.Type == PHTypes::Float;
502 if( wasFloat ) {
503 if ( placeholderPS.Precision >= 0 )
504 placeholder.NF.FractionalPartWidth= int8_t( placeholderPS.Precision );
505 else if( placeholder.NF.FractionalPartWidth < 0 )
506 placeholder.NF.FractionalPartWidth= int8_t( placeholderPS.DefaultPrecision);
507 }
508
510
511 if( !wasFloat && placeholder.Type == PHTypes::Float ) {
512 if ( placeholderPS.Precision >= 0 )
513 placeholder.NF.FractionalPartWidth= int8_t( placeholderPS.Precision );
514 }
515
516 if( placeholder.Type == PHTypes::String
517 || placeholder.Type == PHTypes::Bool )
518 placeholder.CutContent= placeholderPS.Precision;
519 else if ( placeholderPS.Precision >= 0
520 && placeholder.Type != PHTypes::Float )
522 formatString, placeholderPS.PrecisionPos
523 #if ALIB_DEBUG
524 , A_CHAR("\nDbgInfo: Native argument type: <{}>")
525 , placeholder.Arg->TypeID()
526 #endif
527 );
528 return result;
529
530}
531
532
533} // namespace [alib::format]
#define ALIB_FALLTHROUGH
#define ALIB_CALLER_NULLED
#define A_CHAR(STR)
#define ALIB_ERROR(domain,...)
#define ALIB_DEBUG
void InsertDerived(TArgs &&... args)
virtual integer findPlaceholder() override
virtual void writeStringPortion(integer length) override
virtual bool preAndPostProcess(integer startIdx, AString *target) override
virtual bool checkStdFieldAgainstArgument() override
AutoSizes SizesDefaultInstance
The default instance of field #"Sizes". This might be replaced with an external object.
PlaceholderAttributesPS placeholderPS
The extended placeholder attributes.
virtual SPFormatter Clone() override
AString * targetString
The target string as provided with method #"Formatter::Format".
FormatterStdImpl(const String &formatterClassName)
Substring parser
The current (remaining) format string.
integer targetStringStartLength
The length of the target string before adding the formatted contents.
@ Float
Outputs a number in floating point format.
@ IntBinary
Outputs a given number in base 2.
@ IntBase10
Outputs a given number in base 10. The default.
@ IntHex
Outputs a given number in base 16.
@ IntOctal
Outputs a given number in base 8.
String formatString
The format string as provided with method #"Formatter::Format".
NumberFormat DefaultNumberFormat
virtual void CloneSettings(Formatter &reference)
Definition formatter.cpp:76
NumberFormat AlternativeNumberFormat
SharedPtr< Formatter > Next
integer SearchAndReplace(TChar needle, TChar replacement, integer startIdx=0, integer endIdx=strings::MAX_LEN)
constexpr integer Length() const
Definition string.hpp:300
constexpr bool IsEmpty() const
Definition string.hpp:349
TChar CharAtStart() const
Definition string.hpp:417
TChar CharAt(integer idx) const
Definition string.hpp:399
constexpr bool IsNotEmpty() const
Definition string.hpp:353
constexpr bool IsNull() const
Definition string.hpp:334
TString< TChar > ConsumeField(TChar startChar, TChar endChar)
integer ConsumePartOf(const TString< TChar > &consumable, int minChars=1)
bool ConsumeDecDigits(std::integral auto &result)
integer ConsumeChars(integer regionLength, TSubstring *target=nullptr)
@ Field
denotes a field width entry.
Definition autosizes.hpp:56
@ Tabstop
denotes a tab stop entry.
Definition autosizes.hpp:55
@ Center
Chooses centered alignment.
@ Right
Chooses right alignment.
@ Left
Chooses left alignment.
Switch
Denotes if sth. is switched on or off.
@ On
Switch it on, switched on, etc.
@ Off
Switch it off, switched off, etc.
@ Include
Chooses inclusion.
containers::SharedPtr< format::Formatter > SPFormatter
Definition formatter.hpp:41
LocalString< 8 > String8
Type alias name for #"TLocalString;TLocalString<character,8>".
lang::integer integer
Type alias in namespace #"%alib".
Definition integers.hpp:149
strings::TString< character > String
Type alias in namespace #"%alib".
Definition string.hpp:2165
strings::TSubstring< character > Substring
Type alias in namespace #"%alib".
exceptions::Exception Exception
Type alias in namespace #"%alib".
strings::TAString< character, lang::HeapAllocator > AString
Type alias in namespace #"%alib".
characters::character character
Type alias in namespace #"%alib".