You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

426 lines
11 KiB

3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
  1. /*
  2. * This program source code file is part of KiCad, a free EDA CAD application.
  3. *
  4. * Copyright (C) 2015-2017 Chris Pavlina <pavlina.chris@gmail.com>
  5. * Copyright (C) 2015-2022 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. #include <eda_pattern_match.h>
  25. #include <wx/log.h>
  26. #include <wx/tokenzr.h>
  27. #include <algorithm>
  28. #include <climits>
  29. bool EDA_PATTERN_MATCH_SUBSTR::SetPattern( const wxString& aPattern )
  30. {
  31. m_pattern = aPattern;
  32. return true;
  33. }
  34. wxString const& EDA_PATTERN_MATCH_SUBSTR::GetPattern() const
  35. {
  36. return m_pattern;
  37. }
  38. EDA_PATTERN_MATCH::FIND_RESULT EDA_PATTERN_MATCH_SUBSTR::Find( const wxString& aCandidate ) const
  39. {
  40. int loc = aCandidate.Find( m_pattern );
  41. if( loc == wxNOT_FOUND )
  42. return {};
  43. else
  44. return { loc, static_cast<int>( m_pattern.size() ) };
  45. }
  46. /**
  47. * Context class to set wx loglevel for a block, and always restore it at the end.
  48. */
  49. class WX_LOGLEVEL_CONTEXT
  50. {
  51. wxLogLevel m_old_level;
  52. public:
  53. WX_LOGLEVEL_CONTEXT( wxLogLevel level )
  54. {
  55. m_old_level = wxLog::GetLogLevel();
  56. wxLog::SetLogLevel( level );
  57. }
  58. ~WX_LOGLEVEL_CONTEXT()
  59. {
  60. wxLog::SetLogLevel( m_old_level );
  61. }
  62. };
  63. bool EDA_PATTERN_MATCH_REGEX::SetPattern( const wxString& aPattern )
  64. {
  65. m_pattern = aPattern;
  66. // Evil and undocumented: wxRegEx::Compile calls wxLogError on error, even
  67. // though it promises to just return false. Silence the error.
  68. WX_LOGLEVEL_CONTEXT ctx( wxLOG_FatalError );
  69. return m_regex.Compile( aPattern, wxRE_ADVANCED );
  70. }
  71. bool EDA_PATTERN_MATCH_REGEX_EXPLICIT::SetPattern( const wxString& aPattern )
  72. {
  73. wxString pattern( aPattern );
  74. if( !pattern.StartsWith( wxT( "^" ) ) )
  75. pattern = wxT( "^" ) + pattern;
  76. if( !pattern.EndsWith( wxT( "$" ) ) )
  77. pattern += wxT( "$" );
  78. return EDA_PATTERN_MATCH_REGEX::SetPattern( pattern );
  79. }
  80. wxString const& EDA_PATTERN_MATCH_REGEX::GetPattern() const
  81. {
  82. return m_pattern;
  83. }
  84. EDA_PATTERN_MATCH::FIND_RESULT EDA_PATTERN_MATCH_REGEX::Find( const wxString& aCandidate ) const
  85. {
  86. if( m_regex.IsValid() )
  87. {
  88. if( m_regex.Matches( aCandidate ) )
  89. {
  90. size_t start, len;
  91. m_regex.GetMatch( &start, &len, 0 );
  92. return { static_cast<int>( std::min( start, static_cast<size_t>( INT_MAX ) ) ),
  93. static_cast<int>( std::min( len, static_cast<size_t>( INT_MAX ) ) ) };
  94. }
  95. else
  96. {
  97. return {};
  98. }
  99. }
  100. else
  101. {
  102. int loc = aCandidate.Find( m_pattern );
  103. if( loc == wxNOT_FOUND )
  104. return {};
  105. else
  106. return { loc, static_cast<int>( m_pattern.size() ) };
  107. }
  108. }
  109. bool EDA_PATTERN_MATCH_WILDCARD::SetPattern( const wxString& aPattern )
  110. {
  111. m_wildcard_pattern = aPattern;
  112. // Compile the wildcard string to a regular expression
  113. wxString regex;
  114. regex.Alloc( 2 * aPattern.Length() ); // no need to keep resizing, we know the size roughly
  115. const wxString to_replace = wxT( ".*+?^${}()|[]/\\" );
  116. for( wxString::const_iterator it = aPattern.begin(); it < aPattern.end(); ++it )
  117. {
  118. wxUniChar c = *it;
  119. if( c == '?' )
  120. {
  121. regex += wxT( "." );
  122. }
  123. else if( c == '*' )
  124. {
  125. regex += wxT( ".*" );
  126. }
  127. else if( to_replace.Find( c ) != wxNOT_FOUND )
  128. {
  129. regex += "\\";
  130. regex += c;
  131. }
  132. else
  133. {
  134. regex += c;
  135. }
  136. }
  137. return EDA_PATTERN_MATCH_REGEX::SetPattern( regex );
  138. }
  139. wxString const& EDA_PATTERN_MATCH_WILDCARD::GetPattern() const
  140. {
  141. return m_wildcard_pattern;
  142. }
  143. EDA_PATTERN_MATCH::FIND_RESULT EDA_PATTERN_MATCH_WILDCARD::Find( const wxString& aCandidate ) const
  144. {
  145. return EDA_PATTERN_MATCH_REGEX::Find( aCandidate );
  146. }
  147. bool EDA_PATTERN_MATCH_WILDCARD_EXPLICIT::SetPattern( const wxString& aPattern )
  148. {
  149. m_wildcard_pattern = aPattern;
  150. // Compile the wildcard string to a regular expression
  151. wxString regex;
  152. regex.Alloc( 2 * aPattern.Length() ); // no need to keep resizing, we know the size roughly
  153. const wxString to_replace = wxT( ".*+?^${}()|[]/\\" );
  154. regex += wxT( "^" );
  155. for( wxString::const_iterator it = aPattern.begin(); it < aPattern.end(); ++it )
  156. {
  157. wxUniChar c = *it;
  158. if( c == '?' )
  159. {
  160. regex += wxT( "." );
  161. }
  162. else if( c == '*' )
  163. {
  164. regex += wxT( ".*" );
  165. }
  166. else if( to_replace.Find( c ) != wxNOT_FOUND )
  167. {
  168. regex += "\\";
  169. regex += c;
  170. }
  171. else
  172. {
  173. regex += c;
  174. }
  175. }
  176. regex += wxT( "$" );
  177. return EDA_PATTERN_MATCH_REGEX::SetPattern( regex );
  178. }
  179. bool EDA_PATTERN_MATCH_RELATIONAL::SetPattern( const wxString& aPattern )
  180. {
  181. bool matches = m_regex_search.Matches( aPattern );
  182. if( !matches || m_regex_search.GetMatchCount() < 5 )
  183. return false;
  184. m_pattern = aPattern;
  185. wxString key = m_regex_search.GetMatch( aPattern, 1 );
  186. wxString rel = m_regex_search.GetMatch( aPattern, 2 );
  187. wxString val = m_regex_search.GetMatch( aPattern, 3 );
  188. wxString unit = m_regex_search.GetMatch( aPattern, 4 );
  189. m_key = key.Lower();
  190. if( rel == "<" )
  191. m_relation = LT;
  192. else if( rel == "<=" )
  193. m_relation = LE;
  194. else if( rel == "=" )
  195. m_relation = EQ;
  196. else if( rel == ">=" )
  197. m_relation = GE;
  198. else if( rel == ">" )
  199. m_relation = GT;
  200. else
  201. return false;
  202. if( val == "" )
  203. {
  204. // Matching on empty values keeps the match list from going empty when the user
  205. // types the relational operator character, which helps prevent confusion.
  206. m_relation = ANY;
  207. }
  208. else if( !val.ToCDouble( &m_value ) )
  209. {
  210. return false;
  211. }
  212. auto unit_it = m_units.find( unit.Lower() );
  213. if( unit_it != m_units.end() )
  214. m_value *= unit_it->second;
  215. else
  216. return false;
  217. m_pattern = aPattern;
  218. return true;
  219. }
  220. wxString const& EDA_PATTERN_MATCH_RELATIONAL::GetPattern() const
  221. {
  222. return m_pattern;
  223. }
  224. EDA_PATTERN_MATCH::FIND_RESULT EDA_PATTERN_MATCH_RELATIONAL::Find( const wxString& aCandidate ) const
  225. {
  226. wxStringTokenizer tokenizer( aCandidate );
  227. size_t lastpos = 0;
  228. while( tokenizer.HasMoreTokens() )
  229. {
  230. const wxString token = tokenizer.GetNextToken();
  231. int found_delta = FindOne( token );
  232. if( found_delta != EDA_PATTERN_NOT_FOUND )
  233. {
  234. size_t found = (size_t) found_delta + lastpos;
  235. return { static_cast<int>( std::min( found, static_cast<size_t>( INT_MAX ) ) ), 0 };
  236. }
  237. lastpos = tokenizer.GetPosition();
  238. }
  239. return {};
  240. }
  241. int EDA_PATTERN_MATCH_RELATIONAL::FindOne( const wxString& aCandidate ) const
  242. {
  243. bool matches = m_regex_description.Matches( aCandidate );
  244. if( !matches )
  245. return EDA_PATTERN_NOT_FOUND;
  246. size_t start, len;
  247. m_regex_description.GetMatch( &start, &len, 0 );
  248. wxString key = m_regex_description.GetMatch( aCandidate, 1 );
  249. wxString val = m_regex_description.GetMatch( aCandidate, 2 );
  250. wxString unit = m_regex_description.GetMatch( aCandidate, 3 );
  251. int istart = ( start > INT_MAX ) ? INT_MAX : start;
  252. if( key.Lower() != m_key )
  253. return EDA_PATTERN_NOT_FOUND;
  254. double val_parsed;
  255. if( !val.ToCDouble( &val_parsed ) )
  256. return EDA_PATTERN_NOT_FOUND;
  257. auto unit_it = m_units.find( unit.Lower() );
  258. if( unit_it != m_units.end() )
  259. val_parsed *= unit_it->second;
  260. switch( m_relation )
  261. {
  262. case LT: return val_parsed < m_value ? istart : EDA_PATTERN_NOT_FOUND;
  263. case LE: return val_parsed <= m_value ? istart : EDA_PATTERN_NOT_FOUND;
  264. case EQ: return val_parsed == m_value ? istart : EDA_PATTERN_NOT_FOUND;
  265. case GE: return val_parsed >= m_value ? istart : EDA_PATTERN_NOT_FOUND;
  266. case GT: return val_parsed > m_value ? istart : EDA_PATTERN_NOT_FOUND;
  267. case ANY: return istart;
  268. default: return EDA_PATTERN_NOT_FOUND;
  269. }
  270. }
  271. wxRegEx EDA_PATTERN_MATCH_RELATIONAL::m_regex_description(
  272. R"((\w+)[=:]([-+]?[\d.]+)(\w*))", wxRE_ADVANCED );
  273. wxRegEx EDA_PATTERN_MATCH_RELATIONAL::m_regex_search(
  274. R"(^(\w+)(<|<=|=|>=|>)([-+]?[\d.]*)(\w*)$)", wxRE_ADVANCED );
  275. const std::map<wxString, double> EDA_PATTERN_MATCH_RELATIONAL::m_units = {
  276. { "p", 1e-12 },
  277. { "n", 1e-9 },
  278. { "u", 1e-6 },
  279. { "m", 1e-3 },
  280. { "", 1. },
  281. { "k", 1e3 },
  282. { "meg",1e6 },
  283. { "g", 1e9 },
  284. { "t", 1e12 },
  285. { "ki", 1024. },
  286. { "mi", 1048576. },
  287. { "gi", 1073741824. },
  288. { "ti", 1099511627776. } };
  289. EDA_COMBINED_MATCHER::EDA_COMBINED_MATCHER( const wxString& aPattern,
  290. COMBINED_MATCHER_CONTEXT aContext ) :
  291. m_pattern( aPattern )
  292. {
  293. switch( aContext )
  294. {
  295. case CTX_LIBITEM:
  296. // Whatever syntax users prefer, it shall be matched.
  297. AddMatcher( aPattern, std::make_unique<EDA_PATTERN_MATCH_REGEX>() );
  298. AddMatcher( aPattern, std::make_unique<EDA_PATTERN_MATCH_WILDCARD>() );
  299. AddMatcher( aPattern, std::make_unique<EDA_PATTERN_MATCH_RELATIONAL>() );
  300. // If any of the above matchers couldn't be created because the pattern
  301. // syntax does not match, the substring will try its best.
  302. AddMatcher( aPattern, std::make_unique<EDA_PATTERN_MATCH_SUBSTR>() );
  303. break;
  304. case CTX_NETCLASS:
  305. AddMatcher( aPattern, std::make_unique<EDA_PATTERN_MATCH_REGEX_EXPLICIT>() );
  306. AddMatcher( aPattern, std::make_unique<EDA_PATTERN_MATCH_WILDCARD_EXPLICIT>() );
  307. break;
  308. }
  309. }
  310. bool EDA_COMBINED_MATCHER::Find( const wxString& aTerm, int& aMatchersTriggered, int& aPosition )
  311. {
  312. aPosition = EDA_PATTERN_NOT_FOUND;
  313. aMatchersTriggered = 0;
  314. for( const std::unique_ptr<EDA_PATTERN_MATCH>& matcher : m_matchers )
  315. {
  316. EDA_PATTERN_MATCH::FIND_RESULT local_find = matcher->Find( aTerm );
  317. if( local_find )
  318. {
  319. aMatchersTriggered += 1;
  320. if( local_find.start < aPosition || aPosition == EDA_PATTERN_NOT_FOUND )
  321. aPosition = local_find.start;
  322. }
  323. }
  324. return aPosition != EDA_PATTERN_NOT_FOUND;
  325. }
  326. wxString const& EDA_COMBINED_MATCHER::GetPattern() const
  327. {
  328. return m_pattern;
  329. }
  330. void EDA_COMBINED_MATCHER::AddMatcher( const wxString &aPattern,
  331. std::unique_ptr<EDA_PATTERN_MATCH> aMatcher )
  332. {
  333. if ( aMatcher->SetPattern( aPattern ) )
  334. m_matchers.push_back( std::move( aMatcher ) );
  335. }