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