ENH: change internal dictionary separator to '/' (#1073)

- simplifies internal handling (like a fileName) and allows the
  dictionary name to be used with unambiguous addressing.
  The previous dot (.) separator is ambiguous (ie, as dictionary
  separator or as part of a keyword).

ENH: foamDictionary report -add/-set to stderr
This commit is contained in:
Mark Olesen
2023-08-16 18:01:41 +02:00
parent 778796853d
commit 886ba89ddb
8 changed files with 114 additions and 95 deletions

View File

@ -6,7 +6,7 @@
\\/ M anipulation | \\/ M anipulation |
------------------------------------------------------------------------------- -------------------------------------------------------------------------------
Copyright (C) 2016-2017 OpenFOAM Foundation Copyright (C) 2016-2017 OpenFOAM Foundation
Copyright (C) 2017-2022 OpenCFD Ltd. Copyright (C) 2017-2023 OpenCFD Ltd.
------------------------------------------------------------------------------- -------------------------------------------------------------------------------
License License
This file is part of OpenFOAM. This file is part of OpenFOAM.
@ -78,23 +78,23 @@ Usage
- Change solver: - Change solver:
\verbatim \verbatim
foamDictionary system/fvSolution -entry solvers.p.solver -set PCG foamDictionary system/fvSolution -entry solvers/p/solver -set PCG
\endverbatim \endverbatim
- Print bc type: - Print bc type:
\verbatim \verbatim
foamDictionary 0/U -entry boundaryField.movingWall.type foamDictionary 0/U -entry boundaryField/movingWall/type
\endverbatim \endverbatim
- Change bc parameter: - Change bc parameter:
\verbatim \verbatim
foamDictionary 0/U -entry boundaryField.movingWall.value \ foamDictionary 0/U -entry boundaryField/movingWall/value \
-set "uniform (2 0 0)" -set "uniform (2 0 0)"
\endverbatim \endverbatim
- Change whole bc type: - Change whole bc type:
\verbatim \verbatim
foamDictionary 0/U -entry boundaryField.movingWall \ foamDictionary 0/U -entry boundaryField/movingWall \
-set "{type uniformFixedValue; uniformValue (2 0 0);}" -set "{type uniformFixedValue; uniformValue (2 0 0);}"
\endverbatim \endverbatim
@ -113,7 +113,7 @@ Usage
- Change patch type: - Change patch type:
\verbatim \verbatim
foamDictionary constant/polyMesh/boundary \ foamDictionary constant/polyMesh/boundary \
-entry entry0.fixedWalls.type -set patch -entry entry0/fixedWalls/type -set patch
\endverbatim \endverbatim
This uses special parsing of Lists which stores these in the This uses special parsing of Lists which stores these in the
dictionary with keyword 'entryDDD' where DDD is the position dictionary with keyword 'entryDDD' where DDD is the position
@ -140,21 +140,31 @@ using namespace Foam;
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
//- Convert older ':' scope syntax to newer '.' scope syntax, //- Convert very old ':' scope syntax to less old '.' scope syntax,
// but leave anything with '/' delimiters untouched // but leave anything with '/' delimiters untouched
bool upgradeScope(word& entryName) bool upgradeScope(word& entryName)
{ {
if (!entryName.contains('/') && entryName.contains(':')) if (entryName.contains(':') && !entryName.contains('/'))
{ {
const wordList names(fileName(entryName).components(':')); InfoErr
<< "Warning: upgrading very old ':' scope syntax: \""
<< entryName << '"' << endl;
entryName.resize(0); // Make copy - cannot use stringOps::split
const wordList cmpts(fileName(entryName).components(':'));
for (const word& name : names) entryName.clear();
bool addSep = false;
for (const word& cmpt : cmpts)
{ {
if (entryName.size()) entryName.append("."); if (addSep) entryName += '.';
if (!cmpt.empty())
entryName.append(name); {
addSep = true;
entryName += cmpt;
}
} }
return true; return true;
@ -176,12 +186,12 @@ public:
dictAndKeyword(const word& scopedName) dictAndKeyword(const word& scopedName)
{ {
auto i = scopedName.rfind('/'); auto i = scopedName.rfind('/');
if (i == string::npos) if (i == std::string::npos)
{ {
i = scopedName.rfind('.'); i = scopedName.rfind('.');
} }
if (i != string::npos) if (i != std::string::npos)
{ {
dict_ = scopedName.substr(0, i); dict_ = scopedName.substr(0, i);
key_ = scopedName.substr(i+1); key_ = scopedName.substr(i+1);
@ -192,15 +202,9 @@ public:
} }
} }
inline const word& dict() const const word& dict() const noexcept { return dict_; }
{
return dict_;
}
inline const word& key() const const word& key() const noexcept { return key_; }
{
return key_;
}
}; };
@ -347,10 +351,7 @@ int main(int argc, char *argv[])
if (disableEntries) if (disableEntries)
{ {
// Report on stderr (once) to avoid polluting the output // Report on stderr (once) to avoid polluting the output
if (Pstream::master()) InfoErr<< "Not expanding variables or dictionary directives" << endl;
{
Serr<< "Not expanding variables or dictionary directives" << endl;
}
entry::disableFunctionEntries = true; entry::disableFunctionEntries = true;
} }
@ -359,11 +360,7 @@ int main(int argc, char *argv[])
const unsigned prec = args.getOrDefault<unsigned>("precision", 0u); const unsigned prec = args.getOrDefault<unsigned>("precision", 0u);
if (prec) if (prec)
{ {
// if (Pstream::master()) // InfoErr<< "Output write precision set to " << prec << endl;
// {
// Serr<< "Output write precision set to " << prec << endl;
// }
IOstream::defaultPrecision(prec); IOstream::defaultPrecision(prec);
Sout.precision(prec); Sout.precision(prec);
} }
@ -479,12 +476,12 @@ int main(int argc, char *argv[])
} }
changed = true; changed = true;
// Print the changed entry
const auto finder = dict.csearchScoped(scopedName, keyType::REGEX); const auto finder = dict.csearchScoped(scopedName, keyType::REGEX);
// Print the changed entry to stderr
if (finder.good()) if (finder.good())
{ {
Info<< finder.ref(); InfoErr<< finder.ref();
} }
} }
else if (args.found("remove")) else if (args.found("remove"))
@ -539,6 +536,7 @@ int main(int argc, char *argv[])
} }
else if (args.found("keywords")) else if (args.found("keywords"))
{ {
// Report keywords to stdout
for (const entry& e : finder.dict()) for (const entry& e : finder.dict())
{ {
Info<< e.keyword() << endl; Info<< e.keyword() << endl;
@ -546,32 +544,36 @@ int main(int argc, char *argv[])
} }
else if (args.found("value")) else if (args.found("value"))
{ {
// Report value to stdout
if (finder.isDict()) if (finder.isDict())
{ {
Info<< finder.dict(); Info<< finder.dict();
} }
else if (finder.ref().isStream()) else if (finder.ref().isStream())
{ {
bool addSep = false;
const tokenList& tokens = finder.ref().stream(); const tokenList& tokens = finder.ref().stream();
forAll(tokens, i)
for (const token& tok : tokens)
{ {
Info<< tokens[i]; if (addSep) Info<< token::SPACE;
if (i < tokens.size() - 1) addSep = true;
{ Info<< tok;
Info<< token::SPACE;
}
} }
Info<< endl; Info<< endl;
} }
} }
else else
{ {
// Report entry to stdout
Info<< finder.ref(); Info<< finder.ref();
} }
} }
} }
else if (args.found("keywords")) else if (args.found("keywords"))
{ {
// Report keywords to stdout
for (const entry& e : dict) for (const entry& e : dict)
{ {
Info<< e.keyword() << endl; Info<< e.keyword() << endl;
@ -579,11 +581,13 @@ int main(int argc, char *argv[])
} }
else if (optDiff) else if (optDiff)
{ {
// Report difference to stdout
removeDict(dict, diffDict); removeDict(dict, diffDict);
dict.write(Info, false); dict.write(Info, false);
} }
else else
{ {
// Report dictionary to stdout
dict.write(Info, false); dict.write(Info, false);
} }

View File

@ -153,7 +153,7 @@ Foam::dictionary::dictionary
parent_(parentDict) parent_(parentDict)
{ {
transfer(dict); transfer(dict);
name() = fileName::concat(parentDict.name(), name(), '.'); name() = fileName::concat(parentDict.name(), name(), '/');
} }
@ -658,7 +658,7 @@ Foam::entry* Foam::dictionary::add(entry* entryPtr, bool mergeEntry)
if (hashedEntries_.insert(entryPtr->keyword(), entryPtr)) if (hashedEntries_.insert(entryPtr->keyword(), entryPtr))
{ {
entryPtr->name() = entryPtr->name() =
fileName::concat(name(), entryPtr->keyword(), '.'); fileName::concat(name(), entryPtr->keyword(), '/');
if (entryPtr->keyword().isPattern()) if (entryPtr->keyword().isPattern())
{ {
@ -684,7 +684,7 @@ Foam::entry* Foam::dictionary::add(entry* entryPtr, bool mergeEntry)
if (hashedEntries_.insert(entryPtr->keyword(), entryPtr)) if (hashedEntries_.insert(entryPtr->keyword(), entryPtr))
{ {
entryPtr->name() = entryPtr->name() =
fileName::concat(name(), entryPtr->keyword(), '.'); fileName::concat(name(), entryPtr->keyword(), '/');
parent_type::push_back(entryPtr); parent_type::push_back(entryPtr);

View File

@ -43,35 +43,44 @@ Description
as a bootstrap dictionary for the objectRegistry data dictionaries. as a bootstrap dictionary for the objectRegistry data dictionaries.
Note Note
Within dictionaries, entries can be referenced by using the '$' syntax Within dictionaries, entries can be referenced by using the
familiar from shell programming. \c '$' syntax familiar from shell programming.
A '.' separator is used when referencing sub-dictionary entries. Similarly, the \c '/' separator is used when referencing
Leading '.' prefixes can be used to specify an entry from a parent sub-dictionary entries:
dictionary. - <b> "./" </b> : the current dictionary
An initial '^' anchor (or ':' for backward compatibility) specifies - <b> "../" </b> : the parent dictionary
starting from the top-level entry. - <b> "../../" </b> : the grandparent dictionary
For example, .
An initial \c '/' anchor specifies that the path starts from the
top-level entry. It is also possible to use the '${}' syntax for clarity.
For example,
\verbatim \verbatim
key1 val1; key1 val1;
key2 $key1; // use key1 value from current scope key2 $key1; // Use key1 value from current scope
key3 $.key1; // use key1 value from current scope key3 $./key1; // Use key1 value from current scope
subdict1 subdict1
{ {
key1 val1b; key1 val1b;
key2 $..key1; // use key1 value from parent key2 $../key1; // Use key1 value from parent
subdict2 subdict2
{ {
key2 val2; key2 val2;
key3 $...key1; // use key1 value from grandparent key3 $../../key1; // Use key1 value from grandparent
} }
} }
key4 $^subdict1.subdict2.key3; // lookup with absolute scoping key4 $/subdict1/subdict2/key3; // Lookup with absolute scoping
\endverbatim \endverbatim
It is also possible to use the '${}' syntax for clarity. Prior to OpenFOAM-v1712, a dot-scoping (.) syntax was used, which is
still supported (AUG-2023) but deprecated in favour of the
less ambiguous slash-scoping (/) syntax.
With dot-scoping, an initial \c '^' anchor, or an initial (:), was used
to specify that the path starts from the top-level entry.
SourceFiles SourceFiles
dictionary.C dictionary.C

View File

@ -5,7 +5,7 @@
\\ / A nd | www.openfoam.com \\ / A nd | www.openfoam.com
\\/ M anipulation | \\/ M anipulation |
------------------------------------------------------------------------------- -------------------------------------------------------------------------------
Copyright (C) 2021 OpenCFD Ltd. Copyright (C) 2021-2023 OpenCFD Ltd.
------------------------------------------------------------------------------- -------------------------------------------------------------------------------
License License
This file is part of OpenFOAM. This file is part of OpenFOAM.
@ -59,15 +59,19 @@ inline Foam::fileName& Foam::dictionary::name() noexcept
inline Foam::word Foam::dictionary::dictName() const inline Foam::word Foam::dictionary::dictName() const
{ {
word scopedName(name_.name()); // With other (non-slash) separator. Eg, with '.'
// word scopedName(name_.name());
//
// const auto i = scopedName.rfind('.');
// if (i == std::string::npos)
// {
// return scopedName;
// }
//
// return scopedName.substr(i+1);
const auto i = scopedName.rfind('.'); // With '/' separator, this is just fileName::name()
if (i == std::string::npos) return name_.name();
{
return scopedName;
}
return scopedName.substr(i+1);
} }

View File

@ -39,7 +39,7 @@ Foam::dictionary::dictionary
bool keepHeader bool keepHeader
) )
: :
name_(fileName::concat(parentDict.name(), name, '.')), name_(fileName::concat(parentDict.name(), name, '/')),
parent_(parentDict) parent_(parentDict)
{ {
read(is, keepHeader); read(is, keepHeader);

View File

@ -5,7 +5,7 @@
\\ / A nd | www.openfoam.com \\ / A nd | www.openfoam.com
\\/ M anipulation | \\/ M anipulation |
------------------------------------------------------------------------------- -------------------------------------------------------------------------------
Copyright (C) 2017-2022 OpenCFD Ltd. Copyright (C) 2017-2023 OpenCFD Ltd.
------------------------------------------------------------------------------- -------------------------------------------------------------------------------
License License
This file is part of OpenFOAM. This file is part of OpenFOAM.
@ -38,7 +38,7 @@ namespace
template<class WcIterator, class ReIterator> template<class WcIterator, class ReIterator>
bool findInPatterns bool findInPatterns
( (
const bool patternMatch, const bool literal,
const Foam::word& keyword, const Foam::word& keyword,
WcIterator& wcIter, WcIterator& wcIter,
ReIterator& reIter ReIterator& reIter
@ -48,9 +48,9 @@ namespace
{ {
if if
( (
patternMatch literal
? reIter()->match(keyword) ? wcIter()->keyword() == keyword
: wcIter()->keyword() == keyword : reIter()->match(keyword)
) )
{ {
return true; return true;
@ -76,7 +76,7 @@ Foam::dictionary::const_searcher Foam::dictionary::csearchDotScoped
{ {
auto scopePos = keyword.find('.'); auto scopePos = keyword.find('.');
if (scopePos == string::npos) if (scopePos == std::string::npos)
{ {
// Normal, non-scoped search // Normal, non-scoped search
return csearch(keyword, matchOpt); return csearch(keyword, matchOpt);
@ -141,7 +141,7 @@ Foam::dictionary::const_searcher Foam::dictionary::csearchDotScoped
// Local entry: // Local entry:
finder = csearch(keyword.substr(0, scopePos), matchOpt); finder = csearch(keyword.substr(0, scopePos), matchOpt);
if (scopePos == string::npos) if (scopePos == std::string::npos)
{ {
// Parsed the whole word. Return entry or null. // Parsed the whole word. Return entry or null.
return finder; return finder;
@ -176,7 +176,7 @@ Foam::dictionary::const_searcher Foam::dictionary::csearchSlashScoped
const auto slash = keyword.find('/'); const auto slash = keyword.find('/');
if (slash == string::npos) if (slash == std::string::npos)
{ {
// No slashes: // No slashes:
// Can use normal (non-scoped) search at the current dictionary level // Can use normal (non-scoped) search at the current dictionary level
@ -282,8 +282,8 @@ Foam::dictionary::const_searcher Foam::dictionary::csearch
auto wcLink = patterns_.cbegin(); auto wcLink = patterns_.cbegin();
auto reLink = regexps_.cbegin(); auto reLink = regexps_.cbegin();
// Find in patterns using regular expressions only // Find in patterns : non-literal matching
if (findInPatterns(true, keyword, wcLink, reLink)) if (findInPatterns(false, keyword, wcLink, reLink))
{ {
finder.set(*wcLink); finder.set(*wcLink);
return finder; return finder;
@ -334,7 +334,7 @@ Foam::dictionary::const_searcher Foam::dictionary::csearchScoped
if (keyword[0] == ':' || keyword[0] == '^') if (keyword[0] == ':' || keyword[0] == '^')
{ {
// It is ':' scoped - force non-recusive searching // It is ':' scoped - force non-recursive searching
matchOpt = keyType::option(matchOpt & ~(keyType::RECURSIVE)); matchOpt = keyType::option(matchOpt & ~(keyType::RECURSIVE));
// Ascend to top-level // Ascend to top-level
@ -397,9 +397,9 @@ const Foam::dictionary* Foam::dictionary::cfindScopedDict
fileName path(dictPath); // Work on copy fileName path(dictPath); // Work on copy
path.clean(); // Remove unneeded ".." path.clean(); // Remove unneeded ".."
const wordList dictCmpts(path.components()); // Split on '/' auto dictCmpts = stringOps::split(path, '/'); // Split on '/'
for (const word& cmpt : dictCmpts) for (const auto& cmpt : dictCmpts)
{ {
if (cmpt == ".") if (cmpt == ".")
{ {
@ -424,10 +424,12 @@ const Foam::dictionary* Foam::dictionary::cfindScopedDict
} }
else else
{ {
// Non-recursive, no patternMatch // Non-recursive, no patternMatch:
// -> can do direct lookup, without csearch(cmpt, false, false); // do direct lookup, without csearch(cmpt, keyType::LITERAL)
auto iter = dictPtr->hashedEntries_.cfind(cmpt); const word cmptName(cmpt.str(), false);
auto iter = dictPtr->hashedEntries_.cfind(cmptName);
if (iter.good()) if (iter.good())
{ {
@ -440,7 +442,7 @@ const Foam::dictionary* Foam::dictionary::cfindScopedDict
else else
{ {
FatalIOErrorInFunction(*dictPtr) FatalIOErrorInFunction(*dictPtr)
<< "Found entry '" << cmpt << "Found entry '" << cmptName
<< "' but not a dictionary, while searching scoped" << "' but not a dictionary, while searching scoped"
<< nl << nl
<< " " << path << " " << path
@ -527,9 +529,9 @@ Foam::dictionary* Foam::dictionary::makeScopedDict(const fileName& dictPath)
} }
else else
{ {
// Non-recursive, no patternMatch // Non-recursive, no patternMatch:
// -> can do direct lookup, // do direct lookup, without csearch(cmptName, keyType::LITERAL)
// without csearch(cmptName, keyType::LITERAL);
const word cmptName(cmpt.str(), false); const word cmptName(cmpt.str(), false);
auto iter = dictPtr->hashedEntries_.find(cmptName); auto iter = dictPtr->hashedEntries_.find(cmptName);
@ -589,8 +591,8 @@ bool Foam::dictionary::remove(const word& keyword)
auto wcLink = patterns_.begin(); auto wcLink = patterns_.begin();
auto reLink = regexps_.begin(); auto reLink = regexps_.begin();
// Find in pattern using exact match only // Find in patterns : literal matching
if (findInPatterns(false, keyword, wcLink, reLink)) if (findInPatterns(true, keyword, wcLink, reLink))
{ {
patterns_.remove(wcLink); patterns_.remove(wcLink);
regexps_.remove(reLink); regexps_.remove(reLink);
@ -650,8 +652,8 @@ bool Foam::dictionary::changeKeyword
auto wcLink = patterns_.begin(); auto wcLink = patterns_.begin();
auto reLink = regexps_.begin(); auto reLink = regexps_.begin();
// Find in patterns using exact match only // Find in patterns : literal matching
if (findInPatterns(false, iter2()->keyword(), wcLink, reLink)) if (findInPatterns(true, iter2()->keyword(), wcLink, reLink))
{ {
patterns_.remove(wcLink); patterns_.remove(wcLink);
regexps_.remove(reLink); regexps_.remove(reLink);
@ -674,7 +676,7 @@ bool Foam::dictionary::changeKeyword
// Change name and HashTable, but leave DL-List untouched // Change name and HashTable, but leave DL-List untouched
iter()->keyword() = newKeyword; iter()->keyword() = newKeyword;
iter()->name() = name() + '.' + newKeyword; iter()->name() = fileName::concat(name(), newKeyword, '/');
hashedEntries_.erase(oldKeyword); hashedEntries_.erase(oldKeyword);
hashedEntries_.insert(newKeyword, iter()); hashedEntries_.insert(newKeyword, iter());

View File

@ -267,7 +267,7 @@ Foam::primitiveEntry::primitiveEntry
entry(key), entry(key),
ITstream(is) ITstream(is)
{ {
ITstream::name() += '.' + key; ITstream::name() = fileName::concat(ITstream::name(), key, '/');
} }

View File

@ -262,7 +262,7 @@ Foam::primitiveEntry::primitiveEntry
ITstream ITstream
( (
static_cast<IOstreamOption>(is), static_cast<IOstreamOption>(is),
is.name() + '.' + key fileName::concat(is.name(), key, '/')
) )
{ {
readEntry(dict, is); readEntry(dict, is);