Allowed solo entries in a flow map to be read as keys with null value

This commit is contained in:
jbeder
2009-09-05 22:42:01 +00:00
parent ba472cc9a3
commit f21456972c
7 changed files with 113 additions and 52 deletions

View File

@@ -8,7 +8,7 @@
namespace YAML
{
Scanner::Scanner(std::istream& in)
: INPUT(in), m_startedStream(false), m_endedStream(false), m_simpleKeyAllowed(false), m_flowLevel(0)
: INPUT(in), m_startedStream(false), m_endedStream(false), m_simpleKeyAllowed(false)
{
}
@@ -136,10 +136,10 @@ namespace YAML
if(Exp::BlockEntry.Matches(INPUT))
return ScanBlockEntry();
if((m_flowLevel == 0 ? Exp::Key : Exp::KeyInFlow).Matches(INPUT))
if((InBlockContext() ? Exp::Key : Exp::KeyInFlow).Matches(INPUT))
return ScanKey();
if((m_flowLevel == 0 ? Exp::Value : Exp::ValueInFlow).Matches(INPUT))
if((InBlockContext() ? Exp::Value : Exp::ValueInFlow).Matches(INPUT))
return ScanValue();
// alias/anchor
@@ -151,14 +151,14 @@ namespace YAML
return ScanTag();
// special scalars
if(m_flowLevel == 0 && (INPUT.peek() == Keys::LiteralScalar || INPUT.peek() == Keys::FoldedScalar))
if(InBlockContext() && (INPUT.peek() == Keys::LiteralScalar || INPUT.peek() == Keys::FoldedScalar))
return ScanBlockScalar();
if(INPUT.peek() == '\'' || INPUT.peek() == '\"')
return ScanQuotedScalar();
// plain scalars
if((m_flowLevel == 0 ? Exp::PlainScalar : Exp::PlainScalarInFlow).Matches(INPUT))
if((InBlockContext() ? Exp::PlainScalar : Exp::PlainScalarInFlow).Matches(INPUT))
return ScanPlainScalar();
// don't know what it is!
@@ -193,7 +193,7 @@ namespace YAML
InvalidateSimpleKey();
// new line - we may be able to accept a simple key now
if(m_flowLevel == 0)
if(InBlockContext())
m_simpleKeyAllowed = true;
}
}
@@ -213,7 +213,7 @@ namespace YAML
if(ch == ' ')
return true;
if(ch == '\t' && (m_flowLevel >= 0 || !m_simpleKeyAllowed))
if(ch == '\t' && (InFlowContext() || !m_simpleKeyAllowed))
return true;
return false;
@@ -251,7 +251,7 @@ namespace YAML
Scanner::IndentMarker *Scanner::PushIndentTo(int column, IndentMarker::INDENT_TYPE type)
{
// are we in flow?
if(m_flowLevel > 0)
if(InFlowContext())
return 0;
IndentMarker indent(column, type);
@@ -283,7 +283,7 @@ namespace YAML
void Scanner::PopIndentToHere()
{
// are we in flow?
if(m_flowLevel > 0)
if(InFlowContext())
return;
// now pop away
@@ -304,7 +304,7 @@ namespace YAML
void Scanner::PopAllIndents()
{
// are we in flow?
if(m_flowLevel > 0)
if(InFlowContext())
return;
// now pop away

View File

@@ -43,6 +43,8 @@ namespace YAML
bool isValid;
Token *pStartToken;
};
enum FLOW_MARKER { FLOW_MAP, FLOW_SEQ };
private:
// scanning
@@ -51,6 +53,11 @@ namespace YAML
void ScanToNextToken();
void StartStream();
void EndStream();
bool InFlowContext() const { return !m_flows.empty(); }
bool InBlockContext() const { return m_flows.empty(); }
int GetFlowLevel() const { return m_flows.size(); }
IndentMarker *PushIndentTo(int column, IndentMarker::INDENT_TYPE type);
void PopIndentToHere();
void PopAllIndents();
@@ -58,6 +65,7 @@ namespace YAML
int GetTopIndent() const;
// checking input
bool CanInsertPotentialSimpleKey() const;
bool ExistsActiveSimpleKey() const;
void InsertPotentialSimpleKey();
void InvalidateSimpleKey();
@@ -109,10 +117,9 @@ namespace YAML
// state info
bool m_startedStream, m_endedStream;
bool m_simpleKeyAllowed;
int m_flowLevel; // number of unclosed '[' and '{' indicators
bool m_isLastKeyValid;
std::stack <SimpleKey> m_simpleKeys;
std::stack <IndentMarker> m_indents;
std::stack <FLOW_MARKER> m_flows;
std::map <std::string, const Node *> m_anchors;
};
}

View File

@@ -87,36 +87,50 @@ namespace YAML
{
// flows can be simple keys
InsertPotentialSimpleKey();
m_flowLevel++;
m_simpleKeyAllowed = true;
// eat
Mark mark = INPUT.mark();
char ch = INPUT.get();
Token::TYPE type = (ch == Keys::FlowSeqStart ? Token::FLOW_SEQ_START : Token::FLOW_MAP_START);
FLOW_MARKER flowType = (ch == Keys::FlowSeqStart ? FLOW_SEQ : FLOW_MAP);
m_flows.push(flowType);
Token::TYPE type = (flowType == FLOW_SEQ ? Token::FLOW_SEQ_START : Token::FLOW_MAP_START);
m_tokens.push(Token(type, mark));
}
// FlowEnd
void Scanner::ScanFlowEnd()
{
if(m_flowLevel == 0)
if(InBlockContext())
throw ParserException(INPUT.mark(), ErrorMsg::FLOW_END);
InvalidateSimpleKey();
m_flowLevel--;
// we might have a solo entry in the flow context
if(VerifySimpleKey())
m_tokens.push(Token(Token::VALUE, INPUT.mark()));
m_simpleKeyAllowed = false;
// eat
Mark mark = INPUT.mark();
char ch = INPUT.get();
Token::TYPE type = (ch == Keys::FlowSeqEnd ? Token::FLOW_SEQ_END : Token::FLOW_MAP_END);
// check that it matches the start
FLOW_MARKER flowType = (ch == Keys::FlowSeqEnd ? FLOW_SEQ : FLOW_MAP);
if(m_flows.top() != flowType)
throw ParserException(mark, ErrorMsg::FLOW_END);
m_flows.pop();
Token::TYPE type = (flowType ? Token::FLOW_SEQ_END : Token::FLOW_MAP_END);
m_tokens.push(Token(type, mark));
}
// FlowEntry
void Scanner::ScanFlowEntry()
{
// we might have a solo entry in the flow context
if(VerifySimpleKey())
m_tokens.push(Token(Token::VALUE, INPUT.mark()));
m_simpleKeyAllowed = true;
// eat
@@ -129,7 +143,7 @@ namespace YAML
void Scanner::ScanBlockEntry()
{
// we better be in the block context!
if(m_flowLevel > 0)
if(InFlowContext())
throw ParserException(INPUT.mark(), ErrorMsg::BLOCK_ENTRY);
// can we put it here?
@@ -149,7 +163,7 @@ namespace YAML
void Scanner::ScanKey()
{
// handle keys diffently in the block context (and manage indents)
if(m_flowLevel == 0) {
if(InBlockContext()) {
if(!m_simpleKeyAllowed)
throw ParserException(INPUT.mark(), ErrorMsg::MAP_KEY);
@@ -157,10 +171,7 @@ namespace YAML
}
// can only put a simple key here if we're in block context
if(m_flowLevel == 0)
m_simpleKeyAllowed = true;
else
m_simpleKeyAllowed = false;
m_simpleKeyAllowed = InBlockContext();
// eat
Mark mark = INPUT.mark();
@@ -182,7 +193,7 @@ namespace YAML
m_simpleKeyAllowed = false;
} else {
// handle values diffently in the block context (and manage indents)
if(m_flowLevel == 0) {
if(InBlockContext()) {
if(!m_simpleKeyAllowed)
throw ParserException(INPUT.mark(), ErrorMsg::MAP_VALUE);
@@ -190,7 +201,7 @@ namespace YAML
}
// can only put a simple key here if we're in block context
m_simpleKeyAllowed = (m_flowLevel == 0);
m_simpleKeyAllowed = InBlockContext();
}
// eat
@@ -206,8 +217,7 @@ namespace YAML
std::string name;
// insert a potential simple key
if(m_simpleKeyAllowed)
InsertPotentialSimpleKey();
InsertPotentialSimpleKey();
m_simpleKeyAllowed = false;
// eat the indicator
@@ -239,8 +249,7 @@ namespace YAML
std::string handle, suffix;
// insert a potential simple key
if(m_simpleKeyAllowed)
InsertPotentialSimpleKey();
InsertPotentialSimpleKey();
m_simpleKeyAllowed = false;
// eat the indicator
@@ -278,9 +287,9 @@ namespace YAML
// set up the scanning parameters
ScanScalarParams params;
params.end = (m_flowLevel > 0 ? Exp::EndScalarInFlow : Exp::EndScalar) || (Exp::BlankOrBreak + Exp::Comment);
params.end = (InFlowContext() ? Exp::EndScalarInFlow : Exp::EndScalar) || (Exp::BlankOrBreak + Exp::Comment);
params.eatEnd = false;
params.indent = (m_flowLevel > 0 ? 0 : GetTopIndent() + 1);
params.indent = (InFlowContext() ? 0 : GetTopIndent() + 1);
params.fold = true;
params.eatLeadingWhitespace = true;
params.trimTrailingSpaces = true;
@@ -289,8 +298,7 @@ namespace YAML
params.onTabInIndentation = THROW;
// insert a potential simple key
if(m_simpleKeyAllowed)
InsertPotentialSimpleKey();
InsertPotentialSimpleKey();
Mark mark = INPUT.mark();
scalar = ScanScalar(INPUT, params);
@@ -329,8 +337,7 @@ namespace YAML
params.onDocIndicator = THROW;
// insert a potential simple key
if(m_simpleKeyAllowed)
InsertPotentialSimpleKey();
InsertPotentialSimpleKey();
Mark mark = INPUT.mark();

View File

@@ -31,6 +31,18 @@ namespace YAML
if(pKey)
pKey->status = Token::INVALID;
}
// CanInsertPotentialSimpleKey
bool Scanner::CanInsertPotentialSimpleKey() const
{
if(!m_simpleKeyAllowed)
return false;
if(InFlowContext() && m_flows.top() != FLOW_MAP)
return false;
return !ExistsActiveSimpleKey();
}
// ExistsActiveSimpleKey
// . Returns true if there's a potential simple key at our flow level
@@ -41,7 +53,7 @@ namespace YAML
return false;
const SimpleKey& key = m_simpleKeys.top();
return key.flowLevel == m_flowLevel;
return key.flowLevel == GetFlowLevel();
}
// InsertPotentialSimpleKey
@@ -49,10 +61,10 @@ namespace YAML
// and save it on a stack.
void Scanner::InsertPotentialSimpleKey()
{
if(ExistsActiveSimpleKey())
if(!CanInsertPotentialSimpleKey())
return;
SimpleKey key(INPUT.mark(), m_flowLevel);
SimpleKey key(INPUT.mark(), GetFlowLevel());
// first add a map start, if necessary
key.pIndent = PushIndentTo(INPUT.column(), IndentMarker::MAP);
@@ -79,7 +91,7 @@ namespace YAML
// grab top key
SimpleKey& key = m_simpleKeys.top();
if(key.flowLevel != m_flowLevel)
if(key.flowLevel != GetFlowLevel())
return;
key.Invalidate();
@@ -91,28 +103,21 @@ namespace YAML
// and if so, makes it valid.
bool Scanner::VerifySimpleKey()
{
m_isLastKeyValid = false;
if(m_simpleKeys.empty())
return m_isLastKeyValid;
return false;
// grab top key
SimpleKey key = m_simpleKeys.top();
// only validate if we're in the correct flow level
if(key.flowLevel != m_flowLevel)
if(key.flowLevel != GetFlowLevel())
return false;
m_simpleKeys.pop();
bool isValid = true;
// needs to be followed immediately by a value
if(m_flowLevel > 0 && !Exp::ValueInFlow.Matches(INPUT))
isValid = false;
if(m_flowLevel == 0 && !Exp::Value.Matches(INPUT))
isValid = false;
// also needs to be less than 1024 characters and inline
// needs to be less than 1024 characters and inline
if(INPUT.line() != key.mark.line || INPUT.pos() - key.mark.pos > 1024)
isValid = false;
@@ -122,7 +127,6 @@ namespace YAML
else
key.Invalidate();
m_isLastKeyValid = isValid;
return isValid;
}

View File

@@ -235,7 +235,7 @@ namespace Test
return true;
}
bool FlowMapWithOmittedValue()
{
std::string input = "{a: b, c:, d:}";
@@ -256,6 +256,45 @@ namespace Test
return true;
}
bool FlowMapWithSoloEntry()
{
std::string input = "{a: b, c, d: e}";
std::stringstream stream(input);
YAML::Parser parser(stream);
YAML::Node doc;
parser.GetNextDocument(doc);
std::string output;
doc["a"] >> output;
if(output != "b")
return false;
if(!IsNull(doc["c"]))
return false;
doc["d"] >> output;
if(output != "e")
return false;
return true;
}
bool FlowMapEndingWithSoloEntry()
{
std::string input = "{a: b, c}";
std::stringstream stream(input);
YAML::Parser parser(stream);
YAML::Node doc;
parser.GetNextDocument(doc);
std::string output;
doc["a"] >> output;
if(output != "b")
return false;
if(!IsNull(doc["c"]))
return false;
return true;
}
bool QuotedSimpleKeys()
{
std::string KeyValue[3] = { "\"double\": double\n", "'single': single\n", "plain: plain\n" };

View File

@@ -268,6 +268,8 @@ namespace Test
RunParserTest(&Parser::FlowMap, "flow map", passed);
RunParserTest(&Parser::FlowMapWithOmittedKey, "flow map with omitted key", passed);
RunParserTest(&Parser::FlowMapWithOmittedValue, "flow map with omitted value", passed);
RunParserTest(&Parser::FlowMapWithSoloEntry, "flow map with solo entry", passed);
RunParserTest(&Parser::FlowMapEndingWithSoloEntry, "flow map ending with solo entry", passed);
RunParserTest(&Parser::QuotedSimpleKeys, "quoted simple keys", passed);
RunParserTest(&Parser::CompressedMapAndSeq, "compressed map and seq", passed);
RunParserTest(&Parser::NullBlockSeqEntry, "null block seq entry", passed);

View File

@@ -35,6 +35,8 @@ namespace Test {
bool FlowMap();
bool FlowMapWithOmittedKey();
bool FlowMapWithOmittedValue();
bool FlowMapWithSoloEntry();
bool FlowMapEndingWithSoloEntry();
bool QuotedSimpleKeys();
bool CompressedMapAndSeq();
bool NullBlockSeqEntry();