Sim: Move Spice model parsing to a separate class hierarchy
Roughly analogous to the SPICE_GENERATOR hierarchy.
This commit is contained in:
parent
42005d9678
commit
8599323dce
|
@ -340,6 +340,7 @@ if( KICAD_SPICE )
|
|||
sim/sim_plot_panel.cpp
|
||||
sim/sim_property.cpp
|
||||
sim/sim_workbook.cpp
|
||||
sim/spice_model_parser.cpp
|
||||
sim/spice_simulator.cpp
|
||||
sim/spice_value.cpp
|
||||
widgets/tuner_slider.cpp
|
||||
|
|
|
@ -931,7 +931,7 @@ std::unique_ptr<SIM_MODEL> SIM_MODEL::Create( TYPE aType )
|
|||
return std::make_unique<SIM_MODEL_SOURCE>( aType );
|
||||
|
||||
case TYPE::SUBCKT:
|
||||
return std::make_unique<SIM_MODEL_SUBCKT>( aType );
|
||||
return std::make_unique<SIM_MODEL_SUBCKT>();
|
||||
|
||||
case TYPE::XSPICE:
|
||||
return std::make_unique<SIM_MODEL_XSPICE>( aType );
|
||||
|
|
|
@ -24,27 +24,9 @@
|
|||
|
||||
#include <sim/sim_model_spice.h>
|
||||
#include <sim/sim_model_raw_spice.h>
|
||||
#include <sim/spice_model_parser.h>
|
||||
|
||||
#include <sim/spice_grammar.h>
|
||||
#include <confirm.h>
|
||||
#include <pegtl.hpp>
|
||||
#include <pegtl/contrib/parse_tree.hpp>
|
||||
|
||||
|
||||
namespace SIM_MODEL_SPICE_PARSER
|
||||
{
|
||||
using namespace SPICE_GRAMMAR;
|
||||
|
||||
template <typename Rule> struct spiceUnitSelector : std::false_type {};
|
||||
|
||||
template <> struct spiceUnitSelector<dotModel> : std::true_type {};
|
||||
template <> struct spiceUnitSelector<modelName> : std::true_type {};
|
||||
template <> struct spiceUnitSelector<dotModelType> : std::true_type {};
|
||||
template <> struct spiceUnitSelector<param> : std::true_type {};
|
||||
template <> struct spiceUnitSelector<paramValue> : std::true_type {};
|
||||
|
||||
template <> struct spiceUnitSelector<dotSubckt> : std::true_type {};
|
||||
}
|
||||
|
||||
|
||||
wxString SPICE_GENERATOR_SPICE::Preview( const wxString& aModelName ) const
|
||||
|
@ -66,96 +48,14 @@ wxString SPICE_GENERATOR_SPICE::Preview( const wxString& aModelName ) const
|
|||
}
|
||||
|
||||
|
||||
SIM_MODEL::TYPE SIM_MODEL_SPICE::ReadTypeFromSpiceCode( const wxString& aSpiceCode )
|
||||
{
|
||||
tao::pegtl::string_input<> in( aSpiceCode.ToUTF8(), "Spice_Code" );
|
||||
std::unique_ptr<tao::pegtl::parse_tree::node> root;
|
||||
|
||||
try
|
||||
{
|
||||
root = tao::pegtl::parse_tree::parse<SIM_MODEL_SPICE_PARSER::spiceUnitGrammar,
|
||||
SIM_MODEL_SPICE_PARSER::spiceUnitSelector,
|
||||
tao::pegtl::nothing,
|
||||
SIM_MODEL_SPICE_PARSER::control>
|
||||
( in );
|
||||
}
|
||||
catch( const tao::pegtl::parse_error& e )
|
||||
{
|
||||
wxLogDebug( "%s", e.what() );
|
||||
return TYPE::NONE;
|
||||
}
|
||||
|
||||
for( const auto& node : root->children )
|
||||
{
|
||||
if( node->is_type<SIM_MODEL_SPICE_PARSER::dotModel>() )
|
||||
{
|
||||
wxString paramName;
|
||||
wxString typeString;
|
||||
wxString level;
|
||||
wxString version;
|
||||
|
||||
for( const auto& subnode : node->children )
|
||||
{
|
||||
if( subnode->is_type<SIM_MODEL_SPICE_PARSER::modelName>() )
|
||||
{
|
||||
// Do nothing.
|
||||
}
|
||||
else if( subnode->is_type<SIM_MODEL_SPICE_PARSER::dotModelType>() )
|
||||
{
|
||||
typeString = subnode->string();
|
||||
TYPE type = readTypeFromSpiceStrings( typeString );
|
||||
|
||||
if( type != TYPE::RAWSPICE )
|
||||
return type;
|
||||
}
|
||||
else if( subnode->is_type<SIM_MODEL_SPICE_PARSER::param>() )
|
||||
{
|
||||
paramName = subnode->string();
|
||||
}
|
||||
else if( subnode->is_type<SIM_MODEL_SPICE_PARSER::paramValue>() )
|
||||
{
|
||||
wxASSERT( paramName != "" );
|
||||
|
||||
if( paramName == "level" )
|
||||
level = subnode->string();
|
||||
else if( paramName == "version" )
|
||||
version = subnode->string();
|
||||
}
|
||||
else
|
||||
{
|
||||
wxFAIL_MSG( "Unhandled parse tree subnode" );
|
||||
return TYPE::NONE;
|
||||
}
|
||||
}
|
||||
|
||||
// Type was not determined from Spice type string alone, so now we take `level` and
|
||||
// `version` variables into account too. This is suboptimal since we read the model
|
||||
// twice this way, and moreover the code is now somewhat duplicated.
|
||||
|
||||
return readTypeFromSpiceStrings( typeString, level, version, false );
|
||||
}
|
||||
else if( node->is_type<SIM_MODEL_SPICE_PARSER::dotSubckt>() )
|
||||
return TYPE::SUBCKT;
|
||||
else
|
||||
{
|
||||
wxFAIL_MSG( "Unhandled parse tree node" );
|
||||
return TYPE::NONE;
|
||||
}
|
||||
}
|
||||
|
||||
wxFAIL_MSG( "Could not derive type from Spice code" );
|
||||
return TYPE::NONE;
|
||||
}
|
||||
|
||||
|
||||
std::unique_ptr<SIM_MODEL_SPICE> SIM_MODEL_SPICE::Create( const wxString& aSpiceCode )
|
||||
{
|
||||
auto model = static_cast<SIM_MODEL_SPICE*>(
|
||||
SIM_MODEL::Create( ReadTypeFromSpiceCode( aSpiceCode ) ).release() );
|
||||
SIM_MODEL::Create( SPICE_MODEL_PARSER::ReadType( aSpiceCode ) ).release() );
|
||||
|
||||
try
|
||||
{
|
||||
model->ReadSpiceCode( aSpiceCode );
|
||||
model->m_spiceModelParser->ReadModel( aSpiceCode );
|
||||
}
|
||||
catch( const IO_ERROR& e )
|
||||
{
|
||||
|
@ -166,72 +66,20 @@ std::unique_ptr<SIM_MODEL_SPICE> SIM_MODEL_SPICE::Create( const wxString& aSpice
|
|||
}
|
||||
|
||||
|
||||
void SIM_MODEL_SPICE::ReadSpiceCode( const wxString& aSpiceCode )
|
||||
SIM_MODEL_SPICE::SIM_MODEL_SPICE( TYPE aType,
|
||||
std::unique_ptr<SPICE_GENERATOR> aSpiceGenerator ) :
|
||||
SIM_MODEL( aType, std::move( aSpiceGenerator ) ),
|
||||
m_spiceModelParser( std::make_unique<SPICE_MODEL_PARSER>( *this ) )
|
||||
{
|
||||
// The default behavior is to treat the Spice param=value pairs as the model parameters and
|
||||
// values (for many models the correspondence is not exact, so this function is overridden).
|
||||
}
|
||||
|
||||
tao::pegtl::string_input<> in( aSpiceCode.ToUTF8(), "Spice_Code" );
|
||||
std::unique_ptr<tao::pegtl::parse_tree::node> root;
|
||||
|
||||
try
|
||||
{
|
||||
root = tao::pegtl::parse_tree::parse<SIM_MODEL_SPICE_PARSER::spiceUnitGrammar,
|
||||
SIM_MODEL_SPICE_PARSER::spiceUnitSelector,
|
||||
tao::pegtl::nothing,
|
||||
SIM_MODEL_SPICE_PARSER::control>
|
||||
( in );
|
||||
}
|
||||
catch( tao::pegtl::parse_error& e )
|
||||
{
|
||||
THROW_IO_ERROR( e.what() );
|
||||
}
|
||||
|
||||
for( const auto& node : root->children )
|
||||
{
|
||||
if( node->is_type<SIM_MODEL_SPICE_PARSER::dotModel>() )
|
||||
{
|
||||
wxString paramName = "";
|
||||
|
||||
for( const auto& subnode : node->children )
|
||||
{
|
||||
if( subnode->is_type<SIM_MODEL_SPICE_PARSER::modelName>() )
|
||||
{
|
||||
// Do nothing.
|
||||
}
|
||||
else if( subnode->is_type<SIM_MODEL_SPICE_PARSER::dotModelType>() )
|
||||
{
|
||||
// Do nothing.
|
||||
}
|
||||
else if( subnode->is_type<SIM_MODEL_SPICE_PARSER::param>() )
|
||||
{
|
||||
paramName = subnode->string();
|
||||
}
|
||||
else if( subnode->is_type<SIM_MODEL_SPICE_PARSER::paramValue>() )
|
||||
{
|
||||
wxASSERT( !paramName.IsEmpty() );
|
||||
|
||||
if( !SetParamFromSpiceCode( paramName, subnode->string() ) )
|
||||
{
|
||||
THROW_IO_ERROR( wxString::Format(
|
||||
_( "Failed to set parameter '%s' to '%s'" ),
|
||||
paramName,
|
||||
subnode->string() ) );
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
wxFAIL_MSG( "Unhandled parse tree subnode" );
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
wxFAIL_MSG( "Unhandled parse tree node" );
|
||||
}
|
||||
}
|
||||
|
||||
m_spiceCode = aSpiceCode;
|
||||
SIM_MODEL_SPICE::SIM_MODEL_SPICE( TYPE aType,
|
||||
std::unique_ptr<SPICE_GENERATOR> aSpiceGenerator,
|
||||
std::unique_ptr<SPICE_MODEL_PARSER> aSpiceModelParser ) :
|
||||
SIM_MODEL( aType, std::move( aSpiceGenerator ) ),
|
||||
m_spiceModelParser( std::move( aSpiceModelParser ) )
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
|
@ -252,37 +100,3 @@ bool SIM_MODEL_SPICE::SetParamFromSpiceCode( const wxString& aParamName,
|
|||
{
|
||||
return SIM_MODEL::SetParamValue( aParamName, aParamValue, aNotation );
|
||||
}
|
||||
|
||||
|
||||
SIM_MODEL::TYPE SIM_MODEL_SPICE::readTypeFromSpiceStrings( const wxString& aTypeString,
|
||||
const wxString& aLevel,
|
||||
const wxString& aVersion,
|
||||
bool aSkipDefaultLevel )
|
||||
{
|
||||
std::unique_ptr<SIM_VALUE> readLevel = SIM_VALUE::Create( SIM_VALUE::TYPE_INT,
|
||||
aLevel.ToStdString() );
|
||||
|
||||
for( TYPE type : TYPE_ITERATOR() )
|
||||
{
|
||||
wxString typePrefix = SpiceInfo( type ).modelType;
|
||||
wxString level = SpiceInfo( type ).level;
|
||||
wxString version = SpiceInfo( type ).version;
|
||||
bool isDefaultLevel = SpiceInfo( type ).isDefaultLevel;
|
||||
|
||||
if( typePrefix == "" )
|
||||
continue;
|
||||
|
||||
// Check if `aTypeString` starts with `typePrefix`.
|
||||
if( aTypeString.Upper().StartsWith( typePrefix )
|
||||
&& ( level == readLevel->ToString()
|
||||
|| ( !aSkipDefaultLevel && isDefaultLevel && aLevel == "" ) )
|
||||
&& version == aVersion )
|
||||
{
|
||||
return type;
|
||||
}
|
||||
}
|
||||
|
||||
// If the type string is not recognized, demote to a raw Spice element. This way the user won't
|
||||
// have an error if there is a type KiCad does not recognize.
|
||||
return TYPE::RAWSPICE;
|
||||
}
|
||||
|
|
|
@ -27,10 +27,12 @@
|
|||
|
||||
#include <sim/sim_model.h>
|
||||
#include <sim/spice_generator.h>
|
||||
#include <sim/spice_model_parser.h>
|
||||
|
||||
|
||||
class SPICE_GENERATOR_SPICE : public SPICE_GENERATOR
|
||||
{
|
||||
public:
|
||||
using SPICE_GENERATOR::SPICE_GENERATOR;
|
||||
|
||||
wxString Preview( const wxString& aModelName ) const override;
|
||||
|
@ -41,15 +43,18 @@ class SIM_MODEL_SPICE : public SIM_MODEL
|
|||
{
|
||||
public:
|
||||
friend class SPICE_GENERATOR_SPICE;
|
||||
friend class SPICE_MODEL_PARSER;
|
||||
|
||||
static TYPE ReadTypeFromSpiceCode( const wxString& aSpiceCode );
|
||||
static std::unique_ptr<SIM_MODEL_SPICE> Create( const wxString& aSpiceCode );
|
||||
|
||||
virtual void ReadSpiceCode( const wxString& aSpiceCode );
|
||||
|
||||
SIM_MODEL_SPICE( TYPE aType,
|
||||
std::unique_ptr<SPICE_GENERATOR> aSpiceGenerator );
|
||||
|
||||
SIM_MODEL_SPICE( TYPE aType,
|
||||
std::unique_ptr<SPICE_GENERATOR> aSpiceGenerator,
|
||||
std::unique_ptr<SPICE_MODEL_PARSER> aSpiceModelParser );
|
||||
|
||||
protected:
|
||||
using SIM_MODEL::SIM_MODEL;
|
||||
|
||||
bool SetParamValue( unsigned aParamIndex, const wxString& aParamValue,
|
||||
SIM_VALUE_GRAMMAR::NOTATION aNotation
|
||||
= SIM_VALUE_GRAMMAR::NOTATION::SI ) override;
|
||||
|
@ -61,10 +66,7 @@ protected:
|
|||
wxString m_spiceCode;
|
||||
|
||||
private:
|
||||
static TYPE readTypeFromSpiceStrings( const wxString& aTypeString,
|
||||
const wxString& aLevel = "",
|
||||
const wxString& aVersion = "",
|
||||
bool aSkipDefaultLevel = true );
|
||||
std::unique_ptr<SPICE_MODEL_PARSER> m_spiceModelParser;
|
||||
};
|
||||
|
||||
#endif // SIM_MODEL_SPICE_H
|
||||
|
|
|
@ -67,13 +67,7 @@ std::vector<wxString> SPICE_GENERATOR_SUBCKT::CurrentNames( const wxString& aRef
|
|||
}
|
||||
|
||||
|
||||
SIM_MODEL_SUBCKT::SIM_MODEL_SUBCKT( TYPE aType ) :
|
||||
SIM_MODEL_SPICE( aType, std::make_unique<SPICE_GENERATOR_SUBCKT>( *this ) )
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
void SIM_MODEL_SUBCKT::ReadSpiceCode( const wxString& aSpiceCode )
|
||||
void SPICE_MODEL_PARSER_SUBCKT::ReadModel( const wxString& aSpiceCode )
|
||||
{
|
||||
tao::pegtl::string_input<> in( aSpiceCode.ToUTF8(), "from_content" );
|
||||
std::unique_ptr<tao::pegtl::parse_tree::node> root;
|
||||
|
@ -91,6 +85,8 @@ void SIM_MODEL_SUBCKT::ReadSpiceCode( const wxString& aSpiceCode )
|
|||
THROW_IO_ERROR( e.what() );
|
||||
}
|
||||
|
||||
SIM_MODEL_SUBCKT& model = static_cast<SIM_MODEL_SUBCKT&>( m_model );
|
||||
|
||||
for( const auto& node : root->children )
|
||||
{
|
||||
if( node->is_type<SIM_MODEL_SUBCKT_SPICE_PARSER::dotSubckt>() )
|
||||
|
@ -102,7 +98,7 @@ void SIM_MODEL_SUBCKT::ReadSpiceCode( const wxString& aSpiceCode )
|
|||
}
|
||||
else if( subnode->is_type<SIM_MODEL_SUBCKT_SPICE_PARSER::dotSubcktPinName>() )
|
||||
{
|
||||
AddPin( { subnode->string(), wxString::FromCDouble( GetPinCount() + 1 ) } );
|
||||
model.AddPin( { subnode->string(), wxString::FromCDouble( model.GetPinCount() + 1 ) } );
|
||||
}
|
||||
else if( subnode->is_type<SIM_MODEL_SUBCKT_SPICE_PARSER::dotSubcktParams>() )
|
||||
{
|
||||
|
@ -110,12 +106,12 @@ void SIM_MODEL_SUBCKT::ReadSpiceCode( const wxString& aSpiceCode )
|
|||
{
|
||||
if( subsubnode->is_type<SIM_MODEL_SUBCKT_SPICE_PARSER::param>() )
|
||||
{
|
||||
m_paramInfos.push_back( std::make_unique<PARAM::INFO>() );
|
||||
m_paramInfos.back()->name = subsubnode->string();
|
||||
m_paramInfos.back()->isInstanceParam = true;
|
||||
m_paramInfos.back()->isSpiceInstanceParam = true;
|
||||
model.m_paramInfos.push_back( std::make_unique<SIM_MODEL::PARAM::INFO>() );
|
||||
model.m_paramInfos.back()->name = subsubnode->string();
|
||||
model.m_paramInfos.back()->isInstanceParam = true;
|
||||
model.m_paramInfos.back()->isSpiceInstanceParam = true;
|
||||
|
||||
AddParam( *m_paramInfos.back() );
|
||||
model.AddParam( *model.m_paramInfos.back() );
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -130,8 +126,8 @@ void SIM_MODEL_SUBCKT::ReadSpiceCode( const wxString& aSpiceCode )
|
|||
SIM_MODEL_SUBCKT_SPICE_PARSER::number<SIM_VALUE::TYPE_FLOAT,
|
||||
SIM_MODEL_SUBCKT_SPICE_PARSER::NOTATION::SPICE>>() )
|
||||
{
|
||||
wxASSERT( m_paramInfos.size() > 0 );
|
||||
m_paramInfos.back()->defaultValue = subnode->string();
|
||||
wxASSERT( model.m_paramInfos.size() > 0 );
|
||||
model.m_paramInfos.back()->defaultValue = subnode->string();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -141,7 +137,15 @@ void SIM_MODEL_SUBCKT::ReadSpiceCode( const wxString& aSpiceCode )
|
|||
}
|
||||
}
|
||||
|
||||
m_spiceCode = aSpiceCode;
|
||||
model.m_spiceCode = aSpiceCode;
|
||||
}
|
||||
|
||||
|
||||
SIM_MODEL_SUBCKT::SIM_MODEL_SUBCKT() :
|
||||
SIM_MODEL_SPICE( TYPE::SUBCKT,
|
||||
std::make_unique<SPICE_GENERATOR_SUBCKT>( *this ),
|
||||
std::make_unique<SPICE_MODEL_PARSER_SUBCKT>( *this ) )
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -39,12 +39,22 @@ public:
|
|||
};
|
||||
|
||||
|
||||
class SPICE_MODEL_PARSER_SUBCKT : public SPICE_MODEL_PARSER
|
||||
{
|
||||
public:
|
||||
using SPICE_MODEL_PARSER::SPICE_MODEL_PARSER;
|
||||
|
||||
void ReadModel( const wxString& aSpiceCode ) override;
|
||||
};
|
||||
|
||||
|
||||
class SIM_MODEL_SUBCKT : public SIM_MODEL_SPICE
|
||||
{
|
||||
public:
|
||||
SIM_MODEL_SUBCKT( SIM_MODEL::TYPE aType );
|
||||
friend class SPICE_MODEL_PARSER_SUBCKT;
|
||||
|
||||
SIM_MODEL_SUBCKT();
|
||||
|
||||
void ReadSpiceCode( const wxString& aSpiceCode ) override;
|
||||
void SetBaseModel( const SIM_MODEL& aBaseModel ) override;
|
||||
|
||||
protected:
|
||||
|
@ -53,7 +63,6 @@ protected:
|
|||
private:
|
||||
bool requiresSpiceModelLine() const override { return true; }
|
||||
|
||||
|
||||
std::vector<std::unique_ptr<PARAM::INFO>> m_paramInfos;
|
||||
};
|
||||
|
||||
|
|
|
@ -0,0 +1,231 @@
|
|||
/*
|
||||
* 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/spice_model_parser.h>
|
||||
#include <sim/spice_grammar.h>
|
||||
#include <sim/sim_model_spice.h>
|
||||
|
||||
#include <pegtl.hpp>
|
||||
#include <pegtl/contrib/parse_tree.hpp>
|
||||
|
||||
|
||||
namespace SIM_MODEL_SPICE_PARSER
|
||||
{
|
||||
using namespace SPICE_GRAMMAR;
|
||||
|
||||
template <typename Rule> struct spiceUnitSelector : std::false_type {};
|
||||
|
||||
template <> struct spiceUnitSelector<dotModel> : std::true_type {};
|
||||
template <> struct spiceUnitSelector<modelName> : std::true_type {};
|
||||
template <> struct spiceUnitSelector<dotModelType> : std::true_type {};
|
||||
template <> struct spiceUnitSelector<param> : std::true_type {};
|
||||
template <> struct spiceUnitSelector<paramValue> : std::true_type {};
|
||||
|
||||
template <> struct spiceUnitSelector<dotSubckt> : std::true_type {};
|
||||
}
|
||||
|
||||
|
||||
SIM_MODEL::TYPE SPICE_MODEL_PARSER::ReadType( const wxString& aSpiceCode )
|
||||
{
|
||||
tao::pegtl::string_input<> in( aSpiceCode.ToUTF8(), "Spice_Code" );
|
||||
std::unique_ptr<tao::pegtl::parse_tree::node> root;
|
||||
|
||||
try
|
||||
{
|
||||
root = tao::pegtl::parse_tree::parse<SIM_MODEL_SPICE_PARSER::spiceUnitGrammar,
|
||||
SIM_MODEL_SPICE_PARSER::spiceUnitSelector,
|
||||
tao::pegtl::nothing,
|
||||
SIM_MODEL_SPICE_PARSER::control>
|
||||
( in );
|
||||
}
|
||||
catch( const tao::pegtl::parse_error& e )
|
||||
{
|
||||
wxLogDebug( "%s", e.what() );
|
||||
return SIM_MODEL::TYPE::NONE;
|
||||
}
|
||||
|
||||
for( const auto& node : root->children )
|
||||
{
|
||||
if( node->is_type<SIM_MODEL_SPICE_PARSER::dotModel>() )
|
||||
{
|
||||
wxString paramName;
|
||||
wxString typeString;
|
||||
wxString level;
|
||||
wxString version;
|
||||
|
||||
for( const auto& subnode : node->children )
|
||||
{
|
||||
if( subnode->is_type<SIM_MODEL_SPICE_PARSER::modelName>() )
|
||||
{
|
||||
// Do nothing.
|
||||
}
|
||||
else if( subnode->is_type<SIM_MODEL_SPICE_PARSER::dotModelType>() )
|
||||
{
|
||||
typeString = subnode->string();
|
||||
SIM_MODEL::TYPE type = ReadTypeFromSpiceStrings( typeString );
|
||||
|
||||
if( type != SIM_MODEL::TYPE::RAWSPICE )
|
||||
return type;
|
||||
}
|
||||
else if( subnode->is_type<SIM_MODEL_SPICE_PARSER::param>() )
|
||||
{
|
||||
paramName = subnode->string();
|
||||
}
|
||||
else if( subnode->is_type<SIM_MODEL_SPICE_PARSER::paramValue>() )
|
||||
{
|
||||
wxASSERT( paramName != "" );
|
||||
|
||||
if( paramName == "level" )
|
||||
level = subnode->string();
|
||||
else if( paramName == "version" )
|
||||
version = subnode->string();
|
||||
}
|
||||
else
|
||||
{
|
||||
wxFAIL_MSG( "Unhandled parse tree subnode" );
|
||||
return SIM_MODEL::TYPE::NONE;
|
||||
}
|
||||
}
|
||||
|
||||
// Type was not determined from Spice type string alone, so now we take `level` and
|
||||
// `version` variables into account too. This is suboptimal since we read the model
|
||||
// twice this way, and moreover the code is now somewhat duplicated.
|
||||
|
||||
return ReadTypeFromSpiceStrings( typeString, level, version, false );
|
||||
}
|
||||
else if( node->is_type<SIM_MODEL_SPICE_PARSER::dotSubckt>() )
|
||||
return SIM_MODEL::TYPE::SUBCKT;
|
||||
else
|
||||
{
|
||||
wxFAIL_MSG( "Unhandled parse tree node" );
|
||||
return SIM_MODEL::TYPE::NONE;
|
||||
}
|
||||
}
|
||||
|
||||
wxFAIL_MSG( "Could not derive type from Spice code" );
|
||||
return SIM_MODEL::TYPE::NONE;
|
||||
}
|
||||
|
||||
|
||||
void SPICE_MODEL_PARSER::ReadModel( const wxString& aSpiceCode )
|
||||
{
|
||||
// The default behavior is to treat the Spice param=value pairs as the model parameters and
|
||||
// values (for many models the correspondence is not exact, so this function is overridden).
|
||||
|
||||
tao::pegtl::string_input<> in( aSpiceCode.ToUTF8(), "Spice_Code" );
|
||||
std::unique_ptr<tao::pegtl::parse_tree::node> root;
|
||||
|
||||
try
|
||||
{
|
||||
root = tao::pegtl::parse_tree::parse<SIM_MODEL_SPICE_PARSER::spiceUnitGrammar,
|
||||
SIM_MODEL_SPICE_PARSER::spiceUnitSelector,
|
||||
tao::pegtl::nothing,
|
||||
SIM_MODEL_SPICE_PARSER::control>
|
||||
( in );
|
||||
}
|
||||
catch( tao::pegtl::parse_error& e )
|
||||
{
|
||||
THROW_IO_ERROR( e.what() );
|
||||
}
|
||||
|
||||
for( const auto& node : root->children )
|
||||
{
|
||||
if( node->is_type<SIM_MODEL_SPICE_PARSER::dotModel>() )
|
||||
{
|
||||
wxString paramName = "";
|
||||
|
||||
for( const auto& subnode : node->children )
|
||||
{
|
||||
if( subnode->is_type<SIM_MODEL_SPICE_PARSER::modelName>() )
|
||||
{
|
||||
// Do nothing.
|
||||
}
|
||||
else if( subnode->is_type<SIM_MODEL_SPICE_PARSER::dotModelType>() )
|
||||
{
|
||||
// Do nothing.
|
||||
}
|
||||
else if( subnode->is_type<SIM_MODEL_SPICE_PARSER::param>() )
|
||||
{
|
||||
paramName = subnode->string();
|
||||
}
|
||||
else if( subnode->is_type<SIM_MODEL_SPICE_PARSER::paramValue>() )
|
||||
{
|
||||
wxASSERT( !paramName.IsEmpty() );
|
||||
|
||||
if( !m_model.SetParamFromSpiceCode( paramName, subnode->string() ) )
|
||||
{
|
||||
THROW_IO_ERROR( wxString::Format(
|
||||
_( "Failed to set parameter '%s' to '%s'" ),
|
||||
paramName,
|
||||
subnode->string() ) );
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
wxFAIL_MSG( "Unhandled parse tree subnode" );
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
wxFAIL_MSG( "Unhandled parse tree node" );
|
||||
}
|
||||
}
|
||||
|
||||
m_model.m_spiceCode = aSpiceCode;
|
||||
}
|
||||
|
||||
|
||||
SIM_MODEL::TYPE SPICE_MODEL_PARSER::ReadTypeFromSpiceStrings( const wxString& aTypeString,
|
||||
const wxString& aLevel,
|
||||
const wxString& aVersion,
|
||||
bool aSkipDefaultLevel )
|
||||
{
|
||||
std::unique_ptr<SIM_VALUE> readLevel = SIM_VALUE::Create( SIM_VALUE::TYPE_INT,
|
||||
aLevel.ToStdString() );
|
||||
|
||||
for( SIM_MODEL::TYPE type : SIM_MODEL::TYPE_ITERATOR() )
|
||||
{
|
||||
wxString typePrefix = SIM_MODEL::SpiceInfo( type ).modelType;
|
||||
wxString level = SIM_MODEL::SpiceInfo( type ).level;
|
||||
wxString version = SIM_MODEL::SpiceInfo( type ).version;
|
||||
bool isDefaultLevel = SIM_MODEL::SpiceInfo( type ).isDefaultLevel;
|
||||
|
||||
if( typePrefix == "" )
|
||||
continue;
|
||||
|
||||
// Check if `aTypeString` starts with `typePrefix`.
|
||||
if( aTypeString.Upper().StartsWith( typePrefix )
|
||||
&& ( level == readLevel->ToString()
|
||||
|| ( !aSkipDefaultLevel && isDefaultLevel && aLevel == "" ) )
|
||||
&& version == aVersion )
|
||||
{
|
||||
return type;
|
||||
}
|
||||
}
|
||||
|
||||
// If the type string is not recognized, demote to a raw Spice element. This way the user won't
|
||||
// have an error if there is a type KiCad does not recognize.
|
||||
return SIM_MODEL::TYPE::RAWSPICE;
|
||||
}
|
|
@ -0,0 +1,51 @@
|
|||
/*
|
||||
* 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 SPICE_MODEL_PARSER_H
|
||||
#define SPICE_MODEL_PARSER_H
|
||||
|
||||
#include <sim/sim_model.h>
|
||||
|
||||
class SIM_MODEL_SPICE;
|
||||
|
||||
|
||||
class SPICE_MODEL_PARSER
|
||||
{
|
||||
public:
|
||||
static SIM_MODEL::TYPE ReadType( const wxString& aSpiceCode );
|
||||
|
||||
SPICE_MODEL_PARSER( SIM_MODEL_SPICE& aModel ) : m_model( aModel ) {}
|
||||
|
||||
virtual void ReadModel( const wxString& aSpiceCode );
|
||||
|
||||
protected:
|
||||
static SIM_MODEL::TYPE ReadTypeFromSpiceStrings( const wxString& aTypeString,
|
||||
const wxString& aLevel = "",
|
||||
const wxString& aVersion = "",
|
||||
bool aSkipDefaultLevel = true );
|
||||
|
||||
SIM_MODEL_SPICE& m_model;
|
||||
};
|
||||
|
||||
#endif // SPICE_MODEL_PARSER
|
Loading…
Reference in New Issue