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.

402 lines
11 KiB

  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-2019 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. wxString const& EDA_PATTERN_MATCH_REGEX::GetPattern() const
  72. {
  73. return m_pattern;
  74. }
  75. EDA_PATTERN_MATCH::FIND_RESULT EDA_PATTERN_MATCH_REGEX::Find( const wxString& aCandidate ) const
  76. {
  77. if( m_regex.IsValid() )
  78. {
  79. if( m_regex.Matches( aCandidate ) )
  80. {
  81. size_t start, len;
  82. m_regex.GetMatch( &start, &len, 0 );
  83. return { static_cast<int>( std::min( start, static_cast<size_t>( INT_MAX ) ) ),
  84. static_cast<int>( std::min( len, static_cast<size_t>( INT_MAX ) ) ) };
  85. }
  86. else
  87. {
  88. return {};
  89. }
  90. }
  91. else
  92. {
  93. int loc = aCandidate.Find( m_pattern );
  94. if( loc == wxNOT_FOUND )
  95. return {};
  96. else
  97. return { loc, static_cast<int>( m_pattern.size() ) };
  98. }
  99. }
  100. bool EDA_PATTERN_MATCH_WILDCARD::SetPattern( const wxString& aPattern )
  101. {
  102. m_wildcard_pattern = aPattern;
  103. // Compile the wildcard string to a regular expression
  104. wxString regex;
  105. regex.Alloc( 2 * aPattern.Length() ); // no need to keep resizing, we know the size roughly
  106. const wxString to_replace = wxT( ".*+?^${}()|[]/\\" );
  107. for( wxString::const_iterator it = aPattern.begin(); it < aPattern.end(); ++it )
  108. {
  109. wxUniChar c = *it;
  110. if( c == '?' )
  111. {
  112. regex += wxT( "." );
  113. }
  114. else if( c == '*' )
  115. {
  116. regex += wxT( ".*" );
  117. }
  118. else if( to_replace.Find( c ) != wxNOT_FOUND )
  119. {
  120. regex += "\\";
  121. regex += c;
  122. }
  123. else
  124. {
  125. regex += c;
  126. }
  127. }
  128. return EDA_PATTERN_MATCH_REGEX::SetPattern( regex );
  129. }
  130. wxString const& EDA_PATTERN_MATCH_WILDCARD::GetPattern() const
  131. {
  132. return m_wildcard_pattern;
  133. }
  134. EDA_PATTERN_MATCH::FIND_RESULT EDA_PATTERN_MATCH_WILDCARD::Find( const wxString& aCandidate ) const
  135. {
  136. return EDA_PATTERN_MATCH_REGEX::Find( aCandidate );
  137. }
  138. bool EDA_PATTERN_MATCH_WILDCARD_EXPLICIT::SetPattern( const wxString& aPattern )
  139. {
  140. m_wildcard_pattern = aPattern;
  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. const wxString to_replace = wxT( ".*+?^${}()|[]/\\" );
  145. regex += wxT( "^" );
  146. for( wxString::const_iterator it = aPattern.begin(); it < aPattern.end(); ++it )
  147. {
  148. wxUniChar c = *it;
  149. if( c == '?' )
  150. {
  151. regex += wxT( "." );
  152. }
  153. else if( c == '*' )
  154. {
  155. regex += wxT( ".*" );
  156. }
  157. else if( to_replace.Find( c ) != wxNOT_FOUND )
  158. {
  159. regex += "\\";
  160. regex += c;
  161. }
  162. else
  163. {
  164. regex += c;
  165. }
  166. }
  167. regex += wxT( "$" );
  168. return EDA_PATTERN_MATCH_REGEX::SetPattern( regex );
  169. }
  170. bool EDA_PATTERN_MATCH_RELATIONAL::SetPattern( const wxString& aPattern )
  171. {
  172. bool matches = m_regex_search.Matches( aPattern );
  173. if( !matches || m_regex_search.GetMatchCount() < 5 )
  174. return false;
  175. m_pattern = aPattern;
  176. wxString key = m_regex_search.GetMatch( aPattern, 1 );
  177. wxString rel = m_regex_search.GetMatch( aPattern, 2 );
  178. wxString val = m_regex_search.GetMatch( aPattern, 3 );
  179. wxString unit = m_regex_search.GetMatch( aPattern, 4 );
  180. m_key = key.Lower();
  181. if( rel == "<" )
  182. m_relation = LT;
  183. else if( rel == "<=" )
  184. m_relation = LE;
  185. else if( rel == "=" )
  186. m_relation = EQ;
  187. else if( rel == ">=" )
  188. m_relation = GE;
  189. else if( rel == ">" )
  190. m_relation = GT;
  191. else
  192. return false;
  193. if( val == "" )
  194. {
  195. // Matching on empty values keeps the match list from going empty when
  196. // the user types the relational operator character, which helps prevent
  197. // confusion.
  198. m_relation = NONE;
  199. }
  200. else if( !val.ToCDouble( &m_value ) )
  201. return false;
  202. auto unit_it = m_units.find( unit.Lower() );
  203. if( unit_it != m_units.end() )
  204. m_value *= unit_it->second;
  205. else
  206. return false;
  207. m_pattern = aPattern;
  208. return true;
  209. }
  210. wxString const& EDA_PATTERN_MATCH_RELATIONAL::GetPattern() const
  211. {
  212. return m_pattern;
  213. }
  214. EDA_PATTERN_MATCH::FIND_RESULT EDA_PATTERN_MATCH_RELATIONAL::Find(
  215. const wxString& aCandidate ) const
  216. {
  217. wxStringTokenizer tokenizer( aCandidate );
  218. size_t lastpos = 0;
  219. while( tokenizer.HasMoreTokens() )
  220. {
  221. const wxString token = tokenizer.GetNextToken();
  222. int found_delta = FindOne( token );
  223. if( found_delta != EDA_PATTERN_NOT_FOUND )
  224. {
  225. size_t found = (size_t) found_delta + lastpos;
  226. return { static_cast<int>( std::min( found, static_cast<size_t>( INT_MAX ) ) ), 0 };
  227. }
  228. lastpos = tokenizer.GetPosition();
  229. }
  230. return {};
  231. }
  232. int EDA_PATTERN_MATCH_RELATIONAL::FindOne( const wxString& aCandidate ) const
  233. {
  234. bool matches = m_regex_description.Matches( aCandidate );
  235. if( !matches )
  236. return EDA_PATTERN_NOT_FOUND;
  237. size_t start, len;
  238. m_regex_description.GetMatch( &start, &len, 0 );
  239. wxString key = m_regex_description.GetMatch( aCandidate, 1 );
  240. wxString val = m_regex_description.GetMatch( aCandidate, 2 );
  241. wxString unit = m_regex_description.GetMatch( aCandidate, 3 );
  242. int istart = ( start > INT_MAX ) ? INT_MAX : start;
  243. if( key.Lower() != m_key )
  244. return EDA_PATTERN_NOT_FOUND;
  245. double val_parsed;
  246. if( !val.ToCDouble( &val_parsed ) )
  247. return EDA_PATTERN_NOT_FOUND;
  248. auto unit_it = m_units.find( unit.Lower() );
  249. if( unit_it != m_units.end() )
  250. val_parsed *= unit_it->second;
  251. switch( m_relation )
  252. {
  253. case LT: return val_parsed < m_value ? istart : EDA_PATTERN_NOT_FOUND;
  254. case LE: return val_parsed <= m_value ? istart : EDA_PATTERN_NOT_FOUND;
  255. case EQ: return val_parsed == m_value ? istart : EDA_PATTERN_NOT_FOUND;
  256. case GE: return val_parsed >= m_value ? istart : EDA_PATTERN_NOT_FOUND;
  257. case GT: return val_parsed > m_value ? istart : EDA_PATTERN_NOT_FOUND;
  258. case NONE: return istart;
  259. default: return EDA_PATTERN_NOT_FOUND;
  260. }
  261. }
  262. wxRegEx EDA_PATTERN_MATCH_RELATIONAL::m_regex_description(
  263. R"((\w+)[=:]([-+]?[\d.]+)(\w*))", wxRE_ADVANCED );
  264. wxRegEx EDA_PATTERN_MATCH_RELATIONAL::m_regex_search(
  265. R"(^(\w+)(<|<=|=|>=|>)([-+]?[\d.]*)(\w*)$)", wxRE_ADVANCED );
  266. const std::map<wxString, double> EDA_PATTERN_MATCH_RELATIONAL::m_units = {
  267. { "p", 1e-12 },
  268. { "n", 1e-9 },
  269. { "u", 1e-6 },
  270. { "m", 1e-3 },
  271. { "", 1. },
  272. { "k", 1e3 },
  273. { "meg",1e6 },
  274. { "g", 1e9 },
  275. { "t", 1e12 },
  276. { "ki", 1024. },
  277. { "mi", 1048576. },
  278. { "gi", 1073741824. },
  279. { "ti", 1099511627776. } };
  280. EDA_COMBINED_MATCHER::EDA_COMBINED_MATCHER( const wxString& aPattern )
  281. : m_pattern( aPattern )
  282. {
  283. // Whatever syntax users prefer, it shall be matched.
  284. AddMatcher( aPattern, std::make_unique<EDA_PATTERN_MATCH_REGEX>() );
  285. AddMatcher( aPattern, std::make_unique<EDA_PATTERN_MATCH_WILDCARD>() );
  286. AddMatcher( aPattern, std::make_unique<EDA_PATTERN_MATCH_RELATIONAL>() );
  287. // If any of the above matchers couldn't be created because the pattern
  288. // syntax does not match, the substring will try its best.
  289. AddMatcher( aPattern, std::make_unique<EDA_PATTERN_MATCH_SUBSTR>() );
  290. }
  291. bool EDA_COMBINED_MATCHER::Find( const wxString& aTerm, int& aMatchersTriggered, int& aPosition )
  292. {
  293. aPosition = EDA_PATTERN_NOT_FOUND;
  294. aMatchersTriggered = 0;
  295. for( auto const& matcher : m_matchers )
  296. {
  297. EDA_PATTERN_MATCH::FIND_RESULT local_find = matcher->Find( aTerm );
  298. if( local_find )
  299. {
  300. aMatchersTriggered += 1;
  301. if( local_find.start < aPosition || aPosition == EDA_PATTERN_NOT_FOUND )
  302. {
  303. aPosition = local_find.start;
  304. }
  305. }
  306. }
  307. return aPosition != EDA_PATTERN_NOT_FOUND;
  308. }
  309. wxString const& EDA_COMBINED_MATCHER::GetPattern() const
  310. {
  311. return m_pattern;
  312. }
  313. void EDA_COMBINED_MATCHER::AddMatcher(
  314. const wxString &aPattern,
  315. std::unique_ptr<EDA_PATTERN_MATCH> aMatcher )
  316. {
  317. if ( aMatcher->SetPattern( aPattern ) )
  318. {
  319. m_matchers.push_back( std::move( aMatcher ) );
  320. }
  321. }