parser.cpp 3.07 KB
Newer Older
1
2
3
4
5
6
#include "crt.h"
#include "parser.h"
#include "scanner.h"
#include "token.h"
#include "exceptions.h"
#include <sstream>
7
#include <cstdio>
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35

namespace YAML
{
	Parser::Parser(std::istream& in): m_pScanner(0)
	{
		Load(in);
	}

	Parser::~Parser()
	{
		delete m_pScanner;
	}

	Parser::operator bool() const
	{
		return !m_pScanner->empty();
	}

	void Parser::Load(std::istream& in)
	{
		delete m_pScanner;
		m_pScanner = new Scanner(in);
		m_state.Reset();
	}

	// GetNextDocument
	// . Reads the next document in the queue (of tokens).
	// . Throws a ParserException on error.
36
	bool Parser::GetNextDocument(Node& document)
37
38
39
40
41
42
43
44
45
	{
		// clear node
		document.Clear();

		// first read directives
		ParseDirectives();

		// we better have some tokens in the queue
		if(m_pScanner->empty())
46
			return false;
47
48

		// first eat doc start (optional)
49
		if(m_pScanner->peek().type == Token::DOC_START)
50
51
52
53
54
55
			m_pScanner->pop();

		// now parse our root node
		document.Parse(m_pScanner, m_state);

		// and finally eat any doc ends we see
56
		while(!m_pScanner->empty() && m_pScanner->peek().type == Token::DOC_END)
57
			m_pScanner->pop();
58
59
60

		// clear anchors from the scanner, which are no longer relevant
		m_pScanner->ClearAnchors();
61
62
		
		return true;
63
64
65
66
67
68
69
70
71
72
73
74
75
	}

	// ParseDirectives
	// . Reads any directives that are next in the queue.
	void Parser::ParseDirectives()
	{
		bool readDirective = false;

		while(1) {
			if(m_pScanner->empty())
				break;

			Token& token = m_pScanner->peek();
76
			if(token.type != Token::DIRECTIVE)
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
				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_state.Reset();

			readDirective = true;
			HandleDirective(&token);
			m_pScanner->pop();
		}
	}

	void Parser::HandleDirective(Token *pToken)
	{
		if(pToken->value == "YAML")
			HandleYamlDirective(pToken);
		else if(pToken->value == "TAG")
			HandleTagDirective(pToken);
	}

	// HandleYamlDirective
	// . Should be of the form 'major.minor' (like a version number)
	void Parser::HandleYamlDirective(Token *pToken)
	{
		if(pToken->params.size() != 1)
103
			throw ParserException(pToken->mark, ErrorMsg::YAML_DIRECTIVE_ARGS);
104
105
106
107
108
109

		std::stringstream str(pToken->params[0]);
		str >> m_state.version.major;
		str.get();
		str >> m_state.version.minor;
		if(!str || str.peek() != EOF)
110
			throw ParserException(pToken->mark, ErrorMsg::YAML_VERSION + pToken->params[0]);
111
112

		if(m_state.version.major > 1)
113
			throw ParserException(pToken->mark, ErrorMsg::YAML_MAJOR_VERSION);
114
115
116
117
118
119
120
121
122

		// TODO: warning on major == 1, minor > 2?
	}

	// HandleTagDirective
	// . Should be of the form 'handle prefix', where 'handle' is converted to 'prefix' in the file.
	void Parser::HandleTagDirective(Token *pToken)
	{
		if(pToken->params.size() != 2)
123
			throw ParserException(pToken->mark, ErrorMsg::TAG_DIRECTIVE_ARGS);
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139

		std::string handle = pToken->params[0], prefix = pToken->params[1];
		m_state.tags[handle] = prefix;
	}

	void Parser::PrintTokens(std::ostream& out)
	{
		while(1) {
			if(m_pScanner->empty())
				break;

			out << m_pScanner->peek() << "\n";
			m_pScanner->pop();
		}
	}
}