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 The 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, see <https://www.gnu.org/licenses/>.
18 */
19
20#include <inspectable_impl.h>
21
22#include <cstdio>
23#include <memory>
24#include <mutex>
25
26#include <board.h>
27#include <footprint.h>
28#include <gal/color4d.h>
29#include <geometry/eda_angle.h>
30#include <lset.h>
32#include <drc/drc_engine.h>
34#include <string_utils.h>
35
36#include "pcbexpr_evaluator.h"
37
38
39/* --------------------------------------------------------------------------------------------
40 * Specialized Expression References
41 */
42
44{
45 wxASSERT( dynamic_cast<const PCBEXPR_CONTEXT*>( aCtx ) );
46
47 const PCBEXPR_CONTEXT* ctx = static_cast<const PCBEXPR_CONTEXT*>( aCtx );
48 BOARD_ITEM* item = ctx->GetItem( m_itemIndex );
49
50 for( PCBEXPR_NAV_STEP step : m_navigation )
51 {
52 if( !item )
53 break;
54
55 switch( step )
56 {
57 // The direct parent, matching the semantics of the "Parent" string property so that
58 // "A.Parent" used as an object refers to the same item as "A.Parent" used as a string.
59 case PCBEXPR_NAV_STEP::PARENT: item = item->GetParent(); break;
60 }
61 }
62
63 return item;
64}
65
66
68{
69public:
71 LIBEVAL::VALUE( LayerName( aLayer ) ),
72 m_layer( aLayer )
73 {};
74
75 virtual bool EqualTo( LIBEVAL::CONTEXT* aCtx, const VALUE* b ) const override
76 {
77 // For boards with user-defined layer names there will be 2 entries for each layer
78 // in the ENUM_MAP: one for the canonical layer name and one for the user layer name.
79 // We need to check against both.
80
81 wxPGChoices& layerMap = ENUM_MAP<PCB_LAYER_ID>::Instance().Choices();
82 const wxString& layerName = b->AsString();
83 BOARD* board = static_cast<PCBEXPR_CONTEXT*>( aCtx )->GetBoard();
84
85 {
86 std::shared_lock<std::shared_mutex> readLock( board->m_CachesMutex );
87
88 auto i = board->m_LayerExpressionCache.find( layerName );
89
90 if( i != board->m_LayerExpressionCache.end() )
91 return i->second.Contains( m_layer );
92 }
93
94 LSET mask;
95
96 for( unsigned ii = 0; ii < layerMap.GetCount(); ++ii )
97 {
98 wxPGChoiceEntry& entry = layerMap[ii];
99
100 if( entry.GetText().Matches( layerName ) )
101 mask.set( ToLAYER_ID( entry.GetValue() ) );
102 }
103
104 {
105 std::unique_lock<std::shared_mutex> writeLock( board->m_CachesMutex );
106 board->m_LayerExpressionCache[ layerName ] = mask;
107 }
108
109 return mask.Contains( m_layer );
110 }
111
112protected:
114};
115
116
118{
119public:
120 PCBEXPR_PINTYPE_VALUE( const wxString& aPinTypeName ) :
121 LIBEVAL::VALUE( aPinTypeName )
122 {};
123
124 bool EqualTo( LIBEVAL::CONTEXT* aCtx, const VALUE* b ) const override
125 {
126 const wxString& thisStr = AsString();
127 const wxString& otherStr = b->AsString();
128
129 // Case insensitive
130 if( thisStr.IsSameAs( otherStr, false ) )
131 return true;
132
133 // Wildcards
134 if( thisStr.Matches( otherStr ) )
135 return true;
136
137 // Handle cases where the netlist token is different from the EEschema token
138 wxString altStr;
139
140 if( thisStr == wxT( "tri_state" ) )
141 altStr = wxT( "Tri-state" );
142 else if( thisStr == wxT( "power_in" ) )
143 altStr = wxT( "Power input" );
144 else if( thisStr == wxT( "power_out" ) )
145 altStr = wxT( "Power output" );
146 else if( thisStr == wxT( "no_connect" ) )
147 altStr = wxT( "Unconnected" );
148
149 if( !altStr.IsEmpty() )
150 {
151 // Case insensitive
152 if( altStr.IsSameAs( otherStr, false ) )
153 return true;
154
155 // Wildcards
156 if( altStr.Matches( otherStr ) )
157 return true;
158 }
159
160 return false;
161 }
162};
163
164
166{
167public:
169 LIBEVAL::VALUE( wxEmptyString ),
170 m_item( aItem )
171 {};
172
173 const wxString& AsString() const override
174 {
175 const_cast<PCBEXPR_NETCLASS_VALUE*>( this )->Set( m_item->GetEffectiveNetClass()->GetName() );
177 }
178
179 bool EqualTo( LIBEVAL::CONTEXT* aCtx, const VALUE* b ) const override
180 {
181 if( const PCBEXPR_NETCLASS_VALUE* bValue = dynamic_cast<const PCBEXPR_NETCLASS_VALUE*>( b ) )
182 return *( m_item->GetEffectiveNetClass() ) == *( bValue->m_item->GetEffectiveNetClass() );
183
184 if( b->GetType() == LIBEVAL::VT_STRING )
185 {
186 // Test constituent net class names. The effective net class name (e.g. CLASS1,CLASS2,OTHER_CLASS) is
187 // tested in the fallthrough condition.
188 for( const NETCLASS* nc : m_item->GetEffectiveNetClass()->GetConstituentNetclasses() )
189 {
190 const wxString& ncName = nc->GetName();
191
192 if( b->StringIsWildcard() )
193 {
194 if( WildCompareString( b->AsString(), ncName, false ) )
195 return true;
196 }
197 else
198 {
199 if( ncName.IsSameAs( b->AsString(), false ) )
200 return true;
201 }
202 }
203 }
204
205 return LIBEVAL::VALUE::EqualTo( aCtx, b );
206 }
207
208 bool NotEqualTo( LIBEVAL::CONTEXT* aCtx, const LIBEVAL::VALUE* b ) const override
209 {
210 if( const PCBEXPR_NETCLASS_VALUE* bValue = dynamic_cast<const PCBEXPR_NETCLASS_VALUE*>( b ) )
211 return *( m_item->GetEffectiveNetClass() ) != *( bValue->m_item->GetEffectiveNetClass() );
212
213 if( b->GetType() == LIBEVAL::VT_STRING )
214 {
215 // Test constituent net class names
216 bool isInConstituents = false;
217
218 for( const NETCLASS* nc : m_item->GetEffectiveNetClass()->GetConstituentNetclasses() )
219 {
220 const wxString& ncName = nc->GetName();
221
222 if( b->StringIsWildcard() )
223 {
224 if( WildCompareString( b->AsString(), ncName, false ) )
225 {
226 isInConstituents = true;
227 break;
228 }
229 }
230 else
231 {
232 if( ncName.IsSameAs( b->AsString(), false ) )
233 {
234 isInConstituents = true;
235 break;
236 }
237 }
238 }
239
240 // Test effective net class name
241 const bool isFullName = LIBEVAL::VALUE::EqualTo( aCtx, b );
242
243 return !isInConstituents && !isFullName;
244 }
245
246 return LIBEVAL::VALUE::NotEqualTo( aCtx, b );
247 }
248
249protected:
251};
252
253
255{
256public:
258 LIBEVAL::VALUE( wxEmptyString ),
259 m_item( dynamic_cast<FOOTPRINT*>( aItem ) )
260 {};
261
262 const wxString& AsString() const override
263 {
264 if( !m_item )
266
267 if( const COMPONENT_CLASS* compClass = m_item->GetComponentClass() )
268 const_cast<PCBEXPR_COMPONENT_CLASS_VALUE*>( this )->Set( compClass->GetName() );
269
271 }
272
273 bool EqualTo( LIBEVAL::CONTEXT* aCtx, const VALUE* b ) const override
274 {
275 if( const PCBEXPR_COMPONENT_CLASS_VALUE* bValue = dynamic_cast<const PCBEXPR_COMPONENT_CLASS_VALUE*>( b ) )
276 {
277 if( !m_item || !bValue->m_item )
278 return LIBEVAL::VALUE::EqualTo( aCtx, b );
279
280 const COMPONENT_CLASS* aClass = m_item->GetComponentClass();
281 const COMPONENT_CLASS* bClass = bValue->m_item->GetComponentClass();
282
283 return *aClass == *bClass;
284 }
285
286 if( b->GetType() == LIBEVAL::VT_STRING )
287 {
288 // Test constituent component class names. The effective component class name
289 // (e.g. CLASS1,CLASS2,OTHER_CLASS) is tested in the fallthrough condition.
290 for( const COMPONENT_CLASS* cc : m_item->GetComponentClass()->GetConstituentClasses() )
291 {
292 const wxString& ccName = cc->GetName();
293
294 if( b->StringIsWildcard() )
295 {
296 if( WildCompareString( b->AsString(), ccName, false ) )
297 return true;
298 }
299 else
300 {
301 if( ccName.IsSameAs( b->AsString(), false ) )
302 return true;
303 }
304 }
305 }
306
307 return LIBEVAL::VALUE::EqualTo( aCtx, b );
308 }
309
310 bool NotEqualTo( LIBEVAL::CONTEXT* aCtx, const LIBEVAL::VALUE* b ) const override
311 {
312 if( const PCBEXPR_COMPONENT_CLASS_VALUE* bValue = dynamic_cast<const PCBEXPR_COMPONENT_CLASS_VALUE*>( b ) )
313 {
314 if( !m_item || !bValue->m_item )
315 return LIBEVAL::VALUE::NotEqualTo( aCtx, b );
316
317 const COMPONENT_CLASS* aClass = m_item->GetComponentClass();
318 const COMPONENT_CLASS* bClass = bValue->m_item->GetComponentClass();
319
320 return *aClass != *bClass;
321 }
322
323 if( b->GetType() == LIBEVAL::VT_STRING )
324 {
325 // Test constituent component class names
326 bool isInConstituents = false;
327
328 for( const COMPONENT_CLASS* cc : m_item->GetComponentClass()->GetConstituentClasses() )
329 {
330 const wxString& ccName = cc->GetName();
331
332 if( b->StringIsWildcard() )
333 {
334 if( WildCompareString( b->AsString(), ccName, false ) )
335 {
336 isInConstituents = true;
337 break;
338 }
339 }
340 else
341 {
342 if( ccName.IsSameAs( b->AsString(), false ) )
343 {
344 isInConstituents = true;
345 break;
346 }
347 }
348 }
349
350 // Test effective component class name
351 const bool isFullName = LIBEVAL::VALUE::EqualTo( aCtx, b );
352
353 return !isInConstituents && !isFullName;
354 }
355
356 return LIBEVAL::VALUE::NotEqualTo( aCtx, b );
357 }
358
359protected:
361};
362
363
365{
366public:
368 LIBEVAL::VALUE( wxEmptyString ),
369 m_item( aItem )
370 {};
371
372 const wxString& AsString() const override
373 {
374 const_cast<PCBEXPR_NET_VALUE*>( this )->Set( m_item->GetNetname() );
376 }
377
378 bool EqualTo( LIBEVAL::CONTEXT* aCtx, const VALUE* b ) const override
379 {
380 if( const PCBEXPR_NET_VALUE* bValue = dynamic_cast<const PCBEXPR_NET_VALUE*>( b ) )
381 return m_item->GetNetCode() == bValue->m_item->GetNetCode();
382 else
383 return LIBEVAL::VALUE::EqualTo( aCtx, b );
384 }
385
386 bool NotEqualTo( LIBEVAL::CONTEXT* aCtx, const LIBEVAL::VALUE* b ) const override
387 {
388 if( const PCBEXPR_NET_VALUE* bValue = dynamic_cast<const PCBEXPR_NET_VALUE*>( b ) )
389 return m_item->GetNetCode() != bValue->m_item->GetNetCode();
390 else
391 return LIBEVAL::VALUE::NotEqualTo( aCtx, b );
392 }
393
394protected:
396};
397
398
400{
401 auto it = m_typeOverrides.find( aItem );
402 return it != m_typeOverrides.end() ? it->second : aItem->Type();
403}
404
405
407{
408 const TYPE_ID type = aProperty->TypeHash();
409
410 if( type == TYPE_HASH( int ) )
412 else if( type == TYPE_HASH( std::optional<int> ) )
414 else if( type == TYPE_HASH( unsigned ) )
416 else if( type == TYPE_HASH( long long int ) )
418 else if( type == TYPE_HASH( double ) )
420 else if( type == TYPE_HASH( std::optional<double> ) )
422 else if( type == TYPE_HASH( bool ) )
424 else if( type == TYPE_HASH( wxString ) )
426 else if( aProperty->HasChoices() )
428 else if( type == TYPE_HASH( EDA_ANGLE ) )
430 else if( type == TYPE_HASH( COLOR4D ) )
432
434}
435
436
460
461
463{
464 PCBEXPR_CONTEXT* context = static_cast<PCBEXPR_CONTEXT*>( aCtx );
465
466 if( m_type == LIBEVAL::VT_NULL )
468
469 if( m_itemIndex == 2 )
470 return new PCBEXPR_LAYER_VALUE( context->GetLayer() );
471
472 BOARD_ITEM* item = GetObject( aCtx );
473
474 if( !item )
475 return new LIBEVAL::VALUE();
476
477 auto it = m_matchingTypes.find( TYPE_HASH( *item ) );
478
479 if( it == m_matchingTypes.end() )
480 {
481 // If the property isn't defined on the item itself but is defined on its parent
482 // footprint (e.g. Reference, Value), resolve against the parent so that conditions
483 // like "A.Reference == 'J1'" match pads and graphics belonging to J1.
484 if( FOOTPRINT* parentFp = item->GetParentFootprint() )
485 {
486 auto parentIt = m_matchingTypes.find( TYPE_HASH( *parentFp ) );
487
488 if( parentIt != m_matchingTypes.end() )
489 {
490 item = parentFp;
491 it = parentIt;
492 }
493 }
494 }
495
496 if( it == m_matchingTypes.end() )
497 {
498 // Don't force user to type "A.Type == 'via' && A.Via_Type == 'buried'" when the
499 // simpler "A.Via_Type == 'buried'" is perfectly clear. Instead, return an undefined
500 // value when the property doesn't appear on a particular object.
501
502 return new LIBEVAL::VALUE();
503 }
504 else
505 {
506 if( !PROPERTY_MANAGER::Instance().IsAvailableFor( TYPE_HASH( *item ), it->second.property, item ) )
507 {
508 return new LIBEVAL::VALUE();
509 }
510
511 if( item->Type() == PCB_FOOTPRINT_T && item->GetBoard() )
512 {
513 const wxString variant = item->GetBoard()->GetCurrentVariant();
514
515 if( !variant.IsEmpty() )
516 {
517 FOOTPRINT* fp = static_cast<FOOTPRINT*>( item );
518 const wxString& name = it->second.property->Name();
519
520 if( name == wxT( "Do not Populate" ) )
521 return new LIBEVAL::VALUE( static_cast<double>( fp->GetDNPForVariant( variant ) ) );
522 else if( name == wxT( "Exclude From Bill of Materials" ) )
523 return new LIBEVAL::VALUE( static_cast<double>( fp->GetExcludedFromBOMForVariant( variant ) ) );
524 else if( name == wxT( "Exclude From Simulation" ) )
525 return new LIBEVAL::VALUE( static_cast<double>( fp->GetExcludedFromSimForVariant( variant ) ) );
526 else if( name == wxT( "Exclude From Position Files" ) )
527 return new LIBEVAL::VALUE(
528 static_cast<double>( fp->GetExcludedFromPosFilesForVariant( variant ) ) );
529 }
530 }
531
532 switch( it->second.kind )
533 {
535 return new LIBEVAL::VALUE( static_cast<double>( item->Get<int>( it->second.property ) ) );
536
538 {
539 std::optional<int> val = item->Get<std::optional<int>>( it->second.property );
540
541 if( val )
542 return new LIBEVAL::VALUE( static_cast<double>( *val ) );
543
545 }
546
548 return new LIBEVAL::VALUE( static_cast<double>( item->Get<unsigned>( it->second.property ) ) );
549
551 return new LIBEVAL::VALUE( static_cast<double>( item->Get<long long int>( it->second.property ) ) );
552
553 case PCBEXPR_PROPERTY_KIND::DOUBLE: return new LIBEVAL::VALUE( item->Get<double>( it->second.property ) );
554
556 {
557 std::optional<double> val = item->Get<std::optional<double>>( it->second.property );
558
559 if( val )
560 return new LIBEVAL::VALUE( *val );
561
563 }
564
566 return new LIBEVAL::VALUE( static_cast<double>( item->Get<bool>( it->second.property ) ) );
567
569 {
570 wxString str = item->Get<wxString>( it->second.property );
571
572 if( it->second.property->Name() == wxT( "Pin Type" ) )
573 return new PCBEXPR_PINTYPE_VALUE( str );
574
575 // If it quacks like a duck, it is a duck
576 double doubleVal;
577
579 return new LIBEVAL::VALUE( doubleVal );
580
581 return new LIBEVAL::VALUE( str );
582 }
583
585 {
586 wxString str;
587
588 if( it->second.property->Name() == wxT( "Layer" ) || it->second.property->Name() == wxT( "Layer Top" )
589 || it->second.property->Name() == wxT( "Layer Bottom" ) )
590 {
591 const wxAny& any = item->Get( it->second.property );
592 PCB_LAYER_ID layer;
593
594 if( any.GetAs<PCB_LAYER_ID>( &layer ) )
595 return new PCBEXPR_LAYER_VALUE( layer );
596 else if( any.GetAs<wxString>( &str ) )
597 return new PCBEXPR_LAYER_VALUE( context->GetBoard()->GetLayerID( str ) );
598 }
599 else
600 {
601 const wxAny& any = item->Get( it->second.property );
602
603 if( any.GetAs<wxString>( &str ) )
604 return new LIBEVAL::VALUE( str );
605 }
606
607 return new LIBEVAL::VALUE();
608 }
609
611 return new LIBEVAL::VALUE( item->Get<EDA_ANGLE>( it->second.property ).AsDegrees() );
612
614 return new LIBEVAL::VALUE( item->Get<COLOR4D>( it->second.property ).ToCSSString() );
615
617 }
618 }
619
620 return new LIBEVAL::VALUE();
621}
622
623
625{
626 BOARD_CONNECTED_ITEM* item = dynamic_cast<BOARD_CONNECTED_ITEM*>( GetObject( aCtx ) );
627
628 if( !item )
629 return new LIBEVAL::VALUE();
630
631 return new PCBEXPR_NETCLASS_VALUE( item );
632}
633
634
636{
637 BOARD_ITEM* item = dynamic_cast<BOARD_ITEM*>( GetObject( aCtx ) );
638
639 if( !item )
640 return new LIBEVAL::VALUE();
641
642 // Resolve component class via the parent footprint so that conditions like
643 // "A.ComponentClass == 'X'" match pads and graphics inside the footprint.
644 if( item->Type() != PCB_FOOTPRINT_T )
645 item = item->GetParentFootprint();
646
647 if( !item )
648 return new LIBEVAL::VALUE();
649
650 return new PCBEXPR_COMPONENT_CLASS_VALUE( item );
651}
652
653
655{
656 BOARD_CONNECTED_ITEM* item = dynamic_cast<BOARD_CONNECTED_ITEM*>( GetObject( aCtx ) );
657
658 if( !item )
659 return new LIBEVAL::VALUE();
660
661 return new PCBEXPR_NET_VALUE( item );
662}
663
664
666{
667 BOARD_ITEM* item = GetObject( aCtx );
668
669 if( !item )
670 return new LIBEVAL::VALUE();
671
672 PCBEXPR_CONTEXT* ctx = static_cast<PCBEXPR_CONTEXT*>( aCtx );
673 KICAD_T type = ctx->GetEffectiveType( item );
674
675 return new LIBEVAL::VALUE( ENUM_MAP<KICAD_T>::Instance().ToString( type ) );
676}
677
678
680{
681 wxString nameLower = aName.Lower();
682
684
685 if( registry.IsGeometryDependent( nameLower ) )
687
688 return registry.Get( nameLower );
689}
690
691
692std::unique_ptr<LIBEVAL::VAR_REF> PCBEXPR_UCODE::CreateVarRef( const wxString& aVar,
693 const wxString& aField )
694{
696 std::unique_ptr<PCBEXPR_VAR_REF> vref;
697
698 if( aVar.IsSameAs( wxT( "null" ), false ) )
699 {
700 vref = std::make_unique<PCBEXPR_VAR_REF>( 0 );
701 vref->SetType( LIBEVAL::VT_NULL );
702 return vref;
703 }
704
705 // The receiver may be a navigation chain such as "A.Parent". Split off the base variable
706 // and translate each remaining segment into a navigation step. Only "Parent" is supported.
707
708 wxString baseVar = aVar;
709 std::vector<PCBEXPR_NAV_STEP> navigation;
710
711 if( aVar.Contains( wxT( "." ) ) )
712 {
713 wxArrayString tokens = wxSplit( aVar, '.' );
714 baseVar = tokens.IsEmpty() ? wxString() : tokens[0];
715
716 for( size_t i = 1; i < tokens.GetCount(); ++i )
717 {
718 if( tokens[i].CmpNoCase( wxT( "Parent" ) ) == 0 )
719 navigation.push_back( PCBEXPR_NAV_STEP::PARENT );
720 else
721 return nullptr;
722 }
723
724 // Navigation requires one concrete item; combined-item "AB" and layer "L" have no parent.
725 if( baseVar != wxT( "A" ) && baseVar != wxT( "B" ) )
726 return nullptr;
727 }
728
729 // Existing rules spell the layer under test "L.Layer"
730 if( baseVar == wxT( "L" ) && aField.CmpNoCase( wxT( "Layer" ) ) == 0 )
731 return std::make_unique<PCBEXPR_VAR_REF>( 2 );
732
733 if( !aField.IsEmpty() && ( baseVar == wxT( "AB" ) || baseVar == wxT( "L" ) ) )
734 return nullptr;
735
736 if( baseVar == wxT( "B" ) || baseVar == wxT( "AB" ) )
737 m_requiresPairItems = true;
738
739 if( baseVar == wxT( "B" ) )
740 m_referencesItemB = true;
741
742 auto withNav =
743 [&navigation]( std::unique_ptr<PCBEXPR_VAR_REF> aRef ) -> std::unique_ptr<PCBEXPR_VAR_REF>
744 {
745 if( aRef && !navigation.empty() )
746 aRef->SetNavigation( navigation );
747
748 return aRef;
749 };
750
751 // Check for a couple of very common cases and compile them straight to "object code".
752
753 if( aField.CmpNoCase( wxT( "NetClass" ) ) == 0 )
754 {
755 if( baseVar == wxT( "A" ) )
756 return withNav( std::make_unique<PCBEXPR_NETCLASS_REF>( 0 ) );
757 else if( baseVar == wxT( "B" ) )
758 return withNav( std::make_unique<PCBEXPR_NETCLASS_REF>( 1 ) );
759 else
760 return nullptr;
761 }
762 else if( aField.CmpNoCase( wxT( "ComponentClass" ) ) == 0 )
763 {
764 if( baseVar == wxT( "A" ) )
765 return withNav( std::make_unique<PCBEXPR_COMPONENT_CLASS_REF>( 0 ) );
766 else if( baseVar == wxT( "B" ) )
767 return withNav( std::make_unique<PCBEXPR_COMPONENT_CLASS_REF>( 1 ) );
768 else
769 return nullptr;
770 }
771 else if( aField.CmpNoCase( wxT( "NetName" ) ) == 0 )
772 {
773 if( baseVar == wxT( "A" ) )
774 return withNav( std::make_unique<PCBEXPR_NETNAME_REF>( 0 ) );
775 else if( baseVar == wxT( "B" ) )
776 return withNav( std::make_unique<PCBEXPR_NETNAME_REF>( 1 ) );
777 else
778 return nullptr;
779 }
780 else if( aField.CmpNoCase( wxT( "Type" ) ) == 0 )
781 {
782 if( baseVar == wxT( "A" ) )
783 return withNav( std::make_unique<PCBEXPR_TYPE_REF>( 0 ) );
784 else if( baseVar == wxT( "B" ) )
785 return withNav( std::make_unique<PCBEXPR_TYPE_REF>( 1 ) );
786 else
787 return nullptr;
788 }
789
790 if( baseVar == wxT( "A" ) || baseVar == wxT( "AB" ) )
791 vref = std::make_unique<PCBEXPR_VAR_REF>( 0 );
792 else if( baseVar == wxT( "B" ) )
793 vref = std::make_unique<PCBEXPR_VAR_REF>( 1 );
794 else if( baseVar == wxT( "L" ) )
795 vref = std::make_unique<PCBEXPR_VAR_REF>( 2 );
796 else
797 return nullptr;
798
799 vref->SetNavigation( navigation );
800
801 if( aField.length() == 0 ) // return reference to base object
802 return vref;
803
804 wxString field( aField );
805 field.Replace( wxT( "_" ), wxT( " " ) );
806
807 // Alias renamed properties so that older custom rules keep working.
808 if( !field.CmpNoCase( wxT( "Origin X" ) ) )
809 field = wxT( "Start X" );
810 else if( !field.CmpNoCase( wxT( "Origin Y" ) ) )
811 field = wxT( "Start Y" );
812
813 for( const PROPERTY_MANAGER::CLASS_INFO& cls : propMgr.GetAllClasses() )
814 {
815 if( propMgr.IsOfType( cls.type, TYPE_HASH( BOARD_ITEM ) ) )
816 {
817 PROPERTY_BASE* prop = propMgr.GetProperty( cls.type, field );
818
819 if( prop )
820 {
823
824 if( expressionType == LIBEVAL::VT_PARSE_ERROR
825 || ( vref->GetType() != LIBEVAL::VT_UNDEFINED && vref->GetType() != expressionType ) )
826 {
827 vref->SetType( LIBEVAL::VT_PARSE_ERROR );
828 return vref;
829 }
830
831 vref->AddAllowedClass( cls.type, prop, kind );
832 vref->SetType( expressionType );
833 }
834 }
835 }
836
837 if( vref->GetType() == LIBEVAL::VT_UNDEFINED )
838 vref->SetType( LIBEVAL::VT_PARSE_ERROR );
839
840 return vref;
841}
842
843
845{
846 if( m_items[0] )
847 return m_items[0]->GetBoard();
848
849 return nullptr;
850}
851
852
853/* --------------------------------------------------------------------------------------------
854 * Unit Resolvers
855 */
856
857const std::vector<wxString>& PCBEXPR_UNIT_RESOLVER::GetSupportedUnits() const
858{
859 static const std::vector<wxString> pcbUnits = { wxT( "mil" ), wxT( "mm" ), wxT( "in" ),
860 wxT( "deg" ), wxT( "fs" ), wxT( "ps" ) };
861
862
863 return pcbUnits;
864}
865
866
868{
869 return _( "must be mm, in, mil, deg, fs, or ps" );
870}
871
872
873const std::vector<EDA_UNITS>& PCBEXPR_UNIT_RESOLVER::GetSupportedUnitsTypes() const
874{
875 static const std::vector<EDA_UNITS> pcbUnits = { EDA_UNITS::MILS, EDA_UNITS::MM, EDA_UNITS::INCH,
877
878 return pcbUnits;
879}
880
881
882double PCBEXPR_UNIT_RESOLVER::Convert( const wxString& aString, int unitId ) const
883{
884 double v = wxAtof( aString );
885
886 switch( unitId )
887 {
891 case 3: return v;
894 default: return v;
895 }
896};
897
898
899const std::vector<wxString>& PCBEXPR_UNITLESS_RESOLVER::GetSupportedUnits() const
900{
901 static const std::vector<wxString> emptyUnits;
902
903 return emptyUnits;
904}
905
906
907const std::vector<EDA_UNITS>& PCBEXPR_UNITLESS_RESOLVER::GetSupportedUnitsTypes() const
908{
909 static const std::vector<EDA_UNITS> emptyUnits;
910
911 return emptyUnits;
912}
913
914
915double PCBEXPR_UNITLESS_RESOLVER::Convert( const wxString& aString, int unitId ) const
916{
917 return wxAtof( aString );
918};
919
920
922{
923 m_unitResolver.reset( aUnitResolver );
924}
925
926
927/* --------------------------------------------------------------------------------------------
928 * PCB Expression Evaluator
929 */
930
932 m_result( 0 ),
933 m_resultAsDouble( 0.0 ),
934 m_units( EDA_UNITS::MM ),
935 m_compiler( aUnitResolver ),
936 m_ucode(),
938{
939}
940
941
945
946
947bool PCBEXPR_EVALUATOR::Evaluate( const wxString& aExpr )
948{
949 PCBEXPR_UCODE ucode;
950 PCBEXPR_CONTEXT preflightContext( NULL_CONSTRAINT, F_Cu );
951
952 if( !m_compiler.Compile( aExpr.ToUTF8().data(), &ucode, &preflightContext ) )
953 return false;
954
955 PCBEXPR_CONTEXT evaluationContext( NULL_CONSTRAINT, F_Cu );
956 LIBEVAL::VALUE* result = ucode.Run( &evaluationContext );
957
958 if( result->GetType() == LIBEVAL::VT_NUMERIC )
959 {
960 m_resultAsDouble = result->AsDouble();
962 m_units = result->GetUnits();
963 }
964
965 return true;
966}
const char * name
constexpr EDA_IU_SCALE pcbIUScale
Definition base_units.h:128
constexpr BOX2I KiROUND(const BOX2D &aBoxD)
Definition box2.h:982
BASE_SET & set(size_t pos)
Definition base_set.h:126
A base class derived from BOARD_ITEM for items that can be connected and have a net,...
A base class for any item which can be embedded within the BOARD container class, and therefore insta...
Definition board_item.h:84
virtual const BOARD * GetBoard() const
Return the BOARD in which this BOARD_ITEM resides, or NULL if none.
FOOTPRINT * GetParentFootprint() const
BOARD_ITEM_CONTAINER * GetParent() const
Definition board_item.h:266
Information pertinent to a Pcbnew printed circuit board.
Definition board.h:410
PCB_LAYER_ID GetLayerID(const wxString &aLayerName) const
Return the ID of a layer.
Definition board.cpp:923
std::unordered_map< wxString, LSET > m_LayerExpressionCache
Definition board.h:1840
wxString GetCurrentVariant() const
Definition board.h:529
std::shared_mutex m_CachesMutex
Definition board.h:1824
A lightweight representation of a component class.
double AsDegrees() const
Definition eda_angle.h:115
KICAD_T Type() const
Returns the type of object.
Definition eda_item.h:110
static ENUM_MAP< T > & Instance()
Definition property.h:783
bool GetExcludedFromSimForVariant(const wxString &aVariantName) const
Get the exclude-from-simulation status for a specific variant.
bool GetDNPForVariant(const wxString &aVariantName) const
Get the DNP status for a specific variant.
bool GetExcludedFromPosFilesForVariant(const wxString &aVariantName) const
Get the exclude-from-position-files status for a specific variant.
bool GetExcludedFromBOMForVariant(const wxString &aVariantName) const
Get the exclude-from-BOM status for a specific variant.
wxAny Get(PROPERTY_BASE *aProperty) const
A color representation with 4 components: red, green, blue, alpha.
Definition color4d.h:101
wxString ToCSSString() const
Definition color4d.cpp:146
std::unique_ptr< UNIT_RESOLVER > m_unitResolver
VALUE * Run(CONTEXT *ctx)
void Set(double aValue)
virtual const wxString & AsString() const
bool StringIsWildcard() const
static VALUE * MakeNullValue()
virtual bool NotEqualTo(CONTEXT *aCtx, const VALUE *b) 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:37
bool Contains(PCB_LAYER_ID aLayer) const
See if the layer set contains a PCB layer.
Definition lset.h:63
A collection of nets and the parameters used to route or test these nets.
Definition netclass.h:43
bool IsGeometryDependent(const wxString &name) const
LIBEVAL::FUNC_CALL_REF Get(const wxString &name)
static PCBEXPR_BUILTIN_FUNCTIONS & Instance()
PCBEXPR_COMPILER(LIBEVAL::UNIT_RESOLVER *aUnitResolver)
LIBEVAL::VALUE * GetValue(LIBEVAL::CONTEXT *aCtx) override
bool NotEqualTo(LIBEVAL::CONTEXT *aCtx, const LIBEVAL::VALUE *b) const override
const wxString & AsString() const override
PCBEXPR_COMPONENT_CLASS_VALUE(BOARD_ITEM *aItem)
bool EqualTo(LIBEVAL::CONTEXT *aCtx, const VALUE *b) const override
BOARD_ITEM * m_items[2]
BOARD * GetBoard() const
KICAD_T GetEffectiveType(const BOARD_ITEM *aItem) const
PCB_LAYER_ID GetLayer() const
std::map< const BOARD_ITEM *, KICAD_T > m_typeOverrides
BOARD_ITEM * GetItem(int index) const
LIBEVAL::ERROR_STATUS m_errorStatus
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
bool m_hasGeometryDependentFunctions
const std::vector< wxString > & GetSupportedUnits() const override
const std::vector< EDA_UNITS > & GetSupportedUnitsTypes() const override
double Convert(const wxString &aString, int unitId) const override
double Convert(const wxString &aString, int unitId) const override
const std::vector< EDA_UNITS > & GetSupportedUnitsTypes() const override
wxString GetSupportedUnitsMessage() const override
const std::vector< wxString > & GetSupportedUnits() const override
std::vector< PCBEXPR_NAV_STEP > m_navigation
static PCBEXPR_PROPERTY_KIND ClassifyProperty(const PROPERTY_BASE *aProperty)
LIBEVAL::VALUE * GetValue(LIBEVAL::CONTEXT *aCtx) override
std::unordered_map< TYPE_ID, MATCHED_PROPERTY > m_matchingTypes
static LIBEVAL::VAR_TYPE_T ExpressionType(PCBEXPR_PROPERTY_KIND aKind)
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:247
Provide class metadata.
CLASSES_INFO GetAllClasses()
static PROPERTY_MANAGER & Instance()
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.
A type-safe container of any type.
Definition ki_any.h:92
@ NULL_CONSTRAINT
Definition drc_rule.h:50
#define _(s)
EDA_UNITS
Definition eda_units.h:44
wxString LayerName(int aLayer)
Returns the default display name for a given layer.
Definition layer_id.cpp:31
PCB_LAYER_ID
A quick note on layer IDs:
Definition layer_ids.h:56
@ F_Cu
Definition layer_ids.h:60
PCB_LAYER_ID ToLAYER_ID(int aLayer)
Definition lset.cpp:750
KICOMMON_API double DoubleValueFromString(const EDA_IU_SCALE &aIuScale, EDA_UNITS aUnits, const wxString &aTextValue, EDA_DATA_TYPE aType=EDA_DATA_TYPE::DISTANCE)
Convert aTextValue to a double.
std::function< void(CONTEXT *, void *)> FUNC_CALL_REF
PCBEXPR_PROPERTY_KIND
PCBEXPR_NAV_STEP
#define TYPE_HASH(x)
Macro to generate unique identifier for a type.
Definition property.h:74
size_t TYPE_ID
Unique type identifier.
bool WildCompareString(const wxString &pattern, const wxString &string_to_tst, bool case_sensitive)
Compare a string against wild card (* and ?) pattern using the usual rules.
static const long long MM
wxString result
Test unit parsing edge cases and error handling.
KICAD_T
The set of class identification values stored in EDA_ITEM::m_structType.
Definition typeinfo.h:70
@ PCB_FOOTPRINT_T
class FOOTPRINT, a footprint
Definition typeinfo.h:78