kicad-source/eeschema/sim/sim_value.cpp
Jeff Young b7d41e0e56 Update simulator with NUMERIC_EVAL, decimal separator processing, etc.
Also includes fixes for instance data and resolving of textvar
references.

Also includes virtual d'tors for IBIS parser to get rid of all the
compile warnings on CLang.

Fixes https://gitlab.com/kicad/code/kicad/issues/12357
2022-12-05 12:40:29 +00:00

748 lines
23 KiB
C++

/*
* This program source code file is part of KiCad, a free EDA CAD application.
*
* Copyright (C) 2022 Mikolaj Wielgus
* Copyright (C) 2022 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 <sim/sim_value.h>
#include <wx/translation.h>
#include <ki_exception.h>
#include <locale_io.h>
#include <pegtl/contrib/parse_tree.hpp>
#include <fmt/core.h>
#define CALL_INSTANCE( ValueType, Notation, func, ... ) \
switch( ValueType ) \
{ \
case SIM_VALUE::TYPE_INT: \
switch( Notation ) \
{ \
case NOTATION::SI: \
func<SIM_VALUE::TYPE_INT, NOTATION::SI>( __VA_ARGS__ ); \
break; \
\
case NOTATION::SPICE: \
func<SIM_VALUE::TYPE_INT, NOTATION::SPICE>( __VA_ARGS__ ); \
break; \
} \
break; \
\
case SIM_VALUE::TYPE_FLOAT: \
switch( Notation ) \
{ \
case NOTATION::SI: \
func<SIM_VALUE::TYPE_FLOAT, NOTATION::SI>( __VA_ARGS__ ); \
break; \
\
case NOTATION::SPICE: \
func<SIM_VALUE::TYPE_FLOAT, NOTATION::SPICE>( __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 <typename Rule>
struct numberSelector : std::false_type {};
// TODO: Reorder. NOTATION should be before TYPE.
template <> struct numberSelector<SIM_VALUE_GRAMMAR::significand<SIM_VALUE::TYPE_INT>>
: std::true_type {};
template <> struct numberSelector<SIM_VALUE_GRAMMAR::significand<SIM_VALUE::TYPE_FLOAT>>
: std::true_type {};
template <> struct numberSelector<intPart> : std::true_type {};
template <> struct numberSelector<fracPart> : std::true_type {};
template <> struct numberSelector<exponent> : std::true_type {};
template <> struct numberSelector<metricSuffix<SIM_VALUE::TYPE_INT, NOTATION::SI>>
: std::true_type {};
template <> struct numberSelector<metricSuffix<SIM_VALUE::TYPE_INT, NOTATION::SPICE>>
: std::true_type {};
template <> struct numberSelector<metricSuffix<SIM_VALUE::TYPE_FLOAT, NOTATION::SI>>
: std::true_type {};
template <> struct numberSelector<metricSuffix<SIM_VALUE::TYPE_FLOAT, NOTATION::SPICE>>
: std::true_type {};
struct PARSE_RESULT
{
bool isOk = true;
bool isEmpty = true;
std::string significand;
std::optional<int64_t> intPart;
std::optional<int64_t> fracPart;
std::optional<int> exponent;
std::optional<int> metricSuffixExponent;
};
PARSE_RESULT Parse( const std::string& aString,
NOTATION aNotation = NOTATION::SI,
SIM_VALUE::TYPE aValueType = SIM_VALUE::TYPE_FLOAT );
int MetricSuffixToExponent( std::string aMetricSuffix, NOTATION aNotation = NOTATION::SI );
std::string ExponentToMetricSuffix( double aExponent, int& aReductionExponent,
NOTATION aNotation = NOTATION::SI );
}
template <SIM_VALUE::TYPE ValueType, SIM_VALUE_PARSER::NOTATION Notation>
static inline void doIsValid( tao::pegtl::string_input<>& aIn )
{
tao::pegtl::parse<SIM_VALUE_PARSER::numberGrammar<ValueType, Notation>>( 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 <SIM_VALUE::TYPE ValueType, SIM_VALUE_PARSER::NOTATION Notation>
static inline std::unique_ptr<tao::pegtl::parse_tree::node> doParse(
tao::pegtl::string_input<>& aIn )
{
return tao::pegtl::parse_tree::parse<SIM_VALUE_PARSER::numberGrammar<ValueType, Notation>,
SIM_VALUE_PARSER::numberSelector>
( aIn );
}
template <SIM_VALUE::TYPE ValueType, SIM_VALUE_PARSER::NOTATION Notation>
static inline void handleNodeForParse( tao::pegtl::parse_tree::node& aNode,
SIM_VALUE_PARSER::PARSE_RESULT& aParseResult )
{
if( aNode.is_type<SIM_VALUE_PARSER::significand<ValueType>>() )
{
aParseResult.significand = aNode.string();
aParseResult.isEmpty = false;
for( const auto& subnode : aNode.children )
{
if( subnode->is_type<SIM_VALUE_PARSER::intPart>() )
aParseResult.intPart = std::stol( subnode->string() );
else if( subnode->is_type<SIM_VALUE_PARSER::fracPart>() )
aParseResult.fracPart = std::stol( subnode->string() );
}
}
else if( aNode.is_type<SIM_VALUE_PARSER::exponent>() )
{
aParseResult.exponent = std::stoi( aNode.string() );
aParseResult.isEmpty = false;
}
else if( aNode.is_type<SIM_VALUE_PARSER::metricSuffix<ValueType, Notation>>() )
{
aParseResult.metricSuffixExponent =
SIM_VALUE_PARSER::MetricSuffixToExponent( 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<tao::pegtl::parse_tree::node> 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::MetricSuffixToExponent( std::string aMetricSuffix, NOTATION aNotation )
{
switch( aNotation )
{
case NOTATION::SI:
if( aMetricSuffix.empty() )
return 0;
switch( aMetricSuffix[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( aMetricSuffix.begin(), aMetricSuffix.end(), aMetricSuffix.begin(),
::tolower );
if( aMetricSuffix == "f" )
return -15;
else if( aMetricSuffix == "p" )
return -12;
else if( aMetricSuffix == "n" )
return -9;
else if( aMetricSuffix == "u" )
return -6;
else if( aMetricSuffix == "m" )
return -3;
else if( aMetricSuffix == "" )
return 0;
else if( aMetricSuffix == "k" )
return 3;
else if( aMetricSuffix == "meg" )
return 6;
else if( aMetricSuffix == "g" )
return 9;
else if( aMetricSuffix == "t" )
return 12;
break;
}
wxFAIL_MSG( fmt::format( "Unknown simulator value suffix: '{:s}'", aMetricSuffix ) );
return 0;
}
std::string SIM_VALUE_PARSER::ExponentToMetricSuffix( double aExponent, int& aReductionExponent,
NOTATION aNotation )
{
if( aNotation == NOTATION::SI && aExponent >= -18 && aExponent <= -15 )
{
aReductionExponent = -18;
return "a";
}
else if( aExponent >= -15 && aExponent < -12 )
{
aReductionExponent = -15;
return "f";
}
else if( aExponent >= -12 && aExponent < -9 )
{
aReductionExponent = -12;
return "p";
}
else if( aExponent >= -9 && aExponent < -6 )
{
aReductionExponent = -9;
return "n";
}
else if( aExponent >= -6 && aExponent < -3 )
{
aReductionExponent = -6;
return "u";
}
else if( aExponent >= -3 && aExponent < 0 )
{
aReductionExponent = -3;
return "m";
}
else if( aExponent >= 0 && aExponent < 3 )
{
aReductionExponent = 0;
return "";
}
else if( aExponent >= 3 && aExponent < 6 )
{
aReductionExponent = 3;
return "k";
}
else if( aExponent >= 6 && aExponent < 9 )
{
aReductionExponent = 6;
return ( aNotation == NOTATION::SI ) ? "M" : "Meg";
}
else if( aExponent >= 9 && aExponent < 12 )
{
aReductionExponent = 9;
return "G";
}
else if( aExponent >= 12 && aExponent < 15 )
{
aReductionExponent = 12;
return "T";
}
else if( aNotation == NOTATION::SI && aExponent >= 15 && aExponent < 18 )
{
aReductionExponent = 15;
return "P";
}
else if( aNotation == NOTATION::SI && aExponent >= 18 && aExponent <= 21 )
{
aReductionExponent = 18;
return "E";
}
aReductionExponent = 0;
return "";
}
std::unique_ptr<SIM_VALUE> SIM_VALUE::Create( TYPE aType, std::string aString, NOTATION aNotation )
{
std::unique_ptr<SIM_VALUE> value = SIM_VALUE::Create( aType );
value->FromString( aString, aNotation );
return value;
}
std::unique_ptr<SIM_VALUE> SIM_VALUE::Create( TYPE aType )
{
switch( aType )
{
case TYPE_BOOL: return std::make_unique<SIM_VALUE_BOOL>();
case TYPE_INT: return std::make_unique<SIM_VALUE_INT>();
case TYPE_FLOAT: return std::make_unique<SIM_VALUE_FLOAT>();
case TYPE_COMPLEX: return std::make_unique<SIM_VALUE_COMPLEX>();
case TYPE_STRING: return std::make_unique<SIM_VALUE_STRING>();
case TYPE_BOOL_VECTOR: return std::make_unique<SIM_VALUE_BOOL>();
case TYPE_INT_VECTOR: return std::make_unique<SIM_VALUE_INT>();
case TYPE_FLOAT_VECTOR: return std::make_unique<SIM_VALUE_FLOAT>();
case TYPE_COMPLEX_VECTOR: return std::make_unique<SIM_VALUE_COMPLEX>();
}
wxFAIL_MSG( _( "Unknown SIM_VALUE type" ) );
return nullptr;
}
void SIM_VALUE::operator=( const std::string& aString )
{
FromString( aString );
}
bool SIM_VALUE::operator!=( const SIM_VALUE& aOther ) const
{
return !( *this == aOther );
}
template <typename T>
SIM_VALUE_INST<T>::SIM_VALUE_INST( const T& aValue ) : m_value( aValue )
{
}
template SIM_VALUE_BOOL::SIM_VALUE_INST( const bool& aValue );
template SIM_VALUE_INT::SIM_VALUE_INST( const int& aValue );
template SIM_VALUE_FLOAT::SIM_VALUE_INST( const double& aValue );
template SIM_VALUE_COMPLEX::SIM_VALUE_INST( const std::complex<double>& aValue );
template SIM_VALUE_STRING::SIM_VALUE_INST( const std::string& aValue );
template <> SIM_VALUE::TYPE SIM_VALUE_BOOL::GetType() const { return TYPE_BOOL; }
template <> SIM_VALUE::TYPE SIM_VALUE_INT::GetType() const { return TYPE_INT; }
template <> SIM_VALUE::TYPE SIM_VALUE_FLOAT::GetType() const { return TYPE_FLOAT; }
template <> SIM_VALUE::TYPE SIM_VALUE_COMPLEX::GetType() const { return TYPE_FLOAT; }
template <> SIM_VALUE::TYPE SIM_VALUE_STRING::GetType() const { return TYPE_STRING; }
// TODO
/*template <> SIM_VALUE::TYPE SIM_VALUE_BOOL_VECTOR::GetType() const { return TYPE_BOOL; }
template <> SIM_VALUE::TYPE SIM_VALUE_INT_VECTOR::GetType() const { return TYPE_INT; }
template <> SIM_VALUE::TYPE SIM_VALUE_FLOAT_VECTOR::GetType() const { return TYPE_FLOAT; }
template <> SIM_VALUE::TYPE SIM_VALUE_COMPLEX_VECTOR::GetType() const { return TYPE_COMPLEX; }*/
template <typename T>
bool SIM_VALUE_INST<T>::HasValue() const
{
return m_value.has_value();
}
template <>
bool SIM_VALUE_BOOL::FromString( const std::string& aString, NOTATION aNotation )
{
SIM_VALUE_PARSER::PARSE_RESULT parseResult = SIM_VALUE_PARSER::Parse( aString, aNotation );
m_value = std::nullopt;
if( !parseResult.isOk )
return false;
if( parseResult.isEmpty )
return true;
if( !parseResult.intPart
|| ( *parseResult.intPart != 0 && *parseResult.intPart != 1 )
|| parseResult.fracPart
|| parseResult.exponent
|| parseResult.metricSuffixExponent )
{
return false;
}
m_value = *parseResult.intPart;
return true;
}
template <>
bool SIM_VALUE_INT::FromString( const std::string& aString, NOTATION aNotation )
{
SIM_VALUE_PARSER::PARSE_RESULT parseResult = SIM_VALUE_PARSER::Parse( aString, aNotation );
m_value = std::nullopt;
if( !parseResult.isOk )
return false;
if( parseResult.isEmpty )
return true;
if( !parseResult.intPart || ( parseResult.fracPart && *parseResult.fracPart != 0 ) )
return false;
int exponent = parseResult.exponent ? *parseResult.exponent : 0;
exponent += parseResult.metricSuffixExponent ? *parseResult.metricSuffixExponent : 0;
m_value = static_cast<double>( *parseResult.intPart ) * std::pow( 10, exponent );
return true;
}
template <>
bool SIM_VALUE_FLOAT::FromString( const std::string& aString, NOTATION aNotation )
{
wxString buf( aString );
// Convert any entered decimal point separators to the one our PEGTL grammar expects
buf.Replace( wxT( "," ), wxT( "." ) );
SIM_VALUE_PARSER::PARSE_RESULT parseResult = SIM_VALUE_PARSER::Parse( buf.ToStdString(),
aNotation );
m_value = std::nullopt;
if( !parseResult.isOk )
return false;
if( parseResult.isEmpty )
return true;
// Single dot should be allowed in fields.
// TODO: disallow single dot in models.
if( parseResult.significand.empty() || parseResult.significand == "." )
return false;
int exponent = parseResult.exponent ? *parseResult.exponent : 0;
exponent += parseResult.metricSuffixExponent ? *parseResult.metricSuffixExponent : 0;
try
{
m_value = std::stod( parseResult.significand ) * std::pow( 10, exponent );
}
catch( const std::invalid_argument& )
{
return false;
}
return true;
}
template <>
bool SIM_VALUE_COMPLEX::FromString( const std::string& aString, NOTATION aNotation )
{
// TODO
/*LOCALE_IO toggle;
double value = 0;
if( !aString.ToDouble( &value ) )
throw KI_PARAM_ERROR( _( "Invalid complex sim value string" ) );
m_value = value;*/
return true;
}
template <>
bool SIM_VALUE_STRING::FromString( const std::string& aString, NOTATION aNotation )
{
m_value = aString;
return true;
}
template <typename T>
std::string SIM_VALUE_INST<T>::ToString( NOTATION aNotation ) const
{
static_assert( std::is_same<T, std::vector<T>>::value );
std::string string = "";
for( auto it = m_value.cbegin(); it != m_value.cend(); it++ )
{
string += SIM_VALUE_INST<T>( *it ).ToString();
string += ",";
}
return string;
}
template <>
std::string SIM_VALUE_BOOL::ToString( NOTATION aNotation ) const
{
if( m_value )
return fmt::format( "{:d}", *m_value );
return "";
}
template <>
std::string SIM_VALUE_INT::ToString( NOTATION aNotation ) const
{
if( m_value )
{
int value = std::abs( *m_value );
int exponent = 0;
while( value != 0 && value % 1000 == 0 )
{
exponent += 3;
value /= 1000;
}
int dummy = 0;
std::string metricSuffix = SIM_VALUE_PARSER::ExponentToMetricSuffix(
static_cast<double>( exponent ), dummy, aNotation );
return fmt::format( "{:d}{:s}", value, metricSuffix );
}
return "";
}
template <>
std::string SIM_VALUE_FLOAT::ToString( NOTATION aNotation ) const
{
if( m_value )
{
double exponent = std::log10( std::abs( *m_value ) );
int reductionExponent = 0;
std::string metricSuffix =
SIM_VALUE_PARSER::ExponentToMetricSuffix( exponent, reductionExponent, aNotation );
double reducedValue = *m_value / std::pow( 10, reductionExponent );
return fmt::format( "{:g}{}", reducedValue, metricSuffix );
}
else
return "";
}
template <>
std::string SIM_VALUE_COMPLEX::ToString( NOTATION aNotation ) const
{
if( m_value )
return fmt::format( "{:g}+{:g}i", m_value->real(), m_value->imag() );
return "";
}
template <>
std::string SIM_VALUE_STRING::ToString( NOTATION aNotation ) const
{
if( m_value )
return *m_value;
return ""; // Empty string is completely equivalent to null string.
}
template <typename T>
std::string SIM_VALUE_INST<T>::ToSimpleString() const
{
if( m_value )
return fmt::format( "{}", *m_value );
return "";
}
template <>
std::string SIM_VALUE_COMPLEX::ToSimpleString() const
{
// TODO
/*std::string result = "";
result << *m_value;
return result;*/
return "";
}
template <typename T>
void SIM_VALUE_INST<T>::operator=( const SIM_VALUE& aOther )
{
auto other = dynamic_cast<const SIM_VALUE_INST<T>*>( &aOther );
m_value = other->m_value;
}
template <typename T>
bool SIM_VALUE_INST<T>::operator==( const T& aOther ) const
{
return m_value == aOther;
}
template <>
bool SIM_VALUE_BOOL::operator==( const bool& aOther ) const
{
// Note that we take nullopt as the same as false here.
if( !m_value )
return false == aOther;
return m_value == aOther;
}
template bool SIM_VALUE_INT::operator==( const int& aOther ) const;
template bool SIM_VALUE_FLOAT::operator==( const double& aOther ) const;
template bool SIM_VALUE_COMPLEX::operator==( const std::complex<double>& aOther ) const;
template bool SIM_VALUE_STRING::operator==( const std::string& aOther ) const;
template <typename T>
bool SIM_VALUE_INST<T>::operator==( const SIM_VALUE& aOther ) const
{
const SIM_VALUE_INST<T>* otherValue = dynamic_cast<const SIM_VALUE_INST<T>*>( &aOther );
if( otherValue )
return m_value == otherValue->m_value;
return false;
}
template <typename T>
SIM_VALUE_INST<T> operator+( const SIM_VALUE_INST<T>& aLeft, const SIM_VALUE_INST<T>& aRight )
{
return SIM_VALUE_INST( aLeft.m_value.value() + aRight.m_value.value() );
}
template SIM_VALUE_INT operator+( const SIM_VALUE_INT& aLeft,
const SIM_VALUE_INT& aRight );
template SIM_VALUE_FLOAT operator+( const SIM_VALUE_FLOAT& aLeft,
const SIM_VALUE_FLOAT& aRight );
template <typename T>
SIM_VALUE_INST<T> operator-( const SIM_VALUE_INST<T>& aLeft, const SIM_VALUE_INST<T>& aRight )
{
return SIM_VALUE_INST( aLeft.m_value.value() - aRight.m_value.value() );
}
template SIM_VALUE_INT operator-( const SIM_VALUE_INT& aLeft,
const SIM_VALUE_INT& aRight );
template SIM_VALUE_FLOAT operator-( const SIM_VALUE_FLOAT& aLeft,
const SIM_VALUE_FLOAT& aRight );
template <typename T>
SIM_VALUE_INST<T> operator*( const SIM_VALUE_INST<T>& aLeft, const SIM_VALUE_INST<T>& aRight )
{
return SIM_VALUE_INST( aLeft.m_value.value() * aRight.m_value.value() );
}
template SIM_VALUE_INT operator*( const SIM_VALUE_INT& aLeft,
const SIM_VALUE_INT& aRight );
template SIM_VALUE_FLOAT operator*( const SIM_VALUE_FLOAT& aLeft,
const SIM_VALUE_FLOAT& aRight );
template <typename T>
SIM_VALUE_INST<T> operator/( const SIM_VALUE_INST<T>& aLeft, const SIM_VALUE_INST<T>& aRight )
{
return SIM_VALUE_INST( aLeft.m_value.value() / aRight.m_value.value() );
}
template SIM_VALUE_INT operator/( const SIM_VALUE_INT& aLeft,
const SIM_VALUE_INT& aRight );
template SIM_VALUE_FLOAT operator/( const SIM_VALUE_FLOAT& aLeft,
const SIM_VALUE_FLOAT& aRight );