Skip to content
Snippets Groups Projects
Commit 2fde6c3a authored by Mark OLESEN's avatar Mark OLESEN
Browse files

ENH: improve handling of mismatched brackets, forgotten ';' (issue #762)

- flags the following type of problems:

  * mismatches:

        keyword  mismatch ( set of { brackets ) in the } entry;

  * underflow (too many closing brackets:

        keyword  too many ( set of ) brackets ) in ) entry;

- a missing semi-colon

        dict
        {
           keyword  entry with missing semi-colon
        }

  will be flagged as 'underflow', since it parses through the '}' but
  did not open with it.

Max monitoring depth is 60 levels of nesting, to avoid incurring any
memory allocation.
parent 76cb38fb
No related branches found
No related tags found
No related merge requests found
/*--------------------------------*- C++ -*----------------------------------*\
| ========= | |
| \\ / F ield | OpenFOAM: The Open Source CFD Toolbox |
| \\ / O peration | Version: plus |
| \\ / A nd | Web: www.OpenFOAM.com |
| \\/ M anipulation | |
\*---------------------------------------------------------------------------*/
FoamFile
{
version 2.0;
format ascii;
class dictionary;
object testDict;
}
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
// Test parsing of primitive entry
// Can pass through foamDictionary -expand for test
dict1
{
entry1 no brackets;
entry2 balanced ( set of { brackets } in the ) entry;
entry3a mismatch ( set of { brackets ) in the } entry;
entry3b mismatch ( set of { brackets } in the } entry;
// Runs on
// entry3c too many ( set of ) brackets ) in ) entry;
//entry4 missing closing ( set of { brackets } in entry;
}
dict2
{
entry1 no brackets;
entry2 no brackets but missing semi-colon
}
dict3
{
entry1 anything;
}
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
...@@ -3,7 +3,7 @@ ...@@ -3,7 +3,7 @@
\\ / F ield | OpenFOAM: The Open Source CFD Toolbox \\ / F ield | OpenFOAM: The Open Source CFD Toolbox
\\ / O peration | \\ / O peration |
\\ / A nd | Copyright (C) 2011-2015 OpenFOAM Foundation \\ / A nd | Copyright (C) 2011-2015 OpenFOAM Foundation
\\/ M anipulation | Copyright (C) 2017 OpenCFD Ltd. \\/ M anipulation | Copyright (C) 2017-2018 OpenCFD Ltd.
------------------------------------------------------------------------------- -------------------------------------------------------------------------------
License License
This file is part of OpenFOAM. This file is part of OpenFOAM.
...@@ -26,6 +26,27 @@ License ...@@ -26,6 +26,27 @@ License
#include "primitiveEntry.H" #include "primitiveEntry.H"
#include "functionEntry.H" #include "functionEntry.H"
// * * * * * * * * * * * * * * * Local Functions * * * * * * * * * * * * * * //
namespace
{
// This is akin to a SafeIOWarning, which does not yet exist
inline void safeIOWarning
(
const Foam::IOstream& is,
const std::string& msg
)
{
std::cerr
<< "--> FOAM Warning :\n"
<< " Reading \"" << is.name() << "\" at line "
<< is.lineNumber() << '\n'
<< " " << msg << std::endl;
}
} // end of anonymous namespace
// * * * * * * * * * * * * * Private Member Functions * * * * * * * * * * * // // * * * * * * * * * * * * * Private Member Functions * * * * * * * * * * * //
bool Foam::primitiveEntry::acceptToken bool Foam::primitiveEntry::acceptToken
...@@ -86,6 +107,17 @@ bool Foam::primitiveEntry::read(const dictionary& dict, Istream& is) ...@@ -86,6 +107,17 @@ bool Foam::primitiveEntry::read(const dictionary& dict, Istream& is)
{ {
is.fatalCheck(FUNCTION_NAME); is.fatalCheck(FUNCTION_NAME);
// Track balanced bracket/brace pairs, with max stack depth of 60.
// Use a bitmask to track the opening char: 0 = '()', 1 = '{}'
//
// Notes
// - the bitmask is set *before* increasing the depth since the left
// shift implicitly carries a 1-offset with it.
// Eg, (1u << 0) already corresponds to depth=1 (the first bit)
//
// - similarly, the bitmask is tested *after* decreasing depth
uint64_t balanced = 0u;
label depth = 0; label depth = 0;
token tok; token tok;
...@@ -98,13 +130,65 @@ bool Foam::primitiveEntry::read(const dictionary& dict, Istream& is) ...@@ -98,13 +130,65 @@ bool Foam::primitiveEntry::read(const dictionary& dict, Istream& is)
if (tok.isPunctuation()) if (tok.isPunctuation())
{ {
const char c = tok.pToken(); const char c = tok.pToken();
if (c == token::BEGIN_BLOCK || c == token::BEGIN_LIST) switch (c)
{
++depth;
}
else if (c == token::END_BLOCK || c == token::END_LIST)
{ {
--depth; case token::BEGIN_LIST:
{
if (depth >= 0 && depth < 61)
{
balanced &= ~(1u << depth); // clear bit
}
++depth;
}
break;
case token::BEGIN_BLOCK:
{
if (depth >= 0 && depth < 61)
{
balanced |= (1u << depth); // set bit
}
++depth;
}
break;
case token::END_LIST:
{
--depth;
if (depth < 0)
{
safeIOWarning
(
is,
"Too many closing ')' ... was a ';' forgotten?"
);
}
else if (depth < 61 && ((balanced >> depth) & 1u))
{
// Bit was set, but expected it to be unset.
safeIOWarning(is, "Imbalanced '{' with ')'");
}
}
break;
case token::END_BLOCK:
{
--depth;
if (depth < 0)
{
safeIOWarning
(
is,
"Too many closing '}' ... was a ';' forgotten?"
);
}
else if (depth < 61 && !((balanced >> depth) & 1u))
{
// Bit was unset, but expected it to be set.
safeIOWarning(is, "Imbalanced '(' with '}'");
}
}
break;
} }
} }
...@@ -119,6 +203,11 @@ bool Foam::primitiveEntry::read(const dictionary& dict, Istream& is) ...@@ -119,6 +203,11 @@ bool Foam::primitiveEntry::read(const dictionary& dict, Istream& is)
tok = token::punctuationToken::NULL_TOKEN; tok = token::punctuationToken::NULL_TOKEN;
} }
if (depth)
{
safeIOWarning(is, "Imbalanced brackets");
}
is.fatalCheck(FUNCTION_NAME); is.fatalCheck(FUNCTION_NAME);
return tok.good(); return tok.good();
} }
...@@ -126,7 +215,7 @@ bool Foam::primitiveEntry::read(const dictionary& dict, Istream& is) ...@@ -126,7 +215,7 @@ bool Foam::primitiveEntry::read(const dictionary& dict, Istream& is)
void Foam::primitiveEntry::readEntry(const dictionary& dict, Istream& is) void Foam::primitiveEntry::readEntry(const dictionary& dict, Istream& is)
{ {
label keywordLineNumber = is.lineNumber(); const label keywordLineNumber = is.lineNumber();
tokenIndex() = 0; tokenIndex() = 0;
if (read(dict, is)) if (read(dict, is))
...@@ -242,7 +331,7 @@ Foam::Ostream& Foam::operator<< ...@@ -242,7 +331,7 @@ Foam::Ostream& Foam::operator<<
os << " primitiveEntry '" << e.keyword() << "' comprises "; os << " primitiveEntry '" << e.keyword() << "' comprises ";
for (label i=0; i<min(e.size(), nPrintTokens); i++) for (label i=0; i<min(e.size(), nPrintTokens); ++i)
{ {
os << nl << " " << e[i].info(); os << nl << " " << e[i].info();
} }
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment