mirror of
https://develop.openfoam.com/Development/openfoam.git
synced 2025-11-28 03:28:01 +00:00
ENH: dictionary: recursive variable expansion
This commit is contained in:
@ -38,6 +38,15 @@ inactive
|
||||
type zeroGradient;
|
||||
}
|
||||
|
||||
|
||||
// Indirection
|
||||
varType active;
|
||||
|
||||
// Indirection of values
|
||||
x 5;
|
||||
varName x;
|
||||
|
||||
|
||||
boundaryField
|
||||
{
|
||||
Default_Boundary_Region
|
||||
@ -52,7 +61,7 @@ boundaryField
|
||||
inlet_5 "a primitiveEntry is squashed by a directory entry";
|
||||
inlet_5 { $inactive }
|
||||
inlet_6 { $.inactive } // Test scoping
|
||||
inlet_7 { $inactive }
|
||||
inlet_7 { ${inactive}} // Test variable expansion
|
||||
inlet_8 { $inactive }
|
||||
|
||||
#include "testDictInc"
|
||||
@ -63,14 +72,14 @@ boundaryField
|
||||
inletValue $internalField;
|
||||
value #include "value";
|
||||
// error #remove self;
|
||||
x 5;
|
||||
x ${${varName}}; // Test indirection/recursive expansion
|
||||
y 6;
|
||||
}
|
||||
|
||||
// this should have no effect
|
||||
#remove inactive
|
||||
|
||||
inlet_7 { $active }
|
||||
inlet_7 { ${${varType}}} // Test indirection/recursive expansion
|
||||
#inputMode overwrite
|
||||
inlet_8 { $active }
|
||||
}
|
||||
|
||||
@ -2,7 +2,7 @@
|
||||
========= |
|
||||
\\ / F ield | OpenFOAM: The Open Source CFD Toolbox
|
||||
\\ / O peration |
|
||||
\\ / A nd | Copyright (C) 2011 OpenFOAM Foundation
|
||||
\\ / A nd | Copyright (C) 2011-2012 OpenFOAM Foundation
|
||||
\\/ M anipulation |
|
||||
-------------------------------------------------------------------------------
|
||||
License
|
||||
@ -27,8 +27,6 @@ License
|
||||
#include "int.H"
|
||||
#include "token.H"
|
||||
#include <cctype>
|
||||
#include "IOstreams.H"
|
||||
|
||||
|
||||
// * * * * * * * * * * * * * * Member Functions * * * * * * * * * * * * * * //
|
||||
|
||||
@ -243,6 +241,44 @@ Foam::Istream& Foam::ISstream::read(token& t)
|
||||
}
|
||||
}
|
||||
|
||||
case '$':
|
||||
{
|
||||
// Look ahead
|
||||
char nextC;
|
||||
if (read(nextC).bad())
|
||||
{
|
||||
// Return $ as word
|
||||
t = token(word(c));
|
||||
return *this;
|
||||
}
|
||||
else if (nextC == token::BEGIN_BLOCK)
|
||||
{
|
||||
putback(nextC);
|
||||
putback(c);
|
||||
|
||||
string* sPtr = new string;
|
||||
|
||||
if (readVariable(*sPtr).bad())
|
||||
{
|
||||
delete sPtr;
|
||||
t.setBad();
|
||||
}
|
||||
else
|
||||
{
|
||||
t = sPtr;
|
||||
t.type() = token::STRING;
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
else
|
||||
{
|
||||
putback(nextC);
|
||||
putback(c);
|
||||
readWordToken(t);
|
||||
return *this;
|
||||
}
|
||||
}
|
||||
|
||||
// Number: integer or floating point
|
||||
//
|
||||
// ideally match the equivalent of this regular expression
|
||||
@ -549,6 +585,127 @@ Foam::Istream& Foam::ISstream::read(string& str)
|
||||
}
|
||||
|
||||
|
||||
// Special handling of '{' in variables
|
||||
Foam::Istream& Foam::ISstream::readVariable(string& str)
|
||||
{
|
||||
static const int maxLen = 1024;
|
||||
static const int errLen = 80; // truncate error message for readability
|
||||
static char buf[maxLen];
|
||||
|
||||
register int nChar = 0;
|
||||
register int blockCount = 0;
|
||||
char c;
|
||||
|
||||
if (!get(c) || c != '$')
|
||||
{
|
||||
FatalIOErrorIn("ISstream::readVariable(string&)", *this)
|
||||
<< "invalid first character found : " << c
|
||||
<< exit(FatalIOError);
|
||||
}
|
||||
|
||||
buf[nChar++] = c;
|
||||
|
||||
// Read next character to see if '{'
|
||||
if (get(c) && c == token::BEGIN_BLOCK)
|
||||
{
|
||||
// Read, counting brackets
|
||||
buf[nChar++] = c;
|
||||
|
||||
while
|
||||
(
|
||||
get(c)
|
||||
&& (
|
||||
c == token::BEGIN_BLOCK
|
||||
|| c == token::END_BLOCK
|
||||
|| word::valid(c)
|
||||
)
|
||||
)
|
||||
{
|
||||
buf[nChar++] = c;
|
||||
if (nChar == maxLen)
|
||||
{
|
||||
buf[errLen] = '\0';
|
||||
|
||||
FatalIOErrorIn("ISstream::readVariable(string&)", *this)
|
||||
<< "word '" << buf << "...'\n"
|
||||
<< " is too long (max. " << maxLen << " characters)"
|
||||
<< exit(FatalIOError);
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
if (c == token::BEGIN_BLOCK)
|
||||
{
|
||||
blockCount++;
|
||||
}
|
||||
else if (c == token::END_BLOCK)
|
||||
{
|
||||
if (blockCount)
|
||||
{
|
||||
blockCount--;
|
||||
}
|
||||
else
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
buf[nChar++] = c;
|
||||
|
||||
while (get(c) && word::valid(c))
|
||||
{
|
||||
buf[nChar++] = c;
|
||||
if (nChar == maxLen)
|
||||
{
|
||||
buf[errLen] = '\0';
|
||||
|
||||
FatalIOErrorIn("ISstream::readVariable(string&)", *this)
|
||||
<< "word '" << buf << "...'\n"
|
||||
<< " is too long (max. " << maxLen << " characters)"
|
||||
<< exit(FatalIOError);
|
||||
|
||||
return *this;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// we could probably skip this check
|
||||
if (bad())
|
||||
{
|
||||
buf[errLen] = buf[nChar] = '\0';
|
||||
|
||||
FatalIOErrorIn("ISstream::readVariable(string&)", *this)
|
||||
<< "problem while reading string '" << buf << "...' after "
|
||||
<< nChar << " characters\n"
|
||||
<< exit(FatalIOError);
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
if (nChar == 0)
|
||||
{
|
||||
FatalIOErrorIn("ISstream::readVariable(string&)", *this)
|
||||
<< "invalid first character found : " << c
|
||||
<< exit(FatalIOError);
|
||||
}
|
||||
|
||||
// done reading
|
||||
buf[nChar] = '\0';
|
||||
str = buf;
|
||||
|
||||
// Note: check if we exited due to '}' or just !word::valid.
|
||||
if (c != token::END_BLOCK)
|
||||
{
|
||||
putback(c);
|
||||
}
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
|
||||
Foam::Istream& Foam::ISstream::readVerbatim(string& str)
|
||||
{
|
||||
static const int maxLen = 8000;
|
||||
|
||||
@ -71,6 +71,8 @@ class ISstream
|
||||
//- Read a verbatim string (excluding block delimiters).
|
||||
Istream& readVerbatim(string&);
|
||||
|
||||
//- Read a variable name (includes '{')
|
||||
Istream& readVariable(string&);
|
||||
|
||||
//- Disallow default bitwise assignment
|
||||
void operator=(const ISstream&);
|
||||
|
||||
@ -28,6 +28,7 @@ License
|
||||
#include "functionEntry.H"
|
||||
#include "includeEntry.H"
|
||||
#include "inputModeEntry.H"
|
||||
#include "stringOps.H"
|
||||
|
||||
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
|
||||
|
||||
@ -110,8 +111,19 @@ bool Foam::entry::New(dictionary& parentDict, Istream& is)
|
||||
else if
|
||||
(
|
||||
!disableFunctionEntries
|
||||
&& keyword[0] == '$') // ... Substitution entry
|
||||
&& keyword[0] == '$'
|
||||
) // ... Substitution entry
|
||||
{
|
||||
if (keyword.size() > 2 && keyword[1] == token::BEGIN_BLOCK)
|
||||
{
|
||||
// Recursive substitution mode. Replace between {} with
|
||||
// expansion.
|
||||
string s(keyword(2, keyword.size()-3));
|
||||
// Substitute dictionary and environment variables. Allow
|
||||
// empty substitutions.
|
||||
stringOps::inplaceExpand(s, parentDict, true, true);
|
||||
keyword.std::string::replace(1, keyword.size()-1, s);
|
||||
}
|
||||
parentDict.substituteScopedKeyword(keyword);
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -74,7 +74,8 @@ Foam::fileName Foam::functionEntries::includeEntry::includeFileName
|
||||
)
|
||||
{
|
||||
fileName fName(is);
|
||||
// Substitute dictionary and environment variables
|
||||
// Substitute dictionary and environment variables. Allow empty
|
||||
// substitutions.
|
||||
stringOps::inplaceExpand(fName, dict, true, true);
|
||||
|
||||
if (fName.empty() || fName.isAbsolute())
|
||||
|
||||
@ -26,6 +26,7 @@ License
|
||||
#include "primitiveEntry.H"
|
||||
#include "dictionary.H"
|
||||
#include "OSspecific.H"
|
||||
#include "stringOps.H"
|
||||
|
||||
// * * * * * * * * * * * * * Private Member Functions * * * * * * * * * * * //
|
||||
|
||||
@ -40,35 +41,52 @@ void Foam::primitiveEntry::append(const UList<token>& varTokens)
|
||||
|
||||
bool Foam::primitiveEntry::expandVariable
|
||||
(
|
||||
const word& w,
|
||||
const string& w,
|
||||
const dictionary& dict
|
||||
)
|
||||
{
|
||||
word varName = w(1, w.size()-1);
|
||||
|
||||
// lookup the variable name in the given dictionary....
|
||||
// Note: allow wildcards to match? For now disabled since following
|
||||
// would expand internalField to wildcard match and not expected
|
||||
// internalField:
|
||||
// internalField XXX;
|
||||
// boundaryField { ".*" {YYY;} movingWall {value $internalField;}
|
||||
const entry* ePtr = dict.lookupScopedEntryPtr(varName, true, false);
|
||||
|
||||
// ...if defined append its tokens into this
|
||||
if (ePtr)
|
||||
if (w.size() > 2 && w[0] == '$' && w[1] == token::BEGIN_BLOCK)
|
||||
{
|
||||
append(ePtr->stream());
|
||||
// Recursive substitution mode. Replace between {} with
|
||||
// expansion.
|
||||
string s(w(2, w.size()-3));
|
||||
// Substitute dictionary and environment variables. Allow
|
||||
// empty substitutions.
|
||||
stringOps::inplaceExpand(s, dict, true, true);
|
||||
|
||||
string newW(w);
|
||||
newW.std::string::replace(1, newW.size()-1, s);
|
||||
|
||||
return expandVariable(newW, dict);
|
||||
}
|
||||
else
|
||||
{
|
||||
// not in the dictionary - try an environment variable
|
||||
string envStr = getEnv(varName);
|
||||
string varName = w(1, w.size()-1);
|
||||
|
||||
if (envStr.empty())
|
||||
// lookup the variable name in the given dictionary....
|
||||
// Note: allow wildcards to match? For now disabled since following
|
||||
// would expand internalField to wildcard match and not expected
|
||||
// internalField:
|
||||
// internalField XXX;
|
||||
// boundaryField { ".*" {YYY;} movingWall {value $internalField;}
|
||||
const entry* ePtr = dict.lookupScopedEntryPtr(varName, true, false);
|
||||
|
||||
// ...if defined append its tokens into this
|
||||
if (ePtr)
|
||||
{
|
||||
return false;
|
||||
append(ePtr->stream());
|
||||
}
|
||||
else
|
||||
{
|
||||
// not in the dictionary - try an environment variable
|
||||
string envStr = getEnv(varName);
|
||||
|
||||
if (envStr.empty())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
append(tokenList(IStringStream('(' + envStr + ')')()));
|
||||
}
|
||||
append(tokenList(IStringStream('(' + envStr + ')')()));
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -2,7 +2,7 @@
|
||||
========= |
|
||||
\\ / F ield | OpenFOAM: The Open Source CFD Toolbox
|
||||
\\ / O peration |
|
||||
\\ / A nd | Copyright (C) 2011 OpenFOAM Foundation
|
||||
\\ / A nd | Copyright (C) 2011-2012 OpenFOAM Foundation
|
||||
\\/ M anipulation |
|
||||
-------------------------------------------------------------------------------
|
||||
License
|
||||
@ -79,7 +79,7 @@ class primitiveEntry
|
||||
);
|
||||
|
||||
//- Expand the given variable (keyword starts with $)
|
||||
bool expandVariable(const word&, const dictionary&);
|
||||
bool expandVariable(const string&, const dictionary&);
|
||||
|
||||
//- Expand the given function (keyword starts with #)
|
||||
bool expandFunction
|
||||
|
||||
@ -55,6 +55,24 @@ void Foam::primitiveEntry::append
|
||||
newElmt(tokenIndex()++) = currToken;
|
||||
}
|
||||
}
|
||||
else if (currToken.isString())
|
||||
{
|
||||
const string& w = currToken.stringToken();
|
||||
|
||||
if
|
||||
(
|
||||
disableFunctionEntries
|
||||
|| w.size() <= 3
|
||||
|| !(
|
||||
w[0] == '$'
|
||||
&& w[1] == token::BEGIN_BLOCK
|
||||
&& expandVariable(w, dict)
|
||||
)
|
||||
)
|
||||
{
|
||||
newElmt(tokenIndex()++) = currToken;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
newElmt(tokenIndex()++) = currToken;
|
||||
|
||||
Reference in New Issue
Block a user