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