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 if( subnode->is_type<SIM_VALUE_PARSER::intPart>() )
168 aParseResult.intPart = std::stol( subnode->string() );
169 else if( subnode->is_type<SIM_VALUE_PARSER::fracPart>() )
170 aParseResult.fracPart = std::stol( subnode->string() );
171 }
172 }
173 else if( aNode.is_type<SIM_VALUE_PARSER::exponent>() )
174 {
175 aParseResult.exponent = std::stoi( aNode.string() );
176 aParseResult.isEmpty = false;
177 }
178 else if( aNode.is_type<SIM_VALUE_PARSER::unitPrefix<ValueType, Notation>>() )
179 {
180 aParseResult.unitPrefixExponent = SIM_VALUE_PARSER::UnitPrefixToExponent( aNode.string(),
181 Notation );
182 aParseResult.isEmpty = false;
183 }
184 else
185 wxFAIL_MSG( "Unhandled parse tree node" );
186}
187
188
190 NOTATION aNotation,
191 SIM_VALUE::TYPE aValueType )
192{
193 LOCALE_IO toggle;
194
195 tao::pegtl::string_input<> in( aString, "from_content" );
196 std::unique_ptr<tao::pegtl::parse_tree::node> root;
197 PARSE_RESULT result;
198
199 try
200 {
201 CALL_INSTANCE( aValueType, aNotation, root = doParse, in );
202 }
203 catch( tao::pegtl::parse_error& )
204 {
205 result.isOk = false;
206 return result;
207 }
208
209 wxASSERT( root );
210
211 try
212 {
213 for( const auto& node : root->children )
214 {
215 CALL_INSTANCE( aValueType, aNotation, handleNodeForParse, *node, result );
216 }
217 }
218 catch( const std::invalid_argument& e )
219 {
220 wxFAIL_MSG( fmt::format( "Parsing simulator value failed: {:s}", e.what() ) );
221 result.isOk = false;
222 }
223
224 return result;
225}
226
227
228int SIM_VALUE_PARSER::UnitPrefixToExponent( std::string aPrefix, NOTATION aNotation )
229{
230 switch( aNotation )
231 {
232 case NOTATION::SI:
233 if( aPrefix.empty() )
234 return 0;
235
236 switch( aPrefix[0] )
237 {
238 case 'a': return -18;
239 case 'f': return -15;
240 case 'p': return -12;
241 case 'n': return -9;
242 case 'u': return -6;
243 case 'm': return -3;
244 case 'k':
245 case 'K': return 3;
246 case 'M': return 6;
247 case 'G': return 9;
248 case 'T': return 12;
249 case 'P': return 15;
250 case 'E': return 18;
251 }
252
253 break;
254
255 case NOTATION::SPICE:
256 std::transform( aPrefix.begin(), aPrefix.end(), aPrefix.begin(),
257 ::tolower );
258
259 if( aPrefix == "f" )
260 return -15;
261 else if( aPrefix == "p" )
262 return -12;
263 else if( aPrefix == "n" )
264 return -9;
265 else if( aPrefix == "u" )
266 return -6;
267 else if( aPrefix == "m" )
268 return -3;
269 else if( aPrefix == "" )
270 return 0;
271 else if( aPrefix == "k" )
272 return 3;
273 else if( aPrefix == "meg" )
274 return 6;
275 else if( aPrefix == "g" )
276 return 9;
277 else if( aPrefix == "t" )
278 return 12;
279
280 break;
281 }
282
283 wxFAIL_MSG( fmt::format( "Unknown simulator value suffix: '{:s}'", aPrefix ) );
284 return 0;
285}
286
287
288std::string SIM_VALUE_PARSER::ExponentToUnitPrefix( double aExponent, int& aExponentReduction,
289 NOTATION aNotation )
290{
291 if( aNotation == NOTATION::SI && aExponent >= -18 && aExponent <= -15 )
292 {
293 aExponentReduction = -18;
294 return "a";
295 }
296 else if( aExponent >= -15 && aExponent < -12 )
297 {
298 aExponentReduction = -15;
299 return "f";
300 }
301 else if( aExponent >= -12 && aExponent < -9 )
302 {
303 aExponentReduction = -12;
304 return "p";
305 }
306 else if( aExponent >= -9 && aExponent < -6 )
307 {
308 aExponentReduction = -9;
309 return "n";
310 }
311 else if( aExponent >= -6 && aExponent < -3 )
312 {
313 aExponentReduction = -6;
314 return "u";
315 }
316 else if( aExponent >= -3 && aExponent < 0 )
317 {
318 aExponentReduction = -3;
319 return "m";
320 }
321 else if( aExponent >= 0 && aExponent < 3 )
322 {
323 aExponentReduction = 0;
324 return "";
325 }
326 else if( aExponent >= 3 && aExponent < 6 )
327 {
328 aExponentReduction = 3;
329 return "k";
330 }
331 else if( aExponent >= 6 && aExponent < 9 )
332 {
333 aExponentReduction = 6;
334 return ( aNotation == NOTATION::SI ) ? "M" : "Meg";
335 }
336 else if( aExponent >= 9 && aExponent < 12 )
337 {
338 aExponentReduction = 9;
339 return "G";
340 }
341 else if( aExponent >= 12 && aExponent < 15 )
342 {
343 aExponentReduction = 12;
344 return "T";
345 }
346 else if( aNotation == NOTATION::SI && aExponent >= 15 && aExponent < 18 )
347 {
348 aExponentReduction = 15;
349 return "P";
350 }
351 else if( aNotation == NOTATION::SI && aExponent >= 18 && aExponent <= 21 )
352 {
353 aExponentReduction = 18;
354 return "E";
355 }
356
357 aExponentReduction = 0;
358 return "";
359}
360
361
362std::string SIM_VALUE::ConvertNotation( const std::string& aString, NOTATION aFromNotation,
363 NOTATION aToNotation )
364{
365 wxString buf( aString );
366 buf.Replace( ',', '.' );
367
368 SIM_VALUE_PARSER::PARSE_RESULT parseResult = SIM_VALUE_PARSER::Parse( buf.ToStdString(),
369 aFromNotation );
370
371 if( parseResult.isOk && !parseResult.isEmpty && !parseResult.significand.empty() )
372 {
373 int exponent = parseResult.exponent ? *parseResult.exponent : 0;
374 exponent += parseResult.unitPrefixExponent ? *parseResult.unitPrefixExponent : 0;
375
376 try
377 {
378 LOCALE_IO toggle;
379 int expReduction = 0;
380 std::string prefix = SIM_VALUE_PARSER::ExponentToUnitPrefix( exponent, expReduction,
381 aToNotation );
382
383 exponent -= expReduction;
384 return fmt::format( "{:g}{}",
385 std::stod( parseResult.significand ) * std::pow( 10, exponent ),
386 prefix );
387 }
388 catch( const std::invalid_argument& )
389 {
390 // best efforts
391 }
392 }
393
394 return aString;
395}
396
397
398std::string SIM_VALUE::Normalize( double aValue )
399{
400 double exponent = std::log10( std::abs( aValue ) );
401 int expReduction = 0;
402
403 std::string prefix = SIM_VALUE_PARSER::ExponentToUnitPrefix( exponent, expReduction,
404 NOTATION::SI );
405 double reducedValue = aValue / std::pow( 10, expReduction );
406
407 return fmt::format( "{:g}{}", reducedValue, prefix );
408}
409
410
411double SIM_VALUE::ToDouble( const std::string& aString, double aDefault )
412{
413 SIM_VALUE_PARSER::PARSE_RESULT parseResult = SIM_VALUE_PARSER::Parse( aString, NOTATION::SI );
414
415 if( parseResult.isOk && !parseResult.isEmpty && !parseResult.significand.empty() )
416 {
417 try
418 {
419 LOCALE_IO toggle;
420 int exponent = parseResult.exponent ? *parseResult.exponent : 0;
421
422 exponent += parseResult.unitPrefixExponent ? *parseResult.unitPrefixExponent : 0;
423
424 return std::stod( parseResult.significand ) * std::pow( 10, exponent );
425 }
426 catch( const std::invalid_argument& )
427 {
428 // best efforts
429 }
430 }
431
432 return aDefault;
433}
434
435
436int SIM_VALUE::ToInt( const std::string& aString, int aDefault )
437{
438 SIM_VALUE_PARSER::PARSE_RESULT parseResult = SIM_VALUE_PARSER::Parse( aString, NOTATION::SI );
439
440 if( parseResult.isOk
441 && !parseResult.isEmpty
442 && parseResult.intPart
443 && ( !parseResult.fracPart || *parseResult.fracPart == 0 ) )
444 {
445 int exponent = parseResult.exponent ? *parseResult.exponent : 0;
446 exponent += parseResult.unitPrefixExponent ? *parseResult.unitPrefixExponent : 0;
447
448 if( exponent >= 0 )
449 return (int) *parseResult.intPart * (int) std::pow( 10, exponent );
450 }
451
452 return aDefault;
453}
454
455
456bool SIM_VALUE::Equal( double aLH, const std::string& aRH )
457{
458 return std::abs( aLH - ToDouble( aRH ) ) <= std::numeric_limits<double>::epsilon();
459}
Instantiate the current locale within a scope in which you are expecting exceptions to be thrown.
Definition: locale_io.h:41
static std::string Normalize(double aValue)
Definition: sim_value.cpp:398
static bool Equal(double aLH, const std::string &aRH)
Definition: sim_value.cpp:456
static std::string ConvertNotation(const std::string &aString, NOTATION aFromNotation, NOTATION aToNotation)
Definition: sim_value.cpp:362
@ TYPE_FLOAT
Definition: sim_value.h:69
static double ToDouble(const std::string &aString, double aDefault=NAN)
Definition: sim_value.cpp:411
static int ToInt(const std::string &aString, int aDefault=-1)
Definition: sim_value.cpp:436
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:189
int UnitPrefixToExponent(std::string aPrefix, NOTATION aNotation=NOTATION::SI)
Definition: sim_value.cpp:228
std::string ExponentToUnitPrefix(double aExponent, int &aExponentReduction, NOTATION aNotation=NOTATION::SI)
Definition: sim_value.cpp:288
EDA_ANGLE abs(const EDA_ANGLE &aAngle)
Definition: eda_angle.h:418
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