KiCad PCB EDA Suite
eda_pattern_match.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) 2015-2017 Chris Pavlina <[email protected]>
5 * Copyright (C) 2015-2023 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 2
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, you may find one here:
19 * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
20 * or you may search the http://www.gnu.org website for the version 2 license,
21 * or you may write to the Free Software Foundation, Inc.,
22 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
23 */
24
25#include <eda_pattern_match.h>
26#include <wx/log.h>
27#include <wx/tokenzr.h>
28#include <algorithm>
29#include <climits>
30
31bool EDA_PATTERN_MATCH_SUBSTR::SetPattern( const wxString& aPattern )
32{
33 m_pattern = aPattern;
34 return true;
35}
36
37
39{
40 return m_pattern;
41}
42
43
45{
46 int loc = aCandidate.Find( m_pattern );
47
48 if( loc == wxNOT_FOUND )
49 return {};
50 else
51 return { loc, static_cast<int>( m_pattern.size() ) };
52}
53
54
59{
60 wxLogLevel m_old_level;
61
62public:
63 WX_LOGLEVEL_CONTEXT( wxLogLevel level )
64 {
65 m_old_level = wxLog::GetLogLevel();
66 wxLog::SetLogLevel( level );
67 }
68
70 {
71 wxLog::SetLogLevel( m_old_level );
72 }
73};
74
75
76bool EDA_PATTERN_MATCH_REGEX::SetPattern( const wxString& aPattern )
77{
78 m_pattern = aPattern;
79
80 // Evil and undocumented: wxRegEx::Compile calls wxLogError on error, even
81 // though it promises to just return false. Silence the error.
82 WX_LOGLEVEL_CONTEXT ctx( wxLOG_FatalError );
83
84 return m_regex.Compile( aPattern, wxRE_ADVANCED );
85}
86
87
88bool EDA_PATTERN_MATCH_REGEX_EXPLICIT::SetPattern( const wxString& aPattern )
89{
90 wxString pattern( aPattern );
91
92 if( !pattern.StartsWith( wxT( "^" ) ) )
93 pattern = wxT( "^" ) + pattern;
94
95 if( !pattern.EndsWith( wxT( "$" ) ) )
96 pattern += wxT( "$" );
97
99}
100
101
103{
104 return m_pattern;
105}
106
107
109{
110 if( m_regex.IsValid() )
111 {
112 if( m_regex.Matches( aCandidate ) )
113 {
114 size_t start, len;
115 m_regex.GetMatch( &start, &len, 0 );
116
117 return { static_cast<int>( std::min( start, static_cast<size_t>( INT_MAX ) ) ),
118 static_cast<int>( std::min( len, static_cast<size_t>( INT_MAX ) ) ) };
119 }
120 else
121 {
122 return {};
123 }
124 }
125 else
126 {
127 int loc = aCandidate.Find( m_pattern );
128
129 if( loc == wxNOT_FOUND )
130 return {};
131 else
132 return { loc, static_cast<int>( m_pattern.size() ) };
133 }
134}
135
136
137bool EDA_PATTERN_MATCH_WILDCARD::SetPattern( const wxString& aPattern )
138{
139 m_wildcard_pattern = aPattern;
140
141 // Compile the wildcard string to a regular expression
142 wxString regex;
143 regex.Alloc( 2 * aPattern.Length() ); // no need to keep resizing, we know the size roughly
144
145 const wxString to_replace = wxT( ".*+?^${}()|[]/\\" );
146
147 for( wxString::const_iterator it = aPattern.begin(); it < aPattern.end(); ++it )
148 {
149 wxUniChar c = *it;
150
151 if( c == '?' )
152 {
153 regex += wxT( "." );
154 }
155 else if( c == '*' )
156 {
157 regex += wxT( ".*" );
158 }
159 else if( to_replace.Find( c ) != wxNOT_FOUND )
160 {
161 regex += "\\";
162 regex += c;
163 }
164 else
165 {
166 regex += c;
167 }
168 }
169
171}
172
173
175{
176 return m_wildcard_pattern;
177}
178
179
181{
182 return EDA_PATTERN_MATCH_REGEX::Find( aCandidate );
183}
184
185
187{
188 m_wildcard_pattern = aPattern;
189
190 // Compile the wildcard string to a regular expression
191 wxString regex;
192 regex.Alloc( 2 * aPattern.Length() ); // no need to keep resizing, we know the size roughly
193
194 const wxString to_replace = wxT( ".*+?^${}()|[]/\\" );
195
196 regex += wxT( "^" );
197
198 for( wxString::const_iterator it = aPattern.begin(); it < aPattern.end(); ++it )
199 {
200 wxUniChar c = *it;
201
202 if( c == '?' )
203 {
204 regex += wxT( "." );
205 }
206 else if( c == '*' )
207 {
208 regex += wxT( ".*" );
209 }
210 else if( to_replace.Find( c ) != wxNOT_FOUND )
211 {
212 regex += wxS( "\\" );
213 regex += c;
214 }
215 else
216 {
217 regex += c;
218 }
219 }
220
221 regex += wxT( "$" );
222
224}
225
226
227bool EDA_PATTERN_MATCH_RELATIONAL::SetPattern( const wxString& aPattern )
228{
229 bool matches = m_regex_search.Matches( aPattern );
230
231 if( !matches || m_regex_search.GetMatchCount() < 5 )
232 return false;
233
234 m_pattern = aPattern;
235 wxString key = m_regex_search.GetMatch( aPattern, 1 );
236 wxString rel = m_regex_search.GetMatch( aPattern, 2 );
237 wxString val = m_regex_search.GetMatch( aPattern, 3 );
238 wxString unit = m_regex_search.GetMatch( aPattern, 4 );
239
240 m_key = key.Lower();
241
242 if( rel == wxS( "<" ) )
243 m_relation = LT;
244 else if( rel == wxS( "<=" ) )
245 m_relation = LE;
246 else if( rel == wxS( "=" ) )
247 m_relation = EQ;
248 else if( rel == wxS( ">=" ) )
249 m_relation = GE;
250 else if( rel == wxS( ">" ) )
251 m_relation = GT;
252 else
253 return false;
254
255 if( val == "" )
256 {
257 // Matching on empty values keeps the match list from going empty when the user
258 // types the relational operator character, which helps prevent confusion.
259 m_relation = ANY;
260 }
261 else if( !val.ToCDouble( &m_value ) )
262 {
263 return false;
264 }
265
266 auto unit_it = m_units.find( unit.Lower() );
267
268 if( unit_it != m_units.end() )
269 m_value *= unit_it->second;
270 else
271 return false;
272
273 m_pattern = aPattern;
274
275 return true;
276}
277
278
280{
281 return m_pattern;
282}
283
284
286{
287 wxStringTokenizer tokenizer( aCandidate );
288 size_t lastpos = 0;
289
290 while( tokenizer.HasMoreTokens() )
291 {
292 const wxString token = tokenizer.GetNextToken();
293 int found_delta = FindOne( token );
294
295 if( found_delta != EDA_PATTERN_NOT_FOUND )
296 {
297 size_t found = (size_t) found_delta + lastpos;
298 return { static_cast<int>( std::min( found, static_cast<size_t>( INT_MAX ) ) ), 0 };
299 }
300
301 lastpos = tokenizer.GetPosition();
302 }
303
304 return {};
305}
306
307
308int EDA_PATTERN_MATCH_RELATIONAL::FindOne( const wxString& aCandidate ) const
309{
310 bool matches = m_regex_description.Matches( aCandidate );
311
312 if( !matches )
314
315 size_t start, len;
316 m_regex_description.GetMatch( &start, &len, 0 );
317 wxString key = m_regex_description.GetMatch( aCandidate, 1 );
318 wxString val = m_regex_description.GetMatch( aCandidate, 2 );
319 wxString unit = m_regex_description.GetMatch( aCandidate, 3 );
320
321 int istart = ( start > INT_MAX ) ? INT_MAX : start;
322
323 if( key.Lower() != m_key )
325
326 double val_parsed;
327
328 if( !val.ToCDouble( &val_parsed ) )
330
331 auto unit_it = m_units.find( unit.Lower() );
332
333 if( unit_it != m_units.end() )
334 val_parsed *= unit_it->second;
335
336 switch( m_relation )
337 {
338 case LT: return val_parsed < m_value ? istart : EDA_PATTERN_NOT_FOUND;
339 case LE: return val_parsed <= m_value ? istart : EDA_PATTERN_NOT_FOUND;
340 case EQ: return val_parsed == m_value ? istart : EDA_PATTERN_NOT_FOUND;
341 case GE: return val_parsed >= m_value ? istart : EDA_PATTERN_NOT_FOUND;
342 case GT: return val_parsed > m_value ? istart : EDA_PATTERN_NOT_FOUND;
343 case ANY: return istart;
344 default: return EDA_PATTERN_NOT_FOUND;
345 }
346}
347
348
350 R"((\w+)[=:]([-+]?[\d.]+)(\w*))", wxRE_ADVANCED );
352 R"(^(\w+)(<|<=|=|>=|>)([-+]?[\d.]*)(\w*)$)", wxRE_ADVANCED );
353const std::map<wxString, double> EDA_PATTERN_MATCH_RELATIONAL::m_units = {
354 { wxS( "p" ), 1e-12 },
355 { wxS( "n" ), 1e-9 },
356 { wxS( "u" ), 1e-6 },
357 { wxS( "m" ), 1e-3 },
358 { wxS( "" ), 1. },
359 { wxS( "k" ), 1e3 },
360 { wxS( "meg" ), 1e6 },
361 { wxS( "g" ), 1e9 },
362 { wxS( "t" ), 1e12 },
363 { wxS( "ki" ), 1024. },
364 { wxS( "mi" ), 1048576. },
365 { wxS( "gi" ), 1073741824. },
366 { wxS( "ti" ), 1099511627776. } };
367
368
370 COMBINED_MATCHER_CONTEXT aContext ) :
371 m_pattern( aPattern )
372{
373 switch( aContext )
374 {
375 case CTX_LIBITEM:
376 // Whatever syntax users prefer, it shall be matched.
377 AddMatcher( aPattern, std::make_unique<EDA_PATTERN_MATCH_REGEX>() );
378 AddMatcher( aPattern, std::make_unique<EDA_PATTERN_MATCH_WILDCARD>() );
379 AddMatcher( aPattern, std::make_unique<EDA_PATTERN_MATCH_RELATIONAL>() );
380 // If any of the above matchers couldn't be created because the pattern
381 // syntax does not match, the substring will try its best.
382 AddMatcher( aPattern, std::make_unique<EDA_PATTERN_MATCH_SUBSTR>() );
383 break;
384
385 case CTX_NETCLASS:
386 AddMatcher( aPattern, std::make_unique<EDA_PATTERN_MATCH_REGEX_EXPLICIT>() );
387 AddMatcher( aPattern, std::make_unique<EDA_PATTERN_MATCH_WILDCARD_EXPLICIT>() );
388 break;
389
390 case CTX_SIGNAL:
391 AddMatcher( aPattern, std::make_unique<EDA_PATTERN_MATCH_REGEX>() );
392 AddMatcher( aPattern, std::make_unique<EDA_PATTERN_MATCH_WILDCARD>() );
393 AddMatcher( aPattern, std::make_unique<EDA_PATTERN_MATCH_SUBSTR>() );
394 break;
395 }
396}
397
398
399bool EDA_COMBINED_MATCHER::Find( const wxString& aTerm, int& aMatchersTriggered, int& aPosition )
400{
401 aPosition = EDA_PATTERN_NOT_FOUND;
402 aMatchersTriggered = 0;
403
404 for( const std::unique_ptr<EDA_PATTERN_MATCH>& matcher : m_matchers )
405 {
406 EDA_PATTERN_MATCH::FIND_RESULT local_find = matcher->Find( aTerm );
407
408 if( local_find )
409 {
410 aMatchersTriggered += 1;
411
412 if( local_find.start < aPosition || aPosition == EDA_PATTERN_NOT_FOUND )
413 aPosition = local_find.start;
414 }
415 }
416
417 return aPosition != EDA_PATTERN_NOT_FOUND;
418}
419
420
421wxString const& EDA_COMBINED_MATCHER::GetPattern() const
422{
423 return m_pattern;
424}
425
426
427void EDA_COMBINED_MATCHER::AddMatcher( const wxString &aPattern,
428 std::unique_ptr<EDA_PATTERN_MATCH> aMatcher )
429{
430 if ( aMatcher->SetPattern( aPattern ) )
431 m_matchers.push_back( std::move( aMatcher ) );
432}
std::vector< std::unique_ptr< EDA_PATTERN_MATCH > > m_matchers
void AddMatcher(const wxString &aPattern, std::unique_ptr< EDA_PATTERN_MATCH > aMatcher)
EDA_COMBINED_MATCHER(const wxString &aPattern, COMBINED_MATCHER_CONTEXT aContext)
bool Find(const wxString &aTerm, int &aMatchersTriggered, int &aPosition)
const wxString & GetPattern() const
virtual bool SetPattern(const wxString &aPattern) override
Set the pattern against which candidates will be matched.
virtual wxString const & GetPattern() const override
Return the pattern passed to SetPattern().
virtual FIND_RESULT Find(const wxString &aCandidate) const override
Return the location and possibly length of a match iff a given candidate string matches the set patte...
virtual bool SetPattern(const wxString &aPattern) override
Set the pattern against which candidates will be matched.
virtual bool SetPattern(const wxString &aPattern) override
Set the pattern against which candidates will be matched.
int FindOne(const wxString &aCandidate) const
virtual FIND_RESULT Find(const wxString &aCandidate) const override
Return the location and possibly length of a match iff a given candidate string matches the set patte...
virtual wxString const & GetPattern() const override
Return the pattern passed to SetPattern().
static const std::map< wxString, double > m_units
virtual wxString const & GetPattern() const override
Return the pattern passed to SetPattern().
virtual bool SetPattern(const wxString &aPattern) override
Set the pattern against which candidates will be matched.
virtual FIND_RESULT Find(const wxString &aCandidate) const override
Return the location and possibly length of a match iff a given candidate string matches the set patte...
virtual bool SetPattern(const wxString &aPattern) override
Set the pattern against which candidates will be matched.
virtual FIND_RESULT Find(const wxString &aCandidate) const override
Return the location and possibly length of a match iff a given candidate string matches the set patte...
virtual bool SetPattern(const wxString &aPattern) override
Set the pattern against which candidates will be matched.
virtual wxString const & GetPattern() const override
Return the pattern passed to SetPattern().
Context class to set wx loglevel for a block, and always restore it at the end.
WX_LOGLEVEL_CONTEXT(wxLogLevel level)
Abstract pattern-matching tool and implementations.
static const int EDA_PATTERN_NOT_FOUND
COMBINED_MATCHER_CONTEXT
@ CTX_SIGNAL
@ CTX_NETCLASS
@ CTX_LIBITEM