From 083181cac4bfa794b3cec3c6dc7a392e8ab51b8c Mon Sep 17 00:00:00 2001 From: Mark Olesen Date: Fri, 21 Feb 2020 14:37:11 +0100 Subject: [PATCH] ENH: handle entry alternatives outside of string expansion - string expansions have supported "${var:-default}" syntax for several versions, but this did not apply plain dictionary expansions. Eg, the following did not parse massFlow ${entry1:-100}; ENH: remove content and length restriction on '${..}' quoted variables - allows this type of content: velocity2 ${velocity1:- ( 0 -100 10) }; - accept empty parameter strings for entries. This allows the following expansion to work as expected: hex (n1 n2..) ${inletBlock:-} (10 10 10) simpleGrading (1 1 1) ie, optionally define the cellZone name for a given block ENH: add single parameter dictionary writeEntry method. - the dictionary knows its own name (dictName), which can be used when writing content --- src/OpenFOAM/db/IOstreams/Sstreams/ISstream.C | 56 +++++--- src/OpenFOAM/db/dictionary/dictionary.H | 5 +- src/OpenFOAM/db/dictionary/dictionaryIO.C | 10 +- .../primitiveEntry/primitiveEntry.C | 132 ++++++++++++++++-- tutorials/IO/dictionary/good-if2.dict | 57 +++++++- 5 files changed, 218 insertions(+), 42 deletions(-) diff --git a/src/OpenFOAM/db/IOstreams/Sstreams/ISstream.C b/src/OpenFOAM/db/IOstreams/Sstreams/ISstream.C index 360fac565e..cfc56d7e9a 100644 --- a/src/OpenFOAM/db/IOstreams/Sstreams/ISstream.C +++ b/src/OpenFOAM/db/IOstreams/Sstreams/ISstream.C @@ -436,7 +436,7 @@ Foam::Istream& Foam::ISstream::read(word& str) { if (!depth) { - break; // Closed ')' without a '(' ? ... stop + break; // Closed ')' without an opening '(' ? ... stop } --depth; } @@ -627,43 +627,55 @@ Foam::Istream& Foam::ISstream::readVariable(std::string& str) } buf[nChar++] = c; - char endChar = token::END_LIST; - + str.clear(); if (c == token::BEGIN_BLOCK) { - // Processing ${...} style - - endChar = token::END_BLOCK; + // Processing ${...} style. ++depth; // Could check that the next char is good and not one of '{}' // since this would indicate "${}", "${{..." or truncated "${" - while - ( - (nChar < maxLen) && get(c) - && - ( - validVariableChar(c) - || (c == token::BEGIN_BLOCK || c == token::END_BLOCK) - ) - ) + while (get(c)) { + buf[nChar++] = c; + if (nChar == maxLen) + { + str.append(buf, nChar); + nChar = 0; + } if (c == token::BEGIN_BLOCK) { ++depth; } else if (c == token::END_BLOCK) { + --depth; if (!depth) { - break; // Closed '}' without a '{' ? ... stop + // Found closing '}' character + str.append(buf, nChar); + return *this; } - --depth; } - - buf[nChar++] = c; } + + // Should never reach here on normal input + + str.append(buf, nChar); // Finalize pending buffer input + + nChar = str.length(); + if (str.length() > errLen) + { + str.erase(errLen); + } + + FatalIOErrorInFunction(*this) + << "stream terminated while reading variable '" + << str.c_str() << "...' [" << nChar << "]\n" + << exit(FatalIOError); + + return *this; } else if (validVariableChar(c)) { @@ -683,7 +695,7 @@ Foam::Istream& Foam::ISstream::readVariable(std::string& str) { if (!depth) { - break; // Closed ')' without a '(' ? ... stop + break; // Closed ')' without an opening '(' ? ... stop } --depth; } @@ -733,7 +745,7 @@ Foam::Istream& Foam::ISstream::readVariable(std::string& str) { IOWarningInFunction(*this) << "Missing " << depth - << " closing '" << endChar << "' while parsing" << nl << nl + << " closing ')' while parsing" << nl << nl << buf << nl << endl; } @@ -762,7 +774,7 @@ Foam::Istream& Foam::ISstream::readVerbatim(std::string& str) get(nextC); if (nextC == token::END_BLOCK) { - // The closing "#}" found + // Found closing "#}" sequence str.append(buf, nChar); return *this; } diff --git a/src/OpenFOAM/db/dictionary/dictionary.H b/src/OpenFOAM/db/dictionary/dictionary.H index e102648c89..9df99994ef 100644 --- a/src/OpenFOAM/db/dictionary/dictionary.H +++ b/src/OpenFOAM/db/dictionary/dictionary.H @@ -6,7 +6,7 @@ \\/ M anipulation | ------------------------------------------------------------------------------- Copyright (C) 2011-2017 OpenFOAM Foundation - Copyright (C) 2016-2019 OpenCFD Ltd. + Copyright (C) 2016-2020 OpenCFD Ltd. ------------------------------------------------------------------------------- License This file is part of OpenFOAM. @@ -937,6 +937,9 @@ public: // Write + //- Write sub-dictionary with its dictName as its header + void writeEntry(Ostream& os) const; + //- Write sub-dictionary with the keyword as its header void writeEntry(const keyType& keyword, Ostream& os) const; diff --git a/src/OpenFOAM/db/dictionary/dictionaryIO.C b/src/OpenFOAM/db/dictionary/dictionaryIO.C index 5a2c88d220..eaed0cc491 100644 --- a/src/OpenFOAM/db/dictionary/dictionaryIO.C +++ b/src/OpenFOAM/db/dictionary/dictionaryIO.C @@ -6,7 +6,7 @@ \\/ M anipulation | ------------------------------------------------------------------------------- Copyright (C) 2011-2017 OpenFOAM Foundation - Copyright (C) 2016-2019 OpenCFD Ltd. + Copyright (C) 2016-2020 OpenCFD Ltd. ------------------------------------------------------------------------------- License This file is part of OpenFOAM. @@ -161,6 +161,14 @@ Foam::Istream& Foam::operator>>(Istream& is, dictionary& dict) // * * * * * * * * * * * * * * Ostream Operator * * * * * * * * * * * * * * // +void Foam::dictionary::writeEntry(Ostream& os) const +{ + os.beginBlock(dictName()); + writeEntries(os); + os.endBlock(); +} + + void Foam::dictionary::writeEntry(const keyType& kw, Ostream& os) const { os.beginBlock(kw); diff --git a/src/OpenFOAM/db/dictionary/primitiveEntry/primitiveEntry.C b/src/OpenFOAM/db/dictionary/primitiveEntry/primitiveEntry.C index c7402d1322..fed40c17a3 100644 --- a/src/OpenFOAM/db/dictionary/primitiveEntry/primitiveEntry.C +++ b/src/OpenFOAM/db/dictionary/primitiveEntry/primitiveEntry.C @@ -6,7 +6,7 @@ \\/ M anipulation | ------------------------------------------------------------------------------- Copyright (C) 2011-2016 OpenFOAM Foundation - Copyright (C) 2017-2019 OpenCFD Ltd. + Copyright (C) 2017-2020 OpenCFD Ltd. ------------------------------------------------------------------------------- License This file is part of OpenFOAM. @@ -31,6 +31,45 @@ License #include "OSspecific.H" #include "stringOps.H" +// * * * * * * * * * * * * * * * Local Functions * * * * * * * * * * * * * * // + +// Find the type/position of the ":-" or ":+" alternative values +// Returns 0, '-', '+' corresponding to not-found or ':-' or ':+' +static inline int findParameterAlternative +( + const std::string& s, + std::string::size_type& pos, + std::string::size_type endPos = std::string::npos +) +{ + while (pos != std::string::npos) + { + pos = s.find(':', pos); + if (pos != std::string::npos) + { + if (pos < endPos) + { + // in-range: check for '+' or '-' following the ':' + const int altType = s[pos+1]; + if (altType == '+' || altType == '-') + { + return altType; + } + + ++pos; // unknown/unsupported - continue at next position + } + else + { + // out-of-range: abort + pos = std::string::npos; + } + } + } + + return 0; +} + + // * * * * * * * * * * * * * Private Member Functions * * * * * * * * * * * // bool Foam::primitiveEntry::expandVariable @@ -39,19 +78,49 @@ bool Foam::primitiveEntry::expandVariable const dictionary& dict ) { + int altType = 0; // Type ('-' or '+') for ":-" or ":+" alternatives + word expanded; + string altValue; + if (varName.size() > 1 && varName[0] == token::BEGIN_BLOCK) { - // Recursive substitution mode. - // Content between {} is replaced with expansion. - string expanded(varName.substr(1, varName.size()-2)); + // Replace content between {} with string expansion and + // handle ${parameter:-word} or ${parameter:+word} + + // Copy into a word without stripping + expanded.assign(varName, 1, varName.size()-2); // Substitute dictionary and environment variables. - // Do not allow empty substitutions. - stringOps::inplaceExpand(expanded, dict, true, false); + // - Allow environment. + // - No empty substitutions. + // - No sub-dictionary lookups - return expandVariable(expanded, dict); + stringOps::inplaceExpand(expanded, dict, true, false, false); + + // Position of ":-" or ":+" alternative values + std::string::size_type altPos = 0; + + // Check for parameter:-word or parameter:+word + altType = findParameterAlternative(expanded, altPos); + + if (altType) + { + altValue = expanded.substr(altPos + 2); + expanded.erase(altPos); + } + + // Catch really bad expansions and let them die soon after. + // Eg, ${:-other} should not be allowed. + if (expanded.empty()) + { + altType = 0; + altValue.clear(); + } + + // Fallthrough for further processing } + // Lookup variable name in the given dictionary WITHOUT pattern matching. // Having a pattern match means that in this example: // { @@ -61,18 +130,36 @@ bool Foam::primitiveEntry::expandVariable // The $internalField would be matched by the ".*" !!! // Recursive, non-patterns - const entry* eptr = dict.findScoped(varName, keyType::LITERAL_RECURSIVE); + + const word& lookupName = (expanded.empty() ? varName : expanded); + + const entry* eptr = + dict.findScoped(lookupName, keyType::LITERAL_RECURSIVE); if (!eptr) { // Not found - revert to environment variable - const string str(Foam::getEnv(varName)); + // and parse into a series of tokens. - if (str.empty()) + // We wish to fail if the environment variable returns + // an empty string and there is no alternative given. + // + // Always allow empty strings as alternative parameters, + // since the user provided them for a reason. + + string str(Foam::getEnv(lookupName)); + + if (str.empty() ? (altType == '-') : (altType == '+')) + { + // Not found or empty: use ":-" alternative value + // Found and not empty: use ":+" alternative value + str = std::move(altValue); + } + else if (str.empty()) { FatalIOErrorInFunction(dict) << "Illegal dictionary entry or environment variable name " - << varName << nl + << lookupName << nl << "Known dictionary entries: " << dict.toc() << nl << exit(FatalIOError); @@ -91,12 +178,33 @@ bool Foam::primitiveEntry::expandVariable tokenList toks(eptr->dict().tokens()); + if (toks.empty() ? (altType == '-') : (altType == '+')) + { + // Not found or empty: use ":-" alternative value + // Found and not empty: use ":+" alternative value + + toks = ITstream::parse(altValue, IOstream::ASCII); + } + ITstream::append(std::move(toks), true); // Lazy resizing } else { // Found primitive entry - copy tokens - ITstream::append(eptr->stream(), true); // Lazy resizing + + if (eptr->stream().empty() ? (altType == '-') : (altType == '+')) + { + // Not found or empty: use ":-" alternative value + // Found and not empty: use ":+" alternative value + + tokenList toks(ITstream::parse(altValue, IOstream::ASCII)); + + ITstream::append(std::move(toks), true); // Lazy resizing + } + else + { + ITstream::append(eptr->stream(), true); // Lazy resizing + } } return true; diff --git a/tutorials/IO/dictionary/good-if2.dict b/tutorials/IO/dictionary/good-if2.dict index c620ff18ba..c795cc37ca 100644 --- a/tutorials/IO/dictionary/good-if2.dict +++ b/tutorials/IO/dictionary/good-if2.dict @@ -1,7 +1,7 @@ /*--------------------------------*- C++ -*----------------------------------*\ | ========= | | | \\ / F ield | OpenFOAM: The Open Source CFD Toolbox | -| \\ / O peration | Version: v1912 | +| \\ / O peration | Version: v2006 | | \\ / A nd | Website: www.openfoam.com | | \\/ M anipulation | | \*---------------------------------------------------------------------------*/ @@ -14,21 +14,48 @@ FoamFile } // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // -// Do comparison +// Do comparison. Handles the first token after then '#if', which should +// correspond to a logical (true/false, ...) and integer (0,1, ...) +// but also a floating-point value with 0-1 range. -#if #eval "${FOAM_API:-0}" - foamApi nonZero; +#if ${FOAM_API:-false} + foamApi nonZero is ${FOAM_API:-0}; #else foamApi zeroValue; #endif -#if #eval "${XX_XXX_FOAM_API:-1000}" - other "some entry"; +#if ${XX_XXX_FOAM_API:-1000} + other "some entry" ${XX_XXX_FOAM_API:-(0 1 0)}; #else other "unexpected"; #endif +#if 0.1 + roundToZero failed; +#else + roundToZero good with ${__expand_or_ignore_:-}; +#endif + +#if 0.99 + roundToOne good; +#else + roundToOne failed; +#endif + +#if -0.1 + roundNegZero failed; +#else + roundNegZero good; +#endif + +#if -0.99 + roundToNegOne good; +#else + roundToNegOne failed; +#endif + + #if #eval "${FOAM_API:-0} >= 1910" evalType hasEvalWithConditionals; #else @@ -44,5 +71,23 @@ FoamFile condition false; #endif +// Some other conditionals + +condition1 true; +condition2 false; + +#if ${unknown:-${condition2:-${condition1}}} + multiExpansion1 failed; +#else + multiExpansion1 good; +#endif + + +#if ${unknown:-${unknown:-${condition2:+true}}} + multiExpansion2 good = ${unkn:-${unkn:-${condition2:+on}}}; +#else + multiExpansion2 failed; +#endif + // ************************************************************************* //