ALib C++ Library
Library Version: 2511 R0
Documentation generated by doxygen
Loading...
Searching...
No Matches
token.cpp
1//##################################################################################################
2// ALib C++ Library
3//
4// Copyright 2013-2025 A-Worx GmbH, Germany
5// Published under 'Boost Software License' (a free software license, see LICENSE.txt)
6//##################################################################################################
7#include "alib_precompile.hpp"
8#if !defined(ALIB_C20_MODULES) || ((ALIB_C20_MODULES != 0) && (ALIB_C20_MODULES != 1))
9# error "Symbol ALIB_C20_MODULES has to be given to the compiler as either 0 or 1"
10#endif
11#if ALIB_C20_MODULES
12 module;
13#endif
14//========================================= Global Fragment ========================================
16//============================================== Module ============================================
17#if ALIB_C20_MODULES
18 module ALib.Strings.Token;
19 import ALib.Characters.Functions;
20 import ALib.EnumOps;
21# if ALIB_RESOURCES
22 import ALib.Resources;
23# endif
24# if ALIB_ENUMRECORDS
25 import ALib.EnumRecords;
26# endif
27#else
28# include "ALib.Strings.Token.H"
30# include "ALib.EnumOps.H"
31# include "ALib.EnumRecords.H"
32# include "ALib.Resources.H"
33#endif
34//========================================== Implementation ========================================
35// Windows.h might bring in max/min macros
36#if defined( max )
37 #undef max
38 #undef min
39#endif
40
41namespace alib::strings::util {
42
43Token::Token(const String& pName, lang::Case sensitivity, int8_t minLength, const String& pExportName)
44: definitionName (pName)
45, exportName (pExportName)
46, format(Formats( int8_t(Formats::Normal )
47 + int8_t(sensitivity == lang::Case::Sensitive ? Formats(0) : ignoreCase) ) ) {
48 minLengths[0]= minLength;
49#if ALIB_DEBUG
50 if( minLength < 0 || minLength > definitionName.Length() )
52
53 if( minLength == 0 )
55#endif
56
57}
58
59Token::Token( const String& definitionSrc, lang::Case sensitivity,
60 int8_t minLength1, int8_t minLength2, int8_t minLength3, int8_t minLength4,
61 int8_t minLength5, int8_t minLength6, int8_t minLength7 )
62: definitionName(definitionSrc)
63, minLengths {minLength1, minLength2, minLength3, minLength4, minLength5, minLength6, minLength7} {
65 if( int(format) >= 0 && sensitivity == lang::Case::Ignore )
66 format= Formats( int8_t(format) | int8_t(ignoreCase) );
67}
68
69
70void Token::GetExportName(AString& target) const {
71 if( exportName.IsNotEmpty() ) {
72 target << exportName;
73 return;
74 }
75
76 target << GetDefinitionName();
77
78 // low the last character in if CamelCase and the last min length equals 0.
80 for( int i= 0 ; i < 7 ; ++i ) {
81 auto minLen= GetMinLength( i );
82 if( minLen == 0 ) {
83 target[target.Length()-1]= characters::ToLower(target[target.Length()-1]);
84 break;
85 }
86 if( minLen == -1 )
87 break;
88} } }
89
90#if ALIB_ENUMRECORDS
91void Token::Define( const String& definitionSrc, character separator ) {
92 minLengths[0]= 0;
93 minLengths[1]= -1;
95
96 Substring parser(definitionSrc);
97
98 // name
99 definitionName = Substring( parser.ConsumeToken( separator ) ).Trim();
100 if( definitionName.IsEmpty() )
101 return;
102
104 size_t qtyMinLengths= 0;
105 if(parser.IsNotEmpty() ) {
106 // letter case sensitivity
107 if( !enumrecords::Parse( parser, letterCase ) ) {
109 return;
110 }
111
112 // list of minimum length values
113 while(parser.ConsumeChar( separator ) ) {
114 if( qtyMinLengths >= 7 ) {
116 return;
117 }
118
119 if( !isdigit(parser.CharAtStart()) ) {
120 // optionally read export name once
121 if( exportName.IsNotNull() ) {
123 return;
124 }
125
127
128 if( exportName.IsEmpty() ) {
130 return;
131 }
132
133 continue;
134 }
135
136 parser.ConsumeDecDigits( minLengths[qtyMinLengths++] );
137 } }
138
139 if( qtyMinLengths == 0 )
140 minLengths[0]= int8_t( definitionName.Length() );
141
142 if( qtyMinLengths > 0 && qtyMinLengths < 7 )
143 minLengths[qtyMinLengths]= -1;
144
145 #if ALIB_DEBUG
146 if( parser.IsNotEmpty() ) {
148 return;
149 }
150 #endif
151
152 detectFormat();
153
154 #if ALIB_DEBUG
155 if( int(format) < 0 )
156 return;
157 #endif
158
159 if( letterCase == lang::Case::Ignore )
161}
162#endif //ALIB_ENUMRECORDS
163
165 // detect number of min length values
166 int qtyMinLength= 1;
167 while( qtyMinLength < 7 && minLengths[qtyMinLength] >= 0 )
168 ++qtyMinLength;
169
170 // just one length given? Keep format "normal"
172 if( qtyMinLength > 1 ) {
173 // count hyphens, underscores, camel humps...
174 bool hasLowerCases= isalpha(definitionName[0]) && islower(definitionName[0]);
175 int qtyUpperCases= 0;
176 int qtyUnderscores= 0;
177 int qtyHyphens= 0;
178 for( integer idx= 1; idx < definitionName.Length() ; ++idx ) {
180 if( c == '_' ) ++qtyUnderscores;
181 else if( c == '-' ) ++qtyHyphens;
182 else if( isalpha(c) ) {
183 if( islower(c) )
184 hasLowerCases= true;
185 else
186 ++qtyUpperCases;
187 }
188 else
189 hasLowerCases= true;
190 }
191
192 // Snake_Case?
193 if( qtyUnderscores > 0 ) {
195 #if ALIB_DEBUG
196 if( (qtyUnderscores >= 7 && qtyMinLength != 7 )
197 || (qtyUnderscores < 7 && qtyMinLength != qtyUnderscores + 1 ) )
199 #endif
200 }
201
202 // Kebab-Case?
203 else if( qtyHyphens > 0 ) {
205 #if ALIB_DEBUG
206 if( (qtyHyphens >= 7 && qtyMinLength != 7 )
207 || (qtyHyphens < 7 && qtyMinLength != qtyHyphens + 1 ) )
209 #endif
210 }
211
212 // CamelCase
213 else if( hasLowerCases && ( qtyUpperCases > 0 ) ) {
215 #if ALIB_DEBUG
216 if( (qtyUpperCases >= 7 && qtyMinLength != 7 )
217 || (qtyUpperCases < 7 && qtyMinLength != qtyUpperCases + 1 ) )
219 #endif
220 }
221
222 // normal
223 #if ALIB_DEBUG
224 else
226 #endif
227 }
228
229 // check segment sizes against minLengths
230 #if ALIB_DEBUG
231 if( int(format) < 0 )
232 return;
233
234 if( GetFormat() == Formats::Normal ) {
235 if( minLengths[0] > definitionName.Length() ) {
237 return;
238 }
239 if( minLengths[0] <= 0 ) {
241 return;
242 }
243 } else {
244 int segmentNo = 0;
245 int segmentLength= 0;
246 integer charIdx = 1;
247 while( charIdx < definitionName.Length() ) {
248 ++segmentLength;
249 character c= definitionName.CharAt( charIdx++ );
250 bool segmentEnd= c == '\0'
251 || (format == Formats::SnakeCase && c == '_' )
252 || (format == Formats::KebabCase && c == '-' )
253 || (format == Formats::CamelCase && isalpha(c) && isupper(c) );
254
255 if( segmentEnd ) {
256 if( segmentNo < 7 && minLengths[segmentNo] > segmentLength ) {
258 return;
259 }
260
261 segmentLength= (format == Formats::CamelCase ? 1 : 0);
262 ++segmentNo;
263 } }
264
265 for( int minLenIdx= 0 ; minLenIdx < 7 && minLengths[minLenIdx] >= 0 ; ++minLenIdx ) {
266 if( minLengths[minLenIdx] == 0
268 || !( minLenIdx == 6 || minLengths[minLenIdx + 1] == -1 ) ) )
269 {
271 return;
272 } } }
273 #endif
274
275
276}
277
278bool Token::Match( const String& needle ) {
279 ALIB_ASSERT_ERROR( needle.Length() > 0, "STRINGS/TOK",
280 "Empty search string in when matching function name." )
281 lang::Case sensitivity= Sensitivity();
282
283 Formats caseType= GetFormat();
284 bool isNormal= (caseType == Formats::Normal );
285 bool isCamel = (caseType == Formats::CamelCase );
286 bool isSnake = (caseType == Formats::SnakeCase );
287 bool isKebab = (caseType == Formats::KebabCase );
288
289 int segNo = 0;
290 int segLen = 0;
291 bool same = false;
292 integer hIdx = 0;
293 integer nIdx = 0;
294 integer rollbackLen = 0;
295 bool isSegOK = false;
296 int segMinLen = minLengths[0];
297 while( hIdx < definitionName.Length() ) {
298 // read current haystack and needle
299 ++segLen;
300 character h= definitionName .CharAt( hIdx++ );
301 character n= needle.CharAt( nIdx++ );
302
303 same= sensitivity == lang::Case::Ignore
306 : h
307 == n;
308
309 // special CamelCase treatment
310 if( isCamel ) {
311 // end of needle and final, omitable segment?
312 if( n == '\0' && segMinLen == 0)
313 return true;
314
315 // rollback
316 if( !same ) {
317 if( segLen == 1 && rollbackLen > 0) {
318 nIdx-= 2;
319 --rollbackLen;
320 --hIdx;
321 --segLen;
322 continue;
323 }
324
325 --nIdx;
326 }
327
328 if( segLen == 1)
329 rollbackLen= 0;
330
331 else if( same && isSegOK )
332 ++rollbackLen;
333 }
334
335 // end of haystack segment?
336 bool isSegEnd= hIdx == definitionName.Length()
337 || (isSnake && h == '_' )
338 || (isKebab && h == '-' )
339 || (isCamel && isalpha(definitionName.CharAt( hIdx ))
340 && isupper(definitionName.CharAt( hIdx )) );
341
342 // update segOK flag
343 if( same ) {
344 isSegOK= ( ( segMinLen >= 0 && segLen >= segMinLen )
345 || ( segMinLen < 0 && isSegEnd ) );
346 }
347
348 // result false, if not same and first of actual segment
349 else if( segLen == 1 && segMinLen != 0 )
350 return false;
351
352
353 // end of segment and needle not empty?
354 if( isSegEnd && n != '\0') {
355 if( !isSegOK )
356 return false;
357 }
358
359 // not same and either not end of segment or empty needle
360 else if( !same ) {
361 if( !isSegOK )
362 return false;
363
364 // skip rest of segment
365 while( h != '\0'
366 && ( ( isCamel && (!isalpha(h) || !isupper(h) ) )
367 || ( isSnake && h != '_' )
368 || ( isKebab && h != '-' ) ) )
369 h= definitionName.CharAt( hIdx++ );
370
371 if( isCamel )
372 --hIdx;
373 }
374
375 // start new segment
376 if( !same || isSegEnd ) {
377 ++segNo;
378 segLen= 0;
379 segMinLen = segNo < 7 ? minLengths[segNo] : -2;
380
381 // oh,oh!
382 if( n == '\0' && (!isCamel || h == '\0' || rollbackLen == 0) )
383 return h == '\0' || isNormal || segMinLen == 0;
384 } }
385
386 return same && isSegOK && (nIdx == needle.Length());
387}
388
389} // namespace [alib::strings::util]
constexpr integer Length() const
Definition string.inl:316
TChar CharAtStart() const
Definition string.inl:433
TChar CharAt(integer idx) const
Definition string.inl:415
constexpr bool IsNotEmpty() const
Definition string.inl:369
bool ConsumeDecDigits(std::integral auto &result)
TSubstring & Trim(const TCString< TChar > &whiteSpaces=CStringConstantsTraits< TChar >::DefaultWhitespaces())
TString< TChar > ConsumeToken(TChar separator=',', lang::Inclusion includeSeparator=lang::Inclusion::Include)
@ ErrorReadingSensitivity
Sensitivity value not found.
Definition token.inl:151
@ TooManyMinLengthsGiven
A maximum of 7 minimum length values was exceeded.
Definition token.inl:153
@ ErrorReadingMinLengths
Error parsing the list of minimum lengths.
Definition token.inl:152
@ DefinitionStringNotConsumed
The definition string was not completely consumed.
Definition token.inl:160
ALIB_DLL void detectFormat()
Detects snake_case, kebab-case or CamelCase.
Definition token.cpp:164
int8_t GetMinLength(int idx) const
Definition token.inl:337
String definitionName
The tokens' definition string part.
Definition token.inl:168
const String & GetDefinitionName() const
Definition token.inl:274
lang::Case Sensitivity() const
Definition token.inl:318
Formats
Format types detected with detectFormat.
Definition token.inl:135
@ CamelCase
UpperCamelCase or lowerCamelCase.
Definition token.inl:139
@ SnakeCase
snake_case using underscores.
Definition token.inl:137
@ Normal
Normal, optionally abbreviated words.
Definition token.inl:136
@ KebabCase
kebab-case using hyphens.
Definition token.inl:138
Token()
Parameterless constructor. Creates an "undefined" token.
Definition token.inl:190
Formats format
Defines the "case type" as well as the letter case sensitivity of this token.
Definition token.inl:175
String exportName
The tokens' optional explicit export name.
Definition token.inl:171
static constexpr Formats ignoreCase
Letter case sensitivity. This is combined with the format bits.
Definition token.inl:183
Formats GetFormat() const
Definition token.inl:305
ALIB_DLL void Define(const String &definition, character separator=';')
Definition token.cpp:91
ALIB_DLL bool Match(const String &needle)
Definition token.cpp:278
ALIB_DLL void GetExportName(AString &target) const
Definition token.cpp:70
#define ALIB_ASSERT_ERROR(cond, domain,...)
Definition alib.inl:1066
#define ALIB_REL_DBG(releaseCode,...)
Definition alib.inl:855
TChar ToUpper(TChar c)
TChar ToLower(TChar c)
bool Parse(strings::TSubstring< TChar > &input, TEnum &result)
Case
Denotes upper and lower case character treatment.
@ Exclude
Chooses exclusion.
strings::TAString< character, lang::HeapAllocator > AString
Type alias in namespace alib.
lang::integer integer
Type alias in namespace alib.
Definition integers.inl:149
strings::TString< character > String
Type alias in namespace alib.
Definition string.inl:2189
characters::character character
Type alias in namespace alib.
strings::TSubstring< character > Substring
Type alias in namespace alib.