KiCad PCB EDA Suite
Loading...
Searching...
No Matches
sim_model_serializer.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 The 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, see <https://www.gnu.org/licenses/>.
19 */
20
22
23#include <ki_exception.h>
24#include <fmt/format.h>
25#include <pegtl.hpp>
26#include <pegtl/contrib/parse_tree.hpp>
27#include <boost/algorithm/string/case_conv.hpp>
28#include <boost/algorithm/string/predicate.hpp>
29#include <string_utils.h>
30
31
33{
34 using namespace SIM_MODEL_SERIALIZER_GRAMMAR;
35
36 template <typename Rule> struct fieldParamValuePairsSelector : std::false_type {};
37 template <> struct fieldParamValuePairsSelector<param> : std::true_type {};
38 template <> struct fieldParamValuePairsSelector<flagParam> : std::true_type {};
39 template <> struct fieldParamValuePairsSelector<quotedStringContent> : std::true_type {};
40 template <> struct fieldParamValuePairsSelector<unquotedString> : std::true_type {};
41
42
43 template <typename Rule> struct pinSequenceSelector : std::false_type {};
44 template <> struct pinSequenceSelector<pinAssignment> : std::true_type {};
45 template <> struct pinSequenceSelector<pinSymbolPinNumber> : std::true_type {};
46 template <> struct pinSequenceSelector<pinName> : std::true_type {};
47
48 template <typename Rule> struct fieldInferValueSelector : std::false_type {};
49 template <> struct fieldInferValueSelector<number<SIM_VALUE::TYPE_FLOAT, NOTATION::SI>> : std::true_type {};
50}
51
52
54{
55 return m_model.GetDeviceInfo().fieldValue;
56}
57
58
60{
61 return m_model.GetTypeInfo().fieldValue;
62}
63
64
66{
67 const SIM_MODEL::PARAM& param = m_model.GetParamOverride( 0 );
68 std::string result = param.value;
69
70 if( result == "" )
71 result = m_model.GetDeviceInfo().fieldValue;
72
73 return result;
74}
75
76
78{
79 std::string result;
80 bool isFirst = true;
81
82 for( int i = 0; i < m_model.GetParamCount(); ++i )
83 {
84 if( i == 0 && m_model.IsStoredInValue() )
85 continue;
86
87 const SIM_MODEL::PARAM& param = m_model.GetParamOverride( i );
88
89 if( param.value == "" && !( i == 0 && m_model.HasPrimaryValue() && !m_model.IsStoredInValue() ) )
90 continue;
91
92 if( m_model.GetBaseModel() && m_model.GetBaseModel()->GetParam( i ).value == param.value )
93 continue;
94
95 // If the parameter is an enum and the value is default, don't write anything.
96 if( param.info.enumValues.size() >= 1 && param.value == param.info.defaultValue )
97 continue;
98
99 std::string paramValuePair = generateParamValuePair( param );
100
101 if( paramValuePair == "" )
102 continue; // Prevent adding empty spaces.
103
104 if( isFirst ) // Don't add a space at the beginning.
105 isFirst = false;
106 else
107 result.append( " " );
108
109 result.append( paramValuePair );
110 }
111
112 return result;
113}
114
115
117{
118 std::string result;
119
120 std::vector<std::reference_wrapper<const SIM_MODEL_PIN>> pins = m_model.GetPins();
121
122 // m_model.GetPins() returns pins in the order they appear in the model, but the keys in the
123 // key=value pairs we create here are symbol pin numbers, so we sort the pins so that they are
124 // ordered by the latter instead.
125 std::sort( pins.begin(), pins.end(),
126 []( const SIM_MODEL_PIN& lhs, const SIM_MODEL_PIN& rhs )
127 {
128 return StrNumCmp( lhs.symbolPinNumber, rhs.symbolPinNumber, true ) < 0;
129 } );
130
131 for( const std::reference_wrapper<const SIM_MODEL_PIN>& pin : pins )
132 {
133 std::string symbolPinNumber( pin.get().symbolPinNumber.ToUTF8() );
134
135 if( symbolPinNumber != "" )
136 {
137 if( !result.empty() )
138 result.append( " " );
139
140 result.append( fmt::format( "{}={}", symbolPinNumber, pin.get().modelPinName ) );
141 }
142 }
143
144 return result;
145}
146
147
149{
150 return m_model.IsEnabled() ? "" : "0";
151}
152
153
154void SIM_MODEL_SERIALIZER::ParseValue( const std::string& aValue )
155{
156 try
157 {
158 tao::pegtl::string_input<> in( aValue, "Value field" );
159 auto root = tao::pegtl::parse_tree::parse<SIM_MODEL_SERIALIZER_PARSER::fieldInferValueGrammar,
161 tao::pegtl::nothing,
163 ( in );
164
165 for( const auto& node : root->children )
166 {
168 && node->string() != "" )
169 {
170 m_model.SetParamValue( 0, node->string() );
171 }
172 }
173 }
174 catch( const tao::pegtl::parse_error& e )
175 {
176 THROW_IO_ERROR( e.what() );
177 }
178
179 m_model.SetIsStoredInValue( true );
180}
181
182
183bool SIM_MODEL_SERIALIZER::ParseParams( const std::string& aParams )
184{
185 tao::pegtl::string_input<> in( aParams, "Sim.Params field" );
186 std::unique_ptr<tao::pegtl::parse_tree::node> root;
187
188 try
189 {
190 // Using parse tree instead of actions because we don't care about performance that much,
191 // and having a tree greatly simplifies things.
192 root = tao::pegtl::parse_tree::parse<SIM_MODEL_SERIALIZER_PARSER::fieldParamValuePairsGrammar,
194 tao::pegtl::nothing,
196 ( in );
197 }
198 catch( const tao::pegtl::parse_error& e )
199 {
200 THROW_IO_ERROR( e.what() );
201 }
202
203 std::string paramName;
204 bool isPrimaryValueSet = false;
205
206 auto unescapeQuoted =
207 []( const std::string& aString ) -> std::string
208 {
209 std::string result;
210 result.reserve( aString.size() );
211
212 for( size_t i = 0; i < aString.size(); ++i )
213 {
214 if( aString[i] == '\\' && i + 1 < aString.size()
215 && ( aString[i + 1] == '"' || aString[i + 1] == '\\' ) )
216 {
217 result.push_back( aString[++i] );
218 }
219 else
220 {
221 result.push_back( aString[i] );
222 }
223 }
224
225 return result;
226 };
227
228 for( const auto& node : root->children )
229 {
230 if( node->is_type<SIM_MODEL_SERIALIZER_PARSER::param>() )
231 {
232 paramName = node->string();
233 }
234 else if( node->is_type<SIM_MODEL_SERIALIZER_PARSER::quotedStringContent>() )
235 {
236 wxASSERT( paramName != "" );
237
238 m_model.SetParamValue( paramName, unescapeQuoted( node->string() ), SIM_VALUE_GRAMMAR::NOTATION::SI );
239
240 if( m_model.GetParam( 0 ).Matches( paramName ) )
241 isPrimaryValueSet = true;
242 }
243 else if( node->is_type<SIM_MODEL_SERIALIZER_PARSER::unquotedString>() )
244 {
245 wxASSERT( paramName != "" );
246
247 m_model.SetParamValue( paramName, node->string(), SIM_VALUE_GRAMMAR::NOTATION::SI );
248
249 if( m_model.GetParam( 0 ).Matches( paramName ) )
250 isPrimaryValueSet = true;
251 }
252 else if( node->is_type<SIM_MODEL_SERIALIZER_PARSER::flagParam>() )
253 {
254 std::string token = node->string();
255
256 m_model.SetParamValue( token, "1", SIM_VALUE_GRAMMAR::NOTATION::SI );
257 }
258 else
259 {
260 wxFAIL;
261 }
262 }
263
264 return !m_model.HasPrimaryValue() || m_model.HasAutofill() || isPrimaryValueSet;
265}
266
267
268void SIM_MODEL_SERIALIZER::ParsePins( const std::string& aPins )
269{
270 if( aPins == "" )
271 return;
272
273 tao::pegtl::string_input<> in( aPins, "Sim.Pins field" );
274 std::unique_ptr<tao::pegtl::parse_tree::node> root;
275
276 try
277 {
278 root = tao::pegtl::parse_tree::parse<SIM_MODEL_SERIALIZER_PARSER::pinSequenceGrammar,
280 tao::pegtl::nothing,
282 ( in );
283
284 for( const auto& node : root->children )
285 {
286 std::string symbolPinNumber = node->children.at( 0 )->string();
287 std::string modelPinName = node->children.at( 1 )->string();
288
289 m_model.AssignSymbolPinNumberToModelPin( modelPinName, symbolPinNumber );
290 }
291 }
292 catch( const tao::pegtl::parse_error& e )
293 {
294 THROW_IO_ERROR( e.what() );
295 }
296}
297
298
299void SIM_MODEL_SERIALIZER::ParseEnable( const std::string& aEnable )
300{
301 if( aEnable == "" )
302 return;
303
304 char c = boost::to_lower_copy( aEnable )[0];
305
306 if( c == 'n' || c == 'f' || c == '0' )
307 m_model.SetIsEnabled( false );
308}
309
310
312{
313 std::string name = aParam.info.name;
314
315 // Because of collisions with instance parameters, we append some model parameters with "_".
316 if( boost::ends_with( name, "_" ) )
317 name = name.substr( 0, aParam.info.name.length() - 1 );
318
319 std::string value = aParam.value;
320
321 if( aParam.info.category == SIM_MODEL::PARAM::CATEGORY::FLAGS )
322 return value == "1" ? name : "";
323
324 if( value.empty()
325 || value.find( ' ' ) != std::string::npos
326 || value.find( '"' ) != std::string::npos
327 || value.find( '\\' ) != std::string::npos )
328 {
329 std::string escaped;
330 escaped.reserve( value.size() + 8 );
331
332 for( char c : value )
333 {
334 if( c == '\\' || c == '"' )
335 escaped.push_back( '\\' );
336
337 escaped.push_back( c );
338 }
339
340 value = fmt::format( "\"{}\"", escaped );
341 }
342
343 return fmt::format( "{}={}", name, value );
344}
const char * name
std::string GeneratePins() const
void ParsePins(const std::string &aPins)
std::string generateParamValuePair(const SIM_MODEL::PARAM &aParam) const
std::string GenerateParams() const
std::string GenerateValue() const
void ParseValue(const std::string &aValue)
void ParseEnable(const std::string &aEnable)
std::string GenerateEnable() const
std::string GenerateDevice() const
bool ParseParams(const std::string &aParams)
std::string GenerateDeviceSubtype() const
#define THROW_IO_ERROR(msg)
macro which captures the "call site" values of FILE_, __FUNCTION & LINE
must_if< error >::control< Rule > control
std::string value
Definition sim_model.h:398
const INFO & info
Definition sim_model.h:399
KIBIS_PIN * pin
wxString result
Test unit parsing edge cases and error handling.