parser.cpp 3.49 KB
Newer Older
1
2
3
4
#include "parser.h"
#include "scanner.h"
#include "token.h"
#include "exceptions.h"
5
#include "parserstate.h"
6
#include <sstream>
7
#include <cstdio>
8
9
10

namespace YAML
{
11
12
13
14
15
	Parser::Parser()
	{
	}
	
	Parser::Parser(std::istream& in)
16
17
18
19
20
21
22
23
24
25
	{
		Load(in);
	}

	Parser::~Parser()
	{
	}

	Parser::operator bool() const
	{
26
		return m_pScanner.get() && !m_pScanner->empty();
27
28
29
30
	}

	void Parser::Load(std::istream& in)
	{
31
		m_pScanner.reset(new Scanner(in));
32
		m_pState.reset(new ParserState);
33
34
35
36
37
	}

	// GetNextDocument
	// . Reads the next document in the queue (of tokens).
	// . Throws a ParserException on error.
38
	bool Parser::GetNextDocument(Node& document)
39
	{
40
41
42
		if(!m_pScanner.get())
			return false;
		
43
44
45
46
47
48
49
50
		// clear node
		document.Clear();

		// first read directives
		ParseDirectives();

		// we better have some tokens in the queue
		if(m_pScanner->empty())
51
			return false;
52
53

		// first eat doc start (optional)
54
		if(m_pScanner->peek().type == Token::DOC_START)
55
56
57
			m_pScanner->pop();

		// now parse our root node
58
		document.Parse(m_pScanner.get(), *m_pState);
59
60

		// and finally eat any doc ends we see
61
		while(!m_pScanner->empty() && m_pScanner->peek().type == Token::DOC_END)
62
			m_pScanner->pop();
63
64
65

		// clear anchors from the scanner, which are no longer relevant
		m_pScanner->ClearAnchors();
66
67
		
		return true;
68
69
70
71
72
73
74
75
76
77
78
79
80
	}

	// 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();
81
			if(token.type != Token::DIRECTIVE)
82
83
84
85
86
				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)
87
				m_pState.reset(new ParserState);
88
89

			readDirective = true;
90
			HandleDirective(token);
91
92
93
94
			m_pScanner->pop();
		}
	}

95
	void Parser::HandleDirective(const Token& token)
96
	{
97
98
99
100
		if(token.value == "YAML")
			HandleYamlDirective(token);
		else if(token.value == "TAG")
			HandleTagDirective(token);
101
102
103
104
	}

	// HandleYamlDirective
	// . Should be of the form 'major.minor' (like a version number)
105
	void Parser::HandleYamlDirective(const Token& token)
106
	{
107
108
109
110
111
		if(token.params.size() != 1)
			throw ParserException(token.mark, ErrorMsg::YAML_DIRECTIVE_ARGS);
		
		if(!m_pState->version.isDefault)
			throw ParserException(token.mark, ErrorMsg::REPEATED_YAML_DIRECTIVE);
112

113
114
		std::stringstream str(token.params[0]);
		str >> m_pState->version.major;
115
		str.get();
116
		str >> m_pState->version.minor;
117
		if(!str || str.peek() != EOF)
118
			throw ParserException(token.mark, ErrorMsg::YAML_VERSION + token.params[0]);
119

120
121
		if(m_pState->version.major > 1)
			throw ParserException(token.mark, ErrorMsg::YAML_MAJOR_VERSION);
122

123
		m_pState->version.isDefault = false;
124
125
126
127
128
		// TODO: warning on major == 1, minor > 2?
	}

	// HandleTagDirective
	// . Should be of the form 'handle prefix', where 'handle' is converted to 'prefix' in the file.
129
	void Parser::HandleTagDirective(const Token& token)
130
	{
131
132
		if(token.params.size() != 2)
			throw ParserException(token.mark, ErrorMsg::TAG_DIRECTIVE_ARGS);
133

134
135
136
137
138
139
		const std::string& handle = token.params[0];
		const std::string& prefix = token.params[1];
		if(m_pState->tags.find(handle) != m_pState->tags.end())
			throw ParserException(token.mark, ErrorMsg::REPEATED_TAG_DIRECTIVE);
		
		m_pState->tags[handle] = prefix;
140
141
142
143
	}

	void Parser::PrintTokens(std::ostream& out)
	{
144
145
146
		if(!m_pScanner.get())
			return;
		
147
148
149
150
151
152
153
154
155
		while(1) {
			if(m_pScanner->empty())
				break;

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