Files
OpenSTA/util/PatternMatch.cc
T

209 lines
5.2 KiB
C++
Raw Normal View History

2018-09-28 08:54:21 -07:00
// OpenSTA, Static Timing Analyzer
2026-03-10 13:21:17 -07:00
// Copyright (c) 2026, Parallax Software, Inc.
2018-09-28 08:54:21 -07:00
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
2022-01-04 10:17:08 -07:00
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
2018-09-28 08:54:21 -07:00
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
2022-01-04 10:17:08 -07:00
// along with this program. If not, see <https://www.gnu.org/licenses/>.
2025-01-21 18:54:33 -07:00
//
// The origin of this software must not be misrepresented; you must not
// claim that you wrote the original software.
//
// Altered source versions must be plainly marked as such, and must not be
// misrepresented as being the original software.
//
// This notice may not be removed or altered from any source distribution.
2018-09-28 08:54:21 -07:00
2020-04-05 14:53:44 -07:00
#include "PatternMatch.hh"
2026-04-13 14:58:16 -07:00
2026-03-28 19:13:35 -07:00
#include <cctype>
2018-09-28 08:54:21 -07:00
#include <tcl.h>
2020-04-05 11:35:51 -07:00
2018-09-28 08:54:21 -07:00
namespace sta {
2026-03-28 19:13:35 -07:00
PatternMatch::PatternMatch(std::string_view pattern,
2026-01-03 16:59:35 -08:00
bool is_regexp,
bool nocase,
Tcl_Interp *interp) :
2018-09-28 08:54:21 -07:00
pattern_(pattern),
is_regexp_(is_regexp),
nocase_(nocase),
interp_(interp),
2019-03-12 17:25:53 -07:00
regexp_(nullptr)
2018-09-28 08:54:21 -07:00
{
if (is_regexp_)
compileRegexp();
}
2026-03-28 19:13:35 -07:00
PatternMatch::PatternMatch(std::string_view pattern) :
2018-09-28 08:54:21 -07:00
pattern_(pattern),
is_regexp_(false),
nocase_(false),
2019-03-12 17:25:53 -07:00
interp_(nullptr),
regexp_(nullptr)
2018-09-28 08:54:21 -07:00
{
}
2026-03-28 19:13:35 -07:00
PatternMatch::PatternMatch(std::string_view pattern,
2026-01-03 16:59:35 -08:00
const PatternMatch *inherit_from) :
2018-09-28 08:54:21 -07:00
pattern_(pattern),
is_regexp_(inherit_from->is_regexp_),
nocase_(inherit_from->nocase_),
interp_(inherit_from->interp_),
2019-03-12 17:25:53 -07:00
regexp_(nullptr)
2018-09-28 08:54:21 -07:00
{
if (is_regexp_)
compileRegexp();
}
void
PatternMatch::compileRegexp()
{
int flags = TCL_REG_ADVANCED;
if (nocase_)
flags |= TCL_REG_NOCASE;
2026-03-02 12:13:13 -08:00
std::string anchored_pattern;
2018-09-28 08:54:21 -07:00
anchored_pattern += '^';
anchored_pattern += pattern_;
anchored_pattern += '$';
Tcl_Obj *pattern_obj = Tcl_NewStringObj(anchored_pattern.c_str(),
2026-01-03 16:59:35 -08:00
anchored_pattern.size());
Tcl_IncrRefCount(pattern_obj);
2018-09-28 08:54:21 -07:00
regexp_ = Tcl_GetRegExpFromObj(interp_, pattern_obj, flags);
Tcl_DecrRefCount(pattern_obj);
2019-03-12 17:25:53 -07:00
if (regexp_ == nullptr && interp_)
2018-09-28 08:54:21 -07:00
throw RegexpCompileError(pattern_);
}
static bool
2026-03-28 19:13:35 -07:00
regexpWildcards(std::string_view pattern)
2018-09-28 08:54:21 -07:00
{
2026-03-28 19:13:35 -07:00
return pattern.find_first_of(".+*?[]") != std::string_view::npos;
2018-09-28 08:54:21 -07:00
}
bool
PatternMatch::hasWildcards() const
{
if (is_regexp_)
return regexpWildcards(pattern_);
else
return patternWildcards(pattern_);
}
2023-03-26 06:34:36 -07:00
bool
2026-03-28 19:13:35 -07:00
PatternMatch::match(std::string_view str) const
2018-09-28 08:54:21 -07:00
{
2026-03-28 19:13:35 -07:00
if (regexp_) {
std::string buf(str);
const char *cstr = buf.c_str();
return Tcl_RegExpExec(nullptr, regexp_, cstr, cstr) == 1;
}
return patternMatch(pattern_, str);
2018-09-28 08:54:21 -07:00
}
bool
2026-03-28 19:13:35 -07:00
PatternMatch::matchNoCase(std::string_view str) const
2018-09-28 08:54:21 -07:00
{
2026-03-28 19:13:35 -07:00
if (regexp_) {
std::string buf(str);
const char *cstr = buf.c_str();
2026-04-13 14:58:16 -07:00
return Tcl_RegExpExec(nullptr, regexp_, cstr, cstr) == 1;
2026-03-28 19:13:35 -07:00
}
return patternMatchNoCase(pattern_, str, nocase_);
2018-09-28 08:54:21 -07:00
}
////////////////////////////////////////////////////////////////
2026-04-13 14:58:16 -07:00
RegexpCompileError::RegexpCompileError(std::string_view pattern)
2018-09-28 08:54:21 -07:00
{
2020-04-06 22:19:50 -07:00
error_ = "TCL failed to compile regular expression '";
2026-03-28 19:13:35 -07:00
error_.append(pattern.data(), pattern.size());
2020-04-06 22:33:06 -07:00
error_ += "'.";
2018-09-28 08:54:21 -07:00
}
const char *
2020-02-15 18:10:24 -07:00
RegexpCompileError::what() const noexcept
2018-09-28 08:54:21 -07:00
{
2020-04-06 22:19:50 -07:00
return error_.c_str();
2018-09-28 08:54:21 -07:00
}
////////////////////////////////////////////////////////////////
bool
2026-03-28 19:13:35 -07:00
patternMatch(std::string_view pattern,
std::string_view str)
{
size_t pi = 0;
size_t si = 0;
while (pi < pattern.size() && si < str.size()
&& (str[si] == pattern[pi] || pattern[pi] == '?')) {
pi++;
si++;
2018-09-28 08:54:21 -07:00
}
2026-03-28 19:13:35 -07:00
if (pi == pattern.size() && si == str.size())
2018-09-28 08:54:21 -07:00
return true;
2026-03-28 19:13:35 -07:00
if (pi < pattern.size() && pattern[pi] == '*') {
if (pi + 1 == pattern.size())
2018-09-28 08:54:21 -07:00
return true;
2026-03-28 19:13:35 -07:00
while (si < str.size()) {
if (patternMatch(pattern.substr(pi + 1), str.substr(si)))
2026-01-03 16:59:35 -08:00
return true;
2026-03-28 19:13:35 -07:00
si++;
2018-09-28 08:54:21 -07:00
}
}
return false;
}
2026-03-28 19:13:35 -07:00
static bool
equalCase(char s,
char p,
bool nocase)
2018-09-28 08:54:21 -07:00
{
return nocase
2026-03-28 19:13:35 -07:00
? std::tolower(static_cast<unsigned char>(s))
== std::tolower(static_cast<unsigned char>(p))
2018-09-28 08:54:21 -07:00
: s == p;
}
bool
2026-03-28 19:13:35 -07:00
patternMatchNoCase(std::string_view pattern,
std::string_view str,
2026-01-03 16:59:35 -08:00
bool nocase)
2018-09-28 08:54:21 -07:00
{
2026-03-28 19:13:35 -07:00
size_t pi = 0;
size_t si = 0;
while (pi < pattern.size() && si < str.size()
&& (equalCase(str[si], pattern[pi], nocase) || pattern[pi] == '?')) {
pi++;
si++;
2018-09-28 08:54:21 -07:00
}
2026-03-28 19:13:35 -07:00
if (pi == pattern.size() && si == str.size())
2018-09-28 08:54:21 -07:00
return true;
2026-03-28 19:13:35 -07:00
if (pi < pattern.size() && pattern[pi] == '*') {
if (pi + 1 == pattern.size())
2018-09-28 08:54:21 -07:00
return true;
2026-03-28 19:13:35 -07:00
while (si < str.size()) {
if (patternMatchNoCase(pattern.substr(pi + 1), str.substr(si), nocase))
2026-01-03 16:59:35 -08:00
return true;
2026-03-28 19:13:35 -07:00
si++;
2018-09-28 08:54:21 -07:00
}
}
return false;
}
bool
2026-03-28 19:13:35 -07:00
patternWildcards(std::string_view pattern)
2018-09-28 08:54:21 -07:00
{
2026-03-28 19:13:35 -07:00
return pattern.find_first_of("*?") != std::string_view::npos;
2018-09-28 08:54:21 -07:00
}
2026-04-13 14:58:16 -07:00
} // namespace sta