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
/*--------------------------------*- 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 @@
\\ / F ield | OpenFOAM: The Open Source CFD Toolbox
\\ / O peration |
\\ / A nd | Copyright (C) 2011-2015 OpenFOAM Foundation
\\/ M anipulation | Copyright (C) 2017 OpenCFD Ltd.
\\/ M anipulation | Copyright (C) 2017-2018 OpenCFD Ltd.
-------------------------------------------------------------------------------
License
This file is part of OpenFOAM.
......@@ -26,6 +26,27 @@ License
#include "primitiveEntry.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 * * * * * * * * * * * //
bool Foam::primitiveEntry::acceptToken
......@@ -86,6 +107,17 @@ bool Foam::primitiveEntry::read(const dictionary& dict, Istream& is)
{
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;
token tok;
......@@ -98,13 +130,65 @@ bool Foam::primitiveEntry::read(const dictionary& dict, Istream& is)
if (tok.isPunctuation())
{
const char c = tok.pToken();
if (c == token::BEGIN_BLOCK || c == token::BEGIN_LIST)
{
++depth;
}
else if (c == token::END_BLOCK || c == token::END_LIST)
switch (c)
{
--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)
tok = token::punctuationToken::NULL_TOKEN;
}
if (depth)
{
safeIOWarning(is, "Imbalanced brackets");
}
is.fatalCheck(FUNCTION_NAME);
return tok.good();
}
......@@ -126,7 +215,7 @@ bool Foam::primitiveEntry::read(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;
if (read(dict, is))
......@@ -242,7 +331,7 @@ Foam::Ostream& Foam::operator<<
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();
}
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment