ALib C++ Library
Library Version: 2412 R0
Documentation generated by doxygen
Loading...
Searching...
No Matches
formatterpythonstyle.cpp
1// #################################################################################################
2// ALib C++ Library
3//
4// Copyright 2013-2024 A-Worx GmbH, Germany
5// Published under 'Boost Software License' (a free software license, see LICENSE.txt)
6// #################################################################################################
8
9#if !DOXYGEN
12#endif // !DOXYGEN
13
14
15// For code compatibility with ALox Java/C++
16// We have to use underscore as the start of the name and for this have to disable a compiler
17// warning. But this is a local code (cpp file) anyhow.
18#if defined(__clang__)
19 #pragma clang diagnostic push
20 #pragma clang diagnostic ignored "-Wreserved-id-macro"
21#endif
22
23
24#if defined(__clang__)
25 #pragma clang diagnostic pop
26#endif
27
28using namespace alib::strings;
29
30namespace alib::lang::format {
31
32
34: FormatterStdImpl( A_CHAR( "FormatterPythonStyle" ) )
35, Sizes(&SizesDefaultInstance)
36{
37 // set number format to python defaults
38 DefaultNumberFormat.Flags-= NumberFormatFlags::ForceDecimalPoint;
39 DefaultNumberFormat.Flags+= NumberFormatFlags::WriteExponentPlusSign;
40}
41
42
44{
45 // create a clone
46 SPFormatter clone;
48
49 // create a clone of #Next, in the case that next is derived from std base class
50 if( Next )
51 clone->Next= Next->Clone();
52
53 // copy my settings, that's it
54 clone->CloneSettings( *this );
55 return clone;
56}
57
59{
60 // first invoke parent's setting...
62
63 // ...then make some "python like" adjustments
64 placeholderPS.Conversion = nullptr;
66
67 placeholder.NF.Flags -= NumberFormatFlags::ForceDecimalPoint;
69 placeholder.NF.INFLiteral = AlternativeNumberFormat.INFLiteral; // choose lower case as default
70 placeholder.NF.NANLiteral = AlternativeNumberFormat.NANLiteral; // choose lower case as default
71 placeholder.NF.Flags += NumberFormatFlags::OmitTrailingFractionalZeros;
72
76}
77
78
80{
81 integer idx= 0;
82 while( (idx= parser.IndexOf('{', idx )) >= 0
83 && parser.CharAt( idx + 1 ) == '{' )
84 {
85 // double ESC found (escaped {{)
86 idx+= + 2;
87 }
88 return idx;
89}
90
91
93{
94 // read position
95 if( isdigit( parser.CharAtStart() ) )
96 {
97 int argNo;
98 parser.ConsumeDecDigits( argNo );
99 setArgument( argNo );
100 }
101
102 // read conversion
103 if( parser.CharAtStart() == '!' )
104 {
105 placeholderPS.ConversionPos = static_cast<int>( formatString.Length()
106 - parser.Length() - 1 );
107 integer endConversion= parser.IndexOfAny<lang::Inclusion::Include>( A_CHAR( ":}" ) );
108 if( endConversion < 0 )
111
112 parser.ConsumeChars( endConversion, &placeholderPS.Conversion );
113 }
114
115 // read colon and format spec
116 if( parser.CharAtStart() == ':' )
117 {
119
120 // find end of format spec: allow "\}", "\{" and nested "{..}" in format string
121 Substring subParser= parser;
122 int depth= 0;
123 while( subParser.IsNotEmpty() )
124 {
125 if( subParser.CharAtStart() == '}' )
126 {
127 if(depth == 0 )
128 break;
129
130 --depth;
131 subParser.ConsumeChars<NC>(1);
132 continue;
133 }
134
135 if( subParser.CharAtStart() == '\\' ) { subParser.ConsumeChars(2); continue; }
136 if( subParser.CharAtStart() == '{' ) ++depth;
137 subParser.ConsumeChars<NC>(1);
138 }
139
140 if ( subParser.IsEmpty() )
144
145 // extract format spec to separate substring
147 }
148
149 // check for closing brace
150 if( parser.CharAtStart() != '}' )
154
156 return true;
157}
158
159
160
162{
163 // parse alignment and fill character. This is a bit tricky to shorten the code.
164 // The alignment token <,>,^ or = can be on first or second position. If second, then the
165 // first was a fill character. Therefore we loop from 0 to 1....
166 integer charsToConsume= 1;
168 Substring& formatSpec= placeholder.FormatSpec;
169
170
171 int cNo= 0;
172 do switch ( formatSpec.CharAt( cNo++ ) )
173 {
178 placeholder.SignPaddingMode= true; break;
179
180 default:
181 if(cNo == 1 )
182 {
183 charsToConsume= 2;
184 placeholder.FillChar= formatSpec.CharAtStart();
185 }
186 else
187 {
188 charsToConsume= 0;
191 }
192 break;
193 }
194 while( cNo < 2 && charsToConsume != 1 );
195 formatSpec.ConsumeChars( charsToConsume );
196
197 // other things to parse
198 character actChar;
199 while( (actChar= formatSpec.CharAtStart()) != '\0' )
200 {
201 // width
202 if( isdigit( actChar ) )
203 {
204 // Python doc says:
205 // When no explicit alignment is given, preceding the width field by a zero ('0') character
206 // enables sign-aware zero-padding for numeric types.
207 // This is equivalent to a fill character of '0' with an alignment type of '='.
208 if ( actChar == '0' )
210 formatSpec.ConsumeDecDigits( placeholder.Width );
211
212 continue; // not consume below
213 }
214
215 // precision. Python doc says:
216 // "The precision is a decimal number indicating how many digits should be displayed
217 // after the decimal point for a floating point value formatted with 'f' and 'F', or
218 // before and after the decimal point for a floating point value formatted with 'g' or 'G'.
219 // For non-number types the field indicates the maximum field size - in other words,
220 // how many characters will be used from the field content. The precision is not
221 // allowed for integral values."
222 else if( actChar == '.' )
223 {
224 placeholderPS.PrecisionPos= static_cast<int>( formatString.Length()
225 - parser.Length()
226 - formatSpec.Length() -1 );
227 formatSpec.ConsumeChars( 1 );
228 if ( ! formatSpec.ConsumeDecDigits( placeholderPS.Precision ) )
232 - parser.Length()
233 - formatSpec.Length() - 1 );
234 continue;
235 }
236
237
238 // ----------------------------- Types -------------------------------------
239 else if (String(A_CHAR( "sdcboxXeEfFngGhHB%" )).IndexOf( actChar ) >= 0 )
240 {
241 if ( placeholder.TypeCode != '\0' )
243 actChar, placeholder.TypeCode,
247 - formatSpec.Length() - 1 );
248
249 placeholder.TypeCode= actChar;
251 - parser.Length()
252 - formatSpec.Length() - 1 );
253
254 if( String(A_CHAR( "EGF" )).IndexOf( actChar ) >= 0 )
255 {
259 }
260
261 switch( actChar )
262 {
263 case 's': placeholder.Type= PHTypes::String; break;
264 case 'd': placeholder.Type= PHTypes::IntBase10; break;
265 case 'c': placeholder.Type= PHTypes::Character; break;
266 case 'b': placeholder.Type= PHTypes::IntBinary; break;
267 case 'o': placeholder.Type= PHTypes::IntOctal; break;
268
269 case 'X': placeholder.NF.Flags-= NumberFormatFlags::HexLowerCase; ALIB_FALLTHROUGH
270 case 'x': placeholder.Type= PHTypes::IntHex; break;
271
272 case 'H': placeholder.NF.Flags-= NumberFormatFlags::HexLowerCase; ALIB_FALLTHROUGH
273 case 'h': placeholder.Type= PHTypes::HashCode; break;
274 case 'B': placeholder.Type= PHTypes::Bool; break;
275
276 // float types
277 case 'E':
279 placeholder.NF.Flags+= NumberFormatFlags::ForceScientific; break;
280
282 case 'f':
283 case 'F': placeholder.NF.Flags-= NumberFormatFlags::OmitTrailingFractionalZeros;
285
289 case 'G': ALIB_FALLTHROUGH
292 break;
293
294 default: ALIB_ERROR( "FMT", "Unhandled character in choices string above" )
295 return false;
296
297 }//switch
298 }
299
300
301 // others
302 else switch( actChar )
303 {
304 // sign
305 case '+': placeholder.NF.PlusSign= '+' ; break;
306 case '-': placeholder.NF.PlusSign= '\0'; break;
307 case ' ': placeholder.NF.PlusSign= ' ' ; break;
308
309 // alternate version ('#')
310 case '#': placeholder.WriteBinOctHexPrefix= true;
311 placeholder.NF.Flags+= NumberFormatFlags::ForceDecimalPoint;
312 placeholder.NF.Flags-= NumberFormatFlags::OmitTrailingFractionalZeros;
313 break;
314
315 // Enable grouping
316 case ',': placeholder.NF.Flags+= NumberFormatFlags::WriteGroupChars;
317 break;
318
319
320 default:
322 actChar,
325 - parser.Length()
326 - formatSpec.Length() - 1,
328 );
329 }
330
331 formatSpec.ConsumeChars(1);
332 }
333
334 return true;
335}
336
337
339{
340 if( length == 0)
341 return;
342
345 auto* src = parser.Buffer();
346 auto* dest= targetString->VBuffer() + targetString->Length();
347 parser.ConsumeChars( length );
348
349 character c1;
350 character c2= *src;
351 while( length > 1 )
352 {
353 c1= c2;
354 c2= *++src;
355
356 if( c1 == '\n' ) // reset auto-sizes when act ual character is '\n'
357 Sizes->Restart();
358
359 else if( ( c1 == '{' && c2 =='{')
360 || ( c1 == '}' && c2 =='}')
361 || c1 == '\\' )
362 {
363 if( c1 == '\\' )
364 switch(c2)
365 {
366 case 'r': c1= '\r' ; break;
367 case 'n': c1= '\n' ;
368 // reset auto-sizes with \n
370 Sizes->Restart();
371 break;
372 case 't': c1= '\t' ; break;
373 case 'a': c1= '\a' ; break;
374 case 'b': c1= '\b' ; break;
375 case 'v': c1= '\v' ; break;
376 case 'f': c1= '\f' ; break;
377 case '"': c1= '"' ; break;
378 default: c1= '?' ; break;
379 }
380
381 c2= *++src;
382 --length;
383 }
384
385 *dest++= c1;
386 --length;
387 }
389
390 // copy last character and adjust target string length:
391 // Note: length usually is 1. Only if last character is an escape sequence, it is 0.
392 if( length == 1)
393 {
394 *dest= *src;
395 // reset auto-sizes when last character is '\n'
396 if( *dest == '\n' )
397 Sizes->Restart();
398 }
399 targetString->SetLength( dest - targetString->VBuffer() + length);
400}
401
402
404{
405 bool isPreProcess= startIdx < 0;
406 bool isPostProcess= startIdx>= 0 && target == nullptr;
409
410 while( conversion.IsNotEmpty() )
411 {
412 if( !conversion.ConsumeChar('!') )
417 - conversion.Length() );
418
419 if( conversion.ConsumePartOf( A_CHAR( "Xtinguish" ) ) > 0 ) { return false; }
420 if( conversion.ConsumePartOf( A_CHAR( "Upper" ) ) > 0 ) { if (isPostProcess) targetString->ToUpper( startIdx ); }
421 else if( conversion.ConsumePartOf( A_CHAR( "Lower" ) ) > 0 ) { if (isPostProcess) targetString->ToLower( startIdx ); }
422 else if( conversion.ConsumePartOf( A_CHAR( "str" ) ) > 0
423 ||conversion.ConsumePartOf( A_CHAR( "Quote" ) ) > 0 )
424 {
425 String8 open = A_CHAR( '"' );
426 String8 close= A_CHAR( '"' );
427 if (conversion.IsNotEmpty() && conversion.CharAtStart() != A_CHAR('!'))
428 {
429 open .Reset( conversion.ConsumeChar() );
430 close.Reset( conversion.IsNotEmpty() && conversion.CharAtStart() != A_CHAR('!')
431 ? conversion.ConsumeChar()
432 : open.CharAtStart() );
433 }
434
435 if (isPostProcess)
436 targetString->InsertAt<NC>( open, startIdx ) .Append<NC>( close );
437 }
438
439 else if( conversion.ConsumePartOf( A_CHAR( "Fill" ) ) > 0 )
440 {
442 placeholder.FillChar= conversion.ConsumeChar<lang::Case::Ignore>('C' ) && conversion.Length() > 0
443 ? conversion.ConsumeChar<NC>()
444 : ' ';
445
446 }
447
448 else if( conversion.ConsumePartOf( A_CHAR( "Tab" ) ) > 0 )
449 {
450 character tabChar= conversion.ConsumeChar<lang::Case::Ignore>('C') && conversion.Length() > 0
451 ? conversion.ConsumeChar<NC>()
452 : ' ';
453 int tabSize;
454 if( !conversion.ConsumeDecDigits<int>( tabSize ) )
455 tabSize= 8;
456
457 if( isPreProcess )
458 targetString->_<NC>( strings::TFormat<character>::Tab( tabSize, -1, 1, tabChar ) );
459
460 }
461
462 else if( conversion.ConsumePartOf( A_CHAR( "ATab" ), 2 ) > 0 )
463 {
464
465 if( conversion.ConsumePartOf( A_CHAR( "Reset") ) > 0 )
466 {
467 if( isPreProcess )
468 Sizes->Reset();
469 }
470 else
471 {
472 character tabChar= conversion.ConsumeChar<lang::Case::Ignore>('C' ) && conversion.Length() > 0
473 ? conversion.ConsumeChar<NC>()
474 : ' ';
475
476 int growth;
477 if( !conversion.ConsumeDecDigits<int>( growth ) )
478 growth= 3;
479
480 if( isPreProcess )
481 {
483 integer tabStop= Sizes->Next( AutoSizes::Types::Tabstop, actPos , growth );
484 targetString->InsertChars<NC>( tabChar, tabStop - actPos );
485 }
486 }
487 }
488
489 else if( conversion.ConsumePartOf( A_CHAR( "AWidth" ), 2 ) > 0 )
490 {
491 if( conversion.ConsumePartOf( A_CHAR( "Reset" ) ) > 0 )
492 {
493 if( isPreProcess )
494 Sizes->Reset();
495 }
496 else
497 {
498 int extraPadding;
499 conversion.ConsumeDecDigits<int>( extraPadding );
500
501 if( isPreProcess )
502 placeholder.Width= int( Sizes->Actual( AutoSizes::Types::Field, 0, extraPadding ) );
503 else if ( isPostProcess )
504 Sizes->Next( AutoSizes::Types::Field, targetString->Length() - startIdx, extraPadding );
505 }
506 }
507
508 else if( conversion.ConsumePartOf( A_CHAR( "Esc" ) ) > 0
509 || conversion.ConsumePartOf( A_CHAR( "A" ) ) > 0 )
510 {
512 conversion.ConsumeChar('<');
513 if(conversion.ConsumeChar('>') )
514 toESC= lang::Switch::Off;
515
516 if( isPostProcess )
517 targetString->_<NC>( typename strings::TFormat<character>::Escape( toESC, startIdx ) );
518 }
519
520 else if( conversion.ConsumePartOf( A_CHAR( "Replace" ), 2 ) > 0 )
521 {
522 String search = conversion.ConsumeField( '<', '>' );
523 String replace= conversion.ConsumeField( '<', '>' );
524 if( search.IsNull() || replace.IsNull() )
529 - conversion.Length() );
530
531 if( target != nullptr )
532 {
533 // special case: fill empty fields
534 if( search.IsEmpty() && target->Length() - startIdx == 0 )
535 {
536 *target << replace;
537 }
538 else
539 target->SearchAndReplace( search, replace, startIdx );
540 }
541 }
542
543 // error (not recognized)
544 else
546 conversion, placeholder.Arg->TypeID(),
549 - conversion.Length() );
550 }
551
552 return true;
553}
554
555
586
587
588} // namespace [alib::lang::format]
589
const std::type_info & TypeID() const
Definition box.inl:941
void InsertDerived(TArgs &&... args)
virtual ALIB_API integer findPlaceholder() override
virtual ALIB_API bool preAndPostProcess(integer startIdx, AString *target) override
virtual ALIB_API bool checkStdFieldAgainstArgument() override
virtual ALIB_API SPFormatter Clone() override
virtual ALIB_API bool parsePlaceholder() override
virtual ALIB_API void writeStringPortion(integer length) override
virtual ALIB_API bool parseStdFormatSpec() override
PlaceholderAttributesPS placeholderPS
The extended placeholder attributes.
virtual ALIB_API void resetPlaceholder() override
@ Float
Outputs a number in floating point format.
@ HashCode
Writes raw box data as hex.
@ 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 Format.
AString * targetString
The target string as provided with method Format.
integer targetStringStartLength
The length of the target string before adding the formatted contents.
Substring parser
The current (remaining) format string.
virtual ALIB_API void CloneSettings(Formatter &reference)
Definition formatter.cpp:95
SharedPtr< Formatter > Next
ALIB_API integer SearchAndReplace(TChar needle, TChar replacement, integer startIdx=0, integer endIdx=strings::MAX_LEN)
TAString & ToLower(integer regionStart=0, integer regionLength=MAX_LEN)
TAString & ToUpper(integer regionStart=0, integer regionLength=MAX_LEN)
TAString & InsertChars(TChar c, integer qty)
TAString & _(const TString< TChar > &src, integer regionStart, integer regionLength=MAX_LEN)
void EnsureRemainingCapacity(integer spaceNeeded)
Definition tastring.inl:742
TChar * VBuffer() const
Definition tastring.inl:843
void SetLength(integer newLength)
Definition tastring.inl:924
TAString & InsertAt(const TString< TChar > &src, integer pos)
constexpr bool IsNull() const
Definition string.hpp:364
integer IndexOf(TChar needle, integer startIdx=0) const
Definition string.hpp:896
constexpr bool IsEmpty() const
Definition string.hpp:383
TChar CharAt(integer idx) const
Definition string.hpp:444
constexpr bool IsNotEmpty() const
Definition string.hpp:389
constexpr integer Length() const
Definition string.hpp:326
TChar CharAtStart() const
Definition string.hpp:466
integer IndexOfAny(const TString &needles, integer startIdx=0) const
Definition string.hpp:1090
constexpr const TChar * Buffer() const
Definition string.hpp:319
TString< TChar > ConsumeField(TChar startChar, TChar endChar)
integer ConsumePartOf(const TString< TChar > &consumable, int minChars=1)
integer ConsumeChars(integer regionLength, TSubstring *target=nullptr)
bool ConsumeDecDigits(TIntegral &result)
void Restart(unsigned int startIdx=0)
@ Field
denotes a field width entry.
@ Tabstop
denotes a tab stop entry.
ALIB_API integer Actual(Types type, integer requestedSize, integer growthPadding)
Definition autosizes.cpp:19
integer Next(Types type, integer requestedSize, integer growthPadding)
#define ALIB_FALLTHROUGH
Definition alib.hpp:852
#define ALIB_CALLER_NULLED
Definition alib.hpp:1173
#define A_CHAR(STR)
#define ALIB_WARNINGS_RESTORE
Definition alib.hpp:849
#define ALIB_ERROR(...)
Definition alib.hpp:1267
#define ALIB_WARNINGS_ALLOW_UNSAFE_BUFFER_USAGE
Definition alib.hpp:760
@ Include
Chooses inclusion.
platform_specific integer
Definition integers.hpp:43
Switch
Denotes if sth. is switched on or off.
@ On
Switch it on, switched on, etc.
@ Off
Switch it off, switched off, etc.
@ Center
Chooses centered alignment.
@ Right
Chooses right alignment.
@ Left
Chooses left alignment.
lang::Exception Exception
Type alias in namespace alib.
characters::character character
Type alias in namespace alib.
int PrecisionPos
The position where the precision was read. This is set to -1 in resetPlaceholder.
int Precision
The value read from the precision field. This is set to -1 in resetPlaceholder.
int ConversionPos
The position where the conversion was read. This is set to -1 in resetPlaceholder.
NumberFormatFlags Flags
The flag field.
TCString< TChar > NANLiteral
Defines what is written and parsed for double values that represent "not a number".
TCString< TChar > ExponentSeparator
TCString< TChar > INFLiteral
Defines what is written and parsed for infinite double values.