ENH: reorganize regular expressions and add C++11 regex support

- new regExpCxx wrapper for C++11 regex support with drop-in
  compatibility with existing code.

- regExpPosix (was regExp), for future phase out in favour of regExpCxx.

- The regExp header will continue to be used for defining an
  appropriate typedef corresponding to the preferred implementation.
This commit is contained in:
Mark Olesen 2019-03-14 13:24:23 +01:00 committed by Andrew Heather
parent e0e0414726
commit 487877377d
17 changed files with 1170 additions and 330 deletions

View File

@ -1,3 +0,0 @@
Test-regex.C
EXE = $(FOAM_USER_APPBIN)/Test-regex

View File

@ -0,0 +1,3 @@
Test-regex1.C
EXE = $(FOAM_USER_APPBIN)/Test-regex1

View File

@ -2,10 +2,8 @@
========= | ========= |
\\ / F ield | OpenFOAM: The Open Source CFD Toolbox \\ / F ield | OpenFOAM: The Open Source CFD Toolbox
\\ / O peration | \\ / O peration |
\\ / A nd | Copyright (C) 2017-2018 OpenCFD Ltd. \\ / A nd | Copyright (C) 2017-2019 OpenCFD Ltd.
\\/ M anipulation | \\/ M anipulation |
-------------------------------------------------------------------------------
| Copyright (C) 2011-2016 OpenFOAM Foundation
------------------------------------------------------------------------------- -------------------------------------------------------------------------------
License License
This file is part of OpenFOAM. This file is part of OpenFOAM.
@ -28,13 +26,14 @@ Description
\*---------------------------------------------------------------------------*/ \*---------------------------------------------------------------------------*/
#include "argList.H"
#include "IOstreams.H" #include "IOstreams.H"
#include "IOobject.H"
#include "IFstream.H" #include "IFstream.H"
#include "regExp.H"
#include "SubStrings.H"
#include "Switch.H" #include "Switch.H"
#include "regExpCxx.H"
#include "regExpPosix.H"
using namespace Foam; using namespace Foam;
@ -63,14 +62,16 @@ struct regexTest
} }
}; };
// Needed for list output. Just treat everything as unequal. // Needed for list output. Just treat everything as unequal.
bool operator!=(const struct regexTest&, const struct regexTest&) bool operator!=(const struct regexTest&, const struct regexTest&)
{ {
return true; return true;
} }
// Simple output of match groups // Simple output of match groups
static Ostream& operator<<(Ostream& os, const regExp::results_type& sm) static Ostream& operator<<(Ostream& os, const regExpCxx::results_type& sm)
{ {
for (std::smatch::size_type i = 1; i < sm.size(); ++i) for (std::smatch::size_type i = 1; i < sm.size(); ++i)
{ {
@ -81,22 +82,144 @@ static Ostream& operator<<(Ostream& os, const regExp::results_type& sm)
} }
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // // Simple output of match groups
// Main program: static Ostream& operator<<(Ostream& os, const regExpPosix::results_type& sm)
int main(int argc, char *argv[])
{ {
List<regexTest> rawList(IFstream("testRegexps")()); for (std::smatch::size_type i = 1; i < sm.size(); ++i)
Info<< "Test expressions:" << rawList << endl; {
IOobject::writeDivider(Info) << endl; os << " " << sm.str(i);
}
regExp::results_type match; return os;
}
template<class RegexType>
void generalTests()
{
Info<< nl << "test regExp(const char*) ..." << endl;
string me("Mark");
// Expect some failures:
const bool throwingError = FatalError.throwExceptions();
try
{
// Handling of null strings
if (RegexType(nullptr).match(me))
{
Info<< "fail - matched: " << me << endl;
}
else
{
Info<< "pass - null pointer is no expression" << endl;
}
}
catch (const Foam::error& err)
{
Info<< "Caught FatalError " << err << nl << endl;
}
try
{
// Normal match
if (RegexType("[Mm]ar[ck]").match(me))
{
Info<< "pass - matched: " << me << endl;
}
else
{
Info<< "no match" << endl;
}
}
catch (const Foam::error& err)
{
Info<< "Caught FatalError " << err << nl << endl;
}
try
{
// Match ignore case
if (RegexType("mar[ck]", true).match(me))
{
Info<< "pass - matched: " << me << endl;
}
else
{
Info<< "no match" << endl;
}
}
catch (const Foam::error& err)
{
Info<< "Caught FatalError " << err << nl << endl;
}
try
{
// Embedded prefix for match ignore case
if (RegexType("(?i)mar[ck]").match(me))
{
Info<< "pass - matched: " << me << endl;
}
else
{
Info<< "no match" << endl;
}
}
catch (const Foam::error& err)
{
Info<< "Caught FatalError " << err << nl << endl;
}
try
{
// Handling of empty expression
if (RegexType("").match(me))
{
Info<< "fail - matched: " << me << endl;
}
else
{
Info<< "pass - no match on empty expression" << endl;
}
}
catch (const Foam::error& err)
{
Info<< "Caught FatalError " << err << nl << endl;
}
try
{
// Embedded prefix - but expression is empty
if (RegexType("(?i)").match(me))
{
Info<< "fail - matched: " << me << endl;
}
else
{
Info<< "pass - no match on empty expression" << endl;
}
}
catch (const Foam::error& err)
{
Info<< "Caught FatalError " << err << nl << endl;
}
FatalError.throwExceptions(throwingError);
}
template<class RegexType>
void testExpressions(const UList<regexTest>& tests)
{
typename RegexType::results_type match;
// Expect some failures: // Expect some failures:
const bool throwingError = FatalError.throwExceptions(); const bool throwingError = FatalError.throwExceptions();
// Report matches: // Report matches:
for (const auto& testseq : rawList) for (const auto& testseq : tests)
{ {
const bool expected = testseq.expected; const bool expected = testseq.expected;
const string& pat = testseq.pattern; const string& pat = testseq.pattern;
@ -105,7 +228,7 @@ int main(int argc, char *argv[])
Info<< "Test " << Switch(expected) << ": " Info<< "Test " << Switch(expected) << ": "
<< str << " =~ m/" << pat.c_str() << "/ == "; << str << " =~ m/" << pat.c_str() << "/ == ";
regExp re; RegexType re;
try try
{ {
@ -137,7 +260,7 @@ int main(int argc, char *argv[])
if (false) if (false)
{ {
regExp re2(std::move(re)); RegexType re2(std::move(re));
Info<<"move construct: " << re.exists() << "/" << re2.exists() Info<<"move construct: " << re.exists() << "/" << re2.exists()
<< endl; << endl;
@ -151,112 +274,76 @@ int main(int argc, char *argv[])
} }
} }
Info<< nl << "test regExp(const char*) ..." << endl;
string me("Mark");
try
{
// Handling of null strings
if (regExp(nullptr).match(me))
{
Info<< "fail - matched: " << me << endl;
}
else
{
Info<< "pass - null pointer is no expression" << endl;
}
}
catch (const Foam::error& err)
{
Info<< "Caught FatalError " << err << nl << endl;
}
try
{
// Normal match
if (regExp("[Mm]ar[ck]").match(me))
{
Info<< "pass - matched: " << me << endl;
}
else
{
Info<< "no match" << endl;
}
}
catch (const Foam::error& err)
{
Info<< "Caught FatalError " << err << nl << endl;
}
try
{
// Match ignore case
if (regExp("mar[ck]", true).match(me))
{
Info<< "pass - matched: " << me << endl;
}
else
{
Info<< "no match" << endl;
}
}
catch (const Foam::error& err)
{
Info<< "Caught FatalError " << err << nl << endl;
}
try
{
// Embedded prefix for match ignore case
if (regExp("(?i)mar[ck]").match(me))
{
Info<< "pass - matched: " << me << endl;
}
else
{
Info<< "no match" << endl;
}
}
catch (const Foam::error& err)
{
Info<< "Caught FatalError " << err << nl << endl;
}
try
{
// Handling of empty expression
if (regExp("").match(me))
{
Info<< "fail - matched: " << me << endl;
}
else
{
Info<< "pass - no match on empty expression" << endl;
}
}
catch (const Foam::error& err)
{
Info<< "Caught FatalError " << err << nl << endl;
}
try
{
// Embedded prefix - but expression is empty
if (regExp("(?i)").match(me))
{
Info<< "fail - matched: " << me << endl;
}
else
{
Info<< "pass - no match on empty expression" << endl;
}
}
catch (const Foam::error& err)
{
Info<< "Caught FatalError " << err << nl << endl;
}
FatalError.throwExceptions(throwingError); FatalError.throwExceptions(throwingError);
}
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
// Main program:
int main(int argc, char *argv[])
{
argList::noBanner();
argList::noFunctionObjects();
argList::noParallel();
argList::addBoolOption
(
"cxx",
"Test C++11 regular expressions"
);
argList::addBoolOption
(
"posix",
"Test POSIX regular expressions"
);
argList::addArgument("file");
argList::addArgument("...");
argList::addArgument("fileN");
argList::noMandatoryArgs();
#include "setRootCase.H"
if (!args.count({"cxx", "posix"}))
{
Info<< "Specified one or more of -cxx, -posix" << nl;
return 1;
}
if (args.size() < 2)
{
Info<< "No test files specified .. restrict to general tests" << nl;
if (args.found("cxx"))
{
generalTests<regExpCxx>();
}
if (args.found("posix"))
{
generalTests<regExpPosix>();
}
}
for (label argi = 1; argi < args.size(); ++argi)
{
List<regexTest> tests(IFstream(args[argi])());
Info<< "Test expressions:" << tests << endl;
IOobject::writeDivider(Info) << endl;
if (args.found("cxx"))
{
testExpressions<regExpCxx>(tests);
}
if (args.found("posix"))
{
testExpressions<regExpPosix>(tests);
}
}
Info<< "\nDone" << nl << endl; Info<< "\nDone" << nl << endl;

View File

@ -4,7 +4,7 @@ signals/sigInt.C
signals/sigQuit.C signals/sigQuit.C
signals/sigStopAtWriteNow.C signals/sigStopAtWriteNow.C
signals/sigWriteNow.C signals/sigWriteNow.C
regExp.C regExpPosix.C
timer.C timer.C
fileStat.C fileStat.C
POSIX.C POSIX.C

View File

@ -2,10 +2,8 @@
========= | ========= |
\\ / F ield | OpenFOAM: The Open Source CFD Toolbox \\ / F ield | OpenFOAM: The Open Source CFD Toolbox
\\ / O peration | \\ / O peration |
\\ / A nd | Copyright (C) 2017-2018 OpenCFD Ltd. \\ / A nd | Copyright (C) 2019 OpenCFD Ltd.
\\/ M anipulation | \\/ M anipulation |
-------------------------------------------------------------------------------
| Copyright (C) 2011-2017 OpenFOAM Foundation
------------------------------------------------------------------------------- -------------------------------------------------------------------------------
License License
This file is part of OpenFOAM. This file is part of OpenFOAM.
@ -23,176 +21,19 @@ License
You should have received a copy of the GNU General Public License You should have received a copy of the GNU General Public License
along with OpenFOAM. If not, see <http://www.gnu.org/licenses/>. along with OpenFOAM. If not, see <http://www.gnu.org/licenses/>.
Class Typedef
Foam::regExp Foam::regExp
Description Description
Wrapper around POSIX extended regular expressions. Selection of preferred regular expression implementation
The PCRE '(?i)' extension is provided to compile the regular expression
as being case-insensitive.
See also
The manpage regex(7) for more information about POSIX regular expressions.
These differ somewhat from \c Perl and \c sed regular expressions.
SourceFiles
regExpI.H
regExp.C
\*---------------------------------------------------------------------------*/ \*---------------------------------------------------------------------------*/
#ifndef regExp_H #ifndef regExp_H
#define regExp_H #define regExp_H
#include <regex.h> #include "regExpPosix.H"
#include <string> #include "regExpFwd.H"
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
namespace Foam
{
// Forward declarations
template<class String> class SubStrings;
/*---------------------------------------------------------------------------*\
Class regExp Declaration
\*---------------------------------------------------------------------------*/
class regExp
{
// Private data
//- Precompiled regular expression
regex_t* preg_;
public:
//- Type for matches
typedef SubStrings<std::string> results_type;
// Static Member Functions
//- Test if character appears to be a regular expression meta-character
// \return true if character is one of the following:
// - any character: '.' \n
// - quantifiers: '*', '+', '?' \n
// - grouping: '(', '|', ')' \n
// - range: '[', ']' \n
//
// \note The presence of '{', '}' regex bounds is not considered
inline static bool meta(char c);
// Constructors
//- Construct null
inline regExp();
//- Copy construct - disallowed
regExp(const regExp&) = delete;
//- Move construct
inline regExp(regExp&& rgx);
//- Construct from character array
inline explicit regExp(const char* pattern);
//- Construct from string
inline explicit regExp(const std::string& pattern);
//- Construct from character array, optionally ignore case
inline regExp(const char* pattern, bool ignoreCase);
//- Construct from string, optionally ignore case
inline regExp(const std::string& pattern, bool ignoreCase);
//- Destructor
inline ~regExp();
// Member functions
// Access
//- Return true if a precompiled expression does not exist
inline bool empty() const;
//- Return true if a precompiled expression exists
inline bool exists() const;
//- The number of capture groups for a non-empty expression
inline unsigned ngroups() const;
// Editing
//- Clear expression.
// \return True if expression had existed prior to the clear.
bool clear();
//- Swap contents
inline void swap(regExp& rgx);
//- Compile pattern into a regular expression, optionally ignore case.
// \return True if the pattern was compiled
bool set(const char* pattern, bool ignoreCase=false);
//- Compile pattern into a regular expression, optionally ignore case.
// \return True if the pattern was compiled
bool set(const std::string& pattern, bool ignoreCase=false);
// Matching/Searching
//- Find position within the text.
// \return The index where it begins or string::npos if not found
std::string::size_type find(const std::string& text) const;
//- True if the regex matches the entire text.
// The begin-of-line (^) and end-of-line ($) anchors are implicit
bool match(const std::string& text) const;
//- True if the regex matches the text, set the matches.
// The first group starts at index 1 (0 is the entire match).
// The begin-of-line (^) and end-of-line ($) anchors are implicit
bool match(const std::string& text, results_type& matches) const;
//- Return true if the regex was found within the text
inline bool search(const std::string& text) const;
// Member Operators
//- Perform match on text
inline bool operator()(const std::string& text) const;
//- Copy assignment - disallowed
void operator=(const regExp&) = delete;
//- Move assignment
inline void operator=(regExp&& rgx);
//- Assign and compile pattern from a character array.
// Matching is case sensitive.
inline void operator=(const char* pattern);
//- Assign and compile pattern from string.
// Matching is case sensitive.
inline void operator=(const std::string& pattern);
};
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
} // End namespace Foam
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
#include "regExpI.H"
#endif #endif

View File

@ -0,0 +1,49 @@
/*---------------------------------------------------------------------------*\
========= |
\\ / F ield | OpenFOAM: The Open Source CFD Toolbox
\\ / O peration |
\\ / A nd | Copyright (C) 2019 OpenCFD Ltd.
\\/ M anipulation |
-------------------------------------------------------------------------------
License
This file is part of OpenFOAM.
OpenFOAM 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.
OpenFOAM is distributed in the hope that it will be useful, but WITHOUT
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
for more details.
You should have received a copy of the GNU General Public License
along with OpenFOAM. If not, see <http://www.gnu.org/licenses/>.
Typedef
Foam::regExp
Description
Selection of preferred regular expression implementation
\*---------------------------------------------------------------------------*/
#ifndef regExpFwd_H
#define regExpFwd_H
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
namespace Foam
{
class regExpCxx;
class regExpPosix;
typedef regExpPosix regExp;
}
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
#endif
// ************************************************************************* //

View File

@ -25,22 +25,32 @@ License
\*---------------------------------------------------------------------------*/ \*---------------------------------------------------------------------------*/
#include "regExp.H" #include "regExpPosix.H"
#include "SubStrings.H" #include "SubStrings.H"
#include "error.H" #include "error.H"
// * * * * * * * * * * * * * * Static Data Members * * * * * * * * * * * * * //
int Foam::regExpPosix::grammar(0);
// * * * * * * * * * * * * * * * Local Functions * * * * * * * * * * * * * * // // * * * * * * * * * * * * * * * Local Functions * * * * * * * * * * * * * * //
namespace
{
// Verify that the entire len was matched // Verify that the entire len was matched
static inline bool fullMatch(const regmatch_t& m, const regoff_t len) static inline bool fullMatch(const regmatch_t& m, const regoff_t len)
{ {
return (m.rm_so == 0 && m.rm_eo == len); return (m.rm_so == 0 && m.rm_eo == len);
} }
} // End anonymous namespace
// * * * * * * * * * * * * * * Member Functions * * * * * * * * * * * * * * // // * * * * * * * * * * * * * * Member Functions * * * * * * * * * * * * * * //
bool Foam::regExp::clear() bool Foam::regExpPosix::clear()
{ {
if (preg_) if (preg_)
{ {
@ -55,7 +65,7 @@ bool Foam::regExp::clear()
} }
bool Foam::regExp::set(const char* pattern, bool ignoreCase) bool Foam::regExpPosix::set(const char* pattern, bool ignoreCase)
{ {
clear(); clear();
@ -106,13 +116,13 @@ bool Foam::regExp::set(const char* pattern, bool ignoreCase)
} }
bool Foam::regExp::set(const std::string& pattern, bool ignoreCase) bool Foam::regExpPosix::set(const std::string& pattern, bool ignoreCase)
{ {
return set(pattern.c_str(), ignoreCase); return set(pattern.c_str(), ignoreCase);
} }
std::string::size_type Foam::regExp::find(const std::string& text) const std::string::size_type Foam::regExpPosix::find(const std::string& text) const
{ {
if (preg_ && !text.empty()) if (preg_ && !text.empty())
{ {
@ -129,7 +139,7 @@ std::string::size_type Foam::regExp::find(const std::string& text) const
} }
bool Foam::regExp::match(const std::string& text) const bool Foam::regExpPosix::match(const std::string& text) const
{ {
const auto len = text.size(); const auto len = text.size();
@ -151,7 +161,7 @@ bool Foam::regExp::match(const std::string& text) const
} }
bool Foam::regExp::match bool Foam::regExpPosix::match
( (
const std::string& text, const std::string& text,
SubStrings<std::string>& matches SubStrings<std::string>& matches

View File

@ -0,0 +1,204 @@
/*---------------------------------------------------------------------------*\
========= |
\\ / F ield | OpenFOAM: The Open Source CFD Toolbox
\\ / O peration |
\\ / A nd | Copyright (C) 2004-2011, 2017-2018 OpenCFD Ltd.
\\/ M anipulation |
-------------------------------------------------------------------------------
| Copyright (C) 2011-2017 OpenFOAM Foundation
-------------------------------------------------------------------------------
License
This file is part of OpenFOAM.
OpenFOAM 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.
OpenFOAM is distributed in the hope that it will be useful, but WITHOUT
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
for more details.
You should have received a copy of the GNU General Public License
along with OpenFOAM. If not, see <http://www.gnu.org/licenses/>.
Class
Foam::regExpPosix
Description
Wrapper around POSIX extended regular expressions.
The PCRE '(?i)' extension is provided to compile the regular expression
as being case-insensitive.
See also
The manpage regex(7) for more information about POSIX regular expressions.
These differ somewhat from \c Perl and \c sed regular expressions.
SourceFiles
regExpPosixI.H
regExpPosix.C
\*---------------------------------------------------------------------------*/
#ifndef regExpPosix_H
#define regExpPosix_H
#include <regex.h>
#include <string>
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
namespace Foam
{
// Forward Declarations
template<class String> class SubStrings;
/*---------------------------------------------------------------------------*\
Class regExpPosix Declaration
\*---------------------------------------------------------------------------*/
class regExpPosix
{
// Private Data
//- Precompiled regular expression
regex_t* preg_;
public:
//- Type for matches
typedef SubStrings<std::string> results_type;
// Static Member Data
//- The default grammar (unused) - for future-compatibility
static int grammar;
// Static Member Functions
//- Test if character appears to be a regular expression meta-character
// \return true if character is one of the following:
// - any character: '.' \n
// - quantifiers: '*', '+', '?' \n
// - grouping: '(', '|', ')' \n
// - range: '[', ']' \n
//
// \note The presence of '{', '}' regex bounds is not considered
inline static bool meta(char c);
// Constructors
//- Construct null
inline regExpPosix();
//- Copy construct - disallowed
regExpPosix(const regExpPosix&) = delete;
//- Move construct
inline regExpPosix(regExpPosix&& rgx);
//- Construct from character array
inline explicit regExpPosix(const char* pattern);
//- Construct from string
inline explicit regExpPosix(const std::string& pattern);
//- Construct from character array, optionally ignore case
inline regExpPosix(const char* pattern, bool ignoreCase);
//- Construct from string, optionally ignore case
inline regExpPosix(const std::string& pattern, bool ignoreCase);
//- Destructor
inline ~regExpPosix();
// Member functions
// Access
//- Return true if a precompiled expression does not exist
inline bool empty() const;
//- Return true if a precompiled expression exists
inline bool exists() const;
//- The number of capture groups for a non-empty expression
inline unsigned ngroups() const;
// Editing
//- Clear expression.
// \return True if expression had existed prior to the clear.
bool clear();
//- Swap contents
inline void swap(regExpPosix& rgx);
//- Compile pattern into a regular expression, optionally ignore case.
// \return True if the pattern was compiled
bool set(const char* pattern, bool ignoreCase=false);
//- Compile pattern into a regular expression, optionally ignore case.
// \return True if the pattern was compiled
bool set(const std::string& pattern, bool ignoreCase=false);
// Matching/Searching
//- Find position within the text.
// \return The index where it begins or string::npos if not found
std::string::size_type find(const std::string& text) const;
//- True if the regex matches the entire text.
// The begin-of-line (^) and end-of-line ($) anchors are implicit
bool match(const std::string& text) const;
//- True if the regex matches the text, set the matches.
// The first group starts at index 1 (0 is the entire match).
// The begin-of-line (^) and end-of-line ($) anchors are implicit
bool match(const std::string& text, results_type& matches) const;
//- Return true if the regex was found within the text
inline bool search(const std::string& text) const;
// Member Operators
//- Perform match on text
inline bool operator()(const std::string& text) const;
//- Copy assignment - disallowed
void operator=(const regExpPosix&) = delete;
//- Move assignment
inline void operator=(regExpPosix&& rgx);
//- Assign and compile pattern from a character array.
// Matching is case sensitive.
inline void operator=(const char* pattern);
//- Assign and compile pattern from string.
// Matching is case sensitive.
inline void operator=(const std::string& pattern);
};
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
} // End namespace Foam
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
#include "regExpPosixI.H"
#endif
// ************************************************************************* //

View File

@ -28,7 +28,7 @@ License
// * * * * * * * * * * * * * Static Member Functions * * * * * * * * * * * * // // * * * * * * * * * * * * * Static Member Functions * * * * * * * * * * * * //
inline bool Foam::regExp::meta(char c) inline bool Foam::regExpPosix::meta(char c)
{ {
return return
( (
@ -42,13 +42,13 @@ inline bool Foam::regExp::meta(char c)
// * * * * * * * * * * * * * * * * Constructors * * * * * * * * * * * * * * // // * * * * * * * * * * * * * * * * Constructors * * * * * * * * * * * * * * //
inline Foam::regExp::regExp() inline Foam::regExpPosix::regExpPosix()
: :
preg_(nullptr) preg_(nullptr)
{} {}
inline Foam::regExp::regExp(const char* pattern) inline Foam::regExpPosix::regExpPosix(const char* pattern)
: :
preg_(nullptr) preg_(nullptr)
{ {
@ -56,7 +56,7 @@ inline Foam::regExp::regExp(const char* pattern)
} }
inline Foam::regExp::regExp(const std::string& pattern) inline Foam::regExpPosix::regExpPosix(const std::string& pattern)
: :
preg_(nullptr) preg_(nullptr)
{ {
@ -64,7 +64,7 @@ inline Foam::regExp::regExp(const std::string& pattern)
} }
inline Foam::regExp::regExp(const char* pattern, bool ignoreCase) inline Foam::regExpPosix::regExpPosix(const char* pattern, bool ignoreCase)
: :
preg_(nullptr) preg_(nullptr)
{ {
@ -72,7 +72,7 @@ inline Foam::regExp::regExp(const char* pattern, bool ignoreCase)
} }
inline Foam::regExp::regExp(const std::string& pattern, bool ignoreCase) inline Foam::regExpPosix::regExpPosix(const std::string& pattern, bool ignoreCase)
: :
preg_(nullptr) preg_(nullptr)
{ {
@ -80,7 +80,7 @@ inline Foam::regExp::regExp(const std::string& pattern, bool ignoreCase)
} }
inline Foam::regExp::regExp(regExp&& rgx) inline Foam::regExpPosix::regExpPosix(regExpPosix&& rgx)
: :
preg_(rgx.preg_) preg_(rgx.preg_)
{ {
@ -90,7 +90,7 @@ inline Foam::regExp::regExp(regExp&& rgx)
// * * * * * * * * * * * * * * * * Destructor * * * * * * * * * * * * * * * // // * * * * * * * * * * * * * * * * Destructor * * * * * * * * * * * * * * * //
inline Foam::regExp::~regExp() inline Foam::regExpPosix::~regExpPosix()
{ {
clear(); clear();
} }
@ -98,31 +98,31 @@ inline Foam::regExp::~regExp()
// * * * * * * * * * * * * * * Member Functions * * * * * * * * * * * * * * // // * * * * * * * * * * * * * * Member Functions * * * * * * * * * * * * * * //
inline bool Foam::regExp::empty() const inline bool Foam::regExpPosix::empty() const
{ {
return !preg_; return !preg_;
} }
inline bool Foam::regExp::exists() const inline bool Foam::regExpPosix::exists() const
{ {
return preg_ ? true : false; return preg_ ? true : false;
} }
inline unsigned Foam::regExp::ngroups() const inline unsigned Foam::regExpPosix::ngroups() const
{ {
return preg_ ? preg_->re_nsub : 0; return preg_ ? preg_->re_nsub : 0;
} }
inline bool Foam::regExp::search(const std::string& text) const inline bool Foam::regExpPosix::search(const std::string& text) const
{ {
return std::string::npos != find(text); return std::string::npos != find(text);
} }
inline void Foam::regExp::swap(regExp& rgx) inline void Foam::regExpPosix::swap(regExpPosix& rgx)
{ {
std::swap(preg_, rgx.preg_); std::swap(preg_, rgx.preg_);
} }
@ -130,26 +130,26 @@ inline void Foam::regExp::swap(regExp& rgx)
// * * * * * * * * * * * * * * Member Operators * * * * * * * * * * * * * * // // * * * * * * * * * * * * * * Member Operators * * * * * * * * * * * * * * //
inline bool Foam::regExp::operator()(const std::string& text) const inline bool Foam::regExpPosix::operator()(const std::string& text) const
{ {
return match(text); return match(text);
} }
inline void Foam::regExp::operator=(regExp&& rgx) inline void Foam::regExpPosix::operator=(regExpPosix&& rgx)
{ {
clear(); clear();
swap(rgx); swap(rgx);
} }
inline void Foam::regExp::operator=(const char* pattern) inline void Foam::regExpPosix::operator=(const char* pattern)
{ {
set(pattern); set(pattern);
} }
inline void Foam::regExp::operator=(const std::string& pattern) inline void Foam::regExpPosix::operator=(const std::string& pattern)
{ {
set(pattern); set(pattern);
} }

View File

@ -120,6 +120,7 @@ $(strings)/word/wordIOList.C
$(strings)/fileName/fileName.C $(strings)/fileName/fileName.C
$(strings)/fileName/fileNameIO.C $(strings)/fileName/fileNameIO.C
$(strings)/keyType/keyType.C $(strings)/keyType/keyType.C
$(strings)/regex/regExpCxx.C
$(strings)/wordRe/wordRe.C $(strings)/wordRe/wordRe.C
$(strings)/wordRes/wordRes.C $(strings)/wordRes/wordRes.C
$(strings)/lists/CStringList.C $(strings)/lists/CStringList.C

View File

@ -100,14 +100,14 @@ SeeAlso
// Some common data types // Some common data types
#include "label.H" #include "label.H"
#include "scalar.H" #include "scalar.H"
#include "regExpFwd.H"
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
namespace Foam namespace Foam
{ {
// Forward declarations // Forward Declarations
class regExp;
class dictionary; class dictionary;
class SHA1Digest; class SHA1Digest;
@ -282,7 +282,7 @@ public:
private: private:
// Private data // Private Data
//- Report optional keywords and values if not present in dictionary //- Report optional keywords and values if not present in dictionary
// Set/unset via an InfoSwitch // Set/unset via an InfoSwitch
@ -309,12 +309,6 @@ private:
//- The storage container //- The storage container
typedef IDLList<entry> parent_type; typedef IDLList<entry> parent_type;
typedef DLList<entry*>::iterator pattern_iterator;
typedef DLList<entry*>::const_iterator pattern_const_iterator;
typedef DLList<autoPtr<regExp>>::iterator regexp_iterator;
typedef DLList<autoPtr<regExp>>::const_iterator regexp_const_iterator;
// Private Member Functions // Private Member Functions

View File

@ -277,8 +277,8 @@ Foam::dictionary::const_searcher Foam::dictionary::csearch
if ((matchOpt & keyType::REGEX) && patterns_.size()) if ((matchOpt & keyType::REGEX) && patterns_.size())
{ {
pattern_const_iterator wcLink = patterns_.begin(); auto wcLink = patterns_.cbegin();
regexp_const_iterator reLink = regexps_.begin(); auto reLink = regexps_.cbegin();
// Find in patterns using regular expressions only // Find in patterns using regular expressions only
if (findInPatterns(true, keyword, wcLink, reLink)) if (findInPatterns(true, keyword, wcLink, reLink))
@ -584,8 +584,8 @@ bool Foam::dictionary::remove(const word& keyword)
if (iter.found()) if (iter.found())
{ {
// Delete from patterns // Delete from patterns
pattern_iterator wcLink = patterns_.begin(); auto wcLink = patterns_.begin();
regexp_iterator reLink = regexps_.begin(); auto reLink = regexps_.begin();
// Find in pattern using exact match only // Find in pattern using exact match only
if (findInPatterns(false, keyword, wcLink, reLink)) if (findInPatterns(false, keyword, wcLink, reLink))
@ -645,8 +645,8 @@ bool Foam::dictionary::changeKeyword
if (iter2()->keyword().isPattern()) if (iter2()->keyword().isPattern())
{ {
// Delete from patterns // Delete from patterns
pattern_iterator wcLink = patterns_.begin(); auto wcLink = patterns_.begin();
regexp_iterator reLink = regexps_.begin(); auto reLink = regexps_.begin();
// Find in patterns using exact match only // Find in patterns using exact match only
if (findInPatterns(false, iter2()->keyword(), wcLink, reLink)) if (findInPatterns(false, iter2()->keyword(), wcLink, reLink))

View File

@ -0,0 +1,207 @@
/*---------------------------------------------------------------------------*\
========= |
\\ / F ield | OpenFOAM: The Open Source CFD Toolbox
\\ / O peration |
\\ / A nd | Copyright (C) 2017-2019 OpenCFD Ltd.
\\/ M anipulation |
-------------------------------------------------------------------------------
License
This file is part of OpenFOAM.
OpenFOAM 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.
OpenFOAM is distributed in the hope that it will be useful, but WITHOUT
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
for more details.
You should have received a copy of the GNU General Public License
along with OpenFOAM. If not, see <http://www.gnu.org/licenses/>.
\*---------------------------------------------------------------------------*/
#include "regExpCxx.H"
#include "debug.H"
#include "error.H"
// * * * * * * * * * * * * * * Static Data Members * * * * * * * * * * * * * //
int Foam::regExpCxx::grammar(Foam::debug::optimisationSwitch("regExpCxx", 0));
// * * * * * * * * * * * * * * * Local Functions * * * * * * * * * * * * * * //
namespace
{
static std::string error_string(const std::regex_error& err)
{
switch (err.code())
{
case std::regex_constants::error_collate :
return "invalid collating element name";
break;
case std::regex_constants::error_ctype :
return "invalid character class name";
break;
case std::regex_constants::error_escape :
return "invalid escaped character or a trailing escape";
break;
case std::regex_constants::error_backref :
return "invalid back reference";
break;
case std::regex_constants::error_brack :
return "mismatched [ and ]";
break;
case std::regex_constants::error_paren :
return "mismatched ( and )";
break;
case std::regex_constants::error_brace :
return "mismatched { and }";
break;
case std::regex_constants::error_badbrace :
return "invalid range in a {..}";
break;
case std::regex_constants::error_range :
return "invalid [..] character range";
break;
case std::regex_constants::error_space :
return "memory error";
break;
case std::regex_constants::error_badrepeat :
return "bad '*?+{' repeat";
break;
case std::regex_constants::error_complexity :
return "expression too complex";
break;
case std::regex_constants::error_stack :
return "memory stack error";
break;
default:
break;
}
return "";
}
} // End anonymous namespace
// * * * * * * * * * * * * * * Member Functions * * * * * * * * * * * * * * //
bool Foam::regExpCxx::set(const char* pattern, bool ignoreCase)
{
clear();
size_t len = (pattern ? strlen(pattern) : 0);
// Avoid nullptr and zero-length patterns
if (!len)
{
return false;
}
std::regex::flag_type flags = syntax();
if (ignoreCase)
{
flags |= std::regex::icase;
}
const char* pat = pattern;
// Has embedded ignore-case prefix?
if (len >= 4 && !strncmp(pattern, "(?i)", 4))
{
flags |= std::regex::icase;
pat += 4;
len -= 4;
}
if (len)
{
try
{
re_.assign(pat, flags);
return true;
}
catch (const std::regex_error& err)
{
FatalErrorInFunction
<< "Failed to compile regular expression '"
<< pattern << "'" << nl
<< err.what() << ": " << error_string(err).c_str() << nl
<< exit(FatalError);
}
}
return false;
}
bool Foam::regExpCxx::set(const std::string& pattern, bool ignoreCase)
{
clear();
auto len = pattern.size();
// Avoid zero-length patterns
if (!len)
{
return false;
}
std::regex::flag_type flags = syntax();
if (ignoreCase)
{
flags |= std::regex::icase;
}
auto pat = pattern.begin();
// Has embedded ignore-case prefix?
if (len >= 4 && !pattern.compare(0, 4, "(?i)"))
{
flags |= std::regex::icase;
pat += 4;
len -= 4;
}
if (len)
{
try
{
re_.assign(pat, pattern.end(), flags);
return true;
}
catch (const std::regex_error& err)
{
FatalErrorInFunction
<< "Failed to compile regular expression '"
<< pattern.c_str() << "'" << nl
<< err.what() << ": " << error_string(err).c_str() << nl
<< exit(FatalError);
}
}
return false;
}
// ************************************************************************* //

View File

@ -0,0 +1,219 @@
/*---------------------------------------------------------------------------*\
========= |
\\ / F ield | OpenFOAM: The Open Source CFD Toolbox
\\ / O peration |
\\ / A nd | Copyright (C) 2017-2019 OpenCFD Ltd.
\\/ M anipulation |
-------------------------------------------------------------------------------
License
This file is part of OpenFOAM.
OpenFOAM 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.
OpenFOAM is distributed in the hope that it will be useful, but WITHOUT
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
for more details.
You should have received a copy of the GNU General Public License
along with OpenFOAM. If not, see <http://www.gnu.org/licenses/>.
Class
Foam::regExpCxx
Description
Wrapper around C++11 regular expressions.
Using either POSIX extended regular expressions or
<a href=
"http://www.cplusplus.com/reference/regex/ECMAScript"
>modified ECMAScript regular expression grammar</a>
Since ECMAScript grammar may not work correctly on all installations,
the current default is to use extended regular expressions.
The JAVA/PCRE '(?i)' extension is supported as a prefix to compile the
regular expression as being case-insensitive.
Note
The C++11 regular expressions may be broken on some compilers.
For example, gcc 4.8 is known to fail.
For these systems the POSIX implementation should be used.
SourceFiles
regExpCxxI.H
regExpCxx.C
\*---------------------------------------------------------------------------*/
#ifndef regExpCxx_H
#define regExpCxx_H
#include <regex>
#include <string>
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
namespace Foam
{
/*---------------------------------------------------------------------------*\
Class regExpCxx Declaration
\*---------------------------------------------------------------------------*/
class regExpCxx
{
// Private data
//- Regular expression (using char type)
std::regex re_;
//- Track if input pattern was OK - ie, has a length
bool ok_;
// Private Member Functions
//- Select grammar based on regExpCxx optimisationSwitch
// 0 = extended, 1 = ECMAScript
static inline std::regex::flag_type syntax();
public:
//- Type for matches
typedef std::smatch results_type;
// Static Member Data
//- The default grammar (extended | ECMAScript).
static int grammar;
// Static Member Functions
//- Test if character appears to be a regular expression meta-character
// \return true if character is one of the following:
// - any character: '.' \n
// - quantifiers: '*', '+', '?' \n
// - grouping: '(', '|', ')' \n
// - range: '[', ']' \n
//
// \note The presence of '{', '}' regex bounds is not considered
inline static bool meta(const char c);
// Constructors
//- Construct null
inline regExpCxx();
//- Copy construct
inline regExpCxx(const regExpCxx& rgx);
//- Move construct
inline regExpCxx(regExpCxx&& rgx);
//- Construct from character array
inline explicit regExpCxx(const char* pattern);
//- Construct from string
inline explicit regExpCxx(const std::string& pattern);
//- Construct from character array, optionally ignore case
inline regExpCxx(const char* pattern, bool ignoreCase);
//- Construct from string, optionally ignore case
inline regExpCxx(const std::string& pattern, bool ignoreCase);
//- Destructor
~regExpCxx() = default;
// Member functions
// Access
//- Return true if expression is empty
inline bool empty() const;
//- Return true if expression is non-empty
inline bool exists() const;
//- The number of capture groups for a non-empty expression
inline unsigned ngroups() const;
// \return True if the pattern was set with ignore-case.
inline bool nocase() const;
// Editing
//- Clear expression.
// \return True if expression had existed prior to the clear.
inline bool clear();
//- Swap contents
inline void swap(regExpCxx& rgx);
//- Compile pattern into a regular expression, optionally ignore case.
// \return True if the pattern was compiled
bool set(const char* pattern, bool ignoreCase=false);
//- Compile pattern into a regular expression, optionally ignore case.
// \return True if the pattern was compiled
bool set(const std::string& pattern, bool ignoreCase=false);
// Matching/Searching
//- Find position within the text.
// \return The index where it begins or string::npos if not found
inline std::string::size_type find(const std::string& text) const;
//- True if the regex matches the entire text.
// The begin-of-line (^) and end-of-line ($) anchors are implicit
inline bool match(const std::string& text) const;
//- True if the regex matches the text, set the matches.
// The first group starts at index 1 (0 is the entire match).
// The begin-of-line (^) and end-of-line ($) anchors are implicit
inline bool match(const std::string& text, results_type& matches) const;
//- Return true if the regex was found within the text
inline bool search(const std::string& text) const;
// Member Operators
//- Perform match on text
inline bool operator()(const std::string& text) const;
//- Copy assignment
inline void operator=(const regExpCxx& rgx);
//- Move assignment
inline void operator=(regExpCxx&& rgx);
//- Assign and compile pattern from a character array.
// Matching is case sensitive.
inline void operator=(const char* pattern);
//- Assign and compile pattern from string.
// Matching is case sensitive.
inline void operator=(const std::string& pattern);
};
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
} // End namespace Foam
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
#include "regExpCxxI.H"
#endif
// ************************************************************************* //

View File

@ -0,0 +1,228 @@
/*---------------------------------------------------------------------------*\
========= |
\\ / F ield | OpenFOAM: The Open Source CFD Toolbox
\\ / O peration |
\\ / A nd | Copyright (C) 2017-2019 OpenCFD Ltd.
\\/ M anipulation |
-------------------------------------------------------------------------------
License
This file is part of OpenFOAM.
OpenFOAM 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.
OpenFOAM is distributed in the hope that it will be useful, but WITHOUT
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
for more details.
You should have received a copy of the GNU General Public License
along with OpenFOAM. If not, see <http://www.gnu.org/licenses/>.
\*---------------------------------------------------------------------------*/
// * * * * * * * * * * * * * Static Member Functions * * * * * * * * * * * * //
inline std::regex::flag_type Foam::regExpCxx::syntax()
{
// 0 = extended, 1 = ECMAScript
return
(
regExpCxx::grammar
? std::regex::ECMAScript
: std::regex::extended
);
}
inline bool Foam::regExpCxx::meta(const char c)
{
return
(
(c == '.') // any character
|| (c == '*' || c == '+' || c == '?') // quantifiers
|| (c == '(' || c == ')' || c == '|') // grouping/branching
|| (c == '[' || c == ']') // range
);
}
// * * * * * * * * * * * * * * * * Constructors * * * * * * * * * * * * * * //
inline Foam::regExpCxx::regExpCxx()
:
re_(),
ok_(false)
{}
inline Foam::regExpCxx::regExpCxx(const regExpCxx& rgx)
:
re_(rgx.re_),
ok_(rgx.ok_)
{}
inline Foam::regExpCxx::regExpCxx(regExpCxx&& rgx)
:
re_(std::move(rgx.re_)),
ok_(rgx.ok_)
{
rgx.ok_ = false;
}
inline Foam::regExpCxx::regExpCxx(const char* pattern)
:
re_(),
ok_(false)
{
set(pattern, false);
}
inline Foam::regExpCxx::regExpCxx(const std::string& pattern)
:
re_(),
ok_(false)
{
set(pattern, false);
}
inline Foam::regExpCxx::regExpCxx(const char* pattern, bool ignoreCase)
:
re_(),
ok_(false)
{
set(pattern, ignoreCase);
}
inline Foam::regExpCxx::regExpCxx(const std::string& pattern, bool ignoreCase)
:
re_(),
ok_(false)
{
set(pattern, ignoreCase);
}
// * * * * * * * * * * * * * * Member Functions * * * * * * * * * * * * * * //
inline bool Foam::regExpCxx::empty() const
{
return !ok_;
}
inline bool Foam::regExpCxx::exists() const
{
return ok_;
}
inline unsigned Foam::regExpCxx::ngroups() const
{
return ok_ ? re_.mark_count() : 0;
}
inline bool Foam::regExpCxx::nocase() const
{
return ok_ && ((re_.flags() & std::regex::icase) == std::regex::icase);
}
inline bool Foam::regExpCxx::clear()
{
if (ok_)
{
re_.assign("");
ok_ = false;
return true;
}
return false;
}
inline void Foam::regExpCxx::swap(regExpCxx& rgx)
{
re_.swap(rgx.re_);
std::swap(ok_, rgx.ok_);
}
inline std::string::size_type Foam::regExpCxx::find(const std::string& text) const
{
std::smatch mat;
if (!text.empty() && std::regex_search(text, mat, re_))
{
return mat.position(0);
}
return std::string::npos;
}
inline bool Foam::regExpCxx::search(const std::string& text) const
{
return (ok_ && !text.empty() && std::regex_search(text, re_));
}
inline bool Foam::regExpCxx::match(const std::string& text) const
{
return (ok_ && !text.empty() && std::regex_match(text, re_));
}
inline bool Foam::regExpCxx::match
(
const std::string& text,
std::smatch& matches
) const
{
return std::regex_match(text, matches, re_);
}
// * * * * * * * * * * * * * * Member Operators * * * * * * * * * * * * * * //
inline bool Foam::regExpCxx::operator()(const std::string& text) const
{
return match(text);
}
inline void Foam::regExpCxx::operator=(const regExpCxx& rgx)
{
re_ = rgx.re_;
ok_ = rgx.ok_;
}
inline void Foam::regExpCxx::operator=(regExpCxx&& rgx)
{
clear();
swap(rgx);
}
inline void Foam::regExpCxx::operator=(const char* pattern)
{
set(pattern);
}
inline void Foam::regExpCxx::operator=(const std::string& pattern)
{
set(pattern);
}
// ************************************************************************* //