ENH: add failsafe accessors for ITstream

- failsafe examine elements: peek(), peekFirst(), peekLast()
- failsafe traversing: skip()

  For example,

      ITstream& is = dict.lookup(key);
      if (is.peek().isWord())
      {
          is.skip();
      }
This commit is contained in:
Mark Olesen
2021-04-16 16:49:45 +02:00
parent 9dc3d2bf40
commit b267f8b6da
3 changed files with 241 additions and 85 deletions

View File

@ -5,7 +5,7 @@
\\ / A nd | www.openfoam.com \\ / A nd | www.openfoam.com
\\/ M anipulation | \\/ M anipulation |
------------------------------------------------------------------------------- -------------------------------------------------------------------------------
Copyright (C) 2017 OpenCFD Ltd. Copyright (C) 2017-2021 OpenCFD Ltd.
------------------------------------------------------------------------------- -------------------------------------------------------------------------------
License License
This file is part of OpenFOAM. This file is part of OpenFOAM.
@ -78,7 +78,7 @@ void printTokens(Istream& is)
if (t.good()) if (t.good())
{ {
++count; ++count;
Info<<"token: " << t << endl; Info<< "token: " << t << endl;
} }
} }
@ -86,14 +86,28 @@ void printTokens(Istream& is)
} }
template<class BUF> Ostream& reportPeek(const ITstream& is)
void doTest(const string& name, const BUF& input, bool verbose=false)
{ {
Info<<"test " << name.c_str() << ":" << nl Info<< " index : " << is.tokenIndex() << nl
<<"====" << nl; << " peek : " << is.peek().info() << nl;
return Info;
}
template<class BUF>
void doTest
(
const string& name,
const BUF& input,
bool verbose = false,
bool testskip = false
)
{
Info<< "test " << name.c_str() << ":" << nl
<< "====" << nl;
toString(Info, input) toString(Info, input)
<< nl << nl
<<"====" << nl << endl; << "====" << nl << endl;
ITstream its(name, input); ITstream its(name, input);
Info<< "got " << its.size() << " tokens - index at " Info<< "got " << its.size() << " tokens - index at "
@ -107,6 +121,35 @@ void doTest(const string& name, const BUF& input, bool verbose=false)
} }
Info<< nl; Info<< nl;
} }
if (testskip)
{
Info<< " first : " << its.peekFirst().info() << nl
<< " last : " << its.peekLast().info() << nl;
Info<< "rewind():" << nl;
reportPeek(its);
its.skip(3);
Info<< "skip(3):" << nl;
reportPeek(its);
its.skip(2);
Info<< "skip(2):" << nl;
reportPeek(its);
its.skip(-2);
Info<< "skip(-2):" << nl;
reportPeek(its);
its.skip(100);
Info<< "skip(100):" << nl;
reportPeek(its);
its.skip(-1000);
Info<< "skip(-1000):" << nl;
reportPeek(its);
}
} }
@ -116,14 +159,16 @@ void doTest(const string& name, const BUF& input, bool verbose=false)
int main(int argc, char *argv[]) int main(int argc, char *argv[])
{ {
const char* charInput = const char* charInput =
"( const char input \"string\" to tokenize )" "( const char input \"string\" to tokenize )\n"
"List<label> 5(0 1 2 3 4);"; "List<label> 5(0 1 2 3 4);";
string stringInput("( string ; input \"string\" to tokenize )"); string stringInput("( string ; input \"string\" to tokenize )");
List<char> listInput(stringInput.cbegin(), stringInput.cend()); List<char> listInput(stringInput.cbegin(), stringInput.cend());
doTest("char*", charInput, true); doTest("empty", "", true, true);
doTest("char*", charInput, true, true);
doTest("string", stringInput, true); doTest("string", stringInput, true);
doTest("List<char>", listInput, true); doTest("List<char>", listInput, true);

View File

@ -31,6 +31,31 @@ License
#include "StringStream.H" #include "StringStream.H"
#include "UIListStream.H" #include "UIListStream.H"
// * * * * * * * * * * * * * * * Local Functions * * * * * * * * * * * * * * //
namespace
{
// Failsafe read-access.
// Return the token at location, or undefinedToken.
inline static const Foam::token& peekTokenAt
(
const Foam::UList<Foam::token>& list,
const Foam::label i
)
{
return
(
i >= 0 && i < list.size()
? list[i]
: Foam::token::undefinedToken
);
}
} // End anonymous namespace
// * * * * * * * * * * * * * Static Member Functions * * * * * * * * * * * * // // * * * * * * * * * * * * * Static Member Functions * * * * * * * * * * * * //
Foam::label Foam::ITstream::parseStream(ISstream& is, tokenList& tokens) Foam::label Foam::ITstream::parseStream(ISstream& is, tokenList& tokens)
@ -270,20 +295,21 @@ void Foam::ITstream::print(Ostream& os) const
{ {
os << "ITstream : " << name_.c_str() << ", line "; os << "ITstream : " << name_.c_str() << ", line ";
if (size()) const tokenList& toks = *this;
{
os << tokenList::first().lineNumber();
if (tokenList::first().lineNumber() < tokenList::last().lineNumber()) if (toks.empty())
{
os << '-' << tokenList::last().lineNumber();
}
}
else
{ {
os << lineNumber(); os << lineNumber();
} }
else
{
os << toks.first().lineNumber();
if (toks.first().lineNumber() < toks.last().lineNumber())
{
os << '-' << toks.last().lineNumber();
}
}
os << ", "; os << ", ";
IOstream::print(os); IOstream::print(os);
@ -292,44 +318,155 @@ void Foam::ITstream::print(Ostream& os) const
std::string Foam::ITstream::toString() const std::string Foam::ITstream::toString() const
{ {
OStringStream buf;
const tokenList& tokens = *this;
label len = tokens.size();
// NOTE: may wish to have special handling if there is a single token // NOTE: may wish to have special handling if there is a single token
// and it is already a string or word // and it is already a string or word
for (const token& tok : tokens) OStringStream buf;
unsigned i = 0;
for (const token& tok : *this)
{ {
buf << tok; if (i++)
if (--len)
{ {
buf << ' '; buf << ' ';
} }
buf << tok;
} }
return buf.str(); return buf.str();
} }
const Foam::token& Foam::ITstream::peekFirst() const
{
return peekTokenAt(*this, 0);
}
const Foam::token& Foam::ITstream::peekLast() const
{
return peekTokenAt(*this, tokenList::size()-1);
}
const Foam::token& Foam::ITstream::peek() const
{
// Use putback token if it exists
if (Istream::hasPutback())
{
return Istream::peekBack();
}
return peekTokenAt(*this, tokenIndex_);
}
void Foam::ITstream::seek(label pos)
{
lineNumber_ = 0;
const tokenList& toks = *this;
const label nToks = toks.size();
if (!pos)
{
// Seek begin (rewind)
tokenIndex_ = 0;
if (nToks)
{
lineNumber_ = toks.first().lineNumber();
}
setOpened();
setGood();
}
else if (pos < 0 || pos >= nToks)
{
// Seek end or seek is out of range
tokenIndex_ = nToks;
if (nToks)
{
lineNumber_ = toks.last().lineNumber();
}
setEof();
}
else
{
// Seek middle (from the beginning)
tokenIndex_ = pos;
if (nToks)
{
lineNumber_ = toks[tokenIndex_].lineNumber();
}
setOpened();
setGood();
}
}
void Foam::ITstream::skip(label n)
{
const tokenList& toks = *this;
const label nToks = toks.size();
if (n < 0)
{
// Move backwards
while (n++ && tokenIndex_)
{
--tokenIndex_;
}
if (tokenIndex_ < nToks)
{
lineNumber_ = toks[tokenIndex_].lineNumber();
setOpened();
setGood();
}
}
else if (n > 0)
{
// Move forward
while (n-- && tokenIndex_ < nToks)
{
++tokenIndex_;
}
if (tokenIndex_ < nToks)
{
lineNumber_ = toks[tokenIndex_].lineNumber();
setOpened();
setGood();
}
else
{
setEof();
}
}
}
Foam::Istream& Foam::ITstream::read(token& tok) Foam::Istream& Foam::ITstream::read(token& tok)
{ {
// Return the put back token if it exists // Use putback token if it exists
if (Istream::getBack(tok)) if (Istream::getBack(tok))
{ {
lineNumber_ = tok.lineNumber(); lineNumber_ = tok.lineNumber();
return *this; return *this;
} }
if (tokenIndex_ < size()) tokenList& toks = *this;
const label nToks = toks.size();
if (tokenIndex_ < nToks)
{ {
tok = operator[](tokenIndex_++); tok = toks[tokenIndex_++];
lineNumber_ = tok.lineNumber(); lineNumber_ = tok.lineNumber();
if (tokenIndex_ == size()) if (tokenIndex_ == nToks)
{ {
setEof(); setEof();
} }
@ -350,9 +487,9 @@ Foam::Istream& Foam::ITstream::read(token& tok)
tok.reset(); tok.reset();
if (size()) if (nToks)
{ {
tok.lineNumber(tokenList::last().lineNumber()); tok.lineNumber(toks.last().lineNumber());
} }
else else
{ {
@ -426,52 +563,6 @@ void Foam::ITstream::rewind()
} }
void Foam::ITstream::seek(label pos)
{
lineNumber_ = 0;
tokenList& toks = *this;
if (!pos)
{
// Seek begin (rewind)
tokenIndex_ = 0;
if (!toks.empty())
{
lineNumber_ = toks.first().lineNumber();
}
setOpened();
setGood();
}
else if (pos < 0 || pos >= toks.size())
{
// Seek end or seek is out of range
tokenIndex_ = toks.size();
if (!toks.empty())
{
lineNumber_ = toks.last().lineNumber();
}
setEof();
}
else
{
// Seek middle (from the beginning)
tokenIndex_ = pos;
if (!toks.empty())
{
lineNumber_ = toks[tokenIndex_].lineNumber();
}
setOpened();
setGood();
}
}
void Foam::ITstream::append(const token& t, const bool lazy) void Foam::ITstream::append(const token& t, const bool lazy)
{ {
reserveCapacity(tokenIndex_ + 1, lazy); reserveCapacity(tokenIndex_ + 1, lazy);

View File

@ -187,29 +187,49 @@ public:
// Token Access // Token Access
//- Failsafe peek at the \b first token in the list.
// \return \c undefinedToken if the list is empty.
const token& peekFirst() const;
//- Failsafe peek at the \b last token in the list.
// \return \c undefinedToken if the list is empty.
const token& peekLast() const;
//- Failsafe peek at what the next read would return,
// including handling of any putback
// \return \c undefinedToken if list is exhausted
const token& peek() const;
//- The current token index when reading, or the insertion point. //- The current token index when reading, or the insertion point.
label tokenIndex() const label tokenIndex() const noexcept
{ {
return tokenIndex_; return tokenIndex_;
} }
//- Non-const access to the current token index //- Non-const access to the current token index
label& tokenIndex() label& tokenIndex() noexcept
{ {
return tokenIndex_; return tokenIndex_;
} }
//- The number of remaining tokens //- Number of tokens remaining
label nRemainingTokens() const label nRemainingTokens() const noexcept
{ {
return size() - tokenIndex_; return size() - tokenIndex_;
} }
//- Move the tokenIndex to the specified position. //- Move tokenIndex to the specified position
// Using seek(0) is identical to rewind. // Using seek(0) is identical to rewind.
// Using seek(-1) moves to the end. // Using seek(-1) moves to the end.
void seek(label pos); void seek(label pos);
//- Move tokenIndex relative to the current position.
// Will not overrun the beginning or end positions.
//
// Use skip(2) to move forward two tokens.
// Use skip(-2) to move backward two tokens.
void skip(label n = 1);
// Inquiry // Inquiry