ENH: refactor function arg splitting -> stringOps::splitFunctionArgs
This commit is contained in:
parent
44a243a94d
commit
c9fda67b5f
3
applications/test/splitFunctionArgs/Make/files
Normal file
3
applications/test/splitFunctionArgs/Make/files
Normal file
@ -0,0 +1,3 @@
|
||||
Test-splitFunctionArgs.C
|
||||
|
||||
EXE = $(FOAM_USER_APPBIN)/Test-splitFunctionArgs
|
2
applications/test/splitFunctionArgs/Make/options
Normal file
2
applications/test/splitFunctionArgs/Make/options
Normal file
@ -0,0 +1,2 @@
|
||||
/* EXE_INC = */
|
||||
/* EXE_LIBS = */
|
155
applications/test/splitFunctionArgs/Test-splitFunctionArgs.C
Normal file
155
applications/test/splitFunctionArgs/Test-splitFunctionArgs.C
Normal file
@ -0,0 +1,155 @@
|
||||
/*---------------------------------------------------------------------------*\
|
||||
========= |
|
||||
\\ / F ield | OpenFOAM: The Open Source CFD Toolbox
|
||||
\\ / O peration |
|
||||
\\ / A nd | www.openfoam.com
|
||||
\\/ M anipulation |
|
||||
-------------------------------------------------------------------------------
|
||||
Copyright (C) 2021 OpenCFD Ltd.
|
||||
-------------------------------------------------------------------------------
|
||||
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/>.
|
||||
|
||||
Application
|
||||
Test-splitFunctionArgs
|
||||
|
||||
Description
|
||||
Test splitting of function name args
|
||||
|
||||
\*---------------------------------------------------------------------------*/
|
||||
|
||||
#include "argList.H"
|
||||
#include "IOstreams.H"
|
||||
#include "IOobject.H"
|
||||
#include "IFstream.H"
|
||||
#include "dictionary.H"
|
||||
#include "stringOps.H"
|
||||
#include "Tuple2.H"
|
||||
|
||||
using namespace Foam;
|
||||
|
||||
// Split out function name and any arguments
|
||||
// - use as per functionObjectList
|
||||
void testFunctionNameAndArgsSplit(const std::string& line)
|
||||
{
|
||||
word funcName;
|
||||
wordRes args;
|
||||
List<Tuple2<word, string>> namedArgs;
|
||||
|
||||
const auto lbracket = line.find('(');
|
||||
if (lbracket == std::string::npos)
|
||||
{
|
||||
funcName = word::validate(line);
|
||||
// No args
|
||||
}
|
||||
else
|
||||
{
|
||||
funcName = word::validate(line.substr(0, lbracket));
|
||||
std::string params;
|
||||
|
||||
const auto rbracket = line.rfind(')');
|
||||
if (rbracket != std::string::npos && lbracket < rbracket)
|
||||
{
|
||||
params = line.substr(lbracket+1, (rbracket - lbracket - 1));
|
||||
}
|
||||
else
|
||||
{
|
||||
params = line.substr(lbracket+1);
|
||||
}
|
||||
|
||||
Info<<"parsing: " << params << nl;
|
||||
|
||||
stringOps::splitFunctionArgs(params, args, namedArgs);
|
||||
}
|
||||
|
||||
Info<< nl
|
||||
<< line << nl
|
||||
<< "function: <" << funcName << '>' << nl
|
||||
<< " args: " << args << nl
|
||||
<< " named: " << namedArgs << nl;
|
||||
}
|
||||
|
||||
|
||||
// Split out any arguments
|
||||
void testArgsSplit(const std::string& line)
|
||||
{
|
||||
wordRes args;
|
||||
List<Tuple2<word, string>> namedArgs;
|
||||
stringOps::splitFunctionArgs(line, args, namedArgs);
|
||||
|
||||
Info<< nl
|
||||
<< line << nl
|
||||
<< " args: " << args << nl
|
||||
<< " named: " << namedArgs << nl;
|
||||
}
|
||||
|
||||
|
||||
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
|
||||
// Main program:
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
argList::noBanner();
|
||||
argList::noParallel();
|
||||
argList::addArgument("file1 .. fileN");
|
||||
argList args(argc, argv, false, true);
|
||||
|
||||
if (args.size() <= 1)
|
||||
{
|
||||
InfoErr<< "Provide a file or files to test" << nl;
|
||||
}
|
||||
else
|
||||
{
|
||||
for (label argi=1; argi < args.size(); ++argi)
|
||||
{
|
||||
IOobject::writeDivider(Info);
|
||||
|
||||
const auto inputFile = args.get<fileName>(argi);
|
||||
IFstream is(inputFile);
|
||||
|
||||
string line;
|
||||
while (is.getLine(line))
|
||||
{
|
||||
if (line.empty() || line[0] == '#')
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (line.starts_with("function:"))
|
||||
{
|
||||
auto trim = line.find(':');
|
||||
++trim;
|
||||
while (isspace(line[trim]))
|
||||
{
|
||||
++trim;
|
||||
}
|
||||
|
||||
line.erase(0, trim);
|
||||
testFunctionNameAndArgsSplit(line);
|
||||
}
|
||||
else
|
||||
{
|
||||
testArgsSplit(line);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
// ************************************************************************* //
|
47
applications/test/splitFunctionArgs/testNames1
Normal file
47
applications/test/splitFunctionArgs/testNames1
Normal file
@ -0,0 +1,47 @@
|
||||
# -----------------------------------------------------------------------------
|
||||
# Test names for splitting. Comment character as per -*- sh -*- mode
|
||||
#
|
||||
# Prefix with "function: " to test with function name splitting
|
||||
|
||||
function: basic
|
||||
|
||||
function: func1( a , b );
|
||||
|
||||
function: func2(a, value=10);
|
||||
|
||||
# discard or flag bad/missing parameters?
|
||||
function: func3(a , , , value=10);
|
||||
|
||||
function: func4();
|
||||
|
||||
function: func5( abc );
|
||||
|
||||
start=1, end=2
|
||||
|
||||
start=1, end=2,
|
||||
|
||||
start=100, end= 200, abc
|
||||
|
||||
value=100
|
||||
|
||||
start=1, end=2
|
||||
|
||||
origin=(0 0 0), scale=2, normal=(0 0 1)
|
||||
|
||||
|
||||
# Canonical with named args
|
||||
function: patchAverage(patch=inlet, p)
|
||||
|
||||
# Canonical with unnamed and named args
|
||||
function: patchAverage(other, patch=inlet, pval)
|
||||
|
||||
|
||||
function: patchAverage(patch=(inlet|outlet), p)
|
||||
|
||||
# General
|
||||
(a, b)
|
||||
|
||||
allow=(inlet|outlet), deny=false, regular(value)
|
||||
|
||||
|
||||
# -----------------------------------------------------------------------------
|
@ -145,6 +145,7 @@ $(strings)/parsing/genericRagelLemonDriver.C
|
||||
$(strings)/stringOps/stringOps.C
|
||||
$(strings)/stringOps/stringOpsEvaluate.C
|
||||
$(strings)/stringOps/stringOpsSort.C
|
||||
$(strings)/stringOps/stringOpsSplit.C
|
||||
|
||||
expr = expressions
|
||||
$(expr)/exprEntry/expressionEntry.C
|
||||
|
@ -6,7 +6,7 @@
|
||||
\\/ M anipulation |
|
||||
-------------------------------------------------------------------------------
|
||||
Copyright (C) 2011-2017 OpenFOAM Foundation
|
||||
Copyright (C) 2015-2020 OpenCFD Ltd.
|
||||
Copyright (C) 2015-2021 OpenCFD Ltd.
|
||||
-------------------------------------------------------------------------------
|
||||
License
|
||||
This file is part of OpenFOAM.
|
||||
@ -243,91 +243,42 @@ bool Foam::functionObjectList::readFunctionObject
|
||||
// 'patchAverage(patch=inlet, p)' -> funcName = patchAverage;
|
||||
// args = (patch=inlet, p); field = p
|
||||
|
||||
word funcName(funcNameArgs);
|
||||
|
||||
int argLevel = 0;
|
||||
word funcName;
|
||||
wordRes args;
|
||||
|
||||
List<Tuple2<word, string>> namedArgs;
|
||||
bool hasNamedArg = false;
|
||||
word argName;
|
||||
|
||||
word::size_type start = 0;
|
||||
word::size_type i = 0;
|
||||
|
||||
for
|
||||
(
|
||||
word::const_iterator iter = funcNameArgs.begin();
|
||||
iter != funcNameArgs.end();
|
||||
++iter
|
||||
)
|
||||
{
|
||||
char c = *iter;
|
||||
|
||||
if (c == '(')
|
||||
const auto argsBeg = funcNameArgs.find('(');
|
||||
if (argsBeg == std::string::npos)
|
||||
{
|
||||
if (argLevel == 0)
|
||||
{
|
||||
funcName = funcNameArgs.substr(start, i - start);
|
||||
start = i+1;
|
||||
}
|
||||
++argLevel;
|
||||
// Function name only, no args
|
||||
funcName = word::validate(funcNameArgs);
|
||||
}
|
||||
else if (c == ',' || c == ')')
|
||||
else
|
||||
{
|
||||
if (argLevel == 1)
|
||||
{
|
||||
if (hasNamedArg)
|
||||
{
|
||||
namedArgs.append
|
||||
(
|
||||
Tuple2<word, string>
|
||||
(
|
||||
argName,
|
||||
funcNameArgs.substr(start, i - start)
|
||||
)
|
||||
);
|
||||
hasNamedArg = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
args.append
|
||||
(
|
||||
wordRe
|
||||
(
|
||||
word::validate
|
||||
(
|
||||
funcNameArgs.substr(start, i - start)
|
||||
)
|
||||
)
|
||||
);
|
||||
}
|
||||
start = i+1;
|
||||
}
|
||||
// Leading function name
|
||||
funcName = word::validate(funcNameArgs.substr(0, argsBeg));
|
||||
|
||||
if (c == ')')
|
||||
{
|
||||
if (argLevel == 1)
|
||||
{
|
||||
break;
|
||||
}
|
||||
--argLevel;
|
||||
}
|
||||
}
|
||||
else if (c == '=')
|
||||
{
|
||||
argName = word::validate
|
||||
const auto argsEnd = funcNameArgs.rfind(')');
|
||||
|
||||
stringOps::splitFunctionArgs
|
||||
(
|
||||
funcNameArgs.substr(start, i - start)
|
||||
funcNameArgs.substr
|
||||
(
|
||||
(argsBeg + 1),
|
||||
(
|
||||
(argsEnd != std::string::npos && argsBeg < argsEnd)
|
||||
? (argsEnd - argsBeg - 1)
|
||||
: std::string::npos
|
||||
)
|
||||
),
|
||||
args,
|
||||
namedArgs
|
||||
);
|
||||
|
||||
start = i+1;
|
||||
hasNamedArg = true;
|
||||
}
|
||||
|
||||
++i;
|
||||
}
|
||||
|
||||
|
||||
// Search for the functionObject dictionary
|
||||
fileName path = functionObjectList::findDict(funcName);
|
||||
|
||||
|
@ -693,7 +693,7 @@ static void expandString
|
||||
} // End namespace Foam
|
||||
|
||||
|
||||
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
|
||||
// * * * * * * * * * * * * * * * Global Functions * * * * * * * * * * * * * //
|
||||
|
||||
std::string::size_type Foam::stringOps::count
|
||||
(
|
||||
|
@ -57,6 +57,7 @@ namespace Foam
|
||||
|
||||
// Forward Declarations
|
||||
class OSstream;
|
||||
template<class T1, class T2> class Tuple2;
|
||||
|
||||
/*---------------------------------------------------------------------------*\
|
||||
Namespace stringOps Declaration
|
||||
@ -282,6 +283,14 @@ namespace stringOps
|
||||
// Return true if a replacement was successful.
|
||||
bool inplaceReplaceVar(std::string& s, const word& varName);
|
||||
|
||||
//- Return a copy of the input string with validated characters
|
||||
template<class StringType, class UnaryPredicate>
|
||||
StringType validate
|
||||
(
|
||||
const std::string& str,
|
||||
const UnaryPredicate& accept,
|
||||
const bool invert=false //!< Invert the test logic
|
||||
);
|
||||
|
||||
//- Find (first, last) non-space locations in string or sub-string.
|
||||
// This may change to std::string_view in the future.
|
||||
@ -334,6 +343,30 @@ namespace stringOps
|
||||
//- Inplace transform string with std::toupper on each character
|
||||
void inplaceUpper(std::string& s);
|
||||
|
||||
//- Split out arguments (named or unnamed) from an input string.
|
||||
//
|
||||
// For example,
|
||||
// \verbatim
|
||||
// (U)
|
||||
// -> named = ()
|
||||
// -> unnamed = (U)
|
||||
//
|
||||
// (patch=inlet, p)
|
||||
// -> named = ((patch inlet))
|
||||
// -> unnamed = (p)
|
||||
//
|
||||
// testing, start=100, stop=200
|
||||
// -> named = ((start 100)(stop 200))
|
||||
// -> unnamed = (testing)
|
||||
// \endverbatim
|
||||
//
|
||||
// \return total number of arguments
|
||||
label splitFunctionArgs
|
||||
(
|
||||
const std::string& str,
|
||||
wordRes& args,
|
||||
List<Tuple2<word, string>>& namedArgs
|
||||
);
|
||||
|
||||
//- Split string into sub-strings at the delimiter character.
|
||||
// Empty sub-strings are normally suppressed.
|
||||
|
221
src/OpenFOAM/primitives/strings/stringOps/stringOpsSplit.C
Normal file
221
src/OpenFOAM/primitives/strings/stringOps/stringOpsSplit.C
Normal file
@ -0,0 +1,221 @@
|
||||
/*---------------------------------------------------------------------------*\
|
||||
========= |
|
||||
\\ / F ield | OpenFOAM: The Open Source CFD Toolbox
|
||||
\\ / O peration |
|
||||
\\ / A nd | www.openfoam.com
|
||||
\\/ M anipulation |
|
||||
-------------------------------------------------------------------------------
|
||||
Copyright (C) 2016 OpenFOAM Foundation
|
||||
Copyright (C) 2021 OpenCFD Ltd.
|
||||
-------------------------------------------------------------------------------
|
||||
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 "stringOps.H"
|
||||
#include "Pair.H"
|
||||
#include "Tuple2.H"
|
||||
#include <vector>
|
||||
|
||||
// * * * * * * * * * * * * * * * Local Functions * * * * * * * * * * * * * * //
|
||||
|
||||
namespace
|
||||
{
|
||||
|
||||
inline Foam::word validateVariableName(const std::string& str)
|
||||
{
|
||||
return Foam::stringOps::validate<Foam::word>
|
||||
(
|
||||
str,
|
||||
[](char c)
|
||||
{
|
||||
return (Foam::word::valid(c) || c == '/' || c == '{' || c == '}');
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
} // End anonymous namespace
|
||||
|
||||
|
||||
// * * * * * * * * * * * * * * * Global Functions * * * * * * * * * * * * * //
|
||||
|
||||
Foam::label Foam::stringOps::splitFunctionArgs
|
||||
(
|
||||
const std::string& str,
|
||||
wordRes& args,
|
||||
List<Tuple2<word, string>>& namedArgs
|
||||
)
|
||||
{
|
||||
args.clear();
|
||||
namedArgs.clear();
|
||||
|
||||
// Similar to code originally in functionObjectList (v2012 and earlier)
|
||||
// except that the function-name handling is now done prior to calling
|
||||
|
||||
// (U)
|
||||
// -> named = ()
|
||||
// -> unnamed = (U)
|
||||
//
|
||||
// (patch=inlet, p)
|
||||
// -> named = ((patch inlet))
|
||||
// -> unnamed = (p)
|
||||
//
|
||||
// start=100, stop=200
|
||||
// -> named = ((start 100) (stop 200) )
|
||||
// -> unnamed = ()
|
||||
//
|
||||
// origin=(0 0 0) , scale=2 , normal=(0 0 1)
|
||||
|
||||
|
||||
// Use begin/end parse positions
|
||||
typedef Pair<std::string::size_type> rangeType;
|
||||
|
||||
// For unnamed: beg/end range of each arg
|
||||
std::vector<rangeType> unnamed;
|
||||
|
||||
// For named: list of beg/end ranges for (name, arg)
|
||||
std::vector<rangeType> named;
|
||||
|
||||
// The beg/end range of the argument name
|
||||
rangeType argName(0, 0);
|
||||
|
||||
// If argName is valid
|
||||
bool isNamed = false;
|
||||
|
||||
// The depth of the argument parsing
|
||||
int argLevel = 0;
|
||||
|
||||
const auto strLen = str.length();
|
||||
|
||||
// Pass 1: parsing begin/end parse positions.
|
||||
|
||||
for (std::string::size_type pos = 0, beg = 0; pos < strLen; ++pos)
|
||||
{
|
||||
const bool penultimate = ((pos + 1) == strLen);
|
||||
const char c = str[pos];
|
||||
|
||||
if (c == ')')
|
||||
{
|
||||
--argLevel;
|
||||
}
|
||||
|
||||
if (c == '=')
|
||||
{
|
||||
// Introducer for named argument
|
||||
argName = rangeType(beg, pos);
|
||||
isNamed = true;
|
||||
beg = pos + 1;
|
||||
}
|
||||
else if (c == '(')
|
||||
{
|
||||
++argLevel;
|
||||
}
|
||||
else if (penultimate || (c == ',')) // OR: (c == ',' || c == ';')
|
||||
{
|
||||
if (penultimate && (c != ',')) // OR: (c != ',' && c != ';')
|
||||
{
|
||||
++pos; // Until the end, but do not include comma
|
||||
}
|
||||
|
||||
if (argLevel == 0)
|
||||
{
|
||||
if (isNamed)
|
||||
{
|
||||
named.push_back(argName);
|
||||
named.push_back(rangeType(beg, pos));
|
||||
}
|
||||
else
|
||||
{
|
||||
unnamed.push_back(rangeType(beg, pos));
|
||||
}
|
||||
isNamed = false;
|
||||
beg = pos + 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Stage 2: Convert to concrete string and store
|
||||
|
||||
|
||||
// unnamed
|
||||
{
|
||||
const label nInputArgs = static_cast<label>(unnamed.size());
|
||||
args.resize(nInputArgs);
|
||||
|
||||
label ngood = 0;
|
||||
for (label i = 0; i < nInputArgs; ++i)
|
||||
{
|
||||
const auto& arg = unnamed[i];
|
||||
|
||||
args[ngood] = wordRe
|
||||
(
|
||||
word::validate
|
||||
(
|
||||
str.substr(arg.first(), (arg.second()-arg.first()))
|
||||
)
|
||||
);
|
||||
|
||||
// Only retain if non-empty
|
||||
if (!args[ngood].empty())
|
||||
{
|
||||
++ngood;
|
||||
}
|
||||
}
|
||||
|
||||
args.resize(ngood);
|
||||
}
|
||||
|
||||
// named
|
||||
{
|
||||
const label nInputArgs = static_cast<label>(named.size());
|
||||
namedArgs.resize(nInputArgs/2);
|
||||
|
||||
label ngood = 0;
|
||||
for (label i = 0; i < nInputArgs; i += 2)
|
||||
{
|
||||
const auto& name = named[i];
|
||||
const auto& arg = named[i+1];
|
||||
|
||||
namedArgs[ngood].first() =
|
||||
validateVariableName
|
||||
(
|
||||
str.substr(name.first(), (name.second()-name.first()))
|
||||
);
|
||||
|
||||
namedArgs[ngood].second() =
|
||||
stringOps::trim
|
||||
(
|
||||
str.substr(arg.first(), (arg.second()-arg.first()))
|
||||
);
|
||||
|
||||
// Only retain if name is non-empty
|
||||
if (!namedArgs[ngood].first().empty())
|
||||
{
|
||||
++ngood;
|
||||
}
|
||||
}
|
||||
|
||||
namedArgs.resize(ngood);
|
||||
}
|
||||
|
||||
// Return total number of arguments
|
||||
return (args.size() + namedArgs.size());
|
||||
}
|
||||
|
||||
|
||||
// ************************************************************************* //
|
@ -66,6 +66,33 @@ StringType Foam::stringOps::quotemeta
|
||||
}
|
||||
|
||||
|
||||
template<class StringType, class UnaryPredicate>
|
||||
StringType Foam::stringOps::validate
|
||||
(
|
||||
const std::string& str,
|
||||
const UnaryPredicate& accept,
|
||||
const bool invert
|
||||
)
|
||||
{
|
||||
StringType out;
|
||||
out.resize(str.length());
|
||||
|
||||
std::string::size_type len = 0;
|
||||
|
||||
for (std::string::size_type i = 0; i < str.length(); ++i)
|
||||
{
|
||||
const char c = str[i];
|
||||
if (accept(c) ? !invert : invert)
|
||||
{
|
||||
out[len++] += c;
|
||||
}
|
||||
}
|
||||
|
||||
out.erase(len);
|
||||
return out;
|
||||
}
|
||||
|
||||
|
||||
template<class StringType>
|
||||
Foam::SubStrings<StringType> Foam::stringOps::split
|
||||
(
|
||||
|
Loading…
Reference in New Issue
Block a user