mirror of
https://github.com/jbeder/yaml-cpp.git
synced 2025-09-09 12:41:17 +00:00
370 lines
9.6 KiB
C++
370 lines
9.6 KiB
C++
#include "singledocparser.h"
|
|
#include "collectionstack.h"
|
|
#include "directives.h"
|
|
#include "yaml-cpp/eventhandler.h"
|
|
#include "yaml-cpp/exceptions.h"
|
|
#include "scanner.h"
|
|
#include "tag.h"
|
|
#include "token.h"
|
|
#include <sstream>
|
|
#include <cstdio>
|
|
|
|
namespace YAML
|
|
{
|
|
SingleDocParser::SingleDocParser(Scanner& scanner, const Directives& directives): m_scanner(scanner), m_directives(directives), m_pCollectionStack(new CollectionStack), m_curAnchor(0)
|
|
{
|
|
}
|
|
|
|
SingleDocParser::~SingleDocParser()
|
|
{
|
|
}
|
|
|
|
// HandleDocument
|
|
// . Handles the next document
|
|
// . Throws a ParserException on error.
|
|
void SingleDocParser::HandleDocument(EventHandler& eventHandler)
|
|
{
|
|
assert(!m_scanner.empty()); // guaranteed that there are tokens
|
|
assert(!m_curAnchor);
|
|
|
|
eventHandler.OnDocumentStart(m_scanner.peek().mark);
|
|
|
|
// eat doc start
|
|
if(m_scanner.peek().type == Token::DOC_START)
|
|
m_scanner.pop();
|
|
|
|
// recurse!
|
|
HandleNode(eventHandler);
|
|
|
|
eventHandler.OnDocumentEnd();
|
|
|
|
// and finally eat any doc ends we see
|
|
while(!m_scanner.empty() && m_scanner.peek().type == Token::DOC_END)
|
|
m_scanner.pop();
|
|
}
|
|
|
|
void SingleDocParser::HandleNode(EventHandler& eventHandler)
|
|
{
|
|
// an empty node *is* a possibility
|
|
if(m_scanner.empty()) {
|
|
eventHandler.OnNull("", NullAnchor);
|
|
return;
|
|
}
|
|
|
|
// save location
|
|
Mark mark = m_scanner.peek().mark;
|
|
|
|
// special case: a value node by itself must be a map, with no header
|
|
if(m_scanner.peek().type == Token::VALUE) {
|
|
eventHandler.OnMapStart(mark, "", NullAnchor);
|
|
HandleMap(eventHandler);
|
|
eventHandler.OnMapEnd();
|
|
return;
|
|
}
|
|
|
|
// special case: an alias node
|
|
if(m_scanner.peek().type == Token::ALIAS) {
|
|
eventHandler.OnAlias(mark, LookupAnchor(mark, m_scanner.peek().value));
|
|
m_scanner.pop();
|
|
return;
|
|
}
|
|
|
|
std::string tag;
|
|
anchor_t anchor;
|
|
ParseProperties(tag, anchor);
|
|
|
|
// now split based on what kind of node we should be
|
|
switch(m_scanner.peek().type) {
|
|
case Token::SCALAR:
|
|
eventHandler.OnScalar(mark, tag, anchor, m_scanner.peek().value);
|
|
m_scanner.pop();
|
|
return;
|
|
case Token::FLOW_SEQ_START:
|
|
case Token::BLOCK_SEQ_START:
|
|
eventHandler.OnSequenceStart(mark, tag, anchor);
|
|
HandleSequence(eventHandler);
|
|
eventHandler.OnSequenceEnd();
|
|
return;
|
|
case Token::FLOW_MAP_START:
|
|
case Token::BLOCK_MAP_START:
|
|
eventHandler.OnMapStart(mark, tag, anchor);
|
|
HandleMap(eventHandler);
|
|
eventHandler.OnMapEnd();
|
|
return;
|
|
case Token::KEY:
|
|
// compact maps can only go in a flow sequence
|
|
if(m_pCollectionStack->GetCurCollectionType() == CollectionType::FlowSeq) {
|
|
eventHandler.OnMapStart(mark, tag, anchor);
|
|
HandleMap(eventHandler);
|
|
eventHandler.OnMapEnd();
|
|
return;
|
|
}
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
eventHandler.OnNull(tag, anchor);
|
|
}
|
|
|
|
void SingleDocParser::HandleSequence(EventHandler& eventHandler)
|
|
{
|
|
// split based on start token
|
|
switch(m_scanner.peek().type) {
|
|
case Token::BLOCK_SEQ_START: HandleBlockSequence(eventHandler); break;
|
|
case Token::FLOW_SEQ_START: HandleFlowSequence(eventHandler); break;
|
|
default: break;
|
|
}
|
|
}
|
|
|
|
void SingleDocParser::HandleBlockSequence(EventHandler& eventHandler)
|
|
{
|
|
// eat start token
|
|
m_scanner.pop();
|
|
m_pCollectionStack->PushCollectionType(CollectionType::BlockSeq);
|
|
|
|
while(1) {
|
|
if(m_scanner.empty())
|
|
throw ParserException(Mark::null(), ErrorMsg::END_OF_SEQ);
|
|
|
|
Token token = m_scanner.peek();
|
|
if(token.type != Token::BLOCK_ENTRY && token.type != Token::BLOCK_SEQ_END)
|
|
throw ParserException(token.mark, ErrorMsg::END_OF_SEQ);
|
|
|
|
m_scanner.pop();
|
|
if(token.type == Token::BLOCK_SEQ_END)
|
|
break;
|
|
|
|
// check for null
|
|
if(!m_scanner.empty()) {
|
|
const Token& token = m_scanner.peek();
|
|
if(token.type == Token::BLOCK_ENTRY || token.type == Token::BLOCK_SEQ_END) {
|
|
eventHandler.OnNull("", NullAnchor);
|
|
continue;
|
|
}
|
|
}
|
|
|
|
HandleNode(eventHandler);
|
|
}
|
|
|
|
m_pCollectionStack->PopCollectionType(CollectionType::BlockSeq);
|
|
}
|
|
|
|
void SingleDocParser::HandleFlowSequence(EventHandler& eventHandler)
|
|
{
|
|
// eat start token
|
|
m_scanner.pop();
|
|
m_pCollectionStack->PushCollectionType(CollectionType::FlowSeq);
|
|
|
|
while(1) {
|
|
if(m_scanner.empty())
|
|
throw ParserException(Mark::null(), ErrorMsg::END_OF_SEQ_FLOW);
|
|
|
|
// first check for end
|
|
if(m_scanner.peek().type == Token::FLOW_SEQ_END) {
|
|
m_scanner.pop();
|
|
break;
|
|
}
|
|
|
|
// then read the node
|
|
HandleNode(eventHandler);
|
|
|
|
// now eat the separator (or could be a sequence end, which we ignore - but if it's neither, then it's a bad node)
|
|
Token& token = m_scanner.peek();
|
|
if(token.type == Token::FLOW_ENTRY)
|
|
m_scanner.pop();
|
|
else if(token.type != Token::FLOW_SEQ_END)
|
|
throw ParserException(token.mark, ErrorMsg::END_OF_SEQ_FLOW);
|
|
}
|
|
|
|
m_pCollectionStack->PopCollectionType(CollectionType::FlowSeq);
|
|
}
|
|
|
|
void SingleDocParser::HandleMap(EventHandler& eventHandler)
|
|
{
|
|
// split based on start token
|
|
switch(m_scanner.peek().type) {
|
|
case Token::BLOCK_MAP_START: HandleBlockMap(eventHandler); break;
|
|
case Token::FLOW_MAP_START: HandleFlowMap(eventHandler); break;
|
|
case Token::KEY: HandleCompactMap(eventHandler); break;
|
|
case Token::VALUE: HandleCompactMapWithNoKey(eventHandler); break;
|
|
default: break;
|
|
}
|
|
}
|
|
|
|
void SingleDocParser::HandleBlockMap(EventHandler& eventHandler)
|
|
{
|
|
// eat start token
|
|
m_scanner.pop();
|
|
m_pCollectionStack->PushCollectionType(CollectionType::BlockMap);
|
|
|
|
while(1) {
|
|
if(m_scanner.empty())
|
|
throw ParserException(Mark::null(), ErrorMsg::END_OF_MAP);
|
|
|
|
Token token = m_scanner.peek();
|
|
if(token.type != Token::KEY && token.type != Token::VALUE && token.type != Token::BLOCK_MAP_END)
|
|
throw ParserException(token.mark, ErrorMsg::END_OF_MAP);
|
|
|
|
if(token.type == Token::BLOCK_MAP_END) {
|
|
m_scanner.pop();
|
|
break;
|
|
}
|
|
|
|
// grab key (if non-null)
|
|
if(token.type == Token::KEY) {
|
|
m_scanner.pop();
|
|
HandleNode(eventHandler);
|
|
} else {
|
|
eventHandler.OnNull("", NullAnchor);
|
|
}
|
|
|
|
// now grab value (optional)
|
|
if(!m_scanner.empty() && m_scanner.peek().type == Token::VALUE) {
|
|
m_scanner.pop();
|
|
HandleNode(eventHandler);
|
|
} else {
|
|
eventHandler.OnNull("", NullAnchor);
|
|
}
|
|
}
|
|
|
|
m_pCollectionStack->PopCollectionType(CollectionType::BlockMap);
|
|
}
|
|
|
|
void SingleDocParser::HandleFlowMap(EventHandler& eventHandler)
|
|
{
|
|
// eat start token
|
|
m_scanner.pop();
|
|
m_pCollectionStack->PushCollectionType(CollectionType::FlowMap);
|
|
|
|
while(1) {
|
|
if(m_scanner.empty())
|
|
throw ParserException(Mark::null(), ErrorMsg::END_OF_MAP_FLOW);
|
|
|
|
Token& token = m_scanner.peek();
|
|
// first check for end
|
|
if(token.type == Token::FLOW_MAP_END) {
|
|
m_scanner.pop();
|
|
break;
|
|
}
|
|
|
|
// grab key (if non-null)
|
|
if(token.type == Token::KEY) {
|
|
m_scanner.pop();
|
|
HandleNode(eventHandler);
|
|
} else {
|
|
eventHandler.OnNull("", NullAnchor);
|
|
}
|
|
|
|
// now grab value (optional)
|
|
if(!m_scanner.empty() && m_scanner.peek().type == Token::VALUE) {
|
|
m_scanner.pop();
|
|
HandleNode(eventHandler);
|
|
} else {
|
|
eventHandler.OnNull("", NullAnchor);
|
|
}
|
|
|
|
// now eat the separator (or could be a map end, which we ignore - but if it's neither, then it's a bad node)
|
|
Token& nextToken = m_scanner.peek();
|
|
if(nextToken.type == Token::FLOW_ENTRY)
|
|
m_scanner.pop();
|
|
else if(nextToken.type != Token::FLOW_MAP_END)
|
|
throw ParserException(nextToken.mark, ErrorMsg::END_OF_MAP_FLOW);
|
|
}
|
|
|
|
m_pCollectionStack->PopCollectionType(CollectionType::FlowMap);
|
|
}
|
|
|
|
// . Single "key: value" pair in a flow sequence
|
|
void SingleDocParser::HandleCompactMap(EventHandler& eventHandler)
|
|
{
|
|
m_pCollectionStack->PushCollectionType(CollectionType::CompactMap);
|
|
|
|
// grab key
|
|
m_scanner.pop();
|
|
HandleNode(eventHandler);
|
|
|
|
// now grab value (optional)
|
|
if(!m_scanner.empty() && m_scanner.peek().type == Token::VALUE) {
|
|
m_scanner.pop();
|
|
HandleNode(eventHandler);
|
|
} else {
|
|
eventHandler.OnNull("", NullAnchor);
|
|
}
|
|
|
|
m_pCollectionStack->PopCollectionType(CollectionType::CompactMap);
|
|
}
|
|
|
|
// . Single ": value" pair in a flow sequence
|
|
void SingleDocParser::HandleCompactMapWithNoKey(EventHandler& eventHandler)
|
|
{
|
|
m_pCollectionStack->PushCollectionType(CollectionType::CompactMap);
|
|
|
|
// null key
|
|
eventHandler.OnNull("", NullAnchor);
|
|
|
|
// grab value
|
|
m_scanner.pop();
|
|
HandleNode(eventHandler);
|
|
|
|
m_pCollectionStack->PopCollectionType(CollectionType::CompactMap);
|
|
}
|
|
|
|
// ParseProperties
|
|
// . Grabs any tag or anchor tokens and deals with them.
|
|
void SingleDocParser::ParseProperties(std::string& tag, anchor_t& anchor)
|
|
{
|
|
tag.clear();
|
|
anchor = NullAnchor;
|
|
|
|
while(1) {
|
|
if(m_scanner.empty())
|
|
return;
|
|
|
|
switch(m_scanner.peek().type) {
|
|
case Token::TAG: ParseTag(tag); break;
|
|
case Token::ANCHOR: ParseAnchor(anchor); break;
|
|
default: return;
|
|
}
|
|
}
|
|
}
|
|
|
|
void SingleDocParser::ParseTag(std::string& tag)
|
|
{
|
|
Token& token = m_scanner.peek();
|
|
if(!tag.empty())
|
|
throw ParserException(token.mark, ErrorMsg::MULTIPLE_TAGS);
|
|
|
|
Tag tagInfo(token);
|
|
tag = tagInfo.Translate(m_directives);
|
|
m_scanner.pop();
|
|
}
|
|
|
|
void SingleDocParser::ParseAnchor(anchor_t& anchor)
|
|
{
|
|
Token& token = m_scanner.peek();
|
|
if(anchor)
|
|
throw ParserException(token.mark, ErrorMsg::MULTIPLE_ANCHORS);
|
|
|
|
anchor = RegisterAnchor(token.value);
|
|
m_scanner.pop();
|
|
}
|
|
|
|
anchor_t SingleDocParser::RegisterAnchor(const std::string& name)
|
|
{
|
|
if(name.empty())
|
|
return NullAnchor;
|
|
|
|
return m_anchors[name] = ++m_curAnchor;
|
|
}
|
|
|
|
anchor_t SingleDocParser::LookupAnchor(const Mark& mark, const std::string& name) const
|
|
{
|
|
Anchors::const_iterator it = m_anchors.find(name);
|
|
if(it == m_anchors.end())
|
|
throw ParserException(mark, ErrorMsg::UNKNOWN_ANCHOR);
|
|
|
|
return it->second;
|
|
}
|
|
}
|