Sim: Move Spice model parsing to a separate class hierarchy

Roughly analogous to the SPICE_GENERATOR hierarchy.
This commit is contained in:
Mikolaj Wielgus 2022-09-19 07:18:06 +02:00
parent 42005d9678
commit 8599323dce
8 changed files with 341 additions and 229 deletions

View File

@ -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

View File

@ -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 );

View File

@ -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;
}

View File

@ -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

View File

@ -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 ) )
{
}

View File

@ -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;
};

View File

@ -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;
}

View File

@ -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