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-2023 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
33
34#define CALL_INSTANCE( ValueType, Notation, func, ... ) \
35 switch( ValueType ) \
36 { \
37 case SIM_VALUE::TYPE_INT: \
38 switch( Notation ) \
39 { \
40 case NOTATION::SI: \
41 func<SIM_VALUE::TYPE_INT, NOTATION::SI>( __VA_ARGS__ ); \
42 break; \
43 \
44 case NOTATION::SPICE: \
45 func<SIM_VALUE::TYPE_INT, NOTATION::SPICE>( __VA_ARGS__ ); \
46 break; \
47 } \
48 break; \
49 \
50 case SIM_VALUE::TYPE_FLOAT: \
51 switch( Notation ) \
52 { \
53 case NOTATION::SI: \
54 func<SIM_VALUE::TYPE_FLOAT, NOTATION::SI>( __VA_ARGS__ ); \
55 break; \
56 \
57 case NOTATION::SPICE: \
58 func<SIM_VALUE::TYPE_FLOAT, NOTATION::SPICE>( __VA_ARGS__ ); \
59 break; \
60 } \
61 break; \
62 \
63 case SIM_VALUE::TYPE_BOOL: \
64 case SIM_VALUE::TYPE_COMPLEX: \
65 case SIM_VALUE::TYPE_STRING: \
66 case SIM_VALUE::TYPE_BOOL_VECTOR: \
67 case SIM_VALUE::TYPE_INT_VECTOR: \
68 case SIM_VALUE::TYPE_FLOAT_VECTOR: \
69 case SIM_VALUE::TYPE_COMPLEX_VECTOR: \
70 wxFAIL_MSG( "Unhandled SIM_VALUE type" ); \
71 break; \
72 }
73
74
76{
77 using namespace SIM_VALUE_GRAMMAR;
78
79 template <typename Rule>
80 struct numberSelector : std::false_type {};
81
82 // TODO: Reorder. NOTATION should be before TYPE.
83
84 template <> struct numberSelector<SIM_VALUE_GRAMMAR::significand<SIM_VALUE::TYPE_INT>>
85 : std::true_type {};
86 template <> struct numberSelector<SIM_VALUE_GRAMMAR::significand<SIM_VALUE::TYPE_FLOAT>>
87 : std::true_type {};
88 template <> struct numberSelector<intPart> : std::true_type {};
89 template <> struct numberSelector<fracPart> : std::true_type {};
90 template <> struct numberSelector<exponent> : std::true_type {};
91 template <> struct numberSelector<unitPrefix<SIM_VALUE::TYPE_INT, NOTATION::SI>>
92 : std::true_type {};
93 template <> struct numberSelector<unitPrefix<SIM_VALUE::TYPE_INT, NOTATION::SPICE>>
94 : std::true_type {};
95 template <> struct numberSelector<unitPrefix<SIM_VALUE::TYPE_FLOAT, NOTATION::SI>>
96 : std::true_type {};
97 template <> struct numberSelector<unitPrefix<SIM_VALUE::TYPE_FLOAT, NOTATION::SPICE>>
98 : std::true_type {};
99
101 {
102 bool isOk = true;
103 bool isEmpty = true;
104 std::string significand;
105 std::optional<int64_t> intPart;
106 std::optional<int64_t> fracPart;
107 std::optional<int> exponent;
108 std::optional<int> unitPrefixExponent;
109 };
110
111 PARSE_RESULT Parse( const std::string& aString,
112 NOTATION aNotation = NOTATION::SI,
114
115 int UnitPrefixToExponent( std::string aPrefix, NOTATION aNotation = NOTATION::SI );
116 std::string ExponentToUnitPrefix( double aExponent, int& aExponentReduction,
117 NOTATION aNotation = NOTATION::SI );
118 }
119
120
121template <SIM_VALUE::TYPE ValueType, SIM_VALUE_PARSER::NOTATION Notation>
122static inline void doIsValid( tao::pegtl::string_input<>& aIn )
123{
124 tao::pegtl::parse<SIM_VALUE_PARSER::numberGrammar<ValueType, Notation>>( aIn );
125}
126
127
128bool SIM_VALUE_GRAMMAR::IsValid( const std::string& aString, SIM_VALUE::TYPE aValueType,
129 NOTATION aNotation )
130{
131 tao::pegtl::string_input<> in( aString, "from_content" );
132
133 try
134 {
135 CALL_INSTANCE( aValueType, aNotation, doIsValid, in );
136 }
137 catch( const tao::pegtl::parse_error& )
138 {
139 return false;
140 }
141
142 return true;
143}
144
145
146template <SIM_VALUE::TYPE ValueType, SIM_VALUE_PARSER::NOTATION Notation>
147static inline std::unique_ptr<tao::pegtl::parse_tree::node> doParse(
148 tao::pegtl::string_input<>& aIn )
149{
150 return tao::pegtl::parse_tree::parse<SIM_VALUE_PARSER::numberGrammar<ValueType, Notation>,
152 ( aIn );
153}
154
155
156template <SIM_VALUE::TYPE ValueType, SIM_VALUE_PARSER::NOTATION Notation>
157static inline void handleNodeForParse( tao::pegtl::parse_tree::node& aNode,
158 SIM_VALUE_PARSER::PARSE_RESULT& aParseResult )
159{
160 if( aNode.is_type<SIM_VALUE_PARSER::significand<ValueType>>() )
161 {
162 aParseResult.significand = aNode.string();
163 aParseResult.isEmpty = false;
164
165 for( const auto& subnode : aNode.children )
166 {
167 try
168 {
169 if( subnode->is_type<SIM_VALUE_PARSER::intPart>() )
170 aParseResult.intPart = std::stoll( subnode->string() );
171 else if( subnode->is_type<SIM_VALUE_PARSER::fracPart>() )
172 aParseResult.fracPart = std::stoll( subnode->string() );
173 }
174 catch( const std::exception& )
175 {
176 aParseResult.isOk = false;
177 }
178 }
179 }
180 else if( aNode.is_type<SIM_VALUE_PARSER::exponent>() )
181 {
182 aParseResult.exponent = std::stoi( aNode.string() );
183 aParseResult.isEmpty = false;
184 }
185 else if( aNode.is_type<SIM_VALUE_PARSER::unitPrefix<ValueType, Notation>>() )
186 {
187 aParseResult.unitPrefixExponent = SIM_VALUE_PARSER::UnitPrefixToExponent( aNode.string(),
188 Notation );
189 aParseResult.isEmpty = false;
190 }
191 else
192 wxFAIL_MSG( "Unhandled parse tree node" );
193}
194
195
197 NOTATION aNotation,
198 SIM_VALUE::TYPE aValueType )
199{
200 LOCALE_IO toggle;
201
202 tao::pegtl::string_input<> in( aString, "from_content" );
203 std::unique_ptr<tao::pegtl::parse_tree::node> root;
204 PARSE_RESULT result;
205
206 try
207 {
208 CALL_INSTANCE( aValueType, aNotation, root = doParse, in );
209 }
210 catch( tao::pegtl::parse_error& )
211 {
212 result.isOk = false;
213 return result;
214 }
215
216 wxASSERT( root );
217
218 try
219 {
220 for( const auto& node : root->children )
221 {
222 CALL_INSTANCE( aValueType, aNotation, handleNodeForParse, *node, result );
223 }
224 }
225 catch( const std::invalid_argument& e )
226 {
227 wxFAIL_MSG( fmt::format( "Parsing simulator value failed: {:s}", e.what() ) );
228 result.isOk = false;
229 }
230
231 return result;
232}
233
234
235int SIM_VALUE_PARSER::UnitPrefixToExponent( std::string aPrefix, NOTATION aNotation )
236{
237 switch( aNotation )
238 {
239 case NOTATION::SI:
240 if( aPrefix.empty() )
241 return 0;
242
243 switch( aPrefix[0] )
244 {
245 case 'a': return -18;
246 case 'f': return -15;
247 case 'p': return -12;
248 case 'n': return -9;
249 case 'u': return -6;
250 case 'm': return -3;
251 case 'k':
252 case 'K': return 3;
253 case 'M': return 6;
254 case 'G': return 9;
255 case 'T': return 12;
256 case 'P': return 15;
257 case 'E': return 18;
258 }
259
260 break;
261
262 case NOTATION::SPICE:
263 std::transform( aPrefix.begin(), aPrefix.end(), aPrefix.begin(),
264 ::tolower );
265
266 if( aPrefix == "f" )
267 return -15;
268 else if( aPrefix == "p" )
269 return -12;
270 else if( aPrefix == "n" )
271 return -9;
272 else if( aPrefix == "u" )
273 return -6;
274 else if( aPrefix == "m" )
275 return -3;
276 else if( aPrefix == "" )
277 return 0;
278 else if( aPrefix == "k" )
279 return 3;
280 else if( aPrefix == "meg" )
281 return 6;
282 else if( aPrefix == "g" )
283 return 9;
284 else if( aPrefix == "t" )
285 return 12;
286
287 break;
288 }
289
290 wxFAIL_MSG( fmt::format( "Unknown simulator value suffix: '{:s}'", aPrefix ) );
291 return 0;
292}
293
294
295std::string SIM_VALUE_PARSER::ExponentToUnitPrefix( double aExponent, int& aExponentReduction,
296 NOTATION aNotation )
297{
298 if( aNotation == NOTATION::SI && aExponent >= -18 && aExponent <= -15 )
299 {
300 aExponentReduction = -18;
301 return "a";
302 }
303 else if( aExponent >= -15 && aExponent < -12 )
304 {
305 aExponentReduction = -15;
306 return "f";
307 }
308 else if( aExponent >= -12 && aExponent < -9 )
309 {
310 aExponentReduction = -12;
311 return "p";
312 }
313 else if( aExponent >= -9 && aExponent < -6 )
314 {
315 aExponentReduction = -9;
316 return "n";
317 }
318 else if( aExponent >= -6 && aExponent < -3 )
319 {
320 aExponentReduction = -6;
321 return "u";
322 }
323 else if( aExponent >= -3 && aExponent < 0 )
324 {
325 aExponentReduction = -3;
326 return "m";
327 }
328 else if( aExponent >= 0 && aExponent < 3 )
329 {
330 aExponentReduction = 0;
331 return "";
332 }
333 else if( aExponent >= 3 && aExponent < 6 )
334 {
335 aExponentReduction = 3;
336 return "k";
337 }
338 else if( aExponent >= 6 && aExponent < 9 )
339 {
340 aExponentReduction = 6;
341 return ( aNotation == NOTATION::SI ) ? "M" : "Meg";
342 }
343 else if( aExponent >= 9 && aExponent < 12 )
344 {
345 aExponentReduction = 9;
346 return "G";
347 }
348 else if( aExponent >= 12 && aExponent < 15 )
349 {
350 aExponentReduction = 12;
351 return "T";
352 }
353 else if( aNotation == NOTATION::SI && aExponent >= 15 && aExponent < 18 )
354 {
355 aExponentReduction = 15;
356 return "P";
357 }
358 else if( aNotation == NOTATION::SI && aExponent >= 18 && aExponent <= 21 )
359 {
360 aExponentReduction = 18;
361 return "E";
362 }
363
364 aExponentReduction = 0;
365 return "";
366}
367
368
369std::string SIM_VALUE::ConvertNotation( const std::string& aString, NOTATION aFromNotation,
370 NOTATION aToNotation )
371{
372 wxString buf( aString );
373 buf.Replace( ',', '.' );
374
375 SIM_VALUE_PARSER::PARSE_RESULT parseResult = SIM_VALUE_PARSER::Parse( buf.ToStdString(),
376 aFromNotation );
377
378 if( parseResult.isOk && !parseResult.isEmpty && !parseResult.significand.empty() )
379 {
380 int exponent = parseResult.exponent ? *parseResult.exponent : 0;
381 exponent += parseResult.unitPrefixExponent ? *parseResult.unitPrefixExponent : 0;
382
383 try
384 {
385 LOCALE_IO toggle;
386 int expReduction = 0;
387 std::string prefix = SIM_VALUE_PARSER::ExponentToUnitPrefix( exponent, expReduction,
388 aToNotation );
389
390 exponent -= expReduction;
391 return fmt::format( "{:g}{}",
392 std::stod( parseResult.significand ) * std::pow( 10, exponent ),
393 prefix );
394 }
395 catch( const std::invalid_argument& )
396 {
397 // best efforts
398 }
399 }
400
401 return aString;
402}
403
404
405std::string SIM_VALUE::Normalize( double aValue )
406{
407 double exponent = std::log10( std::abs( aValue ) );
408 int expReduction = 0;
409
410 std::string prefix = SIM_VALUE_PARSER::ExponentToUnitPrefix( exponent, expReduction,
411 NOTATION::SI );
412 double reducedValue = aValue / std::pow( 10, expReduction );
413
414 return fmt::format( "{:g}{}", reducedValue, prefix );
415}
416
417
418double SIM_VALUE::ToDouble( const std::string& aString, double aDefault )
419{
420 SIM_VALUE_PARSER::PARSE_RESULT parseResult = SIM_VALUE_PARSER::Parse( aString, NOTATION::SI );
421
422 if( parseResult.isOk && !parseResult.isEmpty && !parseResult.significand.empty() )
423 {
424 try
425 {
426 LOCALE_IO toggle;
427 int exponent = parseResult.exponent ? *parseResult.exponent : 0;
428
429 exponent += parseResult.unitPrefixExponent ? *parseResult.unitPrefixExponent : 0;
430
431 return std::stod( parseResult.significand ) * std::pow( 10, exponent );
432 }
433 catch( const std::invalid_argument& )
434 {
435 // best efforts
436 }
437 }
438
439 return aDefault;
440}
441
442
443int SIM_VALUE::ToInt( const std::string& aString, int aDefault )
444{
445 SIM_VALUE_PARSER::PARSE_RESULT parseResult = SIM_VALUE_PARSER::Parse( aString, NOTATION::SI );
446
447 if( parseResult.isOk
448 && !parseResult.isEmpty
449 && parseResult.intPart
450 && ( !parseResult.fracPart || *parseResult.fracPart == 0 ) )
451 {
452 int exponent = parseResult.exponent ? *parseResult.exponent : 0;
453 exponent += parseResult.unitPrefixExponent ? *parseResult.unitPrefixExponent : 0;
454
455 if( exponent >= 0 )
456 return (int) *parseResult.intPart * (int) std::pow( 10, exponent );
457 }
458
459 return aDefault;
460}
461
462
463bool SIM_VALUE::Equal( double aLH, const std::string& aRH )
464{
465 return std::abs( aLH - ToDouble( aRH ) ) <= std::numeric_limits<double>::epsilon();
466}
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:405
static bool Equal(double aLH, const std::string &aRH)
Definition: sim_value.cpp:463
static std::string ConvertNotation(const std::string &aString, NOTATION aFromNotation, NOTATION aToNotation)
Definition: sim_value.cpp:369
@ TYPE_FLOAT
Definition: sim_value.h:69
static double ToDouble(const std::string &aString, double aDefault=NAN)
Definition: sim_value.cpp:418
static int ToInt(const std::string &aString, int aDefault=-1)
Definition: sim_value.cpp:443
bool IsValid(const std::string &aString, SIM_VALUE::TYPE aValueType=SIM_VALUE::TYPE_FLOAT, NOTATION aNotation=NOTATION::SI)
Definition: sim_value.cpp:128
PARSE_RESULT Parse(const std::string &aString, NOTATION aNotation=NOTATION::SI, SIM_VALUE::TYPE aValueType=SIM_VALUE::TYPE_FLOAT)
Definition: sim_value.cpp:196
int UnitPrefixToExponent(std::string aPrefix, NOTATION aNotation=NOTATION::SI)
Definition: sim_value.cpp:235
std::string ExponentToUnitPrefix(double aExponent, int &aExponentReduction, NOTATION aNotation=NOTATION::SI)
Definition: sim_value.cpp:295
EDA_ANGLE abs(const EDA_ANGLE &aAngle)
Definition: eda_angle.h:424
static void handleNodeForParse(tao::pegtl::parse_tree::node &aNode, SIM_VALUE_PARSER::PARSE_RESULT &aParseResult)
Definition: sim_value.cpp:157
static std::unique_ptr< tao::pegtl::parse_tree::node > doParse(tao::pegtl::string_input<> &aIn)
Definition: sim_value.cpp:147
#define CALL_INSTANCE(ValueType, Notation, func,...)
Definition: sim_value.cpp:34
static void doIsValid(tao::pegtl::string_input<> &aIn)
Definition: sim_value.cpp:122
std::optional< int64_t > fracPart
Definition: sim_value.cpp:106
std::optional< int > unitPrefixExponent
Definition: sim_value.cpp:108
std::optional< int > exponent
Definition: sim_value.cpp:107
std::optional< int64_t > intPart
Definition: sim_value.cpp:105