ALib C++ Framework
by
Library Version: 2605 R0
Documentation generated by doxygen
Loading...
Searching...
No Matches
token.cpp
1// Windows.h might bring in max/min macros
2#if defined( max )
3 #undef max
4 #undef min
5#endif
6
7namespace alib::strings::util {
8
9Token::Token(const String& pName, lang::Case sensitivity, int8_t minLength, const String& pExportName)
10: definitionName (pName)
11, exportName (pExportName)
12, format(Formats( int8_t(Formats::Normal )
13 + int8_t(sensitivity == lang::Case::Sensitive ? Formats(0) : ignoreCase) ) ) {
14 minLengths[0]= minLength;
15#if ALIB_DEBUG
16 if( minLength < 0 || minLength > definitionName.Length() )
18
19 if( minLength == 0 )
21#endif
22
23}
24
25Token::Token( const String& definitionSrc, lang::Case sensitivity,
26 int8_t minLength1, int8_t minLength2, int8_t minLength3, int8_t minLength4,
27 int8_t minLength5, int8_t minLength6, int8_t minLength7 )
28: definitionName(definitionSrc)
29, minLengths {minLength1, minLength2, minLength3, minLength4, minLength5, minLength6, minLength7} {
31 if( int(format) >= 0 && sensitivity == lang::Case::Ignore )
32 format= Formats( int8_t(format) | int8_t(ignoreCase) );
33}
34
35
36void Token::GetExportName(AString& target) const {
37 if( exportName.IsNotEmpty() ) {
38 target << exportName;
39 return;
40 }
41
42 target << GetDefinitionName();
43
44 // low the last character in if CamelCase and the last min length equals 0.
46 for( int i= 0 ; i < 7 ; ++i ) {
47 auto minLen= GetMinLength( i );
48 if( minLen == 0 ) {
49 target[target.Length()-1]= characters::ToLower(target[target.Length()-1]);
50 break;
51 }
52 if( minLen == -1 )
53 break;
54} } }
55
56#if ALIB_ENUMRECORDS
57void Token::Define( const String& definitionSrc, character separator ) {
58 minLengths[0]= 0;
59 minLengths[1]= -1;
61
62 Substring parser(definitionSrc);
63
64 // name
65 definitionName = Substring( parser.ConsumeToken( separator ) ).Trim();
66 if( definitionName.IsEmpty() )
67 return;
68
70 size_t qtyMinLengths= 0;
71 if(parser.IsNotEmpty() ) {
72 // letter case sensitivity
73 if( !enumrecords::Parse( parser, letterCase ) ) {
75 return;
76 }
77
78 // list of minimum length values
79 while(parser.ConsumeChar( separator ) ) {
80 if( qtyMinLengths >= 7 ) {
82 return;
83 }
84
85 if( !isdigit(parser.CharAtStart()) ) {
86 // optionally read export name once
87 if( exportName.IsNotNull() ) {
89 return;
90 }
91
93
94 if( exportName.IsEmpty() ) {
96 return;
97 }
98
99 continue;
100 }
101
102 parser.ConsumeDecDigits( minLengths[qtyMinLengths++] );
103 } }
104
105 if( qtyMinLengths == 0 )
106 minLengths[0]= int8_t( definitionName.Length() );
107
108 if( qtyMinLengths > 0 && qtyMinLengths < 7 )
109 minLengths[qtyMinLengths]= -1;
110
111 #if ALIB_DEBUG
112 if( parser.IsNotEmpty() ) {
114 return;
115 }
116 #endif
117
118 detectFormat();
119
120 #if ALIB_DEBUG
121 if( int(format) < 0 )
122 return;
123 #endif
124
125 if( letterCase == lang::Case::Ignore )
127}
128#endif //ALIB_ENUMRECORDS
129
131 // detect number of min length values
132 int qtyMinLength= 1;
133 while( qtyMinLength < 7 && minLengths[qtyMinLength] >= 0 )
134 ++qtyMinLength;
135
136 // just one length given? Keep format "normal"
138 if( qtyMinLength > 1 ) {
139 // count hyphens, underscores, camel humps...
140 bool hasLowerCases= isalpha(definitionName[0]) && islower(definitionName[0]);
141 int qtyUpperCases= 0;
142 int qtyUnderscores= 0;
143 int qtyHyphens= 0;
144 for( integer idx= 1; idx < definitionName.Length() ; ++idx ) {
146 if( c == '_' ) ++qtyUnderscores;
147 else if( c == '-' ) ++qtyHyphens;
148 else if( isalpha(c) ) {
149 if( islower(c) )
150 hasLowerCases= true;
151 else
152 ++qtyUpperCases;
153 }
154 else
155 hasLowerCases= true;
156 }
157
158 // Snake_Case?
159 if( qtyUnderscores > 0 ) {
161 #if ALIB_DEBUG
162 if( (qtyUnderscores >= 7 && qtyMinLength != 7 )
163 || (qtyUnderscores < 7 && qtyMinLength != qtyUnderscores + 1 ) )
165 #endif
166 }
167
168 // Kebab-Case?
169 else if( qtyHyphens > 0 ) {
171 #if ALIB_DEBUG
172 if( (qtyHyphens >= 7 && qtyMinLength != 7 )
173 || (qtyHyphens < 7 && qtyMinLength != qtyHyphens + 1 ) )
175 #endif
176 }
177
178 // CamelCase
179 else if( hasLowerCases && ( qtyUpperCases > 0 ) ) {
181 #if ALIB_DEBUG
182 if( (qtyUpperCases >= 7 && qtyMinLength != 7 )
183 || (qtyUpperCases < 7 && qtyMinLength != qtyUpperCases + 1 ) )
185 #endif
186 }
187
188 // normal
189 #if ALIB_DEBUG
190 else
192 #endif
193 }
194
195 // check segment sizes against minLengths
196 #if ALIB_DEBUG
197 if( int(format) < 0 )
198 return;
199
200 if( GetFormat() == Formats::Normal ) {
201 if( minLengths[0] > definitionName.Length() ) {
203 return;
204 }
205 if( minLengths[0] <= 0 ) {
207 return;
208 }
209 } else {
210 int segmentNo = 0;
211 int segmentLength= 0;
212 integer charIdx = 1;
213 while( charIdx < definitionName.Length() ) {
214 ++segmentLength;
215 character c= definitionName.CharAt( charIdx++ );
216 bool segmentEnd= c == '\0'
217 || (format == Formats::SnakeCase && c == '_' )
218 || (format == Formats::KebabCase && c == '-' )
219 || (format == Formats::CamelCase && isalpha(c) && isupper(c) );
220
221 if( segmentEnd ) {
222 if( segmentNo < 7 && minLengths[segmentNo] > segmentLength ) {
224 return;
225 }
226
227 segmentLength= (format == Formats::CamelCase ? 1 : 0);
228 ++segmentNo;
229 } }
230
231 for( int minLenIdx= 0 ; minLenIdx < 7 && minLengths[minLenIdx] >= 0 ; ++minLenIdx ) {
232 if( minLengths[minLenIdx] == 0
234 || !( minLenIdx == 6 || minLengths[minLenIdx + 1] == -1 ) ) )
235 {
237 return;
238 } } }
239 #endif
240
241
242}
243
244bool Token::Match( const String& needle ) {
245 ALIB_ASSERT_ERROR( needle.Length() > 0, "STRINGS/TOK",
246 "Empty search string in when matching function name." )
247 lang::Case sensitivity= Sensitivity();
248
249 Formats caseType= GetFormat();
250 bool isNormal= (caseType == Formats::Normal );
251 bool isCamel = (caseType == Formats::CamelCase );
252 bool isSnake = (caseType == Formats::SnakeCase );
253 bool isKebab = (caseType == Formats::KebabCase );
254
255 int segNo = 0;
256 int segLen = 0;
257 bool same = false;
258 integer hIdx = 0;
259 integer nIdx = 0;
260 integer rollbackLen = 0;
261 bool isSegOK = false;
262 int segMinLen = minLengths[0];
263 while( hIdx < definitionName.Length() ) {
264 // read current haystack and needle
265 ++segLen;
266 character h= definitionName .CharAt( hIdx++ );
267 character n= needle.CharAt( nIdx++ );
268
269 same= sensitivity == lang::Case::Ignore
272 : h
273 == n;
274
275 // special CamelCase treatment
276 if( isCamel ) {
277 // end of needle and final, omitable segment?
278 if( n == '\0' && segMinLen == 0)
279 return true;
280
281 // rollback
282 if( !same ) {
283 if( segLen == 1 && rollbackLen > 0) {
284 nIdx-= 2;
285 --rollbackLen;
286 --hIdx;
287 --segLen;
288 continue;
289 }
290
291 --nIdx;
292 }
293
294 if( segLen == 1)
295 rollbackLen= 0;
296
297 else if( same && isSegOK )
298 ++rollbackLen;
299 }
300
301 // end of haystack segment?
302 bool isSegEnd= hIdx == definitionName.Length()
303 || (isSnake && h == '_' )
304 || (isKebab && h == '-' )
305 || (isCamel && isalpha(definitionName.CharAt( hIdx ))
306 && isupper(definitionName.CharAt( hIdx )) );
307
308 // update segOK flag
309 if( same ) {
310 isSegOK= ( ( segMinLen >= 0 && segLen >= segMinLen )
311 || ( segMinLen < 0 && isSegEnd ) );
312 }
313
314 // result false, if not same and first of actual segment
315 else if( segLen == 1 && segMinLen != 0 )
316 return false;
317
318
319 // end of segment and needle not empty?
320 if( isSegEnd && n != '\0') {
321 if( !isSegOK )
322 return false;
323 }
324
325 // not same and either not end of segment or empty needle
326 else if( !same ) {
327 if( !isSegOK )
328 return false;
329
330 // skip rest of segment
331 while( h != '\0'
332 && ( ( isCamel && (!isalpha(h) || !isupper(h) ) )
333 || ( isSnake && h != '_' )
334 || ( isKebab && h != '-' ) ) )
335 h= definitionName.CharAt( hIdx++ );
336
337 if( isCamel )
338 --hIdx;
339 }
340
341 // start new segment
342 if( !same || isSegEnd ) {
343 ++segNo;
344 segLen= 0;
345 segMinLen = segNo < 7 ? minLengths[segNo] : -2;
346
347 // oh,oh!
348 if( n == '\0' && (!isCamel || h == '\0' || rollbackLen == 0) )
349 return h == '\0' || isNormal || segMinLen == 0;
350 } }
351
352 return same && isSegOK && (nIdx == needle.Length());
353}
354
355} // namespace [alib::strings::util]
#define ALIB_ASSERT_ERROR(cond, domain,...)
#define ALIB_REL_DBG(releaseCode,...)
constexpr integer Length() const
Definition string.hpp:300
TChar CharAtStart() const
Definition string.hpp:417
TChar CharAt(integer idx) const
Definition string.hpp:399
constexpr bool IsNotEmpty() const
Definition string.hpp:353
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.hpp:149
@ TooManyMinLengthsGiven
A maximum of 7 minimum length values was exceeded.
Definition token.hpp:151
@ ErrorReadingMinLengths
Error parsing the list of minimum lengths.
Definition token.hpp:150
@ DefinitionStringNotConsumed
The definition string was not completely consumed.
Definition token.hpp:158
void detectFormat()
Detects snake_case, kebab-case or CamelCase.
Definition token.cpp:130
int8_t GetMinLength(int idx) const
Definition token.hpp:335
String definitionName
The tokens' definition string part.
Definition token.hpp:166
const String & GetDefinitionName() const
Definition token.hpp:272
lang::Case Sensitivity() const
Definition token.hpp:316
Formats
Format types detected with #".detectFormat".
Definition token.hpp:134
@ CamelCase
UpperCamelCase or lowerCamelCase.
Definition token.hpp:138
@ SnakeCase
snake_case using underscores.
Definition token.hpp:136
@ Normal
Normal, optionally abbreviated words.
Definition token.hpp:135
@ KebabCase
kebab-case using hyphens.
Definition token.hpp:137
Token()
Parameterless constructor. Creates an "undefined" token.
Definition token.hpp:188
Formats format
Defines the "case type" as well as the letter case sensitivity of this token.
Definition token.hpp:173
String exportName
The tokens' optional explicit export name.
Definition token.hpp:169
static constexpr Formats ignoreCase
Letter case sensitivity. This is combined with the format bits.
Definition token.hpp:181
Formats GetFormat() const
Definition token.hpp:303
void Define(const String &definition, character separator=';')
Definition token.cpp:57
bool Match(const String &needle)
Definition token.cpp:244
void GetExportName(AString &target) const
Definition token.cpp:36
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.
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".
strings::TAString< character, lang::HeapAllocator > AString
Type alias in namespace #"%alib".
characters::character character
Type alias in namespace #"%alib".