KiCad PCB EDA Suite
Loading...
Searching...
No Matches
pcbexpr_evaluator.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) 2019-2022 KiCad Developers, see AUTHORS.txt for contributors.
5 *
6 * This program is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU General Public License
8 * as published by the Free Software Foundation; either version 2
9 * of the License, or (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, you may find one here:
18 * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
19 * or you may search the http://www.gnu.org website for the version 2 license,
20 * or you may write to the Free Software Foundation, Inc.,
21 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
22 */
23
24
25#include <cstdio>
26#include <memory>
27#include <mutex>
28#include <board.h>
29#include <lset.h>
31#include <pcbexpr_evaluator.h>
32#include <drc/drc_engine.h>
33
34/* --------------------------------------------------------------------------------------------
35 * Specialized Expression References
36 */
37
39{
40 wxASSERT( dynamic_cast<const PCBEXPR_CONTEXT*>( aCtx ) );
41
42 const PCBEXPR_CONTEXT* ctx = static_cast<const PCBEXPR_CONTEXT*>( aCtx );
43 BOARD_ITEM* item = ctx->GetItem( m_itemIndex );
44 return item;
45}
46
47
49{
50public:
52 LIBEVAL::VALUE( LayerName( aLayer ) ),
53 m_layer( aLayer )
54 {};
55
56 virtual bool EqualTo( LIBEVAL::CONTEXT* aCtx, const VALUE* b ) const override
57 {
58 // For boards with user-defined layer names there will be 2 entries for each layer
59 // in the ENUM_MAP: one for the canonical layer name and one for the user layer name.
60 // We need to check against both.
61
62 wxPGChoices& layerMap = ENUM_MAP<PCB_LAYER_ID>::Instance().Choices();
63 const wxString& layerName = b->AsString();
64 BOARD* board = static_cast<PCBEXPR_CONTEXT*>( aCtx )->GetBoard();
65
66 {
67 std::shared_lock<std::shared_mutex> readLock( board->m_CachesMutex );
68
69 auto i = board->m_LayerExpressionCache.find( layerName );
70
71 if( i != board->m_LayerExpressionCache.end() )
72 return i->second.Contains( m_layer );
73 }
74
75 LSET mask;
76
77 for( unsigned ii = 0; ii < layerMap.GetCount(); ++ii )
78 {
79 wxPGChoiceEntry& entry = layerMap[ii];
80
81 if( entry.GetText().Matches( layerName ) )
82 mask.set( ToLAYER_ID( entry.GetValue() ) );
83 }
84
85 {
86 std::unique_lock<std::shared_mutex> writeLock( board->m_CachesMutex );
87 board->m_LayerExpressionCache[ layerName ] = mask;
88 }
89
90 return mask.Contains( m_layer );
91 }
92
93protected:
95};
96
97
99{
100public:
101 PCBEXPR_PINTYPE_VALUE( const wxString& aPinTypeName ) :
102 LIBEVAL::VALUE( aPinTypeName )
103 {};
104
105 bool EqualTo( LIBEVAL::CONTEXT* aCtx, const VALUE* b ) const override
106 {
107 const wxString& thisStr = AsString();
108 const wxString& otherStr = b->AsString();
109
110 if( thisStr.IsSameAs( otherStr, false ) )
111 return true;
112
113 // Handle cases where the netlist token is different from the EEschema token
114 if( thisStr == wxT( "tri_state" ) )
115 return otherStr.IsSameAs( wxT( "Tri-state" ), false );
116
117 if( thisStr == wxT( "power_in" ) )
118 return otherStr.IsSameAs( wxT( "Power input" ), false );
119
120 if( thisStr == wxT( "power_out" ) )
121 return otherStr.IsSameAs( wxT( "Power output" ), false );
122
123 if( thisStr == wxT( "no_connect" ) )
124 return otherStr.IsSameAs( wxT( "Unconnected" ), false );
125
126 return false;
127 }
128};
129
130
132{
133public:
135 LIBEVAL::VALUE( wxEmptyString ),
136 m_item( aItem )
137 {};
138
139 const wxString& AsString() const override
140 {
141 const_cast<PCBEXPR_NETCLASS_VALUE*>( this )->Set( m_item->GetEffectiveNetClass()->GetName() );
143 }
144
145 bool EqualTo( LIBEVAL::CONTEXT* aCtx, const VALUE* b ) const override
146 {
147 if( const PCBEXPR_NETCLASS_VALUE* bValue = dynamic_cast<const PCBEXPR_NETCLASS_VALUE*>( b ) )
148 return m_item->GetEffectiveNetClass() == bValue->m_item->GetEffectiveNetClass();
149 else
150 return LIBEVAL::VALUE::EqualTo( aCtx, b );
151 }
152
153 bool NotEqualTo( LIBEVAL::CONTEXT* aCtx, const LIBEVAL::VALUE* b ) const override
154 {
155 if( const PCBEXPR_NETCLASS_VALUE* bValue = dynamic_cast<const PCBEXPR_NETCLASS_VALUE*>( b ) )
156 return m_item->GetEffectiveNetClass() != bValue->m_item->GetEffectiveNetClass();
157 else
158 return LIBEVAL::VALUE::NotEqualTo( aCtx, b );
159 }
160
161protected:
163};
164
165
167{
168public:
170 LIBEVAL::VALUE( wxEmptyString ),
171 m_item( aItem )
172 {};
173
174 const wxString& AsString() const override
175 {
176 const_cast<PCBEXPR_NET_VALUE*>( this )->Set( m_item->GetNetname() );
178 }
179
180 bool EqualTo( LIBEVAL::CONTEXT* aCtx, const VALUE* b ) const override
181 {
182 if( const PCBEXPR_NET_VALUE* bValue = dynamic_cast<const PCBEXPR_NET_VALUE*>( b ) )
183 return m_item->GetNetCode() == bValue->m_item->GetNetCode();
184 else
185 return LIBEVAL::VALUE::EqualTo( aCtx, b );
186 }
187
188 bool NotEqualTo( LIBEVAL::CONTEXT* aCtx, const LIBEVAL::VALUE* b ) const override
189 {
190 if( const PCBEXPR_NET_VALUE* bValue = dynamic_cast<const PCBEXPR_NET_VALUE*>( b ) )
191 return m_item->GetNetCode() != bValue->m_item->GetNetCode();
192 else
193 return LIBEVAL::VALUE::NotEqualTo( aCtx, b );
194 }
195
196protected:
198};
199
200
202{
203 PCBEXPR_CONTEXT* context = static_cast<PCBEXPR_CONTEXT*>( aCtx );
204
205 if( m_itemIndex == 2 )
206 return new PCBEXPR_LAYER_VALUE( context->GetLayer() );
207
208 BOARD_ITEM* item = GetObject( aCtx );
209
210 if( !item )
211 return new LIBEVAL::VALUE();
212
213 auto it = m_matchingTypes.find( TYPE_HASH( *item ) );
214
215 if( it == m_matchingTypes.end() )
216 {
217 // Don't force user to type "A.Type == 'via' && A.Via_Type == 'buried'" when the
218 // simpler "A.Via_Type == 'buried'" is perfectly clear. Instead, return an undefined
219 // value when the property doesn't appear on a particular object.
220
221 return new LIBEVAL::VALUE();
222 }
223 else
224 {
226 {
227 return new LIBEVAL::VALUE( (double) item->Get<int>( it->second ) );
228 }
229 else
230 {
231 wxString str;
232
233 if( !m_isEnum )
234 {
235 str = item->Get<wxString>( it->second );
236
237 if( it->second->Name() == wxT( "Pin Type" ) )
238 return new PCBEXPR_PINTYPE_VALUE( str );
239 else
240 return new LIBEVAL::VALUE( str );
241 }
242 else
243 {
244 const wxAny& any = item->Get( it->second );
245 PCB_LAYER_ID layer;
246
247 if( it->second->Name() == wxT( "Layer" )
248 || it->second->Name() == wxT( "Layer Top" )
249 || it->second->Name() == wxT( "Layer Bottom" ) )
250 {
251 if( any.GetAs<PCB_LAYER_ID>( &layer ) )
252 return new PCBEXPR_LAYER_VALUE( layer );
253 else if( any.GetAs<wxString>( &str ) )
254 return new PCBEXPR_LAYER_VALUE( context->GetBoard()->GetLayerID( str ) );
255 }
256 else
257 {
258 if( any.GetAs<wxString>( &str ) )
259 return new LIBEVAL::VALUE( str );
260 }
261 }
262
263 return new LIBEVAL::VALUE();
264 }
265 }
266}
267
268
270{
271 BOARD_CONNECTED_ITEM* item = dynamic_cast<BOARD_CONNECTED_ITEM*>( GetObject( aCtx ) );
272
273 if( !item )
274 return new LIBEVAL::VALUE();
275
276 return new PCBEXPR_NETCLASS_VALUE( item );
277}
278
279
281{
282 BOARD_CONNECTED_ITEM* item = dynamic_cast<BOARD_CONNECTED_ITEM*>( GetObject( aCtx ) );
283
284 if( !item )
285 return new LIBEVAL::VALUE();
286
287 return new PCBEXPR_NET_VALUE( item );
288}
289
290
292{
293 BOARD_ITEM* item = GetObject( aCtx );
294
295 if( !item )
296 return new LIBEVAL::VALUE();
297
298 return new LIBEVAL::VALUE( ENUM_MAP<KICAD_T>::Instance().ToString( item->Type() ) );
299}
300
301
303{
305
306 return registry.Get( aName.Lower() );
307}
308
309
310std::unique_ptr<LIBEVAL::VAR_REF> PCBEXPR_UCODE::CreateVarRef( const wxString& aVar,
311 const wxString& aField )
312{
314 std::unique_ptr<PCBEXPR_VAR_REF> vref;
315
316 // Check for a couple of very common cases and compile them straight to "object code".
317
318 if( aField.CmpNoCase( wxT( "NetClass" ) ) == 0 )
319 {
320 if( aVar == wxT( "A" ) )
321 return std::make_unique<PCBEXPR_NETCLASS_REF>( 0 );
322 else if( aVar == wxT( "B" ) )
323 return std::make_unique<PCBEXPR_NETCLASS_REF>( 1 );
324 else
325 return nullptr;
326 }
327 else if( aField.CmpNoCase( wxT( "NetName" ) ) == 0 )
328 {
329 if( aVar == wxT( "A" ) )
330 return std::make_unique<PCBEXPR_NETNAME_REF>( 0 );
331 else if( aVar == wxT( "B" ) )
332 return std::make_unique<PCBEXPR_NETNAME_REF>( 1 );
333 else
334 return nullptr;
335 }
336 else if( aField.CmpNoCase( wxT( "Type" ) ) == 0 )
337 {
338 if( aVar == wxT( "A" ) )
339 return std::make_unique<PCBEXPR_TYPE_REF>( 0 );
340 else if( aVar == wxT( "B" ) )
341 return std::make_unique<PCBEXPR_TYPE_REF>( 1 );
342 else
343 return nullptr;
344 }
345
346 if( aVar == wxT( "A" ) || aVar == wxT( "AB" ) )
347 vref = std::make_unique<PCBEXPR_VAR_REF>( 0 );
348 else if( aVar == wxT( "B" ) )
349 vref = std::make_unique<PCBEXPR_VAR_REF>( 1 );
350 else if( aVar == wxT( "L" ) )
351 vref = std::make_unique<PCBEXPR_VAR_REF>( 2 );
352 else
353 return nullptr;
354
355 if( aField.length() == 0 ) // return reference to base object
356 {
357 return vref;
358 }
359
360 wxString field( aField );
361 field.Replace( wxT( "_" ), wxT( " " ) );
362
363 for( const PROPERTY_MANAGER::CLASS_INFO& cls : propMgr.GetAllClasses() )
364 {
365 if( propMgr.IsOfType( cls.type, TYPE_HASH( BOARD_ITEM ) ) )
366 {
367 PROPERTY_BASE* prop = propMgr.GetProperty( cls.type, field );
368
369 if( prop )
370 {
371 vref->AddAllowedClass( cls.type, prop );
372
373 if( prop->TypeHash() == TYPE_HASH( int ) )
374 {
375 vref->SetType( LIBEVAL::VT_NUMERIC );
376 }
377 else if( prop->TypeHash() == TYPE_HASH( bool ) )
378 {
379 vref->SetType( LIBEVAL::VT_NUMERIC );
380 }
381 else if( prop->TypeHash() == TYPE_HASH( wxString ) )
382 {
383 vref->SetType( LIBEVAL::VT_STRING );
384 }
385 else if ( prop->HasChoices() )
386 { // it's an enum, we treat it as string
387 vref->SetType( LIBEVAL::VT_STRING );
388 vref->SetIsEnum ( true );
389 }
390 else
391 {
392 wxFAIL_MSG( wxT( "PCBEXPR_UCODE::createVarRef: Unknown property type." ) );
393 }
394 }
395 }
396 }
397
398 if( vref->GetType() == LIBEVAL::VT_UNDEFINED )
399 vref->SetType( LIBEVAL::VT_PARSE_ERROR );
400
401 return vref;
402}
403
404
406{
407 if( m_items[0] )
408 return m_items[0]->GetBoard();
409
410 return nullptr;
411}
412
413
414/* --------------------------------------------------------------------------------------------
415 * Unit Resolvers
416 */
417
418const std::vector<wxString>& PCBEXPR_UNIT_RESOLVER::GetSupportedUnits() const
419{
420 static const std::vector<wxString> pcbUnits = { wxT( "mil" ), wxT( "mm" ), wxT( "in" ) };
421
422 return pcbUnits;
423}
424
425
427{
428 return _( "must be mm, in, or mil" );
429}
430
431
432double PCBEXPR_UNIT_RESOLVER::Convert( const wxString& aString, int unitId ) const
433{
434 double v = wxAtof( aString );
435
436 switch( unitId )
437 {
438 case 0: return EDA_UNIT_UTILS::UI::DoubleValueFromString( pcbIUScale, EDA_UNITS::MILS, aString );
439 case 1: return EDA_UNIT_UTILS::UI::DoubleValueFromString( pcbIUScale, EDA_UNITS::MILLIMETRES, aString );
440 case 2: return EDA_UNIT_UTILS::UI::DoubleValueFromString( pcbIUScale, EDA_UNITS::INCHES, aString );
441 default: return v;
442 }
443};
444
445
446const std::vector<wxString>& PCBEXPR_UNITLESS_RESOLVER::GetSupportedUnits() const
447{
448 static const std::vector<wxString> emptyUnits;
449
450 return emptyUnits;
451}
452
453
454double PCBEXPR_UNITLESS_RESOLVER::Convert( const wxString& aString, int unitId ) const
455{
456 return wxAtof( aString );
457};
458
459
461{
462 m_unitResolver.reset( aUnitResolver );
463}
464
465
466/* --------------------------------------------------------------------------------------------
467 * PCB Expression Evaluator
468 */
469
471 m_result( 0 ),
472 m_compiler( aUnitResolver ),
473 m_ucode(),
474 m_errorStatus()
475{
476}
477
478
480{
481}
482
483
484bool PCBEXPR_EVALUATOR::Evaluate( const wxString& aExpr )
485{
486 PCBEXPR_UCODE ucode;
487 PCBEXPR_CONTEXT preflightContext( NULL_CONSTRAINT, F_Cu );
488
489 if( !m_compiler.Compile( aExpr.ToUTF8().data(), &ucode, &preflightContext ) )
490 return false;
491
492 PCBEXPR_CONTEXT evaluationContext( NULL_CONSTRAINT, F_Cu );
493 LIBEVAL::VALUE* result = ucode.Run( &evaluationContext );
494
495 if( result->GetType() == LIBEVAL::VT_NUMERIC )
496 m_result = KiROUND( result->AsDouble() );
497
498 return true;
499}
500
constexpr EDA_IU_SCALE pcbIUScale
Definition: base_units.h:108
BASE_SET & set(size_t pos=std::numeric_limits< size_t >::max(), bool value=true)
Definition: base_set.h:61
A base class derived from BOARD_ITEM for items that can be connected and have a net,...
virtual NETCLASS * GetEffectiveNetClass() const
Return the NETCLASS for this item.
A base class for any item which can be embedded within the BOARD container class, and therefore insta...
Definition: board_item.h:79
virtual const BOARD * GetBoard() const
Return the BOARD in which this BOARD_ITEM resides, or NULL if none.
Definition: board_item.cpp:47
Information pertinent to a Pcbnew printed circuit board.
Definition: board.h:289
PCB_LAYER_ID GetLayerID(const wxString &aLayerName) const
Return the ID of a layer.
Definition: board.cpp:555
std::unordered_map< wxString, LSET > m_LayerExpressionCache
Definition: board.h:1285
std::shared_mutex m_CachesMutex
Definition: board.h:1279
KICAD_T Type() const
Returns the type of object.
Definition: eda_item.h:101
static ENUM_MAP< T > & Instance()
Definition: property.h:663
wxAny Get(PROPERTY_BASE *aProperty) const
Definition: inspectable.h:99
std::unique_ptr< UNIT_RESOLVER > m_unitResolver
bool Compile(const wxString &aString, UCODE *aCode, CONTEXT *aPreflightContext)
VALUE * Run(CONTEXT *ctx)
void Set(double aValue)
virtual const wxString & AsString() const
virtual bool NotEqualTo(CONTEXT *aCtx, const VALUE *b) const
virtual double AsDouble() const
VAR_TYPE_T GetType() const
virtual bool EqualTo(CONTEXT *aCtx, const VALUE *b) const
LSET is a set of PCB_LAYER_IDs.
Definition: lset.h:35
bool Contains(PCB_LAYER_ID aLayer)
See if the layer set contains a PCB layer.
Definition: lset.h:79
const wxString GetName() const
Definition: netclass.h:62
LIBEVAL::FUNC_CALL_REF Get(const wxString &name)
static PCBEXPR_BUILTIN_FUNCTIONS & Instance()
PCBEXPR_COMPILER(LIBEVAL::UNIT_RESOLVER *aUnitResolver)
BOARD_ITEM * m_items[2]
BOARD * GetBoard() const
PCB_LAYER_ID GetLayer() const
BOARD_ITEM * GetItem(int index) const
PCBEXPR_EVALUATOR(LIBEVAL::UNIT_RESOLVER *aUnitResolver)
PCBEXPR_COMPILER m_compiler
bool Evaluate(const wxString &aExpr)
PCBEXPR_LAYER_VALUE(PCB_LAYER_ID aLayer)
virtual bool EqualTo(LIBEVAL::CONTEXT *aCtx, const VALUE *b) const override
LIBEVAL::VALUE * GetValue(LIBEVAL::CONTEXT *aCtx) override
bool EqualTo(LIBEVAL::CONTEXT *aCtx, const VALUE *b) const override
BOARD_CONNECTED_ITEM * m_item
PCBEXPR_NETCLASS_VALUE(BOARD_CONNECTED_ITEM *aItem)
const wxString & AsString() const override
bool NotEqualTo(LIBEVAL::CONTEXT *aCtx, const LIBEVAL::VALUE *b) const override
LIBEVAL::VALUE * GetValue(LIBEVAL::CONTEXT *aCtx) override
bool EqualTo(LIBEVAL::CONTEXT *aCtx, const VALUE *b) const override
BOARD_CONNECTED_ITEM * m_item
PCBEXPR_NET_VALUE(BOARD_CONNECTED_ITEM *aItem)
bool NotEqualTo(LIBEVAL::CONTEXT *aCtx, const LIBEVAL::VALUE *b) const override
const wxString & AsString() const override
bool EqualTo(LIBEVAL::CONTEXT *aCtx, const VALUE *b) const override
PCBEXPR_PINTYPE_VALUE(const wxString &aPinTypeName)
LIBEVAL::VALUE * GetValue(LIBEVAL::CONTEXT *aCtx) override
virtual std::unique_ptr< LIBEVAL::VAR_REF > CreateVarRef(const wxString &aVar, const wxString &aField) override
virtual LIBEVAL::FUNC_CALL_REF CreateFuncCall(const wxString &aName) override
const std::vector< wxString > & GetSupportedUnits() const override
double Convert(const wxString &aString, int unitId) const override
double Convert(const wxString &aString, int unitId) const override
wxString GetSupportedUnitsMessage() const override
const std::vector< wxString > & GetSupportedUnits() const override
std::unordered_map< TYPE_ID, PROPERTY_BASE * > m_matchingTypes
LIBEVAL::VALUE * GetValue(LIBEVAL::CONTEXT *aCtx) override
LIBEVAL::VAR_TYPE_T m_type
BOARD_ITEM * GetObject(const LIBEVAL::CONTEXT *aCtx) const
virtual size_t TypeHash() const =0
Return type-id of the property type.
virtual bool HasChoices() const
Return true if this PROPERTY has a limited set of possible values.
Definition: property.h:241
Provide class metadata.Helper macro to map type hashes to names.
Definition: property_mgr.h:85
CLASSES_INFO GetAllClasses()
static PROPERTY_MANAGER & Instance()
Definition: property_mgr.h:87
PROPERTY_BASE * GetProperty(TYPE_ID aType, const wxString &aProperty) const
Return a property for a specific type.
bool IsOfType(TYPE_ID aDerived, TYPE_ID aBase) const
Return true if aDerived is inherited from aBase.
@ NULL_CONSTRAINT
Definition: drc_rule.h:47
#define _(s)
wxString LayerName(int aLayer)
Returns the default display name for a given layer.
Definition: layer_id.cpp:30
PCB_LAYER_ID
A quick note on layer IDs:
Definition: layer_ids.h:60
@ F_Cu
Definition: layer_ids.h:64
PCB_LAYER_ID ToLAYER_ID(int aLayer)
Definition: lset.cpp:875
KICOMMON_API double DoubleValueFromString(const EDA_IU_SCALE &aIuScale, EDA_UNITS aUnits, const wxString &aTextValue, EDA_DATA_TYPE aType=EDA_DATA_TYPE::DISTANCE)
Function DoubleValueFromString converts aTextValue to a double.
Definition: eda_units.cpp:576
std::function< void(CONTEXT *, void *)> FUNC_CALL_REF
BOARD * GetBoard()
#define TYPE_HASH(x)
Definition: property.h:71
constexpr ret_type KiROUND(fp_type v)
Round a floating point number to an integer using "round halfway cases away from zero".
Definition: util.h:121