ALib C++ Library
Library Version: 2412 R0
Documentation generated by doxygen
Loading...
Searching...
No Matches
token.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
11# if ALIB_ENUMS
14# endif
15#endif // !DOXYGEN
16
17// Windows.h might bring in max/min macros
18#if defined( max )
19 #undef max
20 #undef min
21#endif
22
23#if ALIB_BOXING
25#endif
26
27namespace alib { namespace strings { namespace util {
28
29Token::Token(const String& pName, lang::Case sensitivity, int8_t minLength, const String& pExportName)
30: definitionName (pName)
31, exportName (pExportName)
32, format(Formats( int8_t(Formats::Normal )
33 + int8_t(sensitivity == lang::Case::Sensitive ? Formats(0) : ignoreCase) ) )
34{
35 minLengths[0]= minLength;
36#if ALIB_DEBUG
37 if( minLength < 0 || minLength > definitionName.Length() )
39
40 if( minLength == 0 )
42#endif
43
44}
45
46Token::Token( const String& definitionSrc, lang::Case sensitivity,
47 int8_t minLength1, int8_t minLength2, int8_t minLength3, int8_t minLength4, int8_t minLength5,
48 int8_t minLength6, int8_t minLength7 )
49: definitionName(definitionSrc)
50, minLengths { minLength1, minLength2, minLength3, minLength4, minLength5, minLength6, minLength7 }
51{
53 if( int(format) >= 0 && sensitivity == lang::Case::Ignore )
54 format= Formats( int8_t(format) | int8_t(ignoreCase) );
55}
56
57
58void Token::GetExportName(AString& target) const
59{
61 {
62 target << exportName;
63 return;
64 }
65
66 target << GetDefinitionName();
67
68 // low the last character in if CamelCase and the last min length equals 0.
70 {
71 for( int i= 0 ; i < 7 ; ++i )
72 {
73 auto minLen= GetMinLength( i );
74 if( minLen == 0 )
75 {
76 target[target.Length()-1]= characters::ToLower(target[target.Length()-1]);
77 break;
78 }
79 if( minLen == -1 )
80 break;
81 }
82 }
83}
84
85#if ALIB_ENUMS
86void Token::Define( const String& definitionSrc, character separator )
87{
89 minLengths[0]= 0;
90 minLengths[1]= -1;
93
94 Substring parser(definitionSrc);
95
96 // name
97 definitionName = Substring( parser.ConsumeToken( separator ) ).Trim();
99 return;
100
102 size_t qtyMinLengths= 0;
103 if(parser.IsNotEmpty() )
104 {
105 // letter case sensitivity
106 if( !enums::Parse( parser, letterCase ) )
107 {
109 return;
110 }
111
112 // list of minimum length values
113 while(parser.ConsumeChar( separator ) )
114 {
115 if( qtyMinLengths >= 7 )
116 {
118 return;
119 }
120
121 if( !isdigit(parser.CharAtStart()) )
122 {
123 // optionally read export name once
124 if( exportName.IsNotNull() )
125 {
127 return;
128 }
129
131
132 if( exportName.IsEmpty() )
133 {
135 return;
136 }
137
138 continue;
139 }
140
142 parser.ConsumeDecDigits( minLengths[qtyMinLengths++] );
144 }
145 }
146
148 if( qtyMinLengths == 0 )
149 minLengths[0]= static_cast<int8_t>( definitionName.Length() );
150
151 if( qtyMinLengths > 0 && qtyMinLengths < 7 )
152 minLengths[qtyMinLengths]= -1;
154
155 #if ALIB_DEBUG
156 if( parser.IsNotEmpty() )
157 {
159 return;
160 }
161 #endif
162
163 detectFormat();
164
165 #if ALIB_DEBUG
166 if( int(format) < 0 )
167 return;
168 #endif
169
170 if( letterCase == lang::Case::Ignore )
172}
173#endif
174
176{
177 // detect number of min length values
179 int qtyMinLength= 1;
180 while( qtyMinLength < 7 && minLengths[qtyMinLength] >= 0 )
181 ++qtyMinLength;
183
184 // just one length given? Keep format "normal"
186 if( qtyMinLength > 1 )
187 {
188 // count hyphens, underscores, camel humps...
189 bool hasLowerCases= isalpha(definitionName[0]) && islower(definitionName[0]);
190 int qtyUpperCases= 0;
191 int qtyUnderscores= 0;
192 int qtyHyphens= 0;
193 for( integer idx= 1; idx < definitionName.Length() ; ++idx )
194 {
196 if( c == '_' ) ++qtyUnderscores;
197 else if( c == '-' ) ++qtyHyphens;
198 else if( isalpha(c) )
199 {
200 if( islower(c) )
201 hasLowerCases= true;
202 else
203 ++qtyUpperCases;
204 }
205 else
206 hasLowerCases= true;
207 }
208
209 // Snake_Case?
210 if( qtyUnderscores > 0 )
211 {
213 #if ALIB_DEBUG
214 if( (qtyUnderscores >= 7 && qtyMinLength != 7 )
215 || (qtyUnderscores < 7 && qtyMinLength != qtyUnderscores + 1 ) )
217 #endif
218 }
219
220 // Kebab-Case?
221 else if( qtyHyphens > 0 )
222 {
224 #if ALIB_DEBUG
225 if( (qtyHyphens >= 7 && qtyMinLength != 7 )
226 || (qtyHyphens < 7 && qtyMinLength != qtyHyphens + 1 ) )
228 #endif
229 }
230
231 // CamelCase
232 else if( hasLowerCases && ( qtyUpperCases > 0 ) )
233 {
235 #if ALIB_DEBUG
236 if( (qtyUpperCases >= 7 && qtyMinLength != 7 )
237 || (qtyUpperCases < 7 && qtyMinLength != qtyUpperCases + 1 ) )
239 #endif
240 }
241
242 // normal
243 #if ALIB_DEBUG
244 else
246 #endif
247 }
248
249 // check segment sizes against minLengths
250 #if ALIB_DEBUG
251 if( int(format) < 0 )
252 return;
253
254 if( GetFormat() == Formats::Normal )
255 {
256 if( minLengths[0] > definitionName.Length() )
257 {
259 return;
260 }
261 if( minLengths[0] <= 0 )
262 {
264 return;
265 }
266 }
267 else
268 {
269 int segmentNo = 0;
270 int segmentLength= 0;
271 integer charIdx = 1;
272 while( charIdx < definitionName.Length() )
273 {
274 ++segmentLength;
275 character c= definitionName.CharAt( charIdx++ );
276 bool segmentEnd= c == '\0'
277 || (format == Formats::SnakeCase && c == '_' )
278 || (format == Formats::KebabCase && c == '-' )
279 || (format == Formats::CamelCase && isalpha(c) && isupper(c) );
280
281 if( segmentEnd )
282 {
284 if( segmentNo < 7 && minLengths[segmentNo] > segmentLength )
285 {
287 return;
288 }
290
291 segmentLength= (format == Formats::CamelCase ? 1 : 0);
292 ++segmentNo;
293 }
294 }
295
297 for( int minLenIdx= 0 ; minLenIdx < 7 && minLengths[minLenIdx] >= 0 ; ++minLenIdx )
298 {
299 if( minLengths[minLenIdx] == 0
301 || !( minLenIdx == 6 || minLengths[minLenIdx + 1] == -1 ) ) )
302 {
304 return;
305 }
306 }
308 }
309 #endif
310
311
312}
313
314bool Token::Match( const String& needle )
315{
316 ALIB_ASSERT_ERROR( needle.Length() > 0,
317 "STRINGS/TOK", "Empty search string in when matching function name." )
318 lang::Case sensitivity= Sensitivity();
319
320 Formats caseType= GetFormat();
321 bool isNormal= (caseType == Formats::Normal );
322 bool isCamel = (caseType == Formats::CamelCase );
323 bool isSnake = (caseType == Formats::SnakeCase );
324 bool isKebab = (caseType == Formats::KebabCase );
325
326 int segNo = 0;
327 int segLen = 0;
328 bool same = false;
329 integer hIdx = 0;
330 integer nIdx = 0;
331 integer rollbackLen = 0;
332 bool isSegOK = false;
333 int segMinLen = minLengths[0];
334 while( hIdx < definitionName.Length() )
335 {
336 // read current haystack and needle
337 ++segLen;
338 character h= definitionName .CharAt( hIdx++ );
339 character n= needle.CharAt( nIdx++ );
340
341 same= sensitivity == lang::Case::Ignore
344 : h
345 == n;
346
347 // special CamelCase treatment
348 if( isCamel )
349 {
350 // end of needle and final, omitable segment?
351 if( n == '\0' && segMinLen == 0)
352 return true;
353
354 // rollback
355 if( !same )
356 {
357 if( segLen == 1 && rollbackLen > 0)
358 {
359 nIdx-= 2;
360 --rollbackLen;
361 --hIdx;
362 --segLen;
363 continue;
364 }
365
366 --nIdx;
367 }
368
369 if( segLen == 1)
370 rollbackLen= 0;
371
372 else if( same && isSegOK )
373 ++rollbackLen;
374 }
375
376 // end of haystack segment?
377 bool isSegEnd= hIdx == definitionName.Length()
378 || (isSnake && h == '_' )
379 || (isKebab && h == '-' )
380 || (isCamel && isalpha(definitionName.CharAt( hIdx ))
381 && isupper(definitionName.CharAt( hIdx )) );
382
383 // update segOK flag
384 if( same )
385 {
386 isSegOK= ( ( segMinLen >= 0 && segLen >= segMinLen )
387 || ( segMinLen < 0 && isSegEnd ) );
388 }
389
390 // result false, if not same and first of actual segment
391 else if( segLen == 1 && segMinLen != 0 )
392 return false;
393
394
395 // end of segment and needle not empty?
396 if( isSegEnd && n != '\0')
397 {
398 if( !isSegOK )
399 return false;
400 }
401
402 // not same and either not end of segment or empty needle
403 else if( !same )
404 {
405 if( !isSegOK )
406 return false;
407
408 // skip rest of segment
409 while( h != '\0'
410 && ( ( isCamel && (!isalpha(h) || !isupper(h) ) )
411 || ( isSnake && h != '_' )
412 || ( isKebab && h != '-' ) ) )
413 h= definitionName.CharAt( hIdx++ );
414
415 if( isCamel )
416 --hIdx;
417 }
418
419 // start new segment
420 if( !same || isSegEnd )
421 {
422 ++segNo;
423 segLen= 0;
425 segMinLen = segNo < 7 ? minLengths[segNo] : -2;
427
428 // oh,oh!
429 if( n == '\0' && (!isCamel || h == '\0' || rollbackLen == 0) )
430 return h == '\0' || isNormal || segMinLen == 0;
431 }
432 }
433
434 return same && isSegOK && (nIdx == needle.Length());
435}
436
437#if ALIB_CAMP && !DOXYGEN
438
441
442void Token::LoadResourcedTokens( ResourcePool& resourcePool,
443 const NString& resourceCategory,
444 const NString& resourceName,
446 ALIB_DBG( int dbgSizeVerifier, )
447 character outerSeparator,
448 character innerSeparator )
449{
451 ALIB_DBG( int tableSize= 0; )
452 int resourceNo= -1; // disable number parsing
453
454 Substring parser= resourcePool.Get( resourceCategory, resourceName ALIB_DBG(, false ) );
455 if( parser.IsNull() )
456 resourceNo= 0; // enable number parsing
457
458 for( ;; )
459 {
460 if (resourceNo >= 0)
461 parser= resourcePool.Get( resourceCategory, NString256() << resourceName << resourceNo++
462 ALIB_DBG(, false ) );
463
464 ALIB_ASSERT_ERROR( resourceNo != 1 || parser.IsNotNull(), "STRINGS/TOK",
465 NString256() << "Resource string(s) \"" << resourceCategory
466 << "/" << resourceName
467 << "(nn)\" not found when parsing token." )
468
469 if( parser.IsEmpty() )
470 break;
471
472 while( parser.IsNotEmpty() )
473 {
474 String actValue= parser.ConsumeToken( outerSeparator );
475 token->Define( actValue, innerSeparator );
476
477 #if ALIB_DEBUG
478 NCString errorMessage;
479 switch( token->DbgGetError() )
480 {
481 case Token::DbgDefinitionError::OK:
482 break;
483 case Token::DbgDefinitionError::EmptyName:
484 errorMessage= "No token name found.";
485 break;
486 case Token::DbgDefinitionError::ErrorReadingSensitivity:
487 errorMessage= "Sensitivity value not found.";
488 break;
489 case Token::DbgDefinitionError::ErrorReadingMinLengths:
490 errorMessage= "Error parsing the list of minimum lengths.";
491 break;
492 case Token::DbgDefinitionError::TooManyMinLengthsGiven:
493 errorMessage= " A maximum of 7 minimum length values was exceeded.";
494 break;
495 case Token::DbgDefinitionError::InconsistentMinLengths:
496 errorMessage= "The number of given minimum length values is greater than 1 "
497 "but does not match the number of segments in the identifier.";
498 break;
499 case Token::DbgDefinitionError::NoCaseSchemeFound:
500 errorMessage= "More than one minimum length value was given but no "
501 "segmentation scheme could be detected." ;
502 break;
503 case Token::DbgDefinitionError::MinLenExceedsSegmentLength:
504 errorMessage= "A minimum length is specified to be higher than the token "
505 "name, respectively the according segment name.";
506 break;
507 case Token::DbgDefinitionError::DefinitionStringNotConsumed:
508 errorMessage= "The definition string was not completely consumed.";
509 break;
510 case Token::DbgDefinitionError::ZeroMinLengthAndNotLastCamelHump:
511 errorMessage= "Zero minimum length provided for segment which is not the last\n"
512 "of a camel case token.";
513 break;
514
515 default: ALIB_ERROR("Illegal switch state.") break;
516 }
517
518 if( errorMessage.IsNotEmpty() )
519 {
520 ALIB_ERROR( "STRINGS", errorMessage, NString512() <<
521 "\n(While reading token table.)\n"
522 " Resource category (module name): \"" << resourceCategory << "\"\n"
523 " Resource name: \"" << resourceName << "\"\n"
524 " Token value parsed: \"" << actValue << "\"" )
525 }
526
527 #endif
528
529
530 ++token;
531 ALIB_DBG( tableSize++; )
532 }
533 }
534
535 // check if there are more coming (a gap in numbered definition)
536 #if ALIB_DEBUG
537 if( resourceNo > 1 )
538 for( int i= 0 ; i < 35 ; ++i )
539 {
540 if( resourcePool.Get( resourceCategory, NString256() << resourceName << (resourceNo + i)
541 ALIB_DBG(, false ) ).IsNotNull() )
542 {
543 ALIB_ERROR( "STRINGS", NString128()
544 << "Detected a \"gap\" in numbering of resource strings while parsing "
545 "resource token table: "
546 "From index " << resourceNo - 1 << " to " << resourceNo + i - 1 << ".\n"
547 "Resource category/name: " << resourceCategory << '/' << resourceName << "." )
548 }
549 }
550 #endif
551
552
553 ALIB_ASSERT_ERROR( dbgSizeVerifier == tableSize, "STRINGS/TOK", NString512() <<
554 "Size mismatch in resourced token table:\n"
555 " Resource category (module name): \"" << resourceCategory << "\"\n"
556 " Resource name: \"" << resourceName << "\"\n"
557 " Resourced table size: [" << tableSize << "]\n"
558 " Expected table size: [" << dbgSizeVerifier << "]" )
559
560}
562
563#endif //ALIB_CAMP && !DOXYGEN
564
565}}} // namespace [alib::strings::util]
566
567
virtual const String & Get(const NString &category, const NString &name, bool dbgAssert)=0
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
constexpr bool IsNotNull() const
Definition string.hpp:371
bool ConsumeDecDigits(TIntegral &result)
TString< TChar > ConsumeToken(TChar separator=',', lang::Inclusion includeSeparator=lang::Inclusion::Include)
TSubstring & Trim(const TCString< TChar > &whiteSpaces=TT_CStringConstants< TChar >::DefaultWhitespaces())
ALIB_API void detectFormat()
Detects snake_case, kebab-case or CamelCase.
Definition token.cpp:175
Token()
Parameterless constructor. Creates an "undefined" token.
Definition token.hpp:211
DbgDefinitionError DbgGetError()
Definition token.hpp:288
const String & GetDefinitionName() const
Definition token.hpp:305
ALIB_API void GetExportName(AString &target) const
Definition token.cpp:58
lang::Case Sensitivity() const
Definition token.hpp:356
String definitionName
The tokens' definition string part.
Definition token.hpp:187
static constexpr Formats ignoreCase
Letter case sensitivity. This is combined with the format bits.
Definition token.hpp:203
Formats GetFormat() const
Definition token.hpp:340
ALIB_API void Define(const String &definition, character separator=';')
Definition token.cpp:86
Formats
Format types detected with detectFormat.
Definition token.hpp:154
@ CamelCase
UpperCamelCase or lowerCamelCase.
@ SnakeCase
snake_case using underscores.
@ Normal
Normal, optionally abbreviated words.
@ KebabCase
kebab-case using hyphens.
static ALIB_API void LoadResourcedTokens(lang::resources::ResourcePool &resourcePool, const NString &resourceCategory, const NString &resourceName, strings::util::Token *target, int dbgSizeVerifier, character outerSeparator=',', character innerSeparator=' ')
Formats format
Defines the "case type" as well as the letter case sensitivity of this token.
Definition token.hpp:194
@ ErrorReadingSensitivity
Sensitivity value not found.
@ TooManyMinLengthsGiven
A maximum of 7 minimum length values was exceeded.
@ ErrorReadingMinLengths
Error parsing the list of minimum lengths.
@ DefinitionStringNotConsumed
The definition string was not completely consumed.
String exportName
The tokens' optional explicit export name.
Definition token.hpp:190
int8_t GetMinLength(int idx) const
Definition token.hpp:377
ALIB_API bool Match(const String &needle)
Definition token.cpp:314
#define ALIB_BOXING_VTABLE_DEFINE(TMapped, Identifier)
Definition vtable.inl:473
#define ALIB_WARNINGS_RESTORE
Definition alib.hpp:849
#define ALIB_ERROR(...)
Definition alib.hpp:1267
#define ALIB_ASSERT_ERROR(cond,...)
Definition alib.hpp:1271
#define ALIB_WARNINGS_ALLOW_UNSAFE_BUFFER_USAGE
Definition alib.hpp:760
#define ALIB_DBG(...)
Definition alib.hpp:390
#define ALIB_REL_DBG(releaseCode,...)
Definition alib.hpp:392
TChar ToUpper(TChar c)
TChar ToLower(TChar c)
bool Parse(strings::TSubstring< TChar > &input, TEnum &result)
@ Exclude
Chooses exclusion.
Case
Denotes upper and lower case character treatment.
Definition alib.cpp:69
NLocalString< 128 > NString128
Type alias name for TLocalString<nchar,128>.
strings::TSubstring< character > Substring
Type alias in namespace alib.
NLocalString< 256 > NString256
Type alias name for TLocalString<nchar,256>.
characters::character character
Type alias in namespace alib.
NLocalString< 512 > NString512
Type alias name for TLocalString<nchar,512>.
lang::integer integer
Type alias in namespace alib.
Definition integers.hpp:273