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