mirror of
https://github.com/jbeder/yaml-cpp.git
synced 2026-02-19 06:36:29 +00:00
Previously, no parsing progress (detecting infinite loops) was detected by an empty Node. This falsely triggers an abort, if an empty YAML document is being parsed. Instead, we detect if progress in the parsing stream is made, by comparing token positions. As long as new tokens are being parsed (detected by position change), we assume we are not in an infinite loop.
136 lines
3.4 KiB
C++
136 lines
3.4 KiB
C++
#include <cstdio>
|
|
#include <sstream>
|
|
|
|
#include "directives.h" // IWYU pragma: keep
|
|
#include "scanner.h" // IWYU pragma: keep
|
|
#include "singledocparser.h"
|
|
#include "token.h"
|
|
#include "yaml-cpp/exceptions.h" // IWYU pragma: keep
|
|
#include "yaml-cpp/parser.h"
|
|
|
|
namespace YAML {
|
|
class EventHandler;
|
|
|
|
Parser::Parser() : m_pScanner{}, m_pDirectives{} {}
|
|
|
|
Parser::Parser(std::istream& in) : Parser() { Load(in); }
|
|
|
|
Parser::~Parser() = default;
|
|
|
|
Parser::operator bool() const { return m_pScanner && !m_pScanner->empty(); }
|
|
|
|
void Parser::Load(std::istream& in) {
|
|
m_pScanner.reset(new Scanner(in));
|
|
m_pDirectives.reset(new Directives);
|
|
}
|
|
|
|
bool Parser::HandleNextDocument(EventHandler& eventHandler) {
|
|
if (!m_pScanner)
|
|
return false;
|
|
|
|
ParseDirectives();
|
|
if (m_pScanner->empty()) {
|
|
return false;
|
|
}
|
|
|
|
auto oldPos = m_pScanner->peek().mark.pos;
|
|
|
|
SingleDocParser sdp(*m_pScanner, *m_pDirectives);
|
|
sdp.HandleDocument(eventHandler);
|
|
|
|
// checks if progress was made
|
|
// 1. if scanner has no more tokens, progress was made
|
|
if (m_pScanner->empty()) {
|
|
return true;
|
|
}
|
|
|
|
// 2. if token position has changed, progress was made
|
|
auto newPos = m_pScanner->peek().mark.pos;
|
|
if (newPos != oldPos) {
|
|
return true;
|
|
}
|
|
// No progress was made, no further processing
|
|
return false;
|
|
}
|
|
|
|
void Parser::ParseDirectives() {
|
|
bool readDirective = false;
|
|
|
|
while (!m_pScanner->empty()) {
|
|
Token& token = m_pScanner->peek();
|
|
if (token.type != Token::DIRECTIVE) {
|
|
break;
|
|
}
|
|
|
|
// we keep the directives from the last document if none are specified;
|
|
// but if any directives are specific, then we reset them
|
|
if (!readDirective) {
|
|
m_pDirectives.reset(new Directives);
|
|
}
|
|
|
|
readDirective = true;
|
|
HandleDirective(token);
|
|
m_pScanner->pop();
|
|
}
|
|
}
|
|
|
|
void Parser::HandleDirective(const Token& token) {
|
|
if (token.value == "YAML") {
|
|
HandleYamlDirective(token);
|
|
} else if (token.value == "TAG") {
|
|
HandleTagDirective(token);
|
|
}
|
|
}
|
|
|
|
void Parser::HandleYamlDirective(const Token& token) {
|
|
if (token.params.size() != 1) {
|
|
throw ParserException(token.mark, ErrorMsg::YAML_DIRECTIVE_ARGS);
|
|
}
|
|
|
|
if (!m_pDirectives->version.isDefault) {
|
|
throw ParserException(token.mark, ErrorMsg::REPEATED_YAML_DIRECTIVE);
|
|
}
|
|
|
|
std::stringstream str(token.params[0]);
|
|
str.imbue(std::locale::classic());
|
|
str >> m_pDirectives->version.major;
|
|
str.get();
|
|
str >> m_pDirectives->version.minor;
|
|
if (!str || str.peek() != EOF) {
|
|
throw ParserException(
|
|
token.mark, std::string(ErrorMsg::YAML_VERSION) + token.params[0]);
|
|
}
|
|
|
|
if (m_pDirectives->version.major > 1) {
|
|
throw ParserException(token.mark, ErrorMsg::YAML_MAJOR_VERSION);
|
|
}
|
|
|
|
m_pDirectives->version.isDefault = false;
|
|
// TODO: warning on major == 1, minor > 2?
|
|
}
|
|
|
|
void Parser::HandleTagDirective(const Token& token) {
|
|
if (token.params.size() != 2)
|
|
throw ParserException(token.mark, ErrorMsg::TAG_DIRECTIVE_ARGS);
|
|
|
|
const std::string& handle = token.params[0];
|
|
const std::string& prefix = token.params[1];
|
|
if (m_pDirectives->tags.find(handle) != m_pDirectives->tags.end()) {
|
|
throw ParserException(token.mark, ErrorMsg::REPEATED_TAG_DIRECTIVE);
|
|
}
|
|
|
|
m_pDirectives->tags[handle] = prefix;
|
|
}
|
|
|
|
void Parser::PrintTokens(std::ostream& out) {
|
|
if (!m_pScanner) {
|
|
return;
|
|
}
|
|
|
|
while (!m_pScanner->empty()) {
|
|
out << m_pScanner->peek() << "\n";
|
|
m_pScanner->pop();
|
|
}
|
|
}
|
|
} // namespace YAML
|