KiCad PCB EDA Suite
Loading...
Searching...
No Matches
pcb_expr_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 <board.h>
29#include <pcb_expr_evaluator.h>
30#include <drc/drc_engine.h>
31
32/* --------------------------------------------------------------------------------------------
33 * Specialized Expression References
34 */
35
37{
38 wxASSERT( dynamic_cast<const PCB_EXPR_CONTEXT*>( aCtx ) );
39
40 const PCB_EXPR_CONTEXT* ctx = static_cast<const PCB_EXPR_CONTEXT*>( aCtx );
41 BOARD_ITEM* item = ctx->GetItem( m_itemIndex );
42 return item;
43}
44
45
47{
48public:
50 LIBEVAL::VALUE( LayerName( aLayer ) ),
51 m_layer( aLayer )
52 {};
53
54 virtual bool EqualTo( LIBEVAL::CONTEXT* aCtx, const VALUE* b ) const override
55 {
56 // For boards with user-defined layer names there will be 2 entries for each layer
57 // in the ENUM_MAP: one for the canonical layer name and one for the user layer name.
58 // We need to check against both.
59
60 wxPGChoices& layerMap = ENUM_MAP<PCB_LAYER_ID>::Instance().Choices();
61 const wxString& layerName = b->AsString();
62 BOARD* board = static_cast<PCB_EXPR_CONTEXT*>( aCtx )->GetBoard();
63 std::unique_lock<std::mutex> cacheLock( board->m_CachesMutex );
64 auto i = board->m_LayerExpressionCache.find( layerName );
65 LSET mask;
66
67 if( i == board->m_LayerExpressionCache.end() )
68 {
69 for( unsigned ii = 0; ii < layerMap.GetCount(); ++ii )
70 {
71 wxPGChoiceEntry& entry = layerMap[ii];
72
73 if( entry.GetText().Matches( layerName ) )
74 mask.set( ToLAYER_ID( entry.GetValue() ) );
75 }
76
77 board->m_LayerExpressionCache[ layerName ] = mask;
78 }
79 else
80 {
81 mask = i->second;
82 }
83
84 return mask.Contains( m_layer );
85 }
86
87protected:
89};
90
91
93{
94 PCB_EXPR_CONTEXT* context = static_cast<PCB_EXPR_CONTEXT*>( aCtx );
95
96 if( m_itemIndex == 2 )
97 return new PCB_LAYER_VALUE( context->GetLayer() );
98
99 BOARD_ITEM* item = GetObject( aCtx );
100
101 if( !item )
102 return new LIBEVAL::VALUE();
103
104 auto it = m_matchingTypes.find( TYPE_HASH( *item ) );
105
106 if( it == m_matchingTypes.end() )
107 {
108 // Don't force user to type "A.Type == 'via' && A.Via_Type == 'buried'" when the
109 // simpler "A.Via_Type == 'buried'" is perfectly clear. Instead, return an undefined
110 // value when the property doesn't appear on a particular object.
111
112 return new LIBEVAL::VALUE();
113 }
114 else
115 {
117 return new LIBEVAL::VALUE( (double) item->Get<int>( it->second ) );
118 else
119 {
120 wxString str;
121
122 if( !m_isEnum )
123 {
124 str = item->Get<wxString>( it->second );
125 return new LIBEVAL::VALUE( str );
126 }
127 else
128 {
129 const wxAny& any = item->Get( it->second );
130 PCB_LAYER_ID layer;
131
132 if( it->second->Name() == wxT( "Layer" ) )
133 {
134 if( any.GetAs<PCB_LAYER_ID>( &layer ) )
135 return new PCB_LAYER_VALUE( layer );
136 else if( any.GetAs<wxString>( &str ) )
137 return new PCB_LAYER_VALUE( context->GetBoard()->GetLayerID( str ) );
138 }
139 else
140 {
141 if( any.GetAs<wxString>( &str ) )
142 return new LIBEVAL::VALUE( str );
143 }
144 }
145
146 return new LIBEVAL::VALUE();
147 }
148 }
149}
150
151
153{
154 BOARD_CONNECTED_ITEM* item = dynamic_cast<BOARD_CONNECTED_ITEM*>( GetObject( aCtx ) );
155
156 if( !item )
157 return new LIBEVAL::VALUE();
158
159 return new LIBEVAL::VALUE( item->GetEffectiveNetClass()->GetName() );
160}
161
162
164{
165 BOARD_CONNECTED_ITEM* item = dynamic_cast<BOARD_CONNECTED_ITEM*>( GetObject( aCtx ) );
166
167 if( !item )
168 return new LIBEVAL::VALUE();
169
170 return new LIBEVAL::VALUE( item->GetNetname() );
171}
172
173
175{
176 BOARD_ITEM* item = GetObject( aCtx );
177
178 if( !item )
179 return new LIBEVAL::VALUE();
180
181 return new LIBEVAL::VALUE( ENUM_MAP<KICAD_T>::Instance().ToString( item->Type() ) );
182}
183
184
186{
188
189 return registry.Get( aName.Lower() );
190}
191
192
193std::unique_ptr<LIBEVAL::VAR_REF> PCB_EXPR_UCODE::CreateVarRef( const wxString& aVar,
194 const wxString& aField )
195{
197 std::unique_ptr<PCB_EXPR_VAR_REF> vref;
198
199 // Check for a couple of very common cases and compile them straight to "object code".
200
201 if( aField.CmpNoCase( wxT( "NetClass" ) ) == 0 )
202 {
203 if( aVar == wxT( "A" ) )
204 return std::make_unique<PCB_EXPR_NETCLASS_REF>( 0 );
205 else if( aVar == wxT( "B" ) )
206 return std::make_unique<PCB_EXPR_NETCLASS_REF>( 1 );
207 else
208 return nullptr;
209 }
210 else if( aField.CmpNoCase( wxT( "NetName" ) ) == 0 )
211 {
212 if( aVar == wxT( "A" ) )
213 return std::make_unique<PCB_EXPR_NETNAME_REF>( 0 );
214 else if( aVar == wxT( "B" ) )
215 return std::make_unique<PCB_EXPR_NETNAME_REF>( 1 );
216 else
217 return nullptr;
218 }
219 else if( aField.CmpNoCase( wxT( "Type" ) ) == 0 )
220 {
221 if( aVar == wxT( "A" ) )
222 return std::make_unique<PCB_EXPR_TYPE_REF>( 0 );
223 else if( aVar == wxT( "B" ) )
224 return std::make_unique<PCB_EXPR_TYPE_REF>( 1 );
225 else
226 return nullptr;
227 }
228
229 if( aVar == wxT( "A" ) || aVar == wxT( "AB" ) )
230 vref = std::make_unique<PCB_EXPR_VAR_REF>( 0 );
231 else if( aVar == wxT( "B" ) )
232 vref = std::make_unique<PCB_EXPR_VAR_REF>( 1 );
233 else if( aVar == wxT( "L" ) )
234 vref = std::make_unique<PCB_EXPR_VAR_REF>( 2 );
235 else
236 return nullptr;
237
238 if( aField.length() == 0 ) // return reference to base object
239 {
240 return std::move( vref );
241 }
242
243 wxString field( aField );
244 field.Replace( wxT( "_" ), wxT( " " ) );
245
246 for( const PROPERTY_MANAGER::CLASS_INFO& cls : propMgr.GetAllClasses() )
247 {
248 if( propMgr.IsOfType( cls.type, TYPE_HASH( BOARD_ITEM ) ) )
249 {
250 PROPERTY_BASE* prop = propMgr.GetProperty( cls.type, field );
251
252 if( prop )
253 {
254 vref->AddAllowedClass( cls.type, prop );
255
256 if( prop->TypeHash() == TYPE_HASH( int ) )
257 {
258 vref->SetType( LIBEVAL::VT_NUMERIC );
259 }
260 else if( prop->TypeHash() == TYPE_HASH( bool ) )
261 {
262 vref->SetType( LIBEVAL::VT_NUMERIC );
263 }
264 else if( prop->TypeHash() == TYPE_HASH( wxString ) )
265 {
266 vref->SetType( LIBEVAL::VT_STRING );
267 }
268 else if ( prop->HasChoices() )
269 { // it's an enum, we treat it as string
270 vref->SetType( LIBEVAL::VT_STRING );
271 vref->SetIsEnum ( true );
272 }
273 else
274 {
275 wxFAIL_MSG( wxT( "PCB_EXPR_UCODE::createVarRef: Unknown property type." ) );
276 }
277 }
278 }
279 }
280
281 if( vref->GetType() == LIBEVAL::VT_UNDEFINED )
282 vref->SetType( LIBEVAL::VT_PARSE_ERROR );
283
284 return std::move( vref );
285}
286
287
289{
290 if( m_items[0] )
291 return m_items[0]->GetBoard();
292
293 return nullptr;
294}
295
296
297/* --------------------------------------------------------------------------------------------
298 * Unit Resolvers
299 */
300
301const std::vector<wxString>& PCB_UNIT_RESOLVER::GetSupportedUnits() const
302{
303 static const std::vector<wxString> pcbUnits = { wxT( "mil" ), wxT( "mm" ), wxT( "in" ) };
304
305 return pcbUnits;
306}
307
308
310{
311 return _( "must be mm, in, or mil" );
312}
313
314
315double PCB_UNIT_RESOLVER::Convert( const wxString& aString, int unitId ) const
316{
317 double v = wxAtof( aString );
318
319 switch( unitId )
320 {
321 case 0: return EDA_UNIT_UTILS::UI::DoubleValueFromString( pcbIUScale, EDA_UNITS::MILS, aString );
322 case 1: return EDA_UNIT_UTILS::UI::DoubleValueFromString( pcbIUScale, EDA_UNITS::MILLIMETRES, aString );
323 case 2: return EDA_UNIT_UTILS::UI::DoubleValueFromString( pcbIUScale, EDA_UNITS::INCHES, aString );
324 default: return v;
325 }
326};
327
328
329const std::vector<wxString>& PCB_UNITLESS_RESOLVER::GetSupportedUnits() const
330{
331 static const std::vector<wxString> emptyUnits;
332
333 return emptyUnits;
334}
335
336
337double PCB_UNITLESS_RESOLVER::Convert( const wxString& aString, int unitId ) const
338{
339 return wxAtof( aString );
340};
341
342
344{
345 m_unitResolver.reset( aUnitResolver );
346}
347
348
349/* --------------------------------------------------------------------------------------------
350 * PCB Expression Evaluator
351 */
352
354 m_result( 0 ),
355 m_compiler( aUnitResolver ),
356 m_ucode(),
357 m_errorStatus()
358{
359}
360
361
363{
364}
365
366
367bool PCB_EXPR_EVALUATOR::Evaluate( const wxString& aExpr )
368{
369 PCB_EXPR_UCODE ucode;
370 PCB_EXPR_CONTEXT preflightContext( NULL_CONSTRAINT, F_Cu );
371
372 if( !m_compiler.Compile( aExpr.ToUTF8().data(), &ucode, &preflightContext ) )
373 return false;
374
375 PCB_EXPR_CONTEXT evaluationContext( NULL_CONSTRAINT, F_Cu );
376 LIBEVAL::VALUE* result = ucode.Run( &evaluationContext );
377
378 if( result->GetType() == LIBEVAL::VT_NUMERIC )
379 m_result = KiROUND( result->AsDouble() );
380
381 return true;
382}
383
constexpr EDA_IU_SCALE pcbIUScale
Definition: base_units.h:109
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:71
virtual const BOARD * GetBoard() const
Return the BOARD in which this BOARD_ITEM resides, or NULL if none.
Definition: board_item.cpp:44
Information pertinent to a Pcbnew printed circuit board.
Definition: board.h:270
PCB_LAYER_ID GetLayerID(const wxString &aLayerName) const
Return the ID of a layer.
Definition: board.cpp:478
std::unordered_map< wxString, LSET > m_LayerExpressionCache
Definition: board.h:1182
std::mutex m_CachesMutex
Definition: board.h:1176
KICAD_T Type() const
Returns the type of object.
Definition: eda_item.h:97
static ENUM_MAP< T > & Instance()
Definition: property.h:627
wxAny Get(PROPERTY_BASE *aProperty) const
Definition: inspectable.h:84
std::unique_ptr< UNIT_RESOLVER > m_unitResolver
bool Compile(const wxString &aString, UCODE *aCode, CONTEXT *aPreflightContext)
VALUE * Run(CONTEXT *ctx)
virtual double AsDouble() const
VAR_TYPE_T GetType() const
LSET is a set of PCB_LAYER_IDs.
Definition: layer_ids.h:536
bool Contains(PCB_LAYER_ID aLayer)
See if the layer set contains a PCB layer.
Definition: layer_ids.h:606
const wxString GetName() const
Definition: netclass.h:65
LIBEVAL::FUNC_CALL_REF Get(const wxString &name)
static PCB_EXPR_BUILTIN_FUNCTIONS & Instance()
PCB_EXPR_COMPILER(LIBEVAL::UNIT_RESOLVER *aUnitResolver)
BOARD_ITEM * GetItem(int index) const
BOARD_ITEM * m_items[2]
BOARD * GetBoard() const
PCB_LAYER_ID GetLayer() const
bool Evaluate(const wxString &aExpr)
PCB_EXPR_EVALUATOR(LIBEVAL::UNIT_RESOLVER *aUnitResolver)
PCB_EXPR_COMPILER m_compiler
LIBEVAL::VALUE * GetValue(LIBEVAL::CONTEXT *aCtx) override
LIBEVAL::VALUE * GetValue(LIBEVAL::CONTEXT *aCtx) override
LIBEVAL::VALUE * GetValue(LIBEVAL::CONTEXT *aCtx) override
virtual LIBEVAL::FUNC_CALL_REF CreateFuncCall(const wxString &aName) override
virtual std::unique_ptr< LIBEVAL::VAR_REF > CreateVarRef(const wxString &aVar, const wxString &aField) override
BOARD_ITEM * GetObject(const LIBEVAL::CONTEXT *aCtx) const
std::unordered_map< TYPE_ID, PROPERTY_BASE * > m_matchingTypes
LIBEVAL::VAR_TYPE_T m_type
LIBEVAL::VALUE * GetValue(LIBEVAL::CONTEXT *aCtx) override
PCB_LAYER_VALUE(PCB_LAYER_ID aLayer)
virtual bool EqualTo(LIBEVAL::CONTEXT *aCtx, const VALUE *b) const 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
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:225
Provide class metadata.Helper macro to map type hashes to names.
Definition: property_mgr.h:74
CLASSES_INFO GetAllClasses()
static PROPERTY_MANAGER & Instance()
Definition: property_mgr.h:76
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:46
#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:59
@ F_Cu
Definition: layer_ids.h:64
PCB_LAYER_ID ToLAYER_ID(int aLayer)
Definition: lset.cpp:932
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:445
std::function< void(CONTEXT *, void *)> FUNC_CALL_REF
BOARD * GetBoard()
#define TYPE_HASH(x)
Definition: property.h:63
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:85