/* * This program source code file is part of KiCad, a free EDA CAD application. * * Copyright (C) 2022 Mikolaj Wielgus * Copyright (C) 2022-2023 KiCad Developers, see AUTHORS.txt for contributors. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 3 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, you may find one here: * https://www.gnu.org/licenses/gpl-3.0.html * or you may search the http://www.gnu.org website for the version 3 license, * or you may write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA */ #include #include #include #include #include #include #include #define CALL_INSTANCE( ValueType, Notation, func, ... ) \ switch( ValueType ) \ { \ case SIM_VALUE::TYPE_INT: \ switch( Notation ) \ { \ case NOTATION::SI: \ func( __VA_ARGS__ ); \ break; \ \ case NOTATION::SPICE: \ func( __VA_ARGS__ ); \ break; \ } \ break; \ \ case SIM_VALUE::TYPE_FLOAT: \ switch( Notation ) \ { \ case NOTATION::SI: \ func( __VA_ARGS__ ); \ break; \ \ case NOTATION::SPICE: \ func( __VA_ARGS__ ); \ break; \ } \ break; \ \ case SIM_VALUE::TYPE_BOOL: \ case SIM_VALUE::TYPE_COMPLEX: \ case SIM_VALUE::TYPE_STRING: \ case SIM_VALUE::TYPE_BOOL_VECTOR: \ case SIM_VALUE::TYPE_INT_VECTOR: \ case SIM_VALUE::TYPE_FLOAT_VECTOR: \ case SIM_VALUE::TYPE_COMPLEX_VECTOR: \ wxFAIL_MSG( "Unhandled SIM_VALUE type" ); \ break; \ } namespace SIM_VALUE_PARSER { using namespace SIM_VALUE_GRAMMAR; template struct numberSelector : std::false_type {}; // TODO: Reorder. NOTATION should be before TYPE. template <> struct numberSelector> : std::true_type {}; template <> struct numberSelector> : std::true_type {}; template <> struct numberSelector : std::true_type {}; template <> struct numberSelector : std::true_type {}; template <> struct numberSelector : std::true_type {}; template <> struct numberSelector> : std::true_type {}; template <> struct numberSelector> : std::true_type {}; template <> struct numberSelector> : std::true_type {}; template <> struct numberSelector> : std::true_type {}; struct PARSE_RESULT { bool isOk = true; bool isEmpty = true; std::string significand; std::optional intPart; std::optional fracPart; std::optional exponent; std::optional unitPrefixExponent; }; 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 ); } template static inline void doIsValid( tao::pegtl::string_input<>& aIn ) { tao::pegtl::parse>( aIn ); } bool SIM_VALUE_GRAMMAR::IsValid( const std::string& aString, SIM_VALUE::TYPE aValueType, NOTATION aNotation ) { tao::pegtl::string_input<> in( aString, "from_content" ); try { CALL_INSTANCE( aValueType, aNotation, doIsValid, in ); } catch( const tao::pegtl::parse_error& ) { return false; } return true; } template static inline std::unique_ptr doParse( tao::pegtl::string_input<>& aIn ) { return tao::pegtl::parse_tree::parse, SIM_VALUE_PARSER::numberSelector> ( aIn ); } template static inline void handleNodeForParse( tao::pegtl::parse_tree::node& aNode, SIM_VALUE_PARSER::PARSE_RESULT& aParseResult ) { if( aNode.is_type>() ) { aParseResult.significand = aNode.string(); aParseResult.isEmpty = false; for( const auto& subnode : aNode.children ) { try { if( subnode->is_type() ) aParseResult.intPart = std::stoll( subnode->string() ); else if( subnode->is_type() ) aParseResult.fracPart = std::stoll( subnode->string() ); } catch( const std::exception& ) { aParseResult.isOk = false; } } } else if( aNode.is_type() ) { aParseResult.exponent = std::stoi( aNode.string() ); aParseResult.isEmpty = false; } else if( aNode.is_type>() ) { aParseResult.unitPrefixExponent = SIM_VALUE_PARSER::UnitPrefixToExponent( aNode.string(), Notation ); aParseResult.isEmpty = false; } else wxFAIL_MSG( "Unhandled parse tree node" ); } SIM_VALUE_PARSER::PARSE_RESULT SIM_VALUE_PARSER::Parse( const std::string& aString, NOTATION aNotation, SIM_VALUE::TYPE aValueType ) { LOCALE_IO toggle; tao::pegtl::string_input<> in( aString, "from_content" ); std::unique_ptr root; PARSE_RESULT result; try { CALL_INSTANCE( aValueType, aNotation, root = doParse, in ); } catch( tao::pegtl::parse_error& ) { result.isOk = false; return result; } wxASSERT( root ); try { for( const auto& node : root->children ) { CALL_INSTANCE( aValueType, aNotation, handleNodeForParse, *node, result ); } } catch( const std::invalid_argument& e ) { wxFAIL_MSG( fmt::format( "Parsing simulator value failed: {:s}", e.what() ) ); result.isOk = false; } return result; } int SIM_VALUE_PARSER::UnitPrefixToExponent( std::string aPrefix, NOTATION aNotation ) { switch( aNotation ) { case NOTATION::SI: if( aPrefix.empty() ) return 0; switch( aPrefix[0] ) { case 'a': return -18; case 'f': return -15; case 'p': return -12; case 'n': return -9; case 'u': return -6; case 'm': return -3; case 'k': case 'K': return 3; case 'M': return 6; case 'G': return 9; case 'T': return 12; case 'P': return 15; case 'E': return 18; } break; case NOTATION::SPICE: std::transform( aPrefix.begin(), aPrefix.end(), aPrefix.begin(), ::tolower ); if( aPrefix == "f" ) return -15; else if( aPrefix == "p" ) return -12; else if( aPrefix == "n" ) return -9; else if( aPrefix == "u" ) return -6; else if( aPrefix == "m" ) return -3; else if( aPrefix == "" ) return 0; else if( aPrefix == "k" ) return 3; else if( aPrefix == "meg" ) return 6; else if( aPrefix == "g" ) return 9; else if( aPrefix == "t" ) return 12; break; } wxFAIL_MSG( fmt::format( "Unknown simulator value suffix: '{:s}'", aPrefix ) ); return 0; } std::string SIM_VALUE_PARSER::ExponentToUnitPrefix( double aExponent, int& aExponentReduction, NOTATION aNotation ) { if( aNotation == NOTATION::SI && aExponent >= -18 && aExponent <= -15 ) { aExponentReduction = -18; return "a"; } else if( aExponent >= -15 && aExponent < -12 ) { aExponentReduction = -15; return "f"; } else if( aExponent >= -12 && aExponent < -9 ) { aExponentReduction = -12; return "p"; } else if( aExponent >= -9 && aExponent < -6 ) { aExponentReduction = -9; return "n"; } else if( aExponent >= -6 && aExponent < -3 ) { aExponentReduction = -6; return "u"; } else if( aExponent >= -3 && aExponent < 0 ) { aExponentReduction = -3; return "m"; } else if( aExponent >= 0 && aExponent < 3 ) { aExponentReduction = 0; return ""; } else if( aExponent >= 3 && aExponent < 6 ) { aExponentReduction = 3; return "k"; } else if( aExponent >= 6 && aExponent < 9 ) { aExponentReduction = 6; return ( aNotation == NOTATION::SI ) ? "M" : "Meg"; } else if( aExponent >= 9 && aExponent < 12 ) { aExponentReduction = 9; return "G"; } else if( aExponent >= 12 && aExponent < 15 ) { aExponentReduction = 12; return "T"; } else if( aNotation == NOTATION::SI && aExponent >= 15 && aExponent < 18 ) { aExponentReduction = 15; return "P"; } else if( aNotation == NOTATION::SI && aExponent >= 18 && aExponent <= 21 ) { aExponentReduction = 18; return "E"; } aExponentReduction = 0; return ""; } std::string SIM_VALUE::ConvertNotation( const std::string& aString, NOTATION aFromNotation, NOTATION aToNotation ) { wxString buf( aString ); buf.Replace( ',', '.' ); SIM_VALUE_PARSER::PARSE_RESULT parseResult = SIM_VALUE_PARSER::Parse( buf.ToStdString(), aFromNotation ); if( parseResult.isOk && !parseResult.isEmpty && !parseResult.significand.empty() ) { int exponent = parseResult.exponent ? *parseResult.exponent : 0; exponent += parseResult.unitPrefixExponent ? *parseResult.unitPrefixExponent : 0; try { LOCALE_IO toggle; int expReduction = 0; std::string prefix = SIM_VALUE_PARSER::ExponentToUnitPrefix( exponent, expReduction, aToNotation ); exponent -= expReduction; return fmt::format( "{:g}{}", std::stod( parseResult.significand ) * std::pow( 10, exponent ), prefix ); } catch( const std::invalid_argument& ) { // best efforts } } return aString; } std::string SIM_VALUE::Normalize( double aValue ) { double exponent = std::log10( std::abs( aValue ) ); int expReduction = 0; std::string prefix = SIM_VALUE_PARSER::ExponentToUnitPrefix( exponent, expReduction, NOTATION::SI ); double reducedValue = aValue / std::pow( 10, expReduction ); return fmt::format( "{:g}{}", reducedValue, prefix ); } double SIM_VALUE::ToDouble( const std::string& aString, double aDefault ) { SIM_VALUE_PARSER::PARSE_RESULT parseResult = SIM_VALUE_PARSER::Parse( aString, NOTATION::SI ); if( parseResult.isOk && !parseResult.isEmpty && !parseResult.significand.empty() ) { try { LOCALE_IO toggle; int exponent = parseResult.exponent ? *parseResult.exponent : 0; exponent += parseResult.unitPrefixExponent ? *parseResult.unitPrefixExponent : 0; return std::stod( parseResult.significand ) * std::pow( 10, exponent ); } catch( const std::invalid_argument& ) { // best efforts } } return aDefault; } int SIM_VALUE::ToInt( const std::string& aString, int aDefault ) { SIM_VALUE_PARSER::PARSE_RESULT parseResult = SIM_VALUE_PARSER::Parse( aString, NOTATION::SI ); if( parseResult.isOk && !parseResult.isEmpty && parseResult.intPart && ( !parseResult.fracPart || *parseResult.fracPart == 0 ) ) { int exponent = parseResult.exponent ? *parseResult.exponent : 0; exponent += parseResult.unitPrefixExponent ? *parseResult.unitPrefixExponent : 0; if( exponent >= 0 ) return (int) *parseResult.intPart * (int) std::pow( 10, exponent ); } return aDefault; } bool SIM_VALUE::Equal( double aLH, const std::string& aRH ) { return std::abs( aLH - ToDouble( aRH ) ) <= std::numeric_limits::epsilon(); }