Commit a1460169 authored by Jesse Beder's avatar Jesse Beder
Browse files

Fixed bug in anchors with no content. This involved refactoring the 'implicit...

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).
parent abe0af76
...@@ -75,10 +75,10 @@ namespace YAML ...@@ -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;
} }
......
...@@ -93,7 +93,6 @@ namespace YAML ...@@ -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 ...@@ -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;
......
...@@ -46,6 +46,7 @@ namespace YAML ...@@ -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 ...@@ -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 ...@@ -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 ...@@ -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,41 +245,87 @@ namespace YAML ...@@ -244,41 +245,87 @@ 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
......
...@@ -34,14 +34,26 @@ namespace YAML ...@@ -34,14 +34,26 @@ namespace YAML
void ClearAnchors(); void ClearAnchors();
private: 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 // scanning
void EnsureTokensInQueue(); void EnsureTokensInQueue();
void ScanNextToken(); void ScanNextToken();
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 ...@@ -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;
}; };
} }
......
...@@ -19,7 +19,7 @@ namespace YAML ...@@ -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 ...@@ -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 ...@@ -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 ...@@ -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 ...@@ -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 ...@@ -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 ...@@ -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 ...@@ -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;
......
...@@ -83,11 +83,11 @@ namespace YAML ...@@ -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 ...@@ -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;
} }
......
...@@ -35,7 +35,7 @@ namespace YAML ...@@ -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;
......
...@@ -18,7 +18,8 @@ namespace YAML ...@@ -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 ...@@ -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",
......
...@@ -272,6 +272,16 @@ namespace Test ...@@ -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;
......
...@@ -248,6 +248,30 @@ namespace Test ...@@ -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 ...@@ -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;
}
} }
} }
...@@ -263,9 +263,12 @@ namespace Test ...@@ -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 ...@@ -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);
......
...@@ -34,9 +34,12 @@ namespace Test { ...@@ -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 { ...@@ -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);
......
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