KiCad PCB EDA Suite
Loading...
Searching...
No Matches
sim_value.cpp
Go to the documentation of this file.
1/*
2 * This program source code file is part of KiCad, a free EDA CAD application.
3 *
4 * Copyright (C) 2022 Mikolaj Wielgus
5 * Copyright (C) 2022-2024 KiCad Developers, see AUTHORS.txt for contributors.
6 *
7 * This program is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU General Public License
9 * as published by the Free Software Foundation; either version 3
10 * of the License, or (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, you may find one here:
19 * https://www.gnu.org/licenses/gpl-3.0.html
20 * or you may search the http://www.gnu.org website for the version 3 license,
21 * or you may write to the Free Software Foundation, Inc.,
22 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
23 */
24
25#include <sim/sim_value.h>
26#include <wx/translation.h>
27#include <ki_exception.h>
28#include <locale_io.h>
29#include <pegtl/contrib/parse_tree.hpp>
30#include <fmt/core.h>
31#include <math/util.h>
32#include <wx/regex.h>
33
34
35#define CALL_INSTANCE( ValueType, Notation, func, ... ) \
36 switch( ValueType ) \
37 { \
38 case SIM_VALUE::TYPE_INT: \
39 switch( Notation ) \
40 { \
41 case NOTATION::SI: \
42 func<SIM_VALUE::TYPE_INT, NOTATION::SI>( __VA_ARGS__ ); \
43 break; \
44 \
45 case NOTATION::SPICE: \
46 func<SIM_VALUE::TYPE_INT, NOTATION::SPICE>( __VA_ARGS__ ); \
47 break; \
48 } \
49 break; \
50 \
51 case SIM_VALUE::TYPE_FLOAT: \
52 switch( Notation ) \
53 { \
54 case NOTATION::SI: \
55 func<SIM_VALUE::TYPE_FLOAT, NOTATION::SI>( __VA_ARGS__ ); \
56 break; \
57 \
58 case NOTATION::SPICE: \
59 func<SIM_VALUE::TYPE_FLOAT, NOTATION::SPICE>( __VA_ARGS__ ); \
60 break; \
61 } \
62 break; \
63 \
64 case SIM_VALUE::TYPE_BOOL: \
65 case SIM_VALUE::TYPE_COMPLEX: \
66 case SIM_VALUE::TYPE_STRING: \
67 case SIM_VALUE::TYPE_BOOL_VECTOR: \
68 case SIM_VALUE::TYPE_INT_VECTOR: \
69 case SIM_VALUE::TYPE_FLOAT_VECTOR: \
70 case SIM_VALUE::TYPE_COMPLEX_VECTOR: \
71 wxFAIL_MSG( "Unhandled SIM_VALUE type" ); \
72 break; \
73 }
74
75
77{
78 using namespace SIM_VALUE_GRAMMAR;
79
80 template <typename Rule>
81 struct numberSelector : std::false_type {};
82
83 // TODO: Reorder. NOTATION should be before TYPE.
84
85 template <> struct numberSelector<SIM_VALUE_GRAMMAR::significand<SIM_VALUE::TYPE_INT>>
86 : std::true_type {};
87 template <> struct numberSelector<SIM_VALUE_GRAMMAR::significand<SIM_VALUE::TYPE_FLOAT>>
88 : std::true_type {};
89 template <> struct numberSelector<intPart> : std::true_type {};
90 template <> struct numberSelector<fracPart> : std::true_type {};
91 template <> struct numberSelector<exponent> : std::true_type {};
92 template <> struct numberSelector<unitPrefix<SIM_VALUE::TYPE_INT, NOTATION::SI>>
93 : std::true_type {};
94 template <> struct numberSelector<unitPrefix<SIM_VALUE::TYPE_INT, NOTATION::SPICE>>
95 : std::true_type {};
96 template <> struct numberSelector<unitPrefix<SIM_VALUE::TYPE_FLOAT, NOTATION::SI>>
97 : std::true_type {};
98 template <> struct numberSelector<unitPrefix<SIM_VALUE::TYPE_FLOAT, NOTATION::SPICE>>
99 : std::true_type {};
100
102 {
103 bool isOk = true;
104 bool isEmpty = true;
105 std::string significand;
106 std::optional<int64_t> intPart;
107 std::optional<int64_t> fracPart;
108 std::optional<int> exponent;
109 std::optional<int> unitPrefixExponent;
110 };
111
112 PARSE_RESULT Parse( const std::string& aString,
113 NOTATION aNotation = NOTATION::SI,
115
116 int UnitPrefixToExponent( std::string aPrefix, NOTATION aNotation = NOTATION::SI );
117 std::string ExponentToUnitPrefix( double aExponent, int& aExponentReduction,
118 NOTATION aNotation = NOTATION::SI );
119 }
120
121
122template <SIM_VALUE::TYPE ValueType, SIM_VALUE_PARSER::NOTATION Notation>
123static inline void doIsValid( tao::pegtl::string_input<>& aIn )
124{
125 tao::pegtl::parse<SIM_VALUE_PARSER::numberGrammar<ValueType, Notation>>( aIn );
126}
127
128
129bool SIM_VALUE_GRAMMAR::IsValid( const std::string& aString, SIM_VALUE::TYPE aValueType,
130 NOTATION aNotation )
131{
132 tao::pegtl::string_input<> in( aString, "from_content" );
133
134 try
135 {
136 CALL_INSTANCE( aValueType, aNotation, doIsValid, in );
137 }
138 catch( const tao::pegtl::parse_error& )
139 {
140 return false;
141 }
142
143 return true;
144}
145
146
147template <SIM_VALUE::TYPE ValueType, SIM_VALUE_PARSER::NOTATION Notation>
148static inline std::unique_ptr<tao::pegtl::parse_tree::node> doParse(
149 tao::pegtl::string_input<>& aIn )
150{
151 return tao::pegtl::parse_tree::parse<SIM_VALUE_PARSER::numberGrammar<ValueType, Notation>,
153 ( aIn );
154}
155
156
157template <SIM_VALUE::TYPE ValueType, SIM_VALUE_PARSER::NOTATION Notation>
158static inline void handleNodeForParse( tao::pegtl::parse_tree::node& aNode,
159 SIM_VALUE_PARSER::PARSE_RESULT& aParseResult )
160{
161 if( aNode.is_type<SIM_VALUE_PARSER::significand<ValueType>>() )
162 {
163 aParseResult.significand = aNode.string();
164 aParseResult.isEmpty = false;
165
166 for( const auto& subnode : aNode.children )
167 {
168 try
169 {
170 if( subnode->is_type<SIM_VALUE_PARSER::intPart>() )
171 aParseResult.intPart = std::stoll( subnode->string() );
172 else if( subnode->is_type<SIM_VALUE_PARSER::fracPart>() )
173 aParseResult.fracPart = std::stoll( subnode->string() );
174 }
175 catch( const std::exception& )
176 {
177 aParseResult.isOk = false;
178 }
179 }
180 }
181 else if( aNode.is_type<SIM_VALUE_PARSER::exponent>() )
182 {
183 aParseResult.exponent = std::stoi( aNode.string() );
184 aParseResult.isEmpty = false;
185 }
186 else if( aNode.is_type<SIM_VALUE_PARSER::unitPrefix<ValueType, Notation>>() )
187 {
188 aParseResult.unitPrefixExponent = SIM_VALUE_PARSER::UnitPrefixToExponent( aNode.string(),
189 Notation );
190 aParseResult.isEmpty = false;
191 }
192 else
193 wxFAIL_MSG( "Unhandled parse tree node" );
194}
195
196
198 NOTATION aNotation,
199 SIM_VALUE::TYPE aValueType )
200{
201 LOCALE_IO toggle;
202
203 tao::pegtl::string_input<> in( aString, "from_content" );
204 std::unique_ptr<tao::pegtl::parse_tree::node> root;
205 PARSE_RESULT result;
206
207 try
208 {
209 CALL_INSTANCE( aValueType, aNotation, root = doParse, in );
210 }
211 catch( tao::pegtl::parse_error& )
212 {
213 result.isOk = false;
214 return result;
215 }
216
217 wxASSERT( root );
218
219 try
220 {
221 for( const auto& node : root->children )
222 {
223 CALL_INSTANCE( aValueType, aNotation, handleNodeForParse, *node, result );
224 }
225 }
226 catch( const std::invalid_argument& e )
227 {
228 wxFAIL_MSG( fmt::format( "Parsing simulator value failed: {:s}", e.what() ) );
229 result.isOk = false;
230 }
231
232 return result;
233}
234
235
236int SIM_VALUE_PARSER::UnitPrefixToExponent( std::string aPrefix, NOTATION aNotation )
237{
238 switch( aNotation )
239 {
240 case NOTATION::SI:
241 if( aPrefix.empty() )
242 return 0;
243
244 switch( aPrefix[0] )
245 {
246 case 'a': return -18;
247 case 'f': return -15;
248 case 'p': return -12;
249 case 'n': return -9;
250 case 'u': return -6;
251 case 'm': return -3;
252 case 'k':
253 case 'K': return 3;
254 case 'M': return 6;
255 case 'G': return 9;
256 case 'T': return 12;
257 case 'P': return 15;
258 case 'E': return 18;
259 }
260
261 break;
262
263 case NOTATION::SPICE:
264 std::transform( aPrefix.begin(), aPrefix.end(), aPrefix.begin(),
265 ::tolower );
266
267 if( aPrefix == "f" )
268 return -15;
269 else if( aPrefix == "p" )
270 return -12;
271 else if( aPrefix == "n" )
272 return -9;
273 else if( aPrefix == "u" )
274 return -6;
275 else if( aPrefix == "m" )
276 return -3;
277 else if( aPrefix == "" )
278 return 0;
279 else if( aPrefix == "k" )
280 return 3;
281 else if( aPrefix == "meg" )
282 return 6;
283 else if( aPrefix == "g" )
284 return 9;
285 else if( aPrefix == "t" )
286 return 12;
287
288 break;
289 }
290
291 wxFAIL_MSG( fmt::format( "Unknown simulator value suffix: '{:s}'", aPrefix ) );
292 return 0;
293}
294
295
296std::string SIM_VALUE_PARSER::ExponentToUnitPrefix( double aExponent, int& aExponentReduction,
297 NOTATION aNotation )
298{
299 if( aNotation == NOTATION::SI && aExponent >= -18 && aExponent <= -15 )
300 {
301 aExponentReduction = -18;
302 return "a";
303 }
304 else if( aExponent >= -15 && aExponent < -12 )
305 {
306 aExponentReduction = -15;
307 return "f";
308 }
309 else if( aExponent >= -12 && aExponent < -9 )
310 {
311 aExponentReduction = -12;
312 return "p";
313 }
314 else if( aExponent >= -9 && aExponent < -6 )
315 {
316 aExponentReduction = -9;
317 return "n";
318 }
319 else if( aExponent >= -6 && aExponent < -3 )
320 {
321 aExponentReduction = -6;
322 return "u";
323 }
324 else if( aExponent >= -3 && aExponent < 0 )
325 {
326 aExponentReduction = -3;
327 return "m";
328 }
329 else if( aExponent >= 0 && aExponent < 3 )
330 {
331 aExponentReduction = 0;
332 return "";
333 }
334 else if( aExponent >= 3 && aExponent < 6 )
335 {
336 aExponentReduction = 3;
337 return "k";
338 }
339 else if( aExponent >= 6 && aExponent < 9 )
340 {
341 aExponentReduction = 6;
342 return ( aNotation == NOTATION::SI ) ? "M" : "Meg";
343 }
344 else if( aExponent >= 9 && aExponent < 12 )
345 {
346 aExponentReduction = 9;
347 return "G";
348 }
349 else if( aExponent >= 12 && aExponent < 15 )
350 {
351 aExponentReduction = 12;
352 return "T";
353 }
354 else if( aNotation == NOTATION::SI && aExponent >= 15 && aExponent < 18 )
355 {
356 aExponentReduction = 15;
357 return "P";
358 }
359 else if( aNotation == NOTATION::SI && aExponent >= 18 && aExponent <= 21 )
360 {
361 aExponentReduction = 18;
362 return "E";
363 }
364
365 aExponentReduction = 0;
366 return "";
367}
368
369
370std::string SIM_VALUE::ConvertNotation( const std::string& aString, NOTATION aFromNotation,
371 NOTATION aToNotation )
372{
373 wxString buf( aString );
374 buf.Replace( ',', '.' );
375
376 SIM_VALUE_PARSER::PARSE_RESULT parseResult = SIM_VALUE_PARSER::Parse( buf.ToStdString(),
377 aFromNotation );
378
379 if( parseResult.isOk && !parseResult.isEmpty && !parseResult.significand.empty() )
380 {
381 int exponent = parseResult.exponent ? *parseResult.exponent : 0;
382 exponent += parseResult.unitPrefixExponent ? *parseResult.unitPrefixExponent : 0;
383
384 try
385 {
386 LOCALE_IO toggle;
387 int expReduction = 0;
388 std::string prefix = SIM_VALUE_PARSER::ExponentToUnitPrefix( exponent, expReduction,
389 aToNotation );
390
391 exponent -= expReduction;
392 return fmt::format( "{:g}{}",
393 std::stod( parseResult.significand ) * std::pow( 10, exponent ),
394 prefix );
395 }
396 catch( const std::invalid_argument& )
397 {
398 // best efforts
399 }
400 }
401
402 return aString;
403}
404
405
406std::string SIM_VALUE::Normalize( double aValue )
407{
408 double exponent = std::log10( std::abs( aValue ) );
409 int expReduction = 0;
410
411 std::string prefix = SIM_VALUE_PARSER::ExponentToUnitPrefix( exponent, expReduction,
412 NOTATION::SI );
413 double reducedValue = aValue / std::pow( 10, expReduction );
414
415 return fmt::format( "{:g}{}", reducedValue, prefix );
416}
417
418
419std::string SIM_VALUE::ToSpice( const std::string& aString )
420{
421 // Notation conversion is very slow. Avoid if possible.
422
423 auto plainNumber =
424 []( const std::string& aStringVal )
425 {
426 for( char c : aStringVal )
427 {
428 if( c != '.' && ( c < '0' || c > '9' ) )
429 return false;
430 }
431
432 return true;
433 };
434
435 if( plainNumber( aString ) )
436 return aString;
437 else
438 return ConvertNotation( aString, NOTATION::SI, NOTATION::SPICE );
439}
440
441
442double SIM_VALUE::ToDouble( const std::string& aString, double aDefault )
443{
444 SIM_VALUE_PARSER::PARSE_RESULT parseResult = SIM_VALUE_PARSER::Parse( aString, NOTATION::SI );
445
446 if( parseResult.isOk && !parseResult.isEmpty && !parseResult.significand.empty() )
447 {
448 try
449 {
450 LOCALE_IO toggle;
451 int exponent = parseResult.exponent ? *parseResult.exponent : 0;
452
453 exponent += parseResult.unitPrefixExponent ? *parseResult.unitPrefixExponent : 0;
454
455 return std::stod( parseResult.significand ) * std::pow( 10, exponent );
456 }
457 catch( const std::invalid_argument& )
458 {
459 // best efforts
460 }
461 }
462
463 return aDefault;
464}
465
466
467int SIM_VALUE::ToInt( const std::string& aString, int aDefault )
468{
469 SIM_VALUE_PARSER::PARSE_RESULT parseResult = SIM_VALUE_PARSER::Parse( aString, NOTATION::SI );
470
471 if( parseResult.isOk
472 && !parseResult.isEmpty
473 && parseResult.intPart
474 && ( !parseResult.fracPart || *parseResult.fracPart == 0 ) )
475 {
476 int exponent = parseResult.exponent ? *parseResult.exponent : 0;
477 exponent += parseResult.unitPrefixExponent ? *parseResult.unitPrefixExponent : 0;
478
479 if( exponent >= 0 )
480 return (int) *parseResult.intPart * (int) std::pow( 10, exponent );
481 }
482
483 return aDefault;
484}
485
486
487bool SIM_VALUE::Equal( double aLH, const std::string& aRH )
488{
489 return std::abs( aLH - ToDouble( aRH ) ) <= std::numeric_limits<double>::epsilon();
490}
Instantiate the current locale within a scope in which you are expecting exceptions to be thrown.
Definition: locale_io.h:49
static std::string Normalize(double aValue)
Definition: sim_value.cpp:406
static bool Equal(double aLH, const std::string &aRH)
Definition: sim_value.cpp:487
static std::string ConvertNotation(const std::string &aString, NOTATION aFromNotation, NOTATION aToNotation)
Definition: sim_value.cpp:370
@ TYPE_FLOAT
Definition: sim_value.h:69
static double ToDouble(const std::string &aString, double aDefault=NAN)
Definition: sim_value.cpp:442
static int ToInt(const std::string &aString, int aDefault=-1)
Definition: sim_value.cpp:467
static std::string ToSpice(const std::string &aString)
Definition: sim_value.cpp:419
bool IsValid(const std::string &aString, SIM_VALUE::TYPE aValueType=SIM_VALUE::TYPE_FLOAT, NOTATION aNotation=NOTATION::SI)
Definition: sim_value.cpp:129
PARSE_RESULT Parse(const std::string &aString, NOTATION aNotation=NOTATION::SI, SIM_VALUE::TYPE aValueType=SIM_VALUE::TYPE_FLOAT)
Definition: sim_value.cpp:197
int UnitPrefixToExponent(std::string aPrefix, NOTATION aNotation=NOTATION::SI)
Definition: sim_value.cpp:236
std::string ExponentToUnitPrefix(double aExponent, int &aExponentReduction, NOTATION aNotation=NOTATION::SI)
Definition: sim_value.cpp:296
EDA_ANGLE abs(const EDA_ANGLE &aAngle)
Definition: eda_angle.h:390
static void handleNodeForParse(tao::pegtl::parse_tree::node &aNode, SIM_VALUE_PARSER::PARSE_RESULT &aParseResult)
Definition: sim_value.cpp:158
static std::unique_ptr< tao::pegtl::parse_tree::node > doParse(tao::pegtl::string_input<> &aIn)
Definition: sim_value.cpp:148
#define CALL_INSTANCE(ValueType, Notation, func,...)
Definition: sim_value.cpp:35
static void doIsValid(tao::pegtl::string_input<> &aIn)
Definition: sim_value.cpp:123
std::optional< int64_t > fracPart
Definition: sim_value.cpp:107
std::optional< int > unitPrefixExponent
Definition: sim_value.cpp:109
std::optional< int > exponent
Definition: sim_value.cpp:108
std::optional< int64_t > intPart
Definition: sim_value.cpp:106