mirror of https://github.com/axmolengine/axmol.git
433 lines
12 KiB
C++
433 lines
12 KiB
C++
#include <algorithm>
|
|
#include <cstdio>
|
|
#include <sstream>
|
|
|
|
#include "collectionstack.h" // IWYU pragma: keep
|
|
#include "scanner.h"
|
|
#include "singledocparser.h"
|
|
#include "tag.h"
|
|
#include "token.h"
|
|
#include "yaml-cpp/depthguard.h"
|
|
#include "yaml-cpp/emitterstyle.h"
|
|
#include "yaml-cpp/eventhandler.h"
|
|
#include "yaml-cpp/exceptions.h" // IWYU pragma: keep
|
|
#include "yaml-cpp/mark.h"
|
|
#include "yaml-cpp/null.h"
|
|
|
|
namespace YAML {
|
|
SingleDocParser::SingleDocParser(Scanner& scanner, const Directives& directives)
|
|
: m_scanner(scanner),
|
|
m_directives(directives),
|
|
m_pCollectionStack(new CollectionStack),
|
|
m_anchors{},
|
|
m_curAnchor(0) {}
|
|
|
|
SingleDocParser::~SingleDocParser() = default;
|
|
|
|
// 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) {
|
|
DepthGuard<500> depthguard(depth, m_scanner.mark(), ErrorMsg::BAD_FILE);
|
|
|
|
// an empty node *is* a possibility
|
|
if (m_scanner.empty()) {
|
|
eventHandler.OnNull(m_scanner.mark(), 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, EmitterStyle::Default);
|
|
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;
|
|
std::string anchor_name;
|
|
anchor_t anchor;
|
|
ParseProperties(tag, anchor, anchor_name);
|
|
|
|
if (!anchor_name.empty())
|
|
eventHandler.OnAnchor(mark, anchor_name);
|
|
|
|
// after parsing properties, an empty node is again a possibility
|
|
if (m_scanner.empty()) {
|
|
eventHandler.OnNull(mark, anchor);
|
|
return;
|
|
}
|
|
|
|
const Token& token = m_scanner.peek();
|
|
|
|
// add non-specific tags
|
|
if (tag.empty())
|
|
tag = (token.type == Token::NON_PLAIN_SCALAR ? "!" : "?");
|
|
|
|
if (token.type == Token::PLAIN_SCALAR
|
|
&& tag.compare("?") == 0 && IsNullString(token.value)) {
|
|
eventHandler.OnNull(mark, anchor);
|
|
m_scanner.pop();
|
|
return;
|
|
}
|
|
|
|
// now split based on what kind of node we should be
|
|
switch (token.type) {
|
|
case Token::PLAIN_SCALAR:
|
|
case Token::NON_PLAIN_SCALAR:
|
|
eventHandler.OnScalar(mark, tag, anchor, token.value);
|
|
m_scanner.pop();
|
|
return;
|
|
case Token::FLOW_SEQ_START:
|
|
eventHandler.OnSequenceStart(mark, tag, anchor, EmitterStyle::Flow);
|
|
HandleSequence(eventHandler);
|
|
eventHandler.OnSequenceEnd();
|
|
return;
|
|
case Token::BLOCK_SEQ_START:
|
|
eventHandler.OnSequenceStart(mark, tag, anchor, EmitterStyle::Block);
|
|
HandleSequence(eventHandler);
|
|
eventHandler.OnSequenceEnd();
|
|
return;
|
|
case Token::FLOW_MAP_START:
|
|
eventHandler.OnMapStart(mark, tag, anchor, EmitterStyle::Flow);
|
|
HandleMap(eventHandler);
|
|
eventHandler.OnMapEnd();
|
|
return;
|
|
case Token::BLOCK_MAP_START:
|
|
eventHandler.OnMapStart(mark, tag, anchor, EmitterStyle::Block);
|
|
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, EmitterStyle::Flow);
|
|
HandleMap(eventHandler);
|
|
eventHandler.OnMapEnd();
|
|
return;
|
|
}
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
if (tag == "?")
|
|
eventHandler.OnNull(mark, anchor);
|
|
else
|
|
eventHandler.OnScalar(mark, 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 (true) {
|
|
if (m_scanner.empty())
|
|
throw ParserException(m_scanner.mark(), 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& nextToken = m_scanner.peek();
|
|
if (nextToken.type == Token::BLOCK_ENTRY ||
|
|
nextToken.type == Token::BLOCK_SEQ_END) {
|
|
eventHandler.OnNull(nextToken.mark, 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 (true) {
|
|
if (m_scanner.empty())
|
|
throw ParserException(m_scanner.mark(), 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);
|
|
|
|
if (m_scanner.empty())
|
|
throw ParserException(m_scanner.mark(), ErrorMsg::END_OF_SEQ_FLOW);
|
|
|
|
// 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 (true) {
|
|
if (m_scanner.empty())
|
|
throw ParserException(m_scanner.mark(), 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(token.mark, NullAnchor);
|
|
}
|
|
|
|
// now grab value (optional)
|
|
if (!m_scanner.empty() && m_scanner.peek().type == Token::VALUE) {
|
|
m_scanner.pop();
|
|
HandleNode(eventHandler);
|
|
} else {
|
|
eventHandler.OnNull(token.mark, NullAnchor);
|
|
}
|
|
}
|
|
|
|
m_pCollectionStack->PopCollectionType(CollectionType::BlockMap);
|
|
}
|
|
|
|
void SingleDocParser::HandleFlowMap(EventHandler& eventHandler) {
|
|
// eat start token
|
|
m_scanner.pop();
|
|
m_pCollectionStack->PushCollectionType(CollectionType::FlowMap);
|
|
|
|
while (true) {
|
|
if (m_scanner.empty())
|
|
throw ParserException(m_scanner.mark(), ErrorMsg::END_OF_MAP_FLOW);
|
|
|
|
Token& token = m_scanner.peek();
|
|
const Mark mark = token.mark;
|
|
// 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(mark, NullAnchor);
|
|
}
|
|
|
|
// now grab value (optional)
|
|
if (!m_scanner.empty() && m_scanner.peek().type == Token::VALUE) {
|
|
m_scanner.pop();
|
|
HandleNode(eventHandler);
|
|
} else {
|
|
eventHandler.OnNull(mark, NullAnchor);
|
|
}
|
|
|
|
if (m_scanner.empty())
|
|
throw ParserException(m_scanner.mark(), ErrorMsg::END_OF_MAP_FLOW);
|
|
|
|
// 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
|
|
Mark mark = m_scanner.peek().mark;
|
|
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(mark, 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(m_scanner.peek().mark, 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,
|
|
std::string& anchor_name) {
|
|
tag.clear();
|
|
anchor_name.clear();
|
|
anchor = NullAnchor;
|
|
|
|
while (true) {
|
|
if (m_scanner.empty())
|
|
return;
|
|
|
|
switch (m_scanner.peek().type) {
|
|
case Token::TAG:
|
|
ParseTag(tag);
|
|
break;
|
|
case Token::ANCHOR:
|
|
ParseAnchor(anchor, anchor_name);
|
|
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, std::string& anchor_name) {
|
|
Token& token = m_scanner.peek();
|
|
if (anchor)
|
|
throw ParserException(token.mark, ErrorMsg::MULTIPLE_ANCHORS);
|
|
|
|
anchor_name = token.value;
|
|
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 {
|
|
auto it = m_anchors.find(name);
|
|
if (it == m_anchors.end())
|
|
throw ParserException(mark, ErrorMsg::UNKNOWN_ANCHOR);
|
|
|
|
return it->second;
|
|
}
|
|
} // namespace YAML
|