KiCad PCB EDA Suite
NUMERIC_EVALUATOR Class Reference

#include <numeric_evaluator.h>

Classes

struct  Token
 
struct  TokenStat
 

Public Member Functions

 NUMERIC_EVALUATOR (EDA_UNITS aUnits)
 
 ~NUMERIC_EVALUATOR ()
 
void Clear ()
 
void SetDefaultUnits (EDA_UNITS aUnits)
 
void LocaleChanged ()
 
void parseError (const char *s)
 
void parseOk ()
 
void parseSetResult (double)
 
bool IsValid () const
 
wxString Result () const
 
bool Process (const wxString &aString)
 
wxString OriginalText () const
 
void SetVar (const wxString &aString, double aValue)
 
double GetVar (const wxString &aString)
 
void RemoveVar (const wxString &aString)
 
void ClearVar ()
 

Protected Member Functions

void newString (const wxString &aString)
 
Token getToken ()
 
void parse (int token, numEval::TokenType value)
 

Private Types

enum class  Unit {
  Invalid , MM , CM , Inch ,
  Mil , Degrees , SI
}
 

Private Attributes

void * m_parser
 
struct NUMERIC_EVALUATOR::TokenStat m_token
 
char m_localeDecimalSeparator
 
bool m_parseError
 
bool m_parseFinished
 
Unit m_defaultUnits
 
wxString m_originalText
 
std::map< wxString, double > m_varMap
 

Detailed Description

Definition at line 93 of file numeric_evaluator.h.

Member Enumeration Documentation

◆ Unit

enum class NUMERIC_EVALUATOR::Unit
strongprivate
Enumerator
Invalid 
MM 
CM 
Inch 
Mil 
Degrees 
SI 

Definition at line 95 of file numeric_evaluator.h.

95{ Invalid, MM, CM, Inch, Mil, Degrees, SI };

Constructor & Destructor Documentation

◆ NUMERIC_EVALUATOR()

NUMERIC_EVALUATOR::NUMERIC_EVALUATOR ( EDA_UNITS  aUnits)

Definition at line 47 of file numeric_evaluator.cpp.

48{
50
51 m_parseError = false;
52 m_parseFinished = false;
53
54 m_parser = numEval::ParseAlloc( malloc );
55
56 SetDefaultUnits( aUnits );
57}
void SetDefaultUnits(EDA_UNITS aUnits)

References LocaleChanged(), m_parseError, m_parseFinished, m_parser, and SetDefaultUnits().

◆ ~NUMERIC_EVALUATOR()

NUMERIC_EVALUATOR::~NUMERIC_EVALUATOR ( )

Definition at line 60 of file numeric_evaluator.cpp.

61{
62 numEval::ParseFree( m_parser, free );
63
64 // Allow explicit call to destructor
65 m_parser = nullptr;
66
67 Clear();
68}

References Clear(), and m_parser.

Member Function Documentation

◆ Clear()

void NUMERIC_EVALUATOR::Clear ( )

◆ ClearVar()

void NUMERIC_EVALUATOR::ClearVar ( )
inline

Definition at line 140 of file numeric_evaluator.h.

140{ m_varMap.clear(); }
std::map< wxString, double > m_varMap

References m_varMap.

◆ getToken()

NUMERIC_EVALUATOR::Token NUMERIC_EVALUATOR::getToken ( )
protected

Definition at line 190 of file numeric_evaluator.cpp.

191{
192 Token retval;
193 size_t idx;
194
195 retval.token = ENDS;
196 retval.value.dValue = 0;
197 retval.value.valid = false;
198 retval.value.text[0] = 0;
199
200 if( m_token.token == nullptr )
201 return retval;
202
203 if( m_token.input == nullptr )
204 return retval;
205
206 if( m_token.pos >= m_token.inputLen )
207 return retval;
208
209 // Support for old school decimal separators (ie: "2K2")
210 auto isOldSchoolDecimalSeparator =
211 []( char ch, double* siScaler ) -> bool
212 {
213 switch( ch )
214 {
215 case 'a': *siScaler = 1.0e-18; return true;
216 case 'f': *siScaler = 1.0e-15; return true;
217 case 'p': *siScaler = 1.0e-12; return true;
218 case 'n': *siScaler = 1.0e-9; return true;
219 case 'u': *siScaler = 1.0e-6; return true;
220 case 'm': *siScaler = 1.0e-3; return true;
221 case 'k':
222 case 'K': *siScaler = 1.0e3; return true;
223 case 'M': *siScaler = 1.0e6; return true;
224 case 'G': *siScaler = 1.0e9; return true;
225 case 'T': *siScaler = 1.0e12; return true;
226 case 'P': *siScaler = 1.0e15; return true;
227 case 'E': *siScaler = 1.0e18; return true;
228 default: return false;
229 }
230 };
231
232 auto isDecimalSeparator =
233 [&]( char ch ) -> bool
234 {
235 double dummy;
236
237 if( ch == m_localeDecimalSeparator || ch == '.' || ch == ',' )
238 return true;
239
240 if( m_defaultUnits == Unit::SI && isOldSchoolDecimalSeparator( ch, &dummy ) )
241 return true;
242
243 return false;
244 };
245
246 // Lambda: get value as string, store into clToken.token and update current index.
247 auto extractNumber =
248 [&]( double* aScaler )
249 {
250 bool haveSeparator = false;
251 double siScaler = 1.0;
252 char ch = m_token.input[ m_token.pos ];
253
254 idx = 0;
255
256 do
257 {
258 if( isDecimalSeparator( ch ) )
259 {
260 if( haveSeparator )
261 break;
262 else
263 haveSeparator = true;
264
265 if( isOldSchoolDecimalSeparator( ch, &siScaler ) )
266 *aScaler = siScaler;
267
269 }
270 else
271 {
272 m_token.token[ idx++ ] = ch;
273 }
274
275 ch = m_token.input[ ++m_token.pos ];
276 } while( isdigit( ch ) || isDecimalSeparator( ch ) );
277
278 m_token.token[ idx ] = 0;
279 };
280
281 // Lamda: Get unit for current token.
282 // Valid units are ", in, mm, mil and thou. Returns Unit::Invalid otherwise.
283 auto checkUnit =
284 [&]( double* siScaler ) -> Unit
285 {
286 char ch = m_token.input[ m_token.pos ];
287
288 if( ch == '"' )
289 {
290 m_token.pos++;
291 return Unit::Inch;
292 }
293
294 // Do not use strcasecmp() as it is not available on all platforms
295 const char* cptr = &m_token.input[ m_token.pos ];
296 const auto sizeLeft = m_token.inputLen - m_token.pos;
297
298 // We should really give this unicode support
299 if( sizeLeft >= 2 && ch == '\xC2' && cptr[1] == '\xB0' )
300 {
301 m_token.pos += 2;
302 return Unit::Degrees;
303 }
304
305 if( sizeLeft >= 2 && ch == 'm' && cptr[ 1 ] == 'm' && !isalnum( cptr[ 2 ] ) )
306 {
307 m_token.pos += 2;
308 return Unit::MM;
309 }
310
311 if( sizeLeft >= 2 && ch == 'c' && cptr[ 1 ] == 'm' && !isalnum( cptr[ 2 ] ) )
312 {
313 m_token.pos += 2;
314 return Unit::CM;
315 }
316
317 if( sizeLeft >= 2 && ch == 'i' && cptr[ 1 ] == 'n' && !isalnum( cptr[ 2 ] ) )
318 {
319 m_token.pos += 2;
320 return Unit::Inch;
321 }
322
323 if( sizeLeft >= 3 && ch == 'm' && cptr[ 1 ] == 'i' && cptr[ 2 ] == 'l'
324 && !isalnum( cptr[ 3 ] ) )
325 {
326 m_token.pos += 3;
327 return Unit::Mil;
328 }
329
330 if( sizeLeft >= 4 && ch == 't' && cptr[ 1 ] == 'h' && cptr[ 2 ] == 'o'
331 && cptr[ 3 ] == 'u' && !isalnum( cptr[ 4 ] ) )
332 {
333 m_token.pos += 4;
334 return Unit::Mil;
335 }
336
337 if( m_defaultUnits == Unit::SI && sizeLeft >= 1
338 && isOldSchoolDecimalSeparator( ch, siScaler ) )
339 {
340 m_token.pos++;
341 return Unit::SI;
342 }
343
344 return Unit::Invalid;
345 };
346
347 char ch;
348
349 // Start processing of first/next token: Remove whitespace
350 for( ;; )
351 {
352 ch = m_token.input[ m_token.pos ];
353
354 if( ch == ' ' )
355 m_token.pos++;
356 else
357 break;
358 }
359
360 double siScaler = 1.0;
361 Unit convertFrom = Unit::Invalid;
362
363 if( ch == 0 )
364 {
365 /* End of input */
366 }
367 else if( isdigit( ch ) || isDecimalSeparator( ch ) )
368 {
369 // VALUE
370 extractNumber( &siScaler );
371 retval.token = VALUE;
372 retval.value.dValue = atof( m_token.token ) * siScaler;
373 }
374 else if( ( convertFrom = checkUnit( &siScaler ) ) != Unit::Invalid )
375 {
376 // UNIT
377 // Units are appended to a VALUE.
378 // Determine factor to default unit if unit for value is given.
379 // Example: Default is mm, unit is inch: factor is 25.4
380 // The factor is assigned to the terminal UNIT. The actual
381 // conversion is done within a parser action.
382 retval.token = UNIT;
383
384 if( m_defaultUnits == Unit::MM )
385 {
386 switch( convertFrom )
387 {
388 case Unit::Inch: retval.value.dValue = 25.4; break;
389 case Unit::Mil: retval.value.dValue = 25.4 / 1000.0; break;
390 case Unit::MM: retval.value.dValue = 1.0; break;
391 case Unit::CM: retval.value.dValue = 10.0; break;
392 default:
393 case Unit::Invalid: break;
394 }
395 }
396 else if( m_defaultUnits == Unit::Inch )
397 {
398 switch( convertFrom )
399 {
400 case Unit::Inch: retval.value.dValue = 1.0; break;
401 case Unit::Mil: retval.value.dValue = 1.0 / 1000.0; break;
402 case Unit::MM: retval.value.dValue = 1.0 / 25.4; break;
403 case Unit::CM: retval.value.dValue = 1.0 / 2.54; break;
404 default:
405 case Unit::Invalid: break;
406 }
407 }
408 else if( m_defaultUnits == Unit::Mil )
409 {
410 switch( convertFrom )
411 {
412 case Unit::Inch: retval.value.dValue = 1.0 * 1000.0; break;
413 case Unit::Mil: retval.value.dValue = 1.0; break;
414 case Unit::MM: retval.value.dValue = 1000.0 / 25.4; break;
415 case Unit::CM: retval.value.dValue = 1000.0 / 2.54; break;
416 default:
417 case Unit::Invalid: break;
418 }
419 }
420 else if( m_defaultUnits == Unit::Degrees && convertFrom == Unit::Degrees )
421 {
422 retval.value.dValue = 1.0;
423 }
424 else if( m_defaultUnits == Unit::SI )
425 {
426 retval.value.dValue = siScaler;
427 }
428 }
429 else if( isalpha( ch ) )
430 {
431 // VAR
432 const char* cptr = &m_token.input[ m_token.pos ];
433 cptr++;
434
435 while( isalnum( *cptr ) )
436 cptr++;
437
438 retval.token = VAR;
439 size_t bytesToCopy = cptr - &m_token.input[ m_token.pos ];
440
441 if( bytesToCopy >= sizeof( retval.value.text ) )
442 bytesToCopy = sizeof( retval.value.text ) - 1;
443
444 strncpy( retval.value.text, &m_token.input[ m_token.pos ], bytesToCopy );
445 retval.value.text[ bytesToCopy ] = 0;
446 m_token.pos += cptr - &m_token.input[ m_token.pos ];
447 }
448 else
449 {
450 // Single char tokens
451 switch( ch )
452 {
453 case '+': retval.token = PLUS; break;
454 case '-': retval.token = MINUS; break;
455 case '*': retval.token = MULT; break;
456 case '/': retval.token = DIVIDE; break;
457 case '(': retval.token = PARENL; break;
458 case ')': retval.token = PARENR; break;
459 case '=': retval.token = ASSIGN; break;
460 case ';': retval.token = SEMCOL; break;
461 default: m_parseError = true; break; /* invalid character */
462 }
463
464 m_token.pos++;
465 }
466
467 if( !m_parseError )
468 retval.value.valid = true;
469
470 return retval;
471}
std::vector< FAB_LAYER_COLOR > dummy

References CM, Degrees, dummy, numEval::TokenType::dValue, Inch, NUMERIC_EVALUATOR::TokenStat::input, NUMERIC_EVALUATOR::TokenStat::inputLen, Invalid, m_defaultUnits, m_localeDecimalSeparator, m_parseError, m_token, Mil, MM, NUMERIC_EVALUATOR::TokenStat::pos, SI, numEval::TokenType::text, NUMERIC_EVALUATOR::Token::token, NUMERIC_EVALUATOR::TokenStat::token, numEval::TokenType::valid, and NUMERIC_EVALUATOR::Token::value.

Referenced by Process().

◆ GetVar()

double NUMERIC_EVALUATOR::GetVar ( const wxString &  aString)

Definition at line 478 of file numeric_evaluator.cpp.

479{
480 auto it = m_varMap.find( aString );
481
482 if( it != m_varMap.end() )
483 {
484 return it->second;
485 }
486 else
487 {
488 m_parseError = true;
489 return 0.0;
490 }
491}

References m_parseError, and m_varMap.

◆ IsValid()

bool NUMERIC_EVALUATOR::IsValid ( ) const
inline

Definition at line 116 of file numeric_evaluator.h.

116{ return !m_parseError; }

References m_parseError.

◆ LocaleChanged()

void NUMERIC_EVALUATOR::LocaleChanged ( )

Definition at line 95 of file numeric_evaluator.cpp.

96{
97 struct lconv* lc = localeconv();
98 m_localeDecimalSeparator = *lc->decimal_point;
99}

References m_localeDecimalSeparator.

Referenced by NUMERIC_EVALUATOR(), and UNIT_BINDER::SetUnits().

◆ newString()

void NUMERIC_EVALUATOR::newString ( const wxString &  aString)
protected

◆ OriginalText()

wxString NUMERIC_EVALUATOR::OriginalText ( ) const

◆ parse()

void NUMERIC_EVALUATOR::parse ( int  token,
numEval::TokenType  value 
)
protected

◆ parseError()

void NUMERIC_EVALUATOR::parseError ( const char *  s)

Definition at line 103 of file numeric_evaluator.cpp.

104{
105 m_parseError = true;
106}

References m_parseError.

◆ parseOk()

void NUMERIC_EVALUATOR::parseOk ( )

Definition at line 110 of file numeric_evaluator.cpp.

111{
112 m_parseFinished = true;
113}

References m_parseFinished.

◆ parseSetResult()

void NUMERIC_EVALUATOR::parseSetResult ( double  val)

Definition at line 117 of file numeric_evaluator.cpp.

118{
119 if( std::isnan( val ) )
120 {
121 // Naively printing this with %g produces "nan" on some platforms
122 // and "-nan(ind)" on others (e.g. MSVC). So force a "standard" string.
123 snprintf( m_token.token, m_token.outputLen, "%s", "NaN" );
124 }
125 else
126 {
127 // Can be printed as a floating point
128 // Warning: DO NOT use a format like %f or %g, because they can create issues.
129 // especially %g can generate an exponent, incompatible with UNIT_BINDER
130 // Use the optimized UIDouble2Str
131 snprintf( m_token.token, m_token.outputLen, "%s", UIDouble2Str( val ).c_str() );
132 }
133}
std::string UIDouble2Str(double aValue)
Print a float number without using scientific notation and no trailing 0 We want to avoid scientific ...

References m_token, NUMERIC_EVALUATOR::TokenStat::outputLen, NUMERIC_EVALUATOR::TokenStat::token, and UIDouble2Str().

◆ Process()

bool NUMERIC_EVALUATOR::Process ( const wxString &  aString)

Definition at line 142 of file numeric_evaluator.cpp.

143{
144 // Feed parser token after token until end of input.
145
146 newString( aString );
147 m_parseError = false;
148 m_parseFinished = false;
149 Token tok;
150
151 if( aString.IsEmpty() )
152 {
153 m_parseFinished = true;
154 return true;
155 }
156
157 do
158 {
159 tok = getToken();
160 numEval::Parse( m_parser, tok.token, tok.value, this );
161
162 if( m_parseFinished || tok.token == ENDS )
163 {
164 // Reset parser by passing zero as token ID, value is ignored.
165 numEval::Parse( m_parser, 0, tok.value, this );
166 break;
167 }
168 } while( tok.token );
169
170 return !m_parseError;
171}
void newString(const wxString &aString)
PARSE_RESULT Parse(const std::string &aString, NOTATION aNotation=NOTATION::SI, SIM_VALUE::TYPE aValueType=SIM_VALUE::TYPE_FLOAT)
Definition: sim_value.cpp:189

References getToken(), m_parseError, m_parseFinished, m_parser, newString(), SIM_VALUE_PARSER::Parse(), NUMERIC_EVALUATOR::Token::token, and NUMERIC_EVALUATOR::Token::value.

Referenced by TEXT_CTRL_EVAL::evaluate(), UNIT_BINDER::GetDoubleValue(), UNIT_BINDER::GetValue(), SIM_STRING_PROPERTY::OnEvent(), UNIT_BINDER::onKillFocus(), and SIM_STRING_PROPERTY::StringToValue().

◆ RemoveVar()

void NUMERIC_EVALUATOR::RemoveVar ( const wxString &  aString)
inline

Definition at line 137 of file numeric_evaluator.h.

137{ m_varMap.erase( aString ); }

References m_varMap.

◆ Result()

wxString NUMERIC_EVALUATOR::Result ( ) const
inline

◆ SetDefaultUnits()

void NUMERIC_EVALUATOR::SetDefaultUnits ( EDA_UNITS  aUnits)

Definition at line 81 of file numeric_evaluator.cpp.

82{
83 switch( aUnits )
84 {
90 default: m_defaultUnits = Unit::MM; break;
91 }
92}

References DEGREES, Degrees, Inch, INCHES, m_defaultUnits, Mil, MILLIMETRES, MILS, MM, SI, and UNSCALED.

Referenced by NUMERIC_EVALUATOR(), and UNIT_BINDER::SetUnits().

◆ SetVar()

void NUMERIC_EVALUATOR::SetVar ( const wxString &  aString,
double  aValue 
)

Definition at line 473 of file numeric_evaluator.cpp.

474{
475 m_varMap[ aString ] = aValue;
476}

References m_varMap.

Member Data Documentation

◆ m_defaultUnits

Unit NUMERIC_EVALUATOR::m_defaultUnits
private

Definition at line 186 of file numeric_evaluator.h.

Referenced by getToken(), and SetDefaultUnits().

◆ m_localeDecimalSeparator

char NUMERIC_EVALUATOR::m_localeDecimalSeparator
private

Definition at line 180 of file numeric_evaluator.h.

Referenced by getToken(), and LocaleChanged().

◆ m_originalText

wxString NUMERIC_EVALUATOR::m_originalText
private

Definition at line 188 of file numeric_evaluator.h.

Referenced by Clear(), newString(), and OriginalText().

◆ m_parseError

bool NUMERIC_EVALUATOR::m_parseError
private

◆ m_parseFinished

bool NUMERIC_EVALUATOR::m_parseFinished
private

Definition at line 184 of file numeric_evaluator.h.

Referenced by newString(), NUMERIC_EVALUATOR(), parseOk(), and Process().

◆ m_parser

void* NUMERIC_EVALUATOR::m_parser
private

Definition at line 160 of file numeric_evaluator.h.

Referenced by NUMERIC_EVALUATOR(), Process(), and ~NUMERIC_EVALUATOR().

◆ m_token

struct NUMERIC_EVALUATOR::TokenStat NUMERIC_EVALUATOR::m_token
private

◆ m_varMap

std::map<wxString, double> NUMERIC_EVALUATOR::m_varMap
private

Definition at line 190 of file numeric_evaluator.h.

Referenced by ClearVar(), GetVar(), RemoveVar(), and SetVar().


The documentation for this class was generated from the following files: