Fixed bug in anchors with no content. This involved refactoring the 'implicit sequence' concept (where a map and a sequence start on the same indent, but we read the sequence as more indented since the '-' is visually an indent).
This commit is contained in:
parent
abe0af76c5
commit
a1460169e6
@ -75,10 +75,10 @@ namespace YAML
|
||||
throw ParserException(Mark::null(), ErrorMsg::END_OF_MAP);
|
||||
|
||||
Token token = pScanner->peek();
|
||||
if(token.type != TT_KEY && token.type != TT_VALUE && token.type != TT_BLOCK_END)
|
||||
if(token.type != TT_KEY && token.type != TT_VALUE && token.type != TT_BLOCK_MAP_END)
|
||||
throw ParserException(token.mark, ErrorMsg::END_OF_MAP);
|
||||
|
||||
if(token.type == TT_BLOCK_END) {
|
||||
if(token.type == TT_BLOCK_MAP_END) {
|
||||
pScanner->pop();
|
||||
break;
|
||||
}
|
||||
|
@ -93,7 +93,6 @@ namespace YAML
|
||||
break;
|
||||
case TT_FLOW_SEQ_START:
|
||||
case TT_BLOCK_SEQ_START:
|
||||
case TT_BLOCK_ENTRY:
|
||||
m_pContent = new Sequence;
|
||||
break;
|
||||
case TT_FLOW_MAP_START:
|
||||
@ -265,7 +264,7 @@ namespace YAML
|
||||
// write content
|
||||
if(node.m_pContent)
|
||||
node.m_pContent->Write(out);
|
||||
else
|
||||
else if(!node.m_alias)
|
||||
out << Null;
|
||||
|
||||
return out;
|
||||
|
@ -46,6 +46,7 @@ namespace YAML
|
||||
assert(!m_tokens.empty()); // should we be asserting here? I mean, we really just be checking
|
||||
// if it's empty before peeking.
|
||||
|
||||
// std::cerr << "peek: (" << &m_tokens.front() << ") " << m_tokens.front() << "\n";
|
||||
return m_tokens.front();
|
||||
}
|
||||
|
||||
@ -98,7 +99,7 @@ namespace YAML
|
||||
VerifySimpleKey();
|
||||
|
||||
// maybe need to end some blocks
|
||||
PopIndentTo(INPUT.column());
|
||||
PopIndentToHere();
|
||||
|
||||
// *****
|
||||
// And now branch based on the next few characters!
|
||||
@ -221,7 +222,7 @@ namespace YAML
|
||||
{
|
||||
m_startedStream = true;
|
||||
m_simpleKeyAllowed = true;
|
||||
m_indents.push(-1);
|
||||
m_indents.push(IndentMarker(-1, IndentMarker::NONE));
|
||||
m_anchors.clear();
|
||||
}
|
||||
|
||||
@ -233,7 +234,7 @@ namespace YAML
|
||||
if(INPUT.column() > 0)
|
||||
INPUT.ResetColumn();
|
||||
|
||||
PopIndentTo(-1);
|
||||
PopAllIndents();
|
||||
VerifyAllSimpleKeys();
|
||||
|
||||
m_simpleKeyAllowed = false;
|
||||
@ -244,41 +245,87 @@ namespace YAML
|
||||
// . Pushes an indentation onto the stack, and enqueues the
|
||||
// proper token (sequence start or mapping start).
|
||||
// . Returns the token it generates (if any).
|
||||
Token *Scanner::PushIndentTo(int column, bool sequence)
|
||||
Token *Scanner::PushIndentTo(int column, IndentMarker::INDENT_TYPE type)
|
||||
{
|
||||
// are we in flow?
|
||||
if(m_flowLevel > 0)
|
||||
return 0;
|
||||
|
||||
IndentMarker indent(column, type);
|
||||
const IndentMarker& lastIndent = m_indents.top();
|
||||
|
||||
// is this actually an indentation?
|
||||
if(column <= m_indents.top())
|
||||
if(indent.column < lastIndent.column)
|
||||
return 0;
|
||||
if(indent.column == lastIndent.column && !(indent.type == IndentMarker::SEQ && lastIndent.type == IndentMarker::MAP))
|
||||
return 0;
|
||||
|
||||
// now push
|
||||
m_indents.push(column);
|
||||
if(sequence)
|
||||
m_indents.push(indent);
|
||||
if(type == IndentMarker::SEQ)
|
||||
m_tokens.push(Token(TT_BLOCK_SEQ_START, INPUT.mark()));
|
||||
else
|
||||
else if(type == IndentMarker::MAP)
|
||||
m_tokens.push(Token(TT_BLOCK_MAP_START, INPUT.mark()));
|
||||
else
|
||||
assert(false);
|
||||
|
||||
return &m_tokens.back();
|
||||
}
|
||||
|
||||
// PopIndentTo
|
||||
// . Pops indentations off the stack until we reach 'column' indentation,
|
||||
// PopIndentToHere
|
||||
// . Pops indentations off the stack until we reach the current indentation level,
|
||||
// and enqueues the proper token each time.
|
||||
void Scanner::PopIndentTo(int column)
|
||||
void Scanner::PopIndentToHere()
|
||||
{
|
||||
// are we in flow?
|
||||
if(m_flowLevel > 0)
|
||||
return;
|
||||
|
||||
// now pop away
|
||||
while(!m_indents.empty() && m_indents.top() > column) {
|
||||
m_indents.pop();
|
||||
m_tokens.push(Token(TT_BLOCK_END, INPUT.mark()));
|
||||
while(!m_indents.empty()) {
|
||||
const IndentMarker& indent = m_indents.top();
|
||||
if(indent.column < INPUT.column())
|
||||
break;
|
||||
if(indent.column == INPUT.column() && !(indent.type == IndentMarker::SEQ && !Exp::BlockEntry.Matches(INPUT)))
|
||||
break;
|
||||
|
||||
PopIndent();
|
||||
}
|
||||
}
|
||||
|
||||
// PopAllIndents
|
||||
// . Pops all indentations off the stack,
|
||||
// and enqueues the proper token each time.
|
||||
void Scanner::PopAllIndents()
|
||||
{
|
||||
// are we in flow?
|
||||
if(m_flowLevel > 0)
|
||||
return;
|
||||
|
||||
// now pop away
|
||||
while(!m_indents.empty())
|
||||
PopIndent();
|
||||
}
|
||||
|
||||
// PopIndent
|
||||
// . Pops a single indent, pushing the proper token
|
||||
void Scanner::PopIndent()
|
||||
{
|
||||
IndentMarker::INDENT_TYPE type = m_indents.top().type;
|
||||
m_indents.pop();
|
||||
if(type == IndentMarker::SEQ)
|
||||
m_tokens.push(Token(TT_BLOCK_SEQ_END, INPUT.mark()));
|
||||
else if(type == IndentMarker::MAP)
|
||||
m_tokens.push(Token(TT_BLOCK_MAP_END, INPUT.mark()));
|
||||
}
|
||||
|
||||
// GetTopIndent
|
||||
int Scanner::GetTopIndent() const
|
||||
{
|
||||
if(m_indents.empty())
|
||||
return 0;
|
||||
return m_indents.top().column;
|
||||
}
|
||||
|
||||
// Save
|
||||
// . Saves a pointer to the Node object referenced by a particular anchor
|
||||
|
@ -34,14 +34,26 @@ namespace YAML
|
||||
void ClearAnchors();
|
||||
|
||||
private:
|
||||
struct IndentMarker {
|
||||
enum INDENT_TYPE { MAP, SEQ, NONE };
|
||||
IndentMarker(int column_, INDENT_TYPE type_): column(column_), type(type_) {}
|
||||
|
||||
int column;
|
||||
INDENT_TYPE type;
|
||||
};
|
||||
|
||||
private:
|
||||
// scanning
|
||||
void EnsureTokensInQueue();
|
||||
void ScanNextToken();
|
||||
void ScanToNextToken();
|
||||
void StartStream();
|
||||
void EndStream();
|
||||
Token *PushIndentTo(int column, bool sequence);
|
||||
void PopIndentTo(int column);
|
||||
Token *PushIndentTo(int column, IndentMarker::INDENT_TYPE type);
|
||||
void PopIndentToHere();
|
||||
void PopAllIndents();
|
||||
void PopIndent();
|
||||
int GetTopIndent() const;
|
||||
|
||||
// checking input
|
||||
void InsertSimpleKey();
|
||||
@ -94,7 +106,7 @@ namespace YAML
|
||||
int m_flowLevel; // number of unclosed '[' and '{' indicators
|
||||
bool m_isLastKeyValid;
|
||||
std::stack <SimpleKey> m_simpleKeys;
|
||||
std::stack <int> m_indents;
|
||||
std::stack <IndentMarker> m_indents;
|
||||
std::map <std::string, const Node *> m_anchors;
|
||||
};
|
||||
}
|
||||
|
@ -19,7 +19,7 @@ namespace YAML
|
||||
std::vector <std::string> params;
|
||||
|
||||
// pop indents and simple keys
|
||||
PopIndentTo(-1);
|
||||
PopAllIndents();
|
||||
VerifyAllSimpleKeys();
|
||||
|
||||
m_simpleKeyAllowed = false;
|
||||
@ -59,7 +59,7 @@ namespace YAML
|
||||
// DocStart
|
||||
void Scanner::ScanDocStart()
|
||||
{
|
||||
PopIndentTo(-1);
|
||||
PopAllIndents();
|
||||
VerifyAllSimpleKeys();
|
||||
m_simpleKeyAllowed = false;
|
||||
|
||||
@ -72,7 +72,7 @@ namespace YAML
|
||||
// DocEnd
|
||||
void Scanner::ScanDocEnd()
|
||||
{
|
||||
PopIndentTo(-1);
|
||||
PopAllIndents();
|
||||
VerifyAllSimpleKeys();
|
||||
m_simpleKeyAllowed = false;
|
||||
|
||||
@ -135,7 +135,7 @@ namespace YAML
|
||||
if(!m_simpleKeyAllowed)
|
||||
throw ParserException(INPUT.mark(), ErrorMsg::BLOCK_ENTRY);
|
||||
|
||||
PushIndentTo(INPUT.column(), true);
|
||||
PushIndentTo(INPUT.column(), IndentMarker::SEQ);
|
||||
m_simpleKeyAllowed = true;
|
||||
|
||||
// eat
|
||||
@ -152,7 +152,7 @@ namespace YAML
|
||||
if(!m_simpleKeyAllowed)
|
||||
throw ParserException(INPUT.mark(), ErrorMsg::MAP_KEY);
|
||||
|
||||
PushIndentTo(INPUT.column(), false);
|
||||
PushIndentTo(INPUT.column(), IndentMarker::MAP);
|
||||
}
|
||||
|
||||
// can only put a simple key here if we're in block context
|
||||
@ -180,7 +180,7 @@ namespace YAML
|
||||
if(!m_simpleKeyAllowed)
|
||||
throw ParserException(INPUT.mark(), ErrorMsg::MAP_VALUE);
|
||||
|
||||
PushIndentTo(INPUT.column(), false);
|
||||
PushIndentTo(INPUT.column(), IndentMarker::MAP);
|
||||
}
|
||||
|
||||
// can only put a simple key here if we're in block context
|
||||
@ -277,7 +277,7 @@ namespace YAML
|
||||
ScanScalarParams params;
|
||||
params.end = (m_flowLevel > 0 ? Exp::EndScalarInFlow : Exp::EndScalar) || (Exp::BlankOrBreak + Exp::Comment);
|
||||
params.eatEnd = false;
|
||||
params.indent = (m_flowLevel > 0 ? 0 : m_indents.top() + 1);
|
||||
params.indent = (m_flowLevel > 0 ? 0 : GetTopIndent() + 1);
|
||||
params.fold = true;
|
||||
params.eatLeadingWhitespace = true;
|
||||
params.trimTrailingSpaces = true;
|
||||
@ -391,8 +391,8 @@ namespace YAML
|
||||
throw ParserException(INPUT.mark(), ErrorMsg::CHAR_IN_BLOCK);
|
||||
|
||||
// set the initial indentation
|
||||
if(m_indents.top() >= 0)
|
||||
params.indent += m_indents.top();
|
||||
if(GetTopIndent() >= 0)
|
||||
params.indent += GetTopIndent();
|
||||
|
||||
params.eatLeadingWhitespace = false;
|
||||
params.trimTrailingSpaces = false;
|
||||
|
@ -83,11 +83,11 @@ namespace YAML
|
||||
throw ParserException(Mark::null(), ErrorMsg::END_OF_SEQ);
|
||||
|
||||
Token token = pScanner->peek();
|
||||
if(token.type != TT_BLOCK_ENTRY && token.type != TT_BLOCK_END)
|
||||
if(token.type != TT_BLOCK_ENTRY && token.type != TT_BLOCK_SEQ_END)
|
||||
throw ParserException(token.mark, ErrorMsg::END_OF_SEQ);
|
||||
|
||||
pScanner->pop();
|
||||
if(token.type == TT_BLOCK_END)
|
||||
if(token.type == TT_BLOCK_SEQ_END)
|
||||
break;
|
||||
|
||||
Node *pNode = new Node;
|
||||
@ -96,7 +96,7 @@ namespace YAML
|
||||
// check for null
|
||||
if(!pScanner->empty()) {
|
||||
const Token& token = pScanner->peek();
|
||||
if(token.type == TT_BLOCK_ENTRY || token.type == TT_BLOCK_END)
|
||||
if(token.type == TT_BLOCK_ENTRY || token.type == TT_BLOCK_SEQ_END)
|
||||
continue;
|
||||
}
|
||||
|
||||
|
@ -35,7 +35,7 @@ namespace YAML
|
||||
SimpleKey key(INPUT.mark(), m_flowLevel);
|
||||
|
||||
// first add a map start, if necessary
|
||||
key.pMapStart = PushIndentTo(INPUT.column(), false);
|
||||
key.pMapStart = PushIndentTo(INPUT.column(), IndentMarker::MAP);
|
||||
if(key.pMapStart)
|
||||
key.pMapStart->status = TS_UNVERIFIED;
|
||||
|
||||
|
@ -18,7 +18,8 @@ namespace YAML
|
||||
TT_DOC_END,
|
||||
TT_BLOCK_SEQ_START,
|
||||
TT_BLOCK_MAP_START,
|
||||
TT_BLOCK_END,
|
||||
TT_BLOCK_SEQ_END,
|
||||
TT_BLOCK_MAP_END,
|
||||
TT_BLOCK_ENTRY,
|
||||
TT_FLOW_SEQ_START,
|
||||
TT_FLOW_MAP_START,
|
||||
@ -39,7 +40,8 @@ namespace YAML
|
||||
"DOC_END",
|
||||
"BLOCK_SEQ_START",
|
||||
"BLOCK_MAP_START",
|
||||
"BLOCK_END",
|
||||
"BLOCK_SEQ_END",
|
||||
"BLOCK_MAP_END",
|
||||
"BLOCK_ENTRY",
|
||||
"FLOW_SEQ_START",
|
||||
"FLOW_MAP_START",
|
||||
|
@ -272,6 +272,16 @@ namespace Test
|
||||
desiredOutput = "- &fred\n name: Fred\n age: 42\n- *fred";
|
||||
}
|
||||
|
||||
void AliasAndAnchorWithNull(YAML::Emitter& out, std::string& desiredOutput)
|
||||
{
|
||||
out << YAML::BeginSeq;
|
||||
out << YAML::Anchor("fred") << YAML::Null;
|
||||
out << YAML::Alias("fred");
|
||||
out << YAML::EndSeq;
|
||||
|
||||
desiredOutput = "- &fred ~\n- *fred";
|
||||
}
|
||||
|
||||
void ComplexDoc(YAML::Emitter& out, std::string& desiredOutput)
|
||||
{
|
||||
out << YAML::BeginMap;
|
||||
|
@ -248,6 +248,30 @@ namespace Test
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CompressedMapAndSeq()
|
||||
{
|
||||
std::string input = "key:\n- one\n- two";
|
||||
|
||||
std::stringstream stream(input);
|
||||
YAML::Parser parser(stream);
|
||||
YAML::Node doc;
|
||||
parser.GetNextDocument(doc);
|
||||
|
||||
const YAML::Node& seq = doc["key"];
|
||||
if(seq.size() != 2)
|
||||
return false;
|
||||
|
||||
std::string output;
|
||||
seq[0] >> output;
|
||||
if(output != "one")
|
||||
return false;
|
||||
seq[1] >> output;
|
||||
if(output != "two")
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool NullBlockSeqEntry()
|
||||
{
|
||||
std::string input = "- hello\n-\n- world";
|
||||
@ -301,5 +325,50 @@ namespace Test
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool SimpleAlias()
|
||||
{
|
||||
std::string input = "- &alias test\n- *alias";
|
||||
|
||||
std::stringstream stream(input);
|
||||
YAML::Parser parser(stream);
|
||||
YAML::Node doc;
|
||||
parser.GetNextDocument(doc);
|
||||
|
||||
std::string output;
|
||||
doc[0] >> output;
|
||||
if(output != "test")
|
||||
return false;
|
||||
|
||||
doc[1] >> output;
|
||||
if(output != "test")
|
||||
return false;
|
||||
|
||||
if(doc.size() != 2)
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool AliasWithNull()
|
||||
{
|
||||
std::string input = "- &alias\n- *alias";
|
||||
|
||||
std::stringstream stream(input);
|
||||
YAML::Parser parser(stream);
|
||||
YAML::Node doc;
|
||||
parser.GetNextDocument(doc);
|
||||
|
||||
if(!IsNull(doc[0]))
|
||||
return false;
|
||||
|
||||
if(!IsNull(doc[1]))
|
||||
return false;
|
||||
|
||||
if(doc.size() != 2)
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -263,9 +263,12 @@ namespace Test
|
||||
RunParserTest(&Parser::FlowSeq, "flow seq", passed);
|
||||
RunParserTest(&Parser::FlowMap, "flow map", passed);
|
||||
RunParserTest(&Parser::QuotedSimpleKeys, "quoted simple keys", passed);
|
||||
RunParserTest(&Parser::CompressedMapAndSeq, "compressed map and seq", passed);
|
||||
RunParserTest(&Parser::NullBlockSeqEntry, "null block seq entry", passed);
|
||||
RunParserTest(&Parser::NullBlockMapKey, "null block map key", passed);
|
||||
RunParserTest(&Parser::NullBlockMapValue, "null block map value", passed);
|
||||
RunParserTest(&Parser::SimpleAlias, "simple alias", passed);
|
||||
RunParserTest(&Parser::AliasWithNull, "alias with null", passed);
|
||||
|
||||
RunEncodingTest(&EncodeToUtf8, false, "UTF-8, no BOM", passed);
|
||||
RunEncodingTest(&EncodeToUtf8, true, "UTF-8 with BOM", passed);
|
||||
@ -346,6 +349,7 @@ namespace Test
|
||||
RunEmitterTest(&Emitter::LongKeyFlowMap, "long key flow map", passed);
|
||||
RunEmitterTest(&Emitter::BlockMapAsKey, "block map as key", passed);
|
||||
RunEmitterTest(&Emitter::AliasAndAnchor, "alias and anchor", passed);
|
||||
RunEmitterTest(&Emitter::AliasAndAnchorWithNull, "alias and anchor with null", passed);
|
||||
RunEmitterTest(&Emitter::ComplexDoc, "complex doc", passed);
|
||||
RunEmitterTest(&Emitter::STLContainers, "STL containers", passed);
|
||||
RunEmitterTest(&Emitter::SimpleComment, "simple comment", passed);
|
||||
|
@ -34,9 +34,12 @@ namespace Test {
|
||||
bool FlowSeq();
|
||||
bool FlowMap();
|
||||
bool QuotedSimpleKeys();
|
||||
bool CompressedMapAndSeq();
|
||||
bool NullBlockSeqEntry();
|
||||
bool NullBlockMapKey();
|
||||
bool NullBlockMapValue();
|
||||
bool SimpleAlias();
|
||||
bool AliasWithNull();
|
||||
}
|
||||
|
||||
namespace Emitter {
|
||||
@ -63,6 +66,7 @@ namespace Test {
|
||||
void LongKeyFlowMap(YAML::Emitter& out, std::string& desiredOutput);
|
||||
void BlockMapAsKey(YAML::Emitter& out, std::string& desiredOutput);
|
||||
void AliasAndAnchor(YAML::Emitter& out, std::string& desiredOutput);
|
||||
void AliasAndAnchorWithNull(YAML::Emitter& out, std::string& desiredOutput);
|
||||
void ComplexDoc(YAML::Emitter& out, std::string& desiredOutput);
|
||||
void STLContainers(YAML::Emitter& out, std::string& desiredOutput);
|
||||
void SimpleComment(YAML::Emitter& out, std::string& desiredOutput);
|
||||
|
Loading…
Reference in New Issue
Block a user