Skip to content

Commit d515dc6

Browse files
committed
[parser] Check for properly balanced #end, braces, parentheses etc.
1 parent c6f8284 commit d515dc6

File tree

4 files changed

+73
-8
lines changed

4 files changed

+73
-8
lines changed

changes.txt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -105,6 +105,9 @@ New Features
105105
and the bit depth.
106106
- Support for blue noise dithering has been added, plus a couple more error
107107
diffusion dithering filters.
108+
- The parser now checks for proper balancing of `#end` directives, braces,
109+
parentheses etc. within each include file, and will report any imbalance
110+
via warnings or, in case of `#end`, outright errors.
108111

109112
Performance Improvements
110113
------------------------

source/parser/parser.cpp

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -769,7 +769,15 @@ void Parser::Parse_End(TokenId openTokenId, TokenId expectTokenId)
769769
{
770770
POV_PARSER_ASSERT(!maBraceStack.empty());
771771
POV_PARSER_ASSERT(openTokenId == maBraceStack.back().openToken);
772+
773+
if (!maIncludeStack.empty() && (maBraceStack.size() <= maIncludeStack.back().braceStackSize))
774+
{
775+
BraceStackEntry& braceStackEntry = maBraceStack.back();
776+
// Include file has closed more braces/parentheses/etc. than it has opened.
777+
Warning("Unbalanced %s in include file", Get_Token_String(CurrentTokenId()));
778+
}
772779
maBraceStack.pop_back();
780+
773781
return;
774782
}
775783

@@ -10611,6 +10619,18 @@ void Parser::Warning(const char *format,...)
1061110619
Warning(kWarningGeneral, localvsbuffer);
1061210620
}
1061310621

10622+
void Parser::Warning(const MessageContext& loc, const char *format, ...)
10623+
{
10624+
va_list marker;
10625+
char localvsbuffer[1024];
10626+
10627+
va_start(marker, format);
10628+
std::vsnprintf(localvsbuffer, sizeof(localvsbuffer), format, marker);
10629+
va_end(marker);
10630+
10631+
Warning(kWarningGeneral, loc, localvsbuffer);
10632+
}
10633+
1061410634
void Parser::Warning(WarningLevel level, const char *format,...)
1061510635
{
1061610636
POV_PARSER_ASSERT(level >= kWarningGeneral);
@@ -10628,6 +10648,20 @@ void Parser::Warning(WarningLevel level, const char *format,...)
1062810648
mMessageFactory.Warning(level, "%s", localvsbuffer);
1062910649
}
1063010650

10651+
void Parser::Warning(WarningLevel level, const MessageContext& loc, const char *format, ...)
10652+
{
10653+
POV_PARSER_ASSERT(level >= kWarningGeneral);
10654+
10655+
va_list marker;
10656+
char localvsbuffer[1024];
10657+
10658+
va_start(marker, format);
10659+
std::vsnprintf(localvsbuffer, sizeof(localvsbuffer), format, marker);
10660+
va_end(marker);
10661+
10662+
mMessageFactory.WarningAt(level, loc, "%s", localvsbuffer);
10663+
}
10664+
1063110665
void Parser::VersionWarning(unsigned int sinceVersion, const char *format,...)
1063210666
{
1063310667
if(sceneData->EffectiveLanguageVersion() >= sinceVersion)

source/parser/parser.h

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -421,7 +421,9 @@ class Parser
421421
void SendFatalError(Exception& e);
422422

423423
void Warning(const char *format,...);
424+
void Warning(const MessageContext& loc, const char *format, ...);
424425
void Warning(WarningLevel level, const char *format,...);
426+
void Warning(WarningLevel level, const MessageContext& loc, const char *format, ...);
425427
void VersionWarning(unsigned int sinceVersion, const char *format,...);
426428
void PossibleError(const char *format,...);
427429
void Error(const char *format,...);
@@ -660,7 +662,17 @@ class Parser
660662
POV_LONG mTokenCount;
661663
int mTokensSinceLastProgressReport;
662664

663-
vector<RawTokenizer::HotBookmark> maIncludeStack;
665+
struct IncludeStackEntry
666+
{
667+
RawTokenizer::HotBookmark returnToBookmark;
668+
int condStackSize;
669+
int braceStackSize;
670+
671+
IncludeStackEntry(const RawTokenizer::HotBookmark& rtb, int css, int bss) :
672+
returnToBookmark(rtb), condStackSize(css), braceStackSize(bss)
673+
{}
674+
};
675+
vector<IncludeStackEntry> maIncludeStack;
664676

665677
struct CS_ENTRY
666678
{

source/parser/parser_tokenizer.cpp

Lines changed: 23 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -294,8 +294,12 @@ void Parser::Get_Token ()
294294

295295
if (!GetRawToken(mToken.raw, fastForwardToDirective))
296296
{
297+
// End of current token stream reached.
298+
297299
if (maIncludeStack.empty())
298300
{
301+
// Not in an include file, i.e. end of main scene file reached.
302+
299303
if (Cond_Stack.size() != 1)
300304
Error("End of file reached but #end expected.");
301305

@@ -305,12 +309,24 @@ void Parser::Get_Token ()
305309
}
306310

307311
// Returning from an include file.
312+
// NB: End of macro is marked by `#end` rather than EOF, so it won't take us here.
308313

309314
Got_EOF=false;
310315

311316
mSymbolStack.PopTable();
312317

313-
GoToBookmark(maIncludeStack.back()); // TODO handle errors
318+
if (Cond_Stack.size() != maIncludeStack.back().condStackSize)
319+
Error("Unbalanced #end directives in include file.");
320+
if (maBraceStack.size() > maIncludeStack.back().braceStackSize)
321+
{
322+
// Include file has opened more braces/parentheses/etc. than it has closed.
323+
for (size_t i = maIncludeStack.back().braceStackSize; i < maBraceStack.size(); ++i)
324+
{
325+
BraceStackEntry& braceStackEntry = maBraceStack[i];
326+
Warning(braceStackEntry, "Unbalanced %s in include file", Get_Token_String(braceStackEntry.openToken));
327+
}
328+
}
329+
GoToBookmark(maIncludeStack.back().returnToBookmark); // TODO handle errors
314330
maIncludeStack.pop_back();
315331

316332
continue;
@@ -1881,9 +1897,9 @@ void Parser::Parse_Version()
18811897

18821898
if (maIncludeStack.empty())
18831899
Error("As of POV-Ray v3.7, the '#version' directive must be the first non-comment "
1884-
"statement in the scene file. To indicate that your scene will dynamically "
1885-
"adapt to whatever POV-Ray version is actually used, start your scene with "
1886-
"'#version version;'.");
1900+
"statement in the scene file. To indicate that your scene will dynamically "
1901+
"adapt to whatever POV-Ray version is actually used, start your scene with "
1902+
"'#version version;'.");
18871903
}
18881904

18891905
// Initialize various defaults depending on language version specified.
@@ -1896,7 +1912,7 @@ void Parser::Parse_Version()
18961912

18971913
if (sceneData->explicitNoiseGenerator == false)
18981914
sceneData->noiseGenerator = (sceneData->EffectiveLanguageVersion() < 350 ?
1899-
kNoiseGen_Original : kNoiseGen_RangeCorrected);
1915+
kNoiseGen_Original : kNoiseGen_RangeCorrected);
19001916
// [CLi] if assumed_gamma is not specified in a pre-v3.7 scene, gammaMode defaults to kPOVList_GammaMode_None;
19011917
// this is enforced later anyway after parsing, but we may need this information /now/ during parsing already
19021918
switch (sceneData->gammaMode)
@@ -1979,7 +1995,7 @@ void Parser::Open_Include()
19791995

19801996
void Parser::Skip_Tokens(COND_TYPE cond)
19811997
{
1982-
int Temp = Cond_Stack.size();
1998+
auto Temp = Cond_Stack.size();
19831999
bool Prev_Skip = Skipping;
19842000

19852001
Skipping = true;
@@ -3267,7 +3283,7 @@ void Parser::IncludeHeader(const UCS2String& formalFileName)
32673283
if (formalFileName.empty())
32683284
return;
32693285

3270-
maIncludeStack.push_back(mTokenizer.GetHotBookmark());
3286+
maIncludeStack.emplace_back(mTokenizer.GetHotBookmark(), int(Cond_Stack.size()), int(maBraceStack.size()));
32713287

32723288
shared_ptr<IStream> is = Locate_File (formalFileName.c_str(),POV_File_Text_INC,actualFileName,true);
32733289
if (is == nullptr)

0 commit comments

Comments
 (0)