KiCad PCB EDA Suite
Loading...
Searching...
No Matches
spice_library_parser.cpp
Go to the documentation of this file.
1/*
2 * This program source code file is part of KiCad, a free EDA CAD application.
3 *
4 * Copyright (C) 2022 Mikolaj Wielgus
5 * Copyright (C) 2022-2023 KiCad Developers, see AUTHORS.txt for contributors.
6 *
7 * This program is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU General Public License
9 * as published by the Free Software Foundation; either version 3
10 * of the License, or (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, you may find one here:
19 * https://www.gnu.org/licenses/gpl-3.0.html
20 * or you may search the http://www.gnu.org website for the version 3 license,
21 * or you may write to the Free Software Foundation, Inc.,
22 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
23 */
24
25#include <confirm.h>
26
29#include <sim/spice_grammar.h>
30#include <sim/sim_model_spice.h>
32#include <ki_exception.h>
33
34#include <pegtl.hpp>
35#include <pegtl/contrib/parse_tree.hpp>
36
37
39{
40 using namespace SPICE_GRAMMAR;
41
42 // TODO: unknownLine is already handled in spiceUnit.
44
45
46 template <typename Rule> struct librarySelector : std::false_type {};
47
48 template <> struct librarySelector<modelUnit> : std::true_type {};
49 template <> struct librarySelector<modelName> : std::true_type {};
50
51 template <> struct librarySelector<dotInclude> : std::true_type {};
52 template <> struct librarySelector<dotIncludePathWithoutQuotes> : std::true_type {};
53 template <> struct librarySelector<dotIncludePathWithoutApostrophes> : std::true_type {};
54 template <> struct librarySelector<dotIncludePath> : std::true_type {};
55
56 // For debugging.
57 template <> struct librarySelector<unknownLine> : std::true_type {};
58};
59
60
61static SIM_MODEL::TYPE getFallbackType( const wxString& aToken, const wxString& aLine )
62{
63 for( SIM_MODEL::TYPE candidate : SIM_MODEL::TYPE_ITERATOR() )
64 {
65 wxString candidate_type = SIM_MODEL::SpiceInfo( candidate ).modelType;
66
67 if( candidate_type.IsEmpty() )
68 continue;
69
70 if( SIM_MODEL::SpiceInfo( candidate ).level != ""
71 && !SIM_MODEL::SpiceInfo( candidate ).isDefaultLevel )
72 {
73 continue;
74 }
75
76 if( candidate_type.StartsWith( wxS( "VDMOS" ) ) && aToken == wxS( "VDMOS" ) )
77 {
78 if( candidate_type.EndsWith( wxS( "PCHAN" ) ) )
79 {
80 if( aLine.Upper().Contains( wxS( "PCHAN" ) ) )
81 return candidate;
82 }
83 else
84 {
85 if( !aLine.Upper().Contains( wxS( "PCHAN" ) ) )
86 return candidate;
87 }
88 }
89 else if( aToken.StartsWith( candidate_type ) )
90 {
91 return candidate;
92 }
93 }
94
95 return SIM_MODEL::TYPE::NONE;
96}
97
98
99void SPICE_LIBRARY_PARSER::readFallbacks( const wxString& aFilePath, REPORTER& aReporter )
100{
101 try
102 {
103 wxArrayString lines = wxSplit( SafeReadFile( aFilePath, wxS( "r" ) ), '\n' );
104
105 for( const wxString& line : lines )
106 {
107 wxStringTokenizer tokenizer( line, wxS( " ()\t\r\n" ), wxTOKEN_STRTOK );
108 wxString token = tokenizer.GetNextToken().Lower();
109
110 if( token == wxS( ".model" ) )
111 {
112 wxString name = tokenizer.GetNextToken();
113 wxString typeToken = tokenizer.GetNextToken().Upper();
114 SIM_MODEL::TYPE type = getFallbackType( typeToken, line );
115
116 m_library.m_models.push_back( std::make_unique<SIM_MODEL_SPICE_FALLBACK>( type ) );
117 m_library.m_modelNames.emplace_back( name );
118 }
119 else if( token == wxS( ".inc" ) )
120 {
121 wxString lib = tokenizer.GetNextToken();
122
124 lib = ( *m_library.m_pathResolver )( lib, aFilePath );
125
126 parseFile( lib, aReporter );
127 }
128 }
129 }
130 catch( IO_ERROR& e )
131 {
132 aReporter.Report( e.What(), RPT_SEVERITY_ERROR );
133 }
134}
135
136
137void SPICE_LIBRARY_PARSER::parseFile( const wxString &aFilePath, REPORTER& aReporter )
138{
139 try
140 {
141 tao::pegtl::string_input<> in( SafeReadFile( aFilePath, wxS( "r" ) ).ToStdString(),
142 aFilePath.ToStdString() );
143 auto root = tao::pegtl::parse_tree::parse<SIM_LIBRARY_SPICE_PARSER::libraryGrammar,
145 tao::pegtl::nothing,
147
148 for( const auto& node : root->children )
149 {
150 if( node->is_type<SIM_LIBRARY_SPICE_PARSER::modelUnit>() )
151 {
152 std::string model = node->string();
153 std::string modelName = node->children.at( 0 )->string();
154
155 try
156 {
158 m_library.m_modelNames.emplace_back( modelName );
159 }
160 catch( const IO_ERROR& e )
161 {
162 aReporter.Report( e.What(), RPT_SEVERITY_ERROR );
163 }
164 catch( ... )
165 {
166 aReporter.Report( wxString::Format( _( "Cannot create sim model from %s" ),
167 model ),
169 }
170 }
171 else if( node->is_type<SIM_LIBRARY_SPICE_PARSER::dotInclude>() )
172 {
173 wxString lib = node->children.at( 0 )->string();
174
175 try
176 {
178 lib = ( *m_library.m_pathResolver )( lib, aFilePath );
179
180 parseFile( lib, aReporter );
181 }
182 catch( const IO_ERROR& e )
183 {
184 aReporter.Report( e.What(), RPT_SEVERITY_ERROR );
185 }
186 }
187 else if( node->is_type<SIM_LIBRARY_SPICE_PARSER::unknownLine>() )
188 {
189 // Do nothing.
190 }
191 else
192 {
193 wxFAIL_MSG( "Unhandled parse tree node" );
194 }
195 }
196 }
197 catch( const IO_ERROR& e )
198 {
199 aReporter.Report( e.What(), RPT_SEVERITY_ERROR );
200 }
201 catch( const tao::pegtl::parse_error& e )
202 {
203 aReporter.Report( e.what(), RPT_SEVERITY_ERROR );
204 }
205}
206
207
208void SPICE_LIBRARY_PARSER::ReadFile( const wxString& aFilePath, REPORTER& aReporter )
209{
210 m_library.m_models.clear();
211 m_library.m_modelNames.clear();
212
213 // Aside from the simulation model editor dialog, about the only data we use from the
214 // complete models are the pin definitions for SUBCKTs. The standard LTSpice "cmp" libraries
215 // (cmp/standard.bjt, cmp/standard.mos, etc.) have copious error which trip up our parser,
216 // and our parser is *really* slow on such large files (nearly 5 seconds on my dev machine).
217 if( !m_forceFullParse && aFilePath.Contains( wxS( "/LTspiceXVII/lib/cmp/standard" ) ) )
218 readFallbacks( aFilePath, aReporter );
219 else
220 parseFile( aFilePath, aReporter );
221}
const char * name
Definition: DXF_plotter.cpp:57
Hold an error message and may be used when throwing exceptions containing meaningful error messages.
Definition: ki_exception.h:77
virtual const wxString What() const
A composite of Problem() and Where()
Definition: exceptions.cpp:30
A pure virtual class used to derive REPORTER objects from.
Definition: reporter.h:71
virtual REPORTER & Report(const wxString &aText, SEVERITY aSeverity=RPT_SEVERITY_UNDEFINED)=0
Report a string with a given severity.
std::function< wxString(const wxString &, const wxString &)> * m_pathResolver
Definition: sim_library.h:80
std::vector< std::string > m_modelNames
Definition: sim_library.h:77
std::vector< std::unique_ptr< SIM_MODEL > > m_models
Definition: sim_library.h:78
static std::unique_ptr< SIM_MODEL_SPICE > Create(const SIM_LIBRARY_SPICE &aLibrary, const std::string &aSpiceCode)
static SPICE_INFO SpiceInfo(TYPE aType)
Definition: sim_model.cpp:245
void parseFile(const wxString &aFilePath, REPORTER &aReporter)
SIM_LIBRARY_SPICE & m_library
void readFallbacks(const wxString &aFilePath, REPORTER &aReporter)
virtual void ReadFile(const wxString &aFilePath, REPORTER &aReporter)
This file is part of the common library.
#define _(s)
must_if< error >::control< Rule > control
@ RPT_SEVERITY_ERROR
wxString SafeReadFile(const wxString &aFilePath, const wxString &aReadType)
Nominally opens a file and reads it into a string.
Definition: richio.cpp:93
static SIM_MODEL::TYPE getFallbackType(const wxString &aToken, const wxString &aLine)
std::string modelType
Definition: sim_model.h:297