Move (de)serialization of models into fields to new SIM_SERDE class
This commit is contained in:
parent
f846bc833b
commit
490069c5e6
|
@ -285,6 +285,7 @@ set( EESCHEMA_SRCS
|
|||
sim/sim_library_spice.cpp
|
||||
sim/sim_library_kibis.cpp
|
||||
sim/sim_lib_mgr.cpp
|
||||
sim/sim_serde.cpp
|
||||
sim/sim_model.cpp
|
||||
sim/sim_model_behavioral.cpp
|
||||
sim/sim_model_ideal.cpp
|
||||
|
|
|
@ -54,27 +54,6 @@ using DEVICE_TYPE = SIM_MODEL::DEVICE_TYPE_;
|
|||
using TYPE = SIM_MODEL::TYPE;
|
||||
|
||||
|
||||
namespace SIM_MODEL_PARSER
|
||||
{
|
||||
using namespace SIM_MODEL_GRAMMAR;
|
||||
|
||||
template <typename Rule> struct fieldParamValuePairsSelector : std::false_type {};
|
||||
template <> struct fieldParamValuePairsSelector<param> : std::true_type {};
|
||||
template <> struct fieldParamValuePairsSelector<quotedStringContent> : std::true_type {};
|
||||
template <> struct fieldParamValuePairsSelector<unquotedString> : std::true_type {};
|
||||
|
||||
|
||||
template <typename Rule> struct pinSequenceSelector : std::false_type {};
|
||||
template <> struct pinSequenceSelector<pinNumber> : std::true_type {};
|
||||
|
||||
template <typename Rule> struct fieldInferValueSelector : std::false_type {};
|
||||
template <> struct fieldInferValueSelector<fieldInferValueType> : std::true_type {};
|
||||
template <> struct fieldInferValueSelector<fieldInferValuePrimaryValue> : std::true_type {};
|
||||
template <> struct fieldInferValueSelector<number<SIM_VALUE::TYPE_FLOAT, NOTATION::SI>> : std::true_type {};
|
||||
template <> struct fieldInferValueSelector<fieldParamValuePairs> : std::true_type {};
|
||||
}
|
||||
|
||||
|
||||
SIM_MODEL::DEVICE_INFO SIM_MODEL::DeviceTypeInfo( DEVICE_TYPE_ aDeviceType )
|
||||
{
|
||||
switch( aDeviceType )
|
||||
|
@ -425,7 +404,7 @@ TYPE SIM_MODEL::ReadTypeFromFields( const std::vector<T>& aFields, int aSymbolPi
|
|||
|
||||
// Still no type information.
|
||||
// We try to infer the model from the mandatory fields in this case.
|
||||
return InferTypeFromRefAndValue( GetFieldValue( &aFields, REFERENCE_FIELD ),
|
||||
return SIM_SERDE::InferTypeFromRefAndValue( GetFieldValue( &aFields, REFERENCE_FIELD ),
|
||||
GetFieldValue( &aFields, VALUE_FIELD ),
|
||||
aSymbolPinCount );
|
||||
}
|
||||
|
@ -451,45 +430,6 @@ DEVICE_TYPE SIM_MODEL::InferDeviceTypeFromRef( const std::string& aRef )
|
|||
}
|
||||
|
||||
|
||||
TYPE SIM_MODEL::InferTypeFromRefAndValue( const std::string& aRef, const std::string& aValue,
|
||||
int aSymbolPinCount )
|
||||
{
|
||||
std::string typeString;
|
||||
|
||||
try
|
||||
{
|
||||
tao::pegtl::string_input<> in( aValue, VALUE_FIELD );
|
||||
auto root = tao::pegtl::parse_tree::parse<SIM_MODEL_PARSER::fieldInferValueGrammar,
|
||||
SIM_MODEL_PARSER::fieldInferValueSelector,
|
||||
tao::pegtl::nothing,
|
||||
SIM_MODEL_PARSER::control>( in );
|
||||
|
||||
for( const auto& node : root->children )
|
||||
{
|
||||
if( node->is_type<SIM_MODEL_PARSER::fieldInferValueType>() )
|
||||
typeString = node->string();
|
||||
}
|
||||
}
|
||||
catch( const tao::pegtl::parse_error& )
|
||||
{
|
||||
}
|
||||
|
||||
DEVICE_TYPE deviceType = InferDeviceTypeFromRef( aRef );
|
||||
|
||||
// Exception. Potentiometer model is determined from pin count.
|
||||
if( deviceType == DEVICE_TYPE_::R && aSymbolPinCount == 3 )
|
||||
return TYPE::R_POT;
|
||||
|
||||
for( TYPE type : TYPE_ITERATOR() )
|
||||
{
|
||||
if( TypeInfo( type ).deviceType == deviceType && TypeInfo( type ).fieldValue == typeString )
|
||||
return type;
|
||||
}
|
||||
|
||||
return TYPE::NONE;
|
||||
}
|
||||
|
||||
|
||||
template <typename T>
|
||||
TYPE SIM_MODEL::InferTypeFromLegacyFields( const std::vector<T>& aFields )
|
||||
{
|
||||
|
@ -967,13 +907,26 @@ std::unique_ptr<SIM_MODEL> SIM_MODEL::Create( TYPE aType )
|
|||
|
||||
|
||||
SIM_MODEL::SIM_MODEL( TYPE aType ) :
|
||||
SIM_MODEL( aType, std::make_unique<SPICE_GENERATOR>( *this ) )
|
||||
SIM_MODEL( aType,
|
||||
std::make_unique<SPICE_GENERATOR>( *this ),
|
||||
std::make_unique<SIM_SERDE>( *this ) )
|
||||
{
|
||||
}
|
||||
|
||||
SIM_MODEL::SIM_MODEL( TYPE aType,
|
||||
std::unique_ptr<SPICE_GENERATOR> aSpiceGenerator ) :
|
||||
SIM_MODEL( aType,
|
||||
std::move( aSpiceGenerator ),
|
||||
std::make_unique<SIM_SERDE>( *this ) )
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
SIM_MODEL::SIM_MODEL( TYPE aType, std::unique_ptr<SPICE_GENERATOR> aSpiceGenerator ) :
|
||||
SIM_MODEL::SIM_MODEL( TYPE aType,
|
||||
std::unique_ptr<SPICE_GENERATOR> aSpiceGenerator,
|
||||
std::unique_ptr<SIM_SERDE> aSerde ) :
|
||||
m_baseModel( nullptr ),
|
||||
m_serde( std::move( aSerde ) ),
|
||||
m_spiceGenerator( std::move( aSpiceGenerator ) ),
|
||||
m_type( aType ),
|
||||
m_isEnabled( true ),
|
||||
|
@ -1037,127 +990,21 @@ void SIM_MODEL::WriteInferredDataFields( std::vector<T>& aFields, const std::str
|
|||
}
|
||||
|
||||
|
||||
std::string SIM_MODEL::GenerateParamValuePair( const PARAM& aParam, bool& aIsFirst ) const
|
||||
{
|
||||
std::string result;
|
||||
|
||||
if( aIsFirst )
|
||||
aIsFirst = false;
|
||||
else
|
||||
result.append( " " );
|
||||
|
||||
std::string name = aParam.info.name;
|
||||
|
||||
// Because of collisions with instance parameters, we append some model parameters with "_".
|
||||
if( boost::ends_with( aParam.info.name, "_" ) )
|
||||
name = aParam.info.name.substr( 0, aParam.info.name.length() - 1);
|
||||
|
||||
std::string value = aParam.value->ToString();
|
||||
if( value.find( " " ) != std::string::npos )
|
||||
value = "\"" + value + "\"";
|
||||
|
||||
result.append( fmt::format( "{}={}", aParam.info.name, value ) );
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
std::string SIM_MODEL::GenerateValueField( const std::string& aPairSeparator ) const
|
||||
{
|
||||
std::string result;
|
||||
bool isFirst = true;
|
||||
|
||||
for( int i = 0; i < GetParamCount(); ++i )
|
||||
{
|
||||
const PARAM& param = GetParam( i );
|
||||
|
||||
if( i == 0 && hasPrimaryValue() )
|
||||
{
|
||||
result.append( param.value->ToString() );
|
||||
isFirst = false;
|
||||
continue;
|
||||
}
|
||||
|
||||
if( param.value->ToString() == "" )
|
||||
continue;
|
||||
|
||||
result.append( GenerateParamValuePair( param, isFirst ) );
|
||||
}
|
||||
|
||||
if( result == "" )
|
||||
result = GetDeviceTypeInfo().fieldValue;
|
||||
|
||||
return result;
|
||||
return m_serde->GenerateValue();
|
||||
}
|
||||
|
||||
|
||||
std::string SIM_MODEL::GenerateParamsField( const std::string& aPairSeparator ) const
|
||||
{
|
||||
std::string result;
|
||||
bool isFirst = true;
|
||||
|
||||
for( const PARAM& param : m_params )
|
||||
{
|
||||
if( param.value->ToString() == "" )
|
||||
continue;
|
||||
|
||||
result.append( GenerateParamValuePair( param, isFirst ) );
|
||||
}
|
||||
|
||||
return result;
|
||||
return m_serde->GenerateParams();
|
||||
}
|
||||
|
||||
|
||||
void SIM_MODEL::ParseParamsField( const std::string& aParamsField )
|
||||
{
|
||||
tao::pegtl::string_input<> in( aParamsField, "Sim_Params" );
|
||||
std::unique_ptr<tao::pegtl::parse_tree::node> root;
|
||||
|
||||
try
|
||||
{
|
||||
// Using parse tree instead of actions because we don't care about performance that much,
|
||||
// and having a tree greatly simplifies things.
|
||||
root = tao::pegtl::parse_tree::parse<
|
||||
SIM_MODEL_PARSER::fieldParamValuePairsGrammar,
|
||||
SIM_MODEL_PARSER::fieldParamValuePairsSelector,
|
||||
tao::pegtl::nothing,
|
||||
SIM_MODEL_PARSER::control>
|
||||
( in );
|
||||
}
|
||||
catch( const tao::pegtl::parse_error& e )
|
||||
{
|
||||
THROW_IO_ERROR( e.what() );
|
||||
}
|
||||
|
||||
std::string paramName;
|
||||
|
||||
for( const auto& node : root->children )
|
||||
{
|
||||
if( node->is_type<SIM_MODEL_PARSER::param>() )
|
||||
paramName = node->string();
|
||||
// TODO: Do something with number<SIM_VALUE::TYPE_INT, ...>.
|
||||
// It doesn't seem too useful?
|
||||
else if( node->is_type<SIM_MODEL_PARSER::quotedStringContent>()
|
||||
|| node->is_type<SIM_MODEL_PARSER::unquotedString>() )
|
||||
{
|
||||
wxASSERT( paramName != "" );
|
||||
// TODO: Shouldn't be named "...fromSpiceCode" here...
|
||||
|
||||
SetParamValue( paramName, node->string(), SIM_VALUE_GRAMMAR::NOTATION::SI );
|
||||
}
|
||||
else if( node->is_type<SIM_MODEL_PARSER::quotedString>() )
|
||||
{
|
||||
std::string str = node->string();
|
||||
|
||||
// Unescape quotes.
|
||||
boost::replace_all( str, "\\\"", "\"" );
|
||||
|
||||
SetParamValue( paramName, str, SIM_VALUE_GRAMMAR::NOTATION::SI );
|
||||
}
|
||||
else
|
||||
{
|
||||
wxFAIL;
|
||||
}
|
||||
}
|
||||
m_serde->ParseParams( aParamsField );
|
||||
}
|
||||
|
||||
|
||||
|
@ -1165,51 +1012,13 @@ void SIM_MODEL::ParsePinsField( unsigned aSymbolPinCount, const std::string& aPi
|
|||
{
|
||||
CreatePins( aSymbolPinCount );
|
||||
|
||||
if( aPinsField == "" )
|
||||
return;
|
||||
|
||||
tao::pegtl::string_input<> in( aPinsField, PINS_FIELD );
|
||||
std::unique_ptr<tao::pegtl::parse_tree::node> root;
|
||||
|
||||
try
|
||||
{
|
||||
root = tao::pegtl::parse_tree::parse<SIM_MODEL_PARSER::pinSequenceGrammar,
|
||||
SIM_MODEL_PARSER::pinSequenceSelector,
|
||||
tao::pegtl::nothing,
|
||||
SIM_MODEL_PARSER::control>( in );
|
||||
}
|
||||
catch( const tao::pegtl::parse_error& e )
|
||||
{
|
||||
THROW_IO_ERROR( e.what() );
|
||||
}
|
||||
|
||||
if( static_cast<int>( root->children.size() ) != GetPinCount() )
|
||||
{
|
||||
THROW_IO_ERROR( wxString::Format( _( "%s describes %lu pins, expected %u" ),
|
||||
PINS_FIELD,
|
||||
root->children.size(),
|
||||
GetPinCount() ) );
|
||||
}
|
||||
|
||||
for( int pinIndex = 0; pinIndex < static_cast<int>( root->children.size() ); ++pinIndex )
|
||||
{
|
||||
if( root->children.at( pinIndex )->string() == "~" )
|
||||
SetPinSymbolPinNumber( pinIndex, "" );
|
||||
else
|
||||
SetPinSymbolPinNumber( pinIndex, root->children.at( pinIndex )->string() );
|
||||
}
|
||||
m_serde->ParsePins( aPinsField );
|
||||
}
|
||||
|
||||
|
||||
void SIM_MODEL::ParseEnableField( const std::string& aEnableField )
|
||||
{
|
||||
if( aEnableField == "" )
|
||||
return;
|
||||
|
||||
char c = boost::to_lower_copy( aEnableField )[0];
|
||||
|
||||
if( c == 'n' || c == 'f' || c == '0' )
|
||||
m_isEnabled = false;
|
||||
m_serde->ParseEnable( aEnableField );
|
||||
}
|
||||
|
||||
|
||||
|
@ -1234,7 +1043,7 @@ void SIM_MODEL::InferredReadDataFields( unsigned aSymbolPinCount, const std::vec
|
|||
return;
|
||||
|
||||
// TODO: Don't call this multiple times.
|
||||
if( InferTypeFromRefAndValue( GetFieldValue( aFields, REFERENCE_FIELD ),
|
||||
if( SIM_SERDE::InferTypeFromRefAndValue( GetFieldValue( aFields, REFERENCE_FIELD ),
|
||||
GetFieldValue( aFields, VALUE_FIELD ),
|
||||
aSymbolPinCount ) != GetType() )
|
||||
{
|
||||
|
@ -1242,46 +1051,7 @@ void SIM_MODEL::InferredReadDataFields( unsigned aSymbolPinCount, const std::vec
|
|||
return;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
// TODO: Don't call this multiple times.
|
||||
tao::pegtl::string_input<> in( GetFieldValue( aFields, VALUE_FIELD ), VALUE_FIELD );
|
||||
auto root = tao::pegtl::parse_tree::parse<SIM_MODEL_PARSER::fieldInferValueGrammar,
|
||||
SIM_MODEL_PARSER::fieldInferValueSelector,
|
||||
tao::pegtl::nothing,
|
||||
SIM_MODEL_PARSER::control>( in );
|
||||
|
||||
for( const auto& node : root->children )
|
||||
{
|
||||
if( node->is_type<SIM_MODEL_PARSER::fieldInferValuePrimaryValue>() )
|
||||
{
|
||||
if( hasPrimaryValue() )
|
||||
{
|
||||
for( const auto& subnode : node->children )
|
||||
{
|
||||
if( subnode->is_type<SIM_MODEL_PARSER::number<SIM_VALUE::TYPE_FLOAT,
|
||||
SIM_VALUE::NOTATION::SI>>() )
|
||||
{
|
||||
SetParamValue( 0, subnode->string() );
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
THROW_IO_ERROR(
|
||||
wxString::Format( _( "Simulation model of type '%s' cannot have a primary value (which is '%s') in Value field" ),
|
||||
GetTypeInfo().fieldValue,
|
||||
node->string() ) );
|
||||
}
|
||||
}
|
||||
else if( node->is_type<SIM_MODEL_PARSER::fieldParamValuePairs>() )
|
||||
ParseParamsField( node->string() );
|
||||
}
|
||||
}
|
||||
catch( const tao::pegtl::parse_error& e )
|
||||
{
|
||||
THROW_IO_ERROR( e.what() );
|
||||
}
|
||||
m_serde->ParseValue( GetFieldValue( aFields, VALUE_FIELD ) );
|
||||
|
||||
SetIsInferred( true );
|
||||
}
|
||||
|
|
|
@ -39,102 +39,7 @@
|
|||
|
||||
class SIM_LIBRARY;
|
||||
class SPICE_GENERATOR;
|
||||
|
||||
|
||||
namespace SIM_MODEL_GRAMMAR
|
||||
{
|
||||
using namespace SIM_VALUE_GRAMMAR;
|
||||
|
||||
struct sep : plus<space> {};
|
||||
|
||||
|
||||
struct legacyPinNumber : digits {};
|
||||
struct legacyPinSequence : list<legacyPinNumber, sep> {};
|
||||
|
||||
struct legacyPinSequenceGrammar : must<legacyPinSequence,
|
||||
tao::pegtl::eof> {};
|
||||
|
||||
|
||||
struct pinNumber : plus<not_at<sep>, any> {};
|
||||
struct pinSequence : list<pinNumber, sep> {};
|
||||
struct pinSequenceGrammar : must<opt<sep>,
|
||||
opt<pinSequence>,
|
||||
opt<sep>,
|
||||
tao::pegtl::eof> {};
|
||||
|
||||
struct param : plus<alnum> {};
|
||||
|
||||
struct unquotedString : plus<not_at<sep>, any> {};
|
||||
struct quotedStringContent : star<not_at<one<'"'>>, any> {}; // TODO: Allow escaping '"'.
|
||||
struct quotedString : seq<one<'"'>,
|
||||
quotedStringContent,
|
||||
one<'"'>> {};
|
||||
|
||||
struct fieldParamValuePair : if_must<param,
|
||||
opt<sep>,
|
||||
one<'='>,
|
||||
opt<sep>,
|
||||
sor<quotedString,
|
||||
unquotedString>> {};
|
||||
struct fieldParamValuePairs : list<fieldParamValuePair, sep> {};
|
||||
struct fieldParamValuePairsGrammar : must<opt<sep>,
|
||||
opt<fieldParamValuePairs>,
|
||||
opt<sep>,
|
||||
tao::pegtl::eof> {};
|
||||
|
||||
struct fieldInferValueType : plus<upper> {};
|
||||
struct fieldInferValuePrimaryValue : seq<// HACK: Because `number` matches empty string,
|
||||
// ensure it is not empty.
|
||||
at<sor<tao::pegtl::digit,
|
||||
seq<one<'.'>>,
|
||||
tao::pegtl::digit>>,
|
||||
// END HACK.
|
||||
number<SIM_VALUE::TYPE_FLOAT, NOTATION::SI>,
|
||||
// Hackish: match anything until param-value pairs.
|
||||
// Because the user may want to write something like
|
||||
// "10k 30% 30mW w=0.4", but we care only about the
|
||||
// "10k" and "w=0.4".
|
||||
star<not_at<sep,
|
||||
try_catch<fieldParamValuePairs>>,
|
||||
any>> {};
|
||||
struct fieldInferValue : sor<seq<fieldInferValueType,
|
||||
opt<sep,
|
||||
fieldParamValuePairs>>,
|
||||
seq<opt<fieldInferValuePrimaryValue>,
|
||||
opt<sep>,
|
||||
opt<fieldParamValuePairs>>> {};
|
||||
struct fieldInferValueGrammar : must<opt<sep>,
|
||||
fieldInferValue,
|
||||
opt<sep>,
|
||||
tao::pegtl::eof> {};
|
||||
|
||||
|
||||
template <typename> inline constexpr const char* errorMessage = nullptr;
|
||||
template <> inline constexpr auto errorMessage<opt<sep>> = "";
|
||||
template <> inline constexpr auto errorMessage<opt<pinSequence>> = "";
|
||||
template <> inline constexpr auto errorMessage<opt<sor<fieldInferValueType,
|
||||
fieldInferValuePrimaryValue>>> = "";
|
||||
template <> inline constexpr auto errorMessage<one<'='>> =
|
||||
"expected '='";
|
||||
template <> inline constexpr auto errorMessage<sor<quotedString,
|
||||
unquotedString>> =
|
||||
"expected quoted or unquoted string";
|
||||
template <> inline constexpr auto errorMessage<fieldParamValuePairs> =
|
||||
"expected parameter=value pairs";
|
||||
template <> inline constexpr auto errorMessage<opt<fieldParamValuePairs>> = "";
|
||||
template <> inline constexpr auto errorMessage<fieldInferValue> =
|
||||
"expected parameter=value pairs, together possibly preceded by a type or primary value";
|
||||
template <> inline constexpr auto errorMessage<tao::pegtl::eof> =
|
||||
"expected end of string";
|
||||
|
||||
struct error
|
||||
{
|
||||
template <typename Rule> static constexpr bool raise_on_failure = false;
|
||||
template <typename Rule> static constexpr auto message = errorMessage<Rule>;
|
||||
};
|
||||
|
||||
template <typename Rule> using control = must_if<error>::control<Rule>;
|
||||
}
|
||||
class SIM_SERDE;
|
||||
|
||||
|
||||
class SIM_MODEL
|
||||
|
@ -485,9 +390,6 @@ public:
|
|||
|
||||
static DEVICE_TYPE_ InferDeviceTypeFromRef( const std::string& aRef );
|
||||
|
||||
static TYPE InferTypeFromRefAndValue( const std::string& aRef, const std::string& aValue,
|
||||
int aSymbolPinCount );
|
||||
|
||||
template <typename T>
|
||||
static TYPE InferTypeFromLegacyFields( const std::vector<T>& aFields );
|
||||
|
||||
|
@ -606,6 +508,8 @@ public:
|
|||
// Can modifying a model parameter also modify other parameters?
|
||||
virtual bool HasAutofill() const { return false; }
|
||||
|
||||
virtual bool HasPrimaryValue() const { return false; }
|
||||
|
||||
void SetIsEnabled( bool aIsEnabled ) { m_isEnabled = aIsEnabled; }
|
||||
bool IsEnabled() const { return m_isEnabled; }
|
||||
|
||||
|
@ -616,15 +520,17 @@ protected:
|
|||
static std::unique_ptr<SIM_MODEL> Create( TYPE aType );
|
||||
|
||||
SIM_MODEL( TYPE aType );
|
||||
SIM_MODEL( TYPE aType, std::unique_ptr<SPICE_GENERATOR> aSpiceGenerator );
|
||||
SIM_MODEL( TYPE aType,
|
||||
std::unique_ptr<SPICE_GENERATOR> aSpiceGenerator );
|
||||
SIM_MODEL( TYPE aType,
|
||||
std::unique_ptr<SPICE_GENERATOR> aSpiceGenerator,
|
||||
std::unique_ptr<SIM_SERDE> aSerde );
|
||||
|
||||
virtual void CreatePins( unsigned aSymbolPinCount );
|
||||
|
||||
template <typename T>
|
||||
void WriteInferredDataFields( std::vector<T>& aFields, const std::string& aValue = "" ) const;
|
||||
|
||||
virtual std::string GenerateParamValuePair( const PARAM& aParam, bool& aIsFirst ) const;
|
||||
|
||||
std::string GenerateValueField( const std::string& aPairSeparator ) const;
|
||||
std::string GenerateParamsField( const std::string& aPairSeparator ) const;
|
||||
void ParseParamsField( const std::string& aParamsField );
|
||||
|
@ -636,6 +542,7 @@ protected:
|
|||
void InferredReadDataFields( unsigned aSymbolPinCount, const std::vector<T>* aFields );
|
||||
std::vector<PARAM> m_params;
|
||||
const SIM_MODEL* m_baseModel;
|
||||
std::unique_ptr<SIM_SERDE> m_serde;
|
||||
|
||||
private:
|
||||
static TYPE readTypeFromSpiceStrings( const std::string& aTypeString,
|
||||
|
@ -658,7 +565,6 @@ private:
|
|||
|
||||
std::string parseFieldFloatValue( std::string aFieldFloatValue );
|
||||
|
||||
virtual bool hasPrimaryValue() const { return false; }
|
||||
virtual bool requiresSpiceModelLine() const;
|
||||
|
||||
virtual std::vector<std::string> getPinNames() const { return {}; }
|
||||
|
|
|
@ -23,6 +23,7 @@
|
|||
*/
|
||||
|
||||
#include <sim/sim_model_behavioral.h>
|
||||
#include <sim/sim_serde.h>
|
||||
|
||||
#include <boost/algorithm/string/replace.hpp>
|
||||
#include <boost/algorithm/string/trim.hpp>
|
||||
|
@ -149,7 +150,7 @@ void SIM_MODEL_BEHAVIORAL::inferredReadDataFields( unsigned aSymbolPinCount,
|
|||
{
|
||||
ParsePinsField( aSymbolPinCount, GetFieldValue( aFields, PINS_FIELD ) );
|
||||
|
||||
if( ( InferTypeFromRefAndValue( GetFieldValue( aFields, REFERENCE_FIELD ),
|
||||
if( ( m_serde->InferTypeFromRefAndValue( GetFieldValue( aFields, REFERENCE_FIELD ),
|
||||
GetFieldValue( aFields, VALUE_FIELD ),
|
||||
aSymbolPinCount ) == GetType()
|
||||
&& parseValueField( GetFieldValue( aFields, VALUE_FIELD ) ) )
|
||||
|
|
|
@ -50,12 +50,12 @@ public:
|
|||
void WriteDataLibFields( std::vector<LIB_FIELD>& aFields ) const override;
|
||||
|
||||
const PARAM* GetTunerParam() const override { return &GetParam( 0 ); }
|
||||
bool HasPrimaryValue() const override { return true; }
|
||||
|
||||
private:
|
||||
template <typename T>
|
||||
void inferredWriteDataFields( std::vector<T>& aFields ) const;
|
||||
|
||||
bool hasPrimaryValue() const override { return true; }
|
||||
std::vector<std::string> getPinNames() const override { return { "+", "-" }; }
|
||||
|
||||
static PARAM::INFO makeParamInfo( std::string aName, std::string aDescription, std::string aUnit );
|
||||
|
|
|
@ -49,13 +49,13 @@ public:
|
|||
void WriteDataLibFields( std::vector<LIB_FIELD>& aFields ) const override;
|
||||
|
||||
const PARAM* GetTunerParam() const override { return FindParam( "pos" ); }
|
||||
bool HasPrimaryValue() const override { return true; }
|
||||
|
||||
private:
|
||||
template <typename T>
|
||||
void inferredWriteDataFields( std::vector<T>& aFields ) const;
|
||||
|
||||
std::vector<std::string> getPinNames() const override { return { "+", "wiper", "-" }; }
|
||||
bool hasPrimaryValue() const override { return true; }
|
||||
|
||||
static const std::vector<PARAM::INFO> makeParamInfos();
|
||||
};
|
||||
|
|
|
@ -23,6 +23,7 @@
|
|||
*/
|
||||
|
||||
#include <sim/sim_model_raw_spice.h>
|
||||
#include <sim/sim_serde.h>
|
||||
|
||||
#include <boost/algorithm/string/predicate.hpp>
|
||||
#include <fmt/core.h>
|
||||
|
@ -32,7 +33,7 @@
|
|||
|
||||
namespace SIM_MODEL_RAW_SPICE_PARSER
|
||||
{
|
||||
using namespace SIM_MODEL_GRAMMAR;
|
||||
using namespace SIM_SERDE_GRAMMAR;
|
||||
|
||||
template <typename Rule> struct legacyPinSequenceSelector : std::false_type {};
|
||||
template <> struct legacyPinSequenceSelector<legacyPinNumber> : std::true_type {};
|
||||
|
|
|
@ -198,6 +198,15 @@ std::string SPICE_GENERATOR_SOURCE::getParamValueString( const std::string& aPar
|
|||
}
|
||||
|
||||
|
||||
std::string SIM_SERDE_SOURCE::GenerateParamValuePair( const SIM_MODEL::PARAM& aParam ) const
|
||||
{
|
||||
if( aParam.value->ToString() == "0" )
|
||||
return "";
|
||||
|
||||
return SIM_SERDE::GenerateParamValuePair( aParam );
|
||||
}
|
||||
|
||||
|
||||
SIM_MODEL_SOURCE::SIM_MODEL_SOURCE( TYPE aType )
|
||||
: SIM_MODEL( aType, std::make_unique<SPICE_GENERATOR_SOURCE>( *this ) )
|
||||
{
|
||||
|
@ -253,15 +262,6 @@ void SIM_MODEL_SOURCE::SetParamValue( int aParamIndex, const SIM_VALUE& aValue )
|
|||
}
|
||||
|
||||
|
||||
std::string SIM_MODEL_SOURCE::GenerateParamValuePair( const PARAM& aParam, bool& aIsFirst ) const
|
||||
{
|
||||
if( aParam.value->ToString() == "0" )
|
||||
return "";
|
||||
|
||||
return SIM_MODEL::GenerateParamValuePair( aParam, aIsFirst );
|
||||
}
|
||||
|
||||
|
||||
template <typename T>
|
||||
void SIM_MODEL_SOURCE::inferredWriteDataFields( std::vector<T>& aFields ) const
|
||||
{
|
||||
|
|
|
@ -26,12 +26,13 @@
|
|||
#define SIM_MODEL_SOURCE_H
|
||||
|
||||
#include <sim/sim_model.h>
|
||||
#include <sim/sim_serde.h>
|
||||
#include <sim/spice_generator.h>
|
||||
|
||||
|
||||
namespace SIM_MODEL_SOURCE_GRAMMAR
|
||||
{
|
||||
using namespace SIM_MODEL_GRAMMAR;
|
||||
using namespace SIM_SERDE_GRAMMAR;
|
||||
|
||||
struct pwlSep : plus<space> {};
|
||||
struct pwlValues : seq<opt<number<SIM_VALUE::TYPE_FLOAT, NOTATION::SI>>,
|
||||
|
@ -58,6 +59,13 @@ private:
|
|||
};
|
||||
|
||||
|
||||
class SIM_SERDE_SOURCE : public SIM_SERDE
|
||||
{
|
||||
protected:
|
||||
std::string GenerateParamValuePair( const SIM_MODEL::PARAM& aParam ) const override;
|
||||
};
|
||||
|
||||
|
||||
class SIM_MODEL_SOURCE : public SIM_MODEL
|
||||
{
|
||||
public:
|
||||
|
@ -69,16 +77,13 @@ public:
|
|||
void SetParamValue( int aParamIndex, const SIM_VALUE& aValue ) override;
|
||||
|
||||
bool HasAutofill() const override { return true; }
|
||||
|
||||
protected:
|
||||
std::string GenerateParamValuePair( const PARAM& aParam, bool& aIsFirst ) const override;
|
||||
bool HasPrimaryValue() const override { return true; }
|
||||
|
||||
private:
|
||||
template <typename T>
|
||||
void inferredWriteDataFields( std::vector<T>& aFields ) const;
|
||||
|
||||
|
||||
bool hasPrimaryValue() const override { return true; }
|
||||
std::vector<std::string> getPinNames() const override { return { "+", "-" }; }
|
||||
|
||||
static const std::vector<PARAM::INFO>& makeParamInfos( TYPE aType );
|
||||
|
|
|
@ -94,8 +94,19 @@ std::vector<std::reference_wrapper<const SIM_MODEL::PIN>> SPICE_GENERATOR_SWITCH
|
|||
}
|
||||
|
||||
|
||||
std::string SIM_SERDE_SWITCH::GenerateParamValuePair( const SIM_MODEL::PARAM& aParam ) const
|
||||
{
|
||||
if( aParam.info.name == "ic" && aParam.value->ToString() == "none" )
|
||||
return "";
|
||||
|
||||
return SIM_SERDE::GenerateParamValuePair( aParam );
|
||||
}
|
||||
|
||||
|
||||
SIM_MODEL_SWITCH::SIM_MODEL_SWITCH( TYPE aType ) :
|
||||
SIM_MODEL( aType, std::make_unique<SPICE_GENERATOR_SWITCH>( *this ) )
|
||||
SIM_MODEL( aType,
|
||||
std::make_unique<SPICE_GENERATOR_SWITCH>( *this ),
|
||||
std::make_unique<SIM_SERDE_SWITCH>( *this ) )
|
||||
{
|
||||
static std::vector<PARAM::INFO> vsw = makeSwVParamInfos();
|
||||
static std::vector<PARAM::INFO> isw = makeSwIParamInfos();
|
||||
|
@ -121,15 +132,6 @@ SIM_MODEL_SWITCH::SIM_MODEL_SWITCH( TYPE aType ) :
|
|||
}
|
||||
|
||||
|
||||
std::string SIM_MODEL_SWITCH::GenerateParamValuePair( const PARAM& aParam, bool& aIsFirst ) const
|
||||
{
|
||||
if( aParam.info.name == "ic" && aParam.value->ToString() == "none" )
|
||||
{
|
||||
return "";
|
||||
}
|
||||
|
||||
return SIM_MODEL::GenerateParamValuePair( aParam, aIsFirst );
|
||||
}
|
||||
|
||||
|
||||
const std::vector<SIM_MODEL::PARAM::INFO> SIM_MODEL_SWITCH::makeSwVParamInfos()
|
||||
|
|
|
@ -27,6 +27,7 @@
|
|||
|
||||
#include <sim/sim_model.h>
|
||||
#include <sim/spice_generator.h>
|
||||
#include <sim/sim_serde.h>
|
||||
|
||||
|
||||
class SPICE_GENERATOR_SWITCH : public SPICE_GENERATOR
|
||||
|
@ -40,14 +41,21 @@ public:
|
|||
};
|
||||
|
||||
|
||||
class SIM_SERDE_SWITCH : public SIM_SERDE
|
||||
{
|
||||
public:
|
||||
using SIM_SERDE::SIM_SERDE;
|
||||
|
||||
protected:
|
||||
std::string GenerateParamValuePair( const SIM_MODEL::PARAM& aParam ) const override;
|
||||
};
|
||||
|
||||
|
||||
class SIM_MODEL_SWITCH : public SIM_MODEL
|
||||
{
|
||||
public:
|
||||
SIM_MODEL_SWITCH( TYPE aType );
|
||||
|
||||
protected:
|
||||
std::string GenerateParamValuePair( const PARAM& aParam, bool& aIsFirst ) const override;
|
||||
|
||||
private:
|
||||
std::vector<std::string> getPinNames() const override
|
||||
{
|
||||
|
|
|
@ -0,0 +1,364 @@
|
|||
/*
|
||||
* 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_serde.h>
|
||||
#include <fmt/core.h>
|
||||
#include <pegtl.hpp>
|
||||
#include <pegtl/contrib/parse_tree.hpp>
|
||||
#include <boost/algorithm/string/replace.hpp>
|
||||
#include <boost/algorithm/string/case_conv.hpp>
|
||||
#include <boost/algorithm/string/predicate.hpp>
|
||||
|
||||
|
||||
namespace SIM_SERDE_PARSER
|
||||
{
|
||||
using namespace SIM_SERDE_GRAMMAR;
|
||||
|
||||
template <typename Rule> struct fieldParamValuePairsSelector : std::false_type {};
|
||||
template <> struct fieldParamValuePairsSelector<param> : std::true_type {};
|
||||
template <> struct fieldParamValuePairsSelector<quotedStringContent> : std::true_type {};
|
||||
template <> struct fieldParamValuePairsSelector<unquotedString> : std::true_type {};
|
||||
|
||||
|
||||
template <typename Rule> struct pinSequenceSelector : std::false_type {};
|
||||
template <> struct pinSequenceSelector<pinNumber> : std::true_type {};
|
||||
|
||||
template <typename Rule> struct fieldInferValueSelector : std::false_type {};
|
||||
template <> struct fieldInferValueSelector<fieldInferValueType> : std::true_type {};
|
||||
template <> struct fieldInferValueSelector<fieldInferValuePrimaryValue> : std::true_type {};
|
||||
template <> struct fieldInferValueSelector<number<SIM_VALUE::TYPE_FLOAT, NOTATION::SI>> : std::true_type {};
|
||||
template <> struct fieldInferValueSelector<fieldParamValuePairs> : std::true_type {};
|
||||
}
|
||||
|
||||
|
||||
std::string SIM_SERDE::GenerateDevice() const
|
||||
{
|
||||
return m_model.GetDeviceTypeInfo().fieldValue;
|
||||
}
|
||||
|
||||
|
||||
std::string SIM_SERDE::GenerateType() const
|
||||
{
|
||||
return m_model.GetTypeInfo().fieldValue;
|
||||
}
|
||||
|
||||
|
||||
std::string SIM_SERDE::GenerateValue() const
|
||||
{
|
||||
std::string result;
|
||||
|
||||
for( int i = 0; i < m_model.GetParamCount(); ++i )
|
||||
{
|
||||
const SIM_MODEL::PARAM& param = m_model.GetParam( i );
|
||||
|
||||
if( i == 0 && m_model.HasPrimaryValue() )
|
||||
{
|
||||
result.append( param.value->ToString() );
|
||||
continue;
|
||||
}
|
||||
|
||||
if( param.value->ToString() == "" )
|
||||
continue;
|
||||
|
||||
result.append( fmt::format( " {}", GenerateParamValuePair( param ) ) );
|
||||
}
|
||||
|
||||
if( result == "" )
|
||||
result = m_model.GetDeviceTypeInfo().fieldValue;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
std::string SIM_SERDE::GenerateParams() const
|
||||
{
|
||||
std::string result;
|
||||
|
||||
for( int i = 0; i < m_model.GetParamCount(); ++i )
|
||||
{
|
||||
const SIM_MODEL::PARAM& param = m_model.GetParam( i );
|
||||
|
||||
if( param.value->ToString() == "" )
|
||||
continue;
|
||||
|
||||
if( i != 0 )
|
||||
result.append( " " );
|
||||
|
||||
result.append( GenerateParamValuePair( param ) );
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
std::string SIM_SERDE::GeneratePins() const
|
||||
{
|
||||
std::string result;
|
||||
|
||||
for( int i = 0; i < m_model.GetPinCount(); ++i )
|
||||
{
|
||||
const SIM_MODEL::PIN& pin = m_model.GetPin( i );
|
||||
|
||||
if( i != 0 )
|
||||
result.append( " " );
|
||||
|
||||
if( pin.symbolPinNumber == "" )
|
||||
result.append( "~" );
|
||||
else
|
||||
result.append( pin.symbolPinNumber );
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
std::string SIM_SERDE::GenerateEnable() const
|
||||
{
|
||||
return m_model.IsEnabled() ? "" : "0";
|
||||
}
|
||||
|
||||
|
||||
SIM_MODEL::TYPE SIM_SERDE::ParseDeviceAndType( const std::string& aDevice,
|
||||
const std::string& aType )
|
||||
{
|
||||
for( SIM_MODEL::TYPE type : SIM_MODEL::TYPE_ITERATOR() )
|
||||
{
|
||||
if( aType == SIM_MODEL::TypeInfo( type ).fieldValue
|
||||
&& aDevice == SIM_MODEL::DeviceTypeInfo( SIM_MODEL::TypeInfo( type ).deviceType ).fieldValue )
|
||||
{
|
||||
return type;
|
||||
}
|
||||
}
|
||||
|
||||
return SIM_MODEL::TYPE::NONE;
|
||||
}
|
||||
|
||||
|
||||
void SIM_SERDE::ParseValue( const std::string& aValue )
|
||||
{
|
||||
try
|
||||
{
|
||||
// TODO: Don't call this multiple times.
|
||||
tao::pegtl::string_input<> in( aValue, SIM_MODEL::VALUE_FIELD );
|
||||
auto root = tao::pegtl::parse_tree::parse<SIM_SERDE_PARSER::fieldInferValueGrammar,
|
||||
SIM_SERDE_PARSER::fieldInferValueSelector,
|
||||
tao::pegtl::nothing,
|
||||
SIM_SERDE_PARSER::control>( in );
|
||||
|
||||
for( const auto& node : root->children )
|
||||
{
|
||||
if( node->is_type<SIM_SERDE_PARSER::fieldInferValuePrimaryValue>() )
|
||||
{
|
||||
if( m_model.HasPrimaryValue() )
|
||||
{
|
||||
for( const auto& subnode : node->children )
|
||||
{
|
||||
if( subnode->is_type<SIM_SERDE_PARSER::number<SIM_VALUE::TYPE_FLOAT,
|
||||
SIM_VALUE::NOTATION::SI>>() )
|
||||
{
|
||||
m_model.SetParamValue( 0, subnode->string() );
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
THROW_IO_ERROR(
|
||||
wxString::Format( _( "Simulation model of type '%s' cannot have a primary value (which is '%s') in Value field" ),
|
||||
m_model.GetTypeInfo().fieldValue,
|
||||
node->string() ) );
|
||||
}
|
||||
}
|
||||
else if( node->is_type<SIM_SERDE_PARSER::fieldParamValuePairs>() )
|
||||
ParseParams( node->string() );
|
||||
}
|
||||
}
|
||||
catch( const tao::pegtl::parse_error& e )
|
||||
{
|
||||
THROW_IO_ERROR( e.what() );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void SIM_SERDE::ParseParams( const std::string& aParams )
|
||||
{
|
||||
tao::pegtl::string_input<> in( aParams, SIM_MODEL::PARAMS_FIELD );
|
||||
std::unique_ptr<tao::pegtl::parse_tree::node> root;
|
||||
|
||||
try
|
||||
{
|
||||
// Using parse tree instead of actions because we don't care about performance that much,
|
||||
// and having a tree greatly simplifies things.
|
||||
root = tao::pegtl::parse_tree::parse<
|
||||
SIM_SERDE_PARSER::fieldParamValuePairsGrammar,
|
||||
SIM_SERDE_PARSER::fieldParamValuePairsSelector,
|
||||
tao::pegtl::nothing,
|
||||
SIM_SERDE_PARSER::control>
|
||||
( in );
|
||||
}
|
||||
catch( const tao::pegtl::parse_error& e )
|
||||
{
|
||||
THROW_IO_ERROR( e.what() );
|
||||
}
|
||||
|
||||
std::string paramName;
|
||||
|
||||
for( const auto& node : root->children )
|
||||
{
|
||||
if( node->is_type<SIM_SERDE_PARSER::param>() )
|
||||
paramName = node->string();
|
||||
// TODO: Do something with number<SIM_VALUE::TYPE_INT, ...>.
|
||||
// It doesn't seem too useful?
|
||||
else if( node->is_type<SIM_SERDE_PARSER::quotedStringContent>()
|
||||
|| node->is_type<SIM_SERDE_PARSER::unquotedString>() )
|
||||
{
|
||||
wxASSERT( paramName != "" );
|
||||
// TODO: Shouldn't be named "...fromSpiceCode" here...
|
||||
|
||||
m_model.SetParamValue( paramName, node->string(), SIM_VALUE_GRAMMAR::NOTATION::SI );
|
||||
}
|
||||
else if( node->is_type<SIM_SERDE_PARSER::quotedString>() )
|
||||
{
|
||||
std::string str = node->string();
|
||||
|
||||
// Unescape quotes.
|
||||
boost::replace_all( str, "\\\"", "\"" );
|
||||
|
||||
m_model.SetParamValue( paramName, str, SIM_VALUE_GRAMMAR::NOTATION::SI );
|
||||
}
|
||||
else
|
||||
{
|
||||
wxFAIL;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void SIM_SERDE::ParsePins( const std::string& aPins )
|
||||
{
|
||||
if( aPins == "" )
|
||||
return;
|
||||
|
||||
tao::pegtl::string_input<> in( aPins, PINS_FIELD );
|
||||
std::unique_ptr<tao::pegtl::parse_tree::node> root;
|
||||
|
||||
try
|
||||
{
|
||||
root = tao::pegtl::parse_tree::parse<SIM_SERDE_PARSER::pinSequenceGrammar,
|
||||
SIM_SERDE_PARSER::pinSequenceSelector,
|
||||
tao::pegtl::nothing,
|
||||
SIM_SERDE_PARSER::control>( in );
|
||||
}
|
||||
catch( const tao::pegtl::parse_error& e )
|
||||
{
|
||||
THROW_IO_ERROR( e.what() );
|
||||
}
|
||||
|
||||
if( static_cast<int>( root->children.size() ) != m_model.GetPinCount() )
|
||||
{
|
||||
THROW_IO_ERROR( wxString::Format( _( "%s describes %lu pins, expected %u" ),
|
||||
PINS_FIELD,
|
||||
root->children.size(),
|
||||
m_model.GetPinCount() ) );
|
||||
}
|
||||
|
||||
for( int pinIndex = 0; pinIndex < static_cast<int>( root->children.size() ); ++pinIndex )
|
||||
{
|
||||
if( root->children.at( pinIndex )->string() == "~" )
|
||||
m_model.SetPinSymbolPinNumber( pinIndex, "" );
|
||||
else
|
||||
m_model.SetPinSymbolPinNumber( pinIndex, root->children.at( pinIndex )->string() );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void SIM_SERDE::ParseEnable( const std::string& aEnable )
|
||||
{
|
||||
if( aEnable == "" )
|
||||
return;
|
||||
|
||||
char c = boost::to_lower_copy( aEnable )[0];
|
||||
|
||||
if( c == 'n' || c == 'f' || c == '0' )
|
||||
m_model.SetIsEnabled( false );
|
||||
}
|
||||
|
||||
|
||||
SIM_MODEL::TYPE SIM_SERDE::InferTypeFromRefAndValue( const std::string& aRef,
|
||||
const std::string& aValue,
|
||||
int aSymbolPinCount )
|
||||
{
|
||||
std::string typeString;
|
||||
|
||||
try
|
||||
{
|
||||
tao::pegtl::string_input<> in( aValue, VALUE_FIELD );
|
||||
auto root = tao::pegtl::parse_tree::parse<SIM_SERDE_PARSER::fieldInferValueGrammar,
|
||||
SIM_SERDE_PARSER::fieldInferValueSelector,
|
||||
tao::pegtl::nothing,
|
||||
SIM_SERDE_PARSER::control>( in );
|
||||
|
||||
for( const auto& node : root->children )
|
||||
{
|
||||
if( node->is_type<SIM_SERDE_PARSER::fieldInferValueType>() )
|
||||
typeString = node->string();
|
||||
}
|
||||
}
|
||||
catch( const tao::pegtl::parse_error& )
|
||||
{
|
||||
}
|
||||
|
||||
SIM_MODEL::DEVICE_TYPE_ deviceType = SIM_MODEL::InferDeviceTypeFromRef( aRef );
|
||||
|
||||
// Exception. Potentiometer model is determined from pin count.
|
||||
if( deviceType == SIM_MODEL::DEVICE_TYPE_::R && aSymbolPinCount == 3 )
|
||||
return SIM_MODEL::TYPE::R_POT;
|
||||
|
||||
for( SIM_MODEL::TYPE type : SIM_MODEL::TYPE_ITERATOR() )
|
||||
{
|
||||
if( SIM_MODEL::TypeInfo( type ).deviceType == deviceType
|
||||
&& SIM_MODEL::TypeInfo( type ).fieldValue == typeString )
|
||||
{
|
||||
return type;
|
||||
}
|
||||
}
|
||||
|
||||
return SIM_MODEL::TYPE::NONE;
|
||||
}
|
||||
|
||||
|
||||
std::string SIM_SERDE::GenerateParamValuePair( const SIM_MODEL::PARAM& aParam ) const
|
||||
{
|
||||
std::string name = aParam.info.name;
|
||||
|
||||
// Because of collisions with instance parameters, we append some model parameters with "_".
|
||||
if( boost::ends_with( aParam.info.name, "_" ) )
|
||||
name = aParam.info.name.substr( 0, aParam.info.name.length() - 1 );
|
||||
|
||||
std::string value = aParam.value->ToString();
|
||||
|
||||
if( value.find( " " ) != std::string::npos )
|
||||
value = fmt::format( "\"{}\"", value );
|
||||
|
||||
return fmt::format( "{}={}", aParam.info.name, value );
|
||||
}
|
|
@ -0,0 +1,169 @@
|
|||
/*
|
||||
* 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
|
||||
*/
|
||||
|
||||
#ifndef SIM_SERDE_H
|
||||
#define SIM_SERDE_H
|
||||
|
||||
#include <sim/sim_model.h>
|
||||
|
||||
|
||||
namespace SIM_SERDE_GRAMMAR
|
||||
{
|
||||
using namespace SIM_VALUE_GRAMMAR;
|
||||
|
||||
struct sep : plus<space> {};
|
||||
|
||||
|
||||
struct legacyPinNumber : digits {};
|
||||
struct legacyPinSequence : list<legacyPinNumber, sep> {};
|
||||
|
||||
struct legacyPinSequenceGrammar : must<legacyPinSequence,
|
||||
tao::pegtl::eof> {};
|
||||
|
||||
|
||||
struct pinNumber : plus<not_at<sep>, any> {};
|
||||
struct pinSequence : list<pinNumber, sep> {};
|
||||
struct pinSequenceGrammar : must<opt<sep>,
|
||||
opt<pinSequence>,
|
||||
opt<sep>,
|
||||
tao::pegtl::eof> {};
|
||||
|
||||
struct param : plus<alnum> {};
|
||||
|
||||
struct unquotedString : plus<not_at<sep>, any> {};
|
||||
struct quotedStringContent : star<not_at<one<'"'>>, any> {}; // TODO: Allow escaping '"'.
|
||||
struct quotedString : seq<one<'"'>,
|
||||
quotedStringContent,
|
||||
one<'"'>> {};
|
||||
|
||||
struct fieldParamValuePair : if_must<param,
|
||||
opt<sep>,
|
||||
one<'='>,
|
||||
opt<sep>,
|
||||
sor<quotedString,
|
||||
unquotedString>> {};
|
||||
struct fieldParamValuePairs : list<fieldParamValuePair, sep> {};
|
||||
struct fieldParamValuePairsGrammar : must<opt<sep>,
|
||||
opt<fieldParamValuePairs>,
|
||||
opt<sep>,
|
||||
tao::pegtl::eof> {};
|
||||
|
||||
struct fieldInferValueType : plus<upper> {};
|
||||
struct fieldInferValuePrimaryValue : seq<// HACK: Because `number` matches empty string,
|
||||
// ensure it is not empty.
|
||||
at<sor<tao::pegtl::digit,
|
||||
seq<one<'.'>>,
|
||||
tao::pegtl::digit>>,
|
||||
// END HACK.
|
||||
number<SIM_VALUE::TYPE_FLOAT, NOTATION::SI>,
|
||||
// Hackish: match anything until param-value pairs.
|
||||
// Because the user may want to write something like
|
||||
// "10k 30% 30mW w=0.4", but we care only about the
|
||||
// "10k" and "w=0.4".
|
||||
star<not_at<sep,
|
||||
try_catch<fieldParamValuePairs>>,
|
||||
any>> {};
|
||||
struct fieldInferValue : sor<seq<fieldInferValueType,
|
||||
opt<sep,
|
||||
fieldParamValuePairs>>,
|
||||
seq<opt<fieldInferValuePrimaryValue>,
|
||||
opt<sep>,
|
||||
opt<fieldParamValuePairs>>> {};
|
||||
struct fieldInferValueGrammar : must<opt<sep>,
|
||||
fieldInferValue,
|
||||
opt<sep>,
|
||||
tao::pegtl::eof> {};
|
||||
|
||||
|
||||
template <typename> inline constexpr const char* errorMessage = nullptr;
|
||||
template <> inline constexpr auto errorMessage<opt<sep>> = "";
|
||||
template <> inline constexpr auto errorMessage<opt<pinSequence>> = "";
|
||||
template <> inline constexpr auto errorMessage<opt<sor<fieldInferValueType,
|
||||
fieldInferValuePrimaryValue>>> = "";
|
||||
template <> inline constexpr auto errorMessage<one<'='>> =
|
||||
"expected '='";
|
||||
template <> inline constexpr auto errorMessage<sor<quotedString,
|
||||
unquotedString>> =
|
||||
"expected quoted or unquoted string";
|
||||
template <> inline constexpr auto errorMessage<fieldParamValuePairs> =
|
||||
"expected parameter=value pairs";
|
||||
template <> inline constexpr auto errorMessage<opt<fieldParamValuePairs>> = "";
|
||||
template <> inline constexpr auto errorMessage<fieldInferValue> =
|
||||
"expected parameter=value pairs, together possibly preceded by a type or primary value";
|
||||
template <> inline constexpr auto errorMessage<tao::pegtl::eof> =
|
||||
"expected end of string";
|
||||
|
||||
struct error
|
||||
{
|
||||
template <typename Rule> static constexpr bool raise_on_failure = false;
|
||||
template <typename Rule> static constexpr auto message = errorMessage<Rule>;
|
||||
};
|
||||
|
||||
template <typename Rule> using control = must_if<error>::control<Rule>;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* SerDe = Serializer-Deserializer
|
||||
*/
|
||||
class SIM_SERDE
|
||||
{
|
||||
public:
|
||||
static constexpr auto REFERENCE_FIELD = "Reference";
|
||||
static constexpr auto VALUE_FIELD = "Value";
|
||||
|
||||
static constexpr auto DEVICE_TYPE_FIELD = "Sim_Device";
|
||||
static constexpr auto TYPE_FIELD = "Sim_Type";
|
||||
static constexpr auto PINS_FIELD = "Sim_Pins";
|
||||
static constexpr auto PARAMS_FIELD = "Sim_Params";
|
||||
static constexpr auto ENABLE_FIELD = "Sim_Enable";
|
||||
|
||||
SIM_SERDE( SIM_MODEL& aModel ) : m_model( aModel ) {}
|
||||
|
||||
std::string GenerateDevice() const;
|
||||
std::string GenerateType() const;
|
||||
std::string GenerateValue() const;
|
||||
std::string GenerateParams() const;
|
||||
std::string GeneratePins() const;
|
||||
std::string GenerateEnable() const;
|
||||
|
||||
SIM_MODEL::TYPE ParseDeviceAndType( const std::string& aDevice,
|
||||
const std::string& aType );
|
||||
void ParseValue( const std::string& aValue );
|
||||
void ParseParams( const std::string& aParams );
|
||||
void ParsePins( const std::string& aPins );
|
||||
void ParseEnable( const std::string& aEnable );
|
||||
|
||||
static SIM_MODEL::TYPE InferTypeFromRefAndValue( const std::string& aRef,
|
||||
const std::string& aValue,
|
||||
int aSymbolPinCount );
|
||||
|
||||
protected:
|
||||
virtual std::string GenerateParamValuePair( const SIM_MODEL::PARAM& aParam ) const;
|
||||
|
||||
private:
|
||||
SIM_MODEL& m_model;
|
||||
};
|
||||
|
||||
#endif // SIM_SERDE_H
|
Loading…
Reference in New Issue