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:
Jesse Beder
2009-08-24 22:56:54 +00:00
parent abe0af76c5
commit a1460169e6
12 changed files with 183 additions and 36 deletions

View File

@@ -75,10 +75,10 @@ namespace YAML
throw ParserException(Mark::null(), ErrorMsg::END_OF_MAP); throw ParserException(Mark::null(), ErrorMsg::END_OF_MAP);
Token token = pScanner->peek(); 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); throw ParserException(token.mark, ErrorMsg::END_OF_MAP);
if(token.type == TT_BLOCK_END) { if(token.type == TT_BLOCK_MAP_END) {
pScanner->pop(); pScanner->pop();
break; break;
} }

View File

@@ -93,7 +93,6 @@ namespace YAML
break; break;
case TT_FLOW_SEQ_START: case TT_FLOW_SEQ_START:
case TT_BLOCK_SEQ_START: case TT_BLOCK_SEQ_START:
case TT_BLOCK_ENTRY:
m_pContent = new Sequence; m_pContent = new Sequence;
break; break;
case TT_FLOW_MAP_START: case TT_FLOW_MAP_START:
@@ -265,7 +264,7 @@ namespace YAML
// write content // write content
if(node.m_pContent) if(node.m_pContent)
node.m_pContent->Write(out); node.m_pContent->Write(out);
else else if(!node.m_alias)
out << Null; out << Null;
return out; return out;

View File

@@ -46,6 +46,7 @@ namespace YAML
assert(!m_tokens.empty()); // should we be asserting here? I mean, we really just be checking assert(!m_tokens.empty()); // should we be asserting here? I mean, we really just be checking
// if it's empty before peeking. // if it's empty before peeking.
// std::cerr << "peek: (" << &m_tokens.front() << ") " << m_tokens.front() << "\n";
return m_tokens.front(); return m_tokens.front();
} }
@@ -98,7 +99,7 @@ namespace YAML
VerifySimpleKey(); VerifySimpleKey();
// maybe need to end some blocks // maybe need to end some blocks
PopIndentTo(INPUT.column()); PopIndentToHere();
// ***** // *****
// And now branch based on the next few characters! // And now branch based on the next few characters!
@@ -221,7 +222,7 @@ namespace YAML
{ {
m_startedStream = true; m_startedStream = true;
m_simpleKeyAllowed = true; m_simpleKeyAllowed = true;
m_indents.push(-1); m_indents.push(IndentMarker(-1, IndentMarker::NONE));
m_anchors.clear(); m_anchors.clear();
} }
@@ -233,7 +234,7 @@ namespace YAML
if(INPUT.column() > 0) if(INPUT.column() > 0)
INPUT.ResetColumn(); INPUT.ResetColumn();
PopIndentTo(-1); PopAllIndents();
VerifyAllSimpleKeys(); VerifyAllSimpleKeys();
m_simpleKeyAllowed = false; m_simpleKeyAllowed = false;
@@ -244,42 +245,88 @@ namespace YAML
// . Pushes an indentation onto the stack, and enqueues the // . Pushes an indentation onto the stack, and enqueues the
// proper token (sequence start or mapping start). // proper token (sequence start or mapping start).
// . Returns the token it generates (if any). // . 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? // are we in flow?
if(m_flowLevel > 0) if(m_flowLevel > 0)
return 0; return 0;
IndentMarker indent(column, type);
const IndentMarker& lastIndent = m_indents.top();
// is this actually an indentation? // 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; return 0;
// now push // now push
m_indents.push(column); m_indents.push(indent);
if(sequence) if(type == IndentMarker::SEQ)
m_tokens.push(Token(TT_BLOCK_SEQ_START, INPUT.mark())); 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())); m_tokens.push(Token(TT_BLOCK_MAP_START, INPUT.mark()));
else
assert(false);
return &m_tokens.back(); return &m_tokens.back();
} }
// PopIndentTo // PopIndentToHere
// . Pops indentations off the stack until we reach 'column' indentation, // . Pops indentations off the stack until we reach the current indentation level,
// and enqueues the proper token each time. // and enqueues the proper token each time.
void Scanner::PopIndentTo(int column) void Scanner::PopIndentToHere()
{ {
// are we in flow? // are we in flow?
if(m_flowLevel > 0) if(m_flowLevel > 0)
return; return;
// now pop away // now pop away
while(!m_indents.empty() && m_indents.top() > column) { while(!m_indents.empty()) {
m_indents.pop(); const IndentMarker& indent = m_indents.top();
m_tokens.push(Token(TT_BLOCK_END, INPUT.mark())); 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 // Save
// . Saves a pointer to the Node object referenced by a particular anchor // . Saves a pointer to the Node object referenced by a particular anchor
// name. // name.

View File

@@ -33,6 +33,15 @@ namespace YAML
const Node *Retrieve(const std::string& anchor) const; const Node *Retrieve(const std::string& anchor) const;
void ClearAnchors(); 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: private:
// scanning // scanning
void EnsureTokensInQueue(); void EnsureTokensInQueue();
@@ -40,8 +49,11 @@ namespace YAML
void ScanToNextToken(); void ScanToNextToken();
void StartStream(); void StartStream();
void EndStream(); void EndStream();
Token *PushIndentTo(int column, bool sequence); Token *PushIndentTo(int column, IndentMarker::INDENT_TYPE type);
void PopIndentTo(int column); void PopIndentToHere();
void PopAllIndents();
void PopIndent();
int GetTopIndent() const;
// checking input // checking input
void InsertSimpleKey(); void InsertSimpleKey();
@@ -94,7 +106,7 @@ namespace YAML
int m_flowLevel; // number of unclosed '[' and '{' indicators int m_flowLevel; // number of unclosed '[' and '{' indicators
bool m_isLastKeyValid; bool m_isLastKeyValid;
std::stack <SimpleKey> m_simpleKeys; std::stack <SimpleKey> m_simpleKeys;
std::stack <int> m_indents; std::stack <IndentMarker> m_indents;
std::map <std::string, const Node *> m_anchors; std::map <std::string, const Node *> m_anchors;
}; };
} }

View File

@@ -19,7 +19,7 @@ namespace YAML
std::vector <std::string> params; std::vector <std::string> params;
// pop indents and simple keys // pop indents and simple keys
PopIndentTo(-1); PopAllIndents();
VerifyAllSimpleKeys(); VerifyAllSimpleKeys();
m_simpleKeyAllowed = false; m_simpleKeyAllowed = false;
@@ -59,7 +59,7 @@ namespace YAML
// DocStart // DocStart
void Scanner::ScanDocStart() void Scanner::ScanDocStart()
{ {
PopIndentTo(-1); PopAllIndents();
VerifyAllSimpleKeys(); VerifyAllSimpleKeys();
m_simpleKeyAllowed = false; m_simpleKeyAllowed = false;
@@ -72,7 +72,7 @@ namespace YAML
// DocEnd // DocEnd
void Scanner::ScanDocEnd() void Scanner::ScanDocEnd()
{ {
PopIndentTo(-1); PopAllIndents();
VerifyAllSimpleKeys(); VerifyAllSimpleKeys();
m_simpleKeyAllowed = false; m_simpleKeyAllowed = false;
@@ -135,7 +135,7 @@ namespace YAML
if(!m_simpleKeyAllowed) if(!m_simpleKeyAllowed)
throw ParserException(INPUT.mark(), ErrorMsg::BLOCK_ENTRY); throw ParserException(INPUT.mark(), ErrorMsg::BLOCK_ENTRY);
PushIndentTo(INPUT.column(), true); PushIndentTo(INPUT.column(), IndentMarker::SEQ);
m_simpleKeyAllowed = true; m_simpleKeyAllowed = true;
// eat // eat
@@ -152,7 +152,7 @@ namespace YAML
if(!m_simpleKeyAllowed) if(!m_simpleKeyAllowed)
throw ParserException(INPUT.mark(), ErrorMsg::MAP_KEY); 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 // can only put a simple key here if we're in block context
@@ -180,7 +180,7 @@ namespace YAML
if(!m_simpleKeyAllowed) if(!m_simpleKeyAllowed)
throw ParserException(INPUT.mark(), ErrorMsg::MAP_VALUE); 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 // can only put a simple key here if we're in block context
@@ -277,7 +277,7 @@ namespace YAML
ScanScalarParams params; ScanScalarParams params;
params.end = (m_flowLevel > 0 ? Exp::EndScalarInFlow : Exp::EndScalar) || (Exp::BlankOrBreak + Exp::Comment); params.end = (m_flowLevel > 0 ? Exp::EndScalarInFlow : Exp::EndScalar) || (Exp::BlankOrBreak + Exp::Comment);
params.eatEnd = false; 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.fold = true;
params.eatLeadingWhitespace = true; params.eatLeadingWhitespace = true;
params.trimTrailingSpaces = true; params.trimTrailingSpaces = true;
@@ -391,8 +391,8 @@ namespace YAML
throw ParserException(INPUT.mark(), ErrorMsg::CHAR_IN_BLOCK); throw ParserException(INPUT.mark(), ErrorMsg::CHAR_IN_BLOCK);
// set the initial indentation // set the initial indentation
if(m_indents.top() >= 0) if(GetTopIndent() >= 0)
params.indent += m_indents.top(); params.indent += GetTopIndent();
params.eatLeadingWhitespace = false; params.eatLeadingWhitespace = false;
params.trimTrailingSpaces = false; params.trimTrailingSpaces = false;

View File

@@ -83,11 +83,11 @@ namespace YAML
throw ParserException(Mark::null(), ErrorMsg::END_OF_SEQ); throw ParserException(Mark::null(), ErrorMsg::END_OF_SEQ);
Token token = pScanner->peek(); 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); throw ParserException(token.mark, ErrorMsg::END_OF_SEQ);
pScanner->pop(); pScanner->pop();
if(token.type == TT_BLOCK_END) if(token.type == TT_BLOCK_SEQ_END)
break; break;
Node *pNode = new Node; Node *pNode = new Node;
@@ -96,7 +96,7 @@ namespace YAML
// check for null // check for null
if(!pScanner->empty()) { if(!pScanner->empty()) {
const Token& token = pScanner->peek(); 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; continue;
} }

View File

@@ -35,7 +35,7 @@ namespace YAML
SimpleKey key(INPUT.mark(), m_flowLevel); SimpleKey key(INPUT.mark(), m_flowLevel);
// first add a map start, if necessary // first add a map start, if necessary
key.pMapStart = PushIndentTo(INPUT.column(), false); key.pMapStart = PushIndentTo(INPUT.column(), IndentMarker::MAP);
if(key.pMapStart) if(key.pMapStart)
key.pMapStart->status = TS_UNVERIFIED; key.pMapStart->status = TS_UNVERIFIED;

View File

@@ -18,7 +18,8 @@ namespace YAML
TT_DOC_END, TT_DOC_END,
TT_BLOCK_SEQ_START, TT_BLOCK_SEQ_START,
TT_BLOCK_MAP_START, TT_BLOCK_MAP_START,
TT_BLOCK_END, TT_BLOCK_SEQ_END,
TT_BLOCK_MAP_END,
TT_BLOCK_ENTRY, TT_BLOCK_ENTRY,
TT_FLOW_SEQ_START, TT_FLOW_SEQ_START,
TT_FLOW_MAP_START, TT_FLOW_MAP_START,
@@ -39,7 +40,8 @@ namespace YAML
"DOC_END", "DOC_END",
"BLOCK_SEQ_START", "BLOCK_SEQ_START",
"BLOCK_MAP_START", "BLOCK_MAP_START",
"BLOCK_END", "BLOCK_SEQ_END",
"BLOCK_MAP_END",
"BLOCK_ENTRY", "BLOCK_ENTRY",
"FLOW_SEQ_START", "FLOW_SEQ_START",
"FLOW_MAP_START", "FLOW_MAP_START",

View File

@@ -272,6 +272,16 @@ namespace Test
desiredOutput = "- &fred\n name: Fred\n age: 42\n- *fred"; 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) void ComplexDoc(YAML::Emitter& out, std::string& desiredOutput)
{ {
out << YAML::BeginMap; out << YAML::BeginMap;

View File

@@ -248,6 +248,30 @@ namespace Test
return true; 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() bool NullBlockSeqEntry()
{ {
std::string input = "- hello\n-\n- world"; std::string input = "- hello\n-\n- world";
@@ -301,5 +325,50 @@ namespace Test
return true; 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;
}
} }
} }

View File

@@ -263,9 +263,12 @@ namespace Test
RunParserTest(&Parser::FlowSeq, "flow seq", passed); RunParserTest(&Parser::FlowSeq, "flow seq", passed);
RunParserTest(&Parser::FlowMap, "flow map", passed); RunParserTest(&Parser::FlowMap, "flow map", passed);
RunParserTest(&Parser::QuotedSimpleKeys, "quoted simple keys", 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::NullBlockSeqEntry, "null block seq entry", passed);
RunParserTest(&Parser::NullBlockMapKey, "null block map key", passed); RunParserTest(&Parser::NullBlockMapKey, "null block map key", passed);
RunParserTest(&Parser::NullBlockMapValue, "null block map value", 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, false, "UTF-8, no BOM", passed);
RunEncodingTest(&EncodeToUtf8, true, "UTF-8 with 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::LongKeyFlowMap, "long key flow map", passed);
RunEmitterTest(&Emitter::BlockMapAsKey, "block map as key", passed); RunEmitterTest(&Emitter::BlockMapAsKey, "block map as key", passed);
RunEmitterTest(&Emitter::AliasAndAnchor, "alias and anchor", 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::ComplexDoc, "complex doc", passed);
RunEmitterTest(&Emitter::STLContainers, "STL containers", passed); RunEmitterTest(&Emitter::STLContainers, "STL containers", passed);
RunEmitterTest(&Emitter::SimpleComment, "simple comment", passed); RunEmitterTest(&Emitter::SimpleComment, "simple comment", passed);

View File

@@ -34,9 +34,12 @@ namespace Test {
bool FlowSeq(); bool FlowSeq();
bool FlowMap(); bool FlowMap();
bool QuotedSimpleKeys(); bool QuotedSimpleKeys();
bool CompressedMapAndSeq();
bool NullBlockSeqEntry(); bool NullBlockSeqEntry();
bool NullBlockMapKey(); bool NullBlockMapKey();
bool NullBlockMapValue(); bool NullBlockMapValue();
bool SimpleAlias();
bool AliasWithNull();
} }
namespace Emitter { namespace Emitter {
@@ -63,6 +66,7 @@ namespace Test {
void LongKeyFlowMap(YAML::Emitter& out, std::string& desiredOutput); void LongKeyFlowMap(YAML::Emitter& out, std::string& desiredOutput);
void BlockMapAsKey(YAML::Emitter& out, std::string& desiredOutput); void BlockMapAsKey(YAML::Emitter& out, std::string& desiredOutput);
void AliasAndAnchor(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 ComplexDoc(YAML::Emitter& out, std::string& desiredOutput);
void STLContainers(YAML::Emitter& out, std::string& desiredOutput); void STLContainers(YAML::Emitter& out, std::string& desiredOutput);
void SimpleComment(YAML::Emitter& out, std::string& desiredOutput); void SimpleComment(YAML::Emitter& out, std::string& desiredOutput);