fix doc example typo
[boost.git] / boost / property_tree / detail / info_parser_read.hpp
blob2d205952e35f7922047d7768265cbe89cbacdd22
1 // ----------------------------------------------------------------------------
2 // Copyright (C) 2002-2006 Marcin Kalicinski
3 //
4 // Distributed under the Boost Software License, Version 1.0.
5 // (See accompanying file LICENSE_1_0.txt or copy at
6 // http://www.boost.org/LICENSE_1_0.txt)
7 //
8 // For more information, see www.boost.org
9 // ----------------------------------------------------------------------------
10 #ifndef BOOST_PROPERTY_TREE_DETAIL_INFO_PARSER_READ_HPP_INCLUDED
11 #define BOOST_PROPERTY_TREE_DETAIL_INFO_PARSER_READ_HPP_INCLUDED
13 #include "boost/property_tree/ptree.hpp"
14 #include "boost/property_tree/detail/info_parser_error.hpp"
15 #include "boost/property_tree/detail/info_parser_utils.hpp"
16 #include <iterator>
17 #include <string>
18 #include <stack>
19 #include <fstream>
20 #include <cctype>
22 namespace boost { namespace property_tree { namespace info_parser
25 // Expand known escape sequences
26 template<class It>
27 std::basic_string<typename std::iterator_traits<It>::value_type>
28 expand_escapes(It b, It e)
30 typedef typename std::iterator_traits<It>::value_type Ch;
31 std::basic_string<Ch> result;
32 while (b != e)
34 if (*b == Ch('\\'))
36 ++b;
37 if (b == e)
39 BOOST_PROPERTY_TREE_THROW(info_parser_error("character expected after backslash", "", 0));
41 else if (*b == Ch('0')) result += Ch('\0');
42 else if (*b == Ch('a')) result += Ch('\a');
43 else if (*b == Ch('b')) result += Ch('\b');
44 else if (*b == Ch('f')) result += Ch('\f');
45 else if (*b == Ch('n')) result += Ch('\n');
46 else if (*b == Ch('r')) result += Ch('\r');
47 else if (*b == Ch('t')) result += Ch('\t');
48 else if (*b == Ch('v')) result += Ch('\v');
49 else if (*b == Ch('"')) result += Ch('"');
50 else if (*b == Ch('\'')) result += Ch('\'');
51 else if (*b == Ch('\\')) result += Ch('\\');
52 else
53 BOOST_PROPERTY_TREE_THROW(info_parser_error("unknown escape sequence", "", 0));
55 else
56 result += *b;
57 ++b;
59 return result;
62 // Advance pointer past whitespace
63 template<class Ch>
64 void skip_whitespace(const Ch *&text)
66 using namespace std;
67 while (isspace(*text))
68 ++text;
71 // Extract word (whitespace delimited) and advance pointer accordingly
72 template<class Ch>
73 std::basic_string<Ch> read_word(const Ch *&text)
75 using namespace std;
76 skip_whitespace(text);
77 const Ch *start = text;
78 while (!isspace(*text) && *text != Ch(';') && *text != Ch('\0'))
79 ++text;
80 return expand_escapes(start, text);
83 // Extract line (eol delimited) and advance pointer accordingly
84 template<class Ch>
85 std::basic_string<Ch> read_line(const Ch *&text)
87 using namespace std;
88 skip_whitespace(text);
89 const Ch *start = text;
90 while (*text != Ch('\0') && *text != Ch(';'))
91 ++text;
92 while (text > start && isspace(*(text - 1)))
93 --text;
94 return expand_escapes(start, text);
97 // Extract string (inside ""), and advance pointer accordingly
98 // Set need_more_lines to true if \ continuator found
99 template<class Ch>
100 std::basic_string<Ch> read_string(const Ch *&text, bool *need_more_lines)
102 skip_whitespace(text);
103 if (*text == Ch('\"'))
106 // Skip "
107 ++text;
109 // Find end of string, but skip escaped "
110 bool escaped = false;
111 const Ch *start = text;
112 while ((escaped || *text != Ch('\"')) && *text != Ch('\0'))
114 escaped = (!escaped && *text == Ch('\\'));
115 ++text;
118 // If end of string found
119 if (*text == Ch('\"'))
121 std::basic_string<Ch> result = expand_escapes(start, text++);
122 skip_whitespace(text);
123 if (*text == Ch('\\'))
125 if (!need_more_lines)
126 BOOST_PROPERTY_TREE_THROW(info_parser_error("unexpected \\", "", 0));
127 ++text;
128 skip_whitespace(text);
129 if (*text == Ch('\0') || *text == Ch(';'))
130 *need_more_lines = true;
131 else
132 BOOST_PROPERTY_TREE_THROW(info_parser_error("expected end of line after \\", "", 0));
134 else
135 if (need_more_lines)
136 *need_more_lines = false;
137 return result;
139 else
140 BOOST_PROPERTY_TREE_THROW(info_parser_error("unexpected end of line", "", 0));
143 else
144 BOOST_PROPERTY_TREE_THROW(info_parser_error("expected \"", "", 0));
147 // Extract key
148 template<class Ch>
149 std::basic_string<Ch> read_key(const Ch *&text)
151 skip_whitespace(text);
152 if (*text == Ch('\"'))
153 return read_string(text, NULL);
154 else
155 return read_word(text);
158 // Extract data
159 template<class Ch>
160 std::basic_string<Ch> read_data(const Ch *&text, bool *need_more_lines)
162 skip_whitespace(text);
163 if (*text == Ch('\"'))
164 return read_string(text, need_more_lines);
165 else
167 *need_more_lines = false;
168 return read_word(text);
172 // Build ptree from info stream
173 template<class Ptree>
174 void read_info_internal(std::basic_istream<typename Ptree::key_type::value_type> &stream,
175 Ptree &pt,
176 const std::string &filename,
177 int include_depth)
180 // Character type
181 typedef typename Ptree::key_type::value_type Ch;
183 // Possible parser states
184 enum state_t {
185 s_key, // Parser expects key
186 s_data, // Parser expects data
187 s_data_cont // Parser expects data continuation
190 unsigned long line_no = 0;
191 state_t state = s_key; // Parser state
192 Ptree *last = NULL; // Pointer to last created ptree
193 std::basic_string<Ch> line; // Define line here to minimize reallocations
195 // Initialize ptree stack (used to handle nesting)
196 std::stack<Ptree *> stack;
197 stack.push(&pt); // Push root ptree on stack initially
202 // While there are characters in the stream
203 while (stream.good())
206 // Read one line from stream
207 ++line_no;
208 std::getline(stream, line);
209 if (!stream.good() && !stream.eof())
210 BOOST_PROPERTY_TREE_THROW(info_parser_error("read error", "", 0));
211 const Ch *text = line.c_str();
213 // If directive found
214 skip_whitespace(text);
215 if (*text == Ch('#'))
218 // Determine directive type
219 ++text; // skip #
220 std::basic_string<Ch> directive = read_word(text);
221 if (directive == convert_chtype<Ch, char>("include")) // #include
223 if (include_depth > 100)
224 BOOST_PROPERTY_TREE_THROW(info_parser_error("include depth too large, probably recursive include", "", 0));
225 std::basic_string<Ch> s = read_string(text, NULL);
226 std::string inc_name = convert_chtype<char, Ch>(s.c_str());
227 std::basic_ifstream<Ch> inc_stream(inc_name.c_str());
228 if (!inc_stream.good())
229 BOOST_PROPERTY_TREE_THROW(info_parser_error("cannot open include file " + inc_name, "", 0));
230 read_info_internal(inc_stream, *stack.top(), inc_name, include_depth + 1);
232 else // Unknown directive
233 BOOST_PROPERTY_TREE_THROW(info_parser_error("unknown directive", "", 0));
235 // Directive must be followed by end of line
236 skip_whitespace(text);
237 if (*text != Ch('\0'))
238 BOOST_PROPERTY_TREE_THROW(info_parser_error("expected end of line", "", 0));
240 // Go to next line
241 continue;
245 // While there are characters left in line
246 while (1)
249 // Stop parsing on end of line or comment
250 skip_whitespace(text);
251 if (*text == Ch('\0') || *text == Ch(';'))
253 if (state == s_data) // If there was no data set state to s_key
254 state = s_key;
255 break;
258 // Process according to current parser state
259 switch (state)
262 // Parser expects key
263 case s_key:
266 if (*text == Ch('{')) // Brace opening found
268 if (!last)
269 BOOST_PROPERTY_TREE_THROW(info_parser_error("unexpected {", "", 0));
270 stack.push(last);
271 last = NULL;
272 ++text;
274 else if (*text == Ch('}')) // Brace closing found
276 if (stack.size() <= 1)
277 BOOST_PROPERTY_TREE_THROW(info_parser_error("unmatched }", "", 0));
278 stack.pop();
279 last = NULL;
280 ++text;
282 else // Key text found
284 std::basic_string<Ch> key = read_key(text);
285 last = &stack.top()->push_back(std::make_pair(key, Ptree()))->second;
286 state = s_data;
289 }; break;
291 // Parser expects data
292 case s_data:
295 // Last ptree must be defined because we are going to add data to it
296 BOOST_ASSERT(last);
298 if (*text == Ch('{')) // Brace opening found
300 stack.push(last);
301 last = NULL;
302 ++text;
303 state = s_key;
305 else if (*text == Ch('}')) // Brace closing found
307 if (stack.size() <= 1)
308 BOOST_PROPERTY_TREE_THROW(info_parser_error("unmatched }", "", 0));
309 stack.pop();
310 last = NULL;
311 ++text;
312 state = s_key;
314 else // Data text found
316 bool need_more_lines;
317 std::basic_string<Ch> data = read_data(text, &need_more_lines);
318 last->data() = data;
319 state = need_more_lines ? s_data_cont : s_key;
323 }; break;
325 // Parser expects continuation of data after \ on previous line
326 case s_data_cont:
329 // Last ptree must be defined because we are going to update its data
330 BOOST_ASSERT(last);
332 if (*text == Ch('\"')) // Continuation must start with "
334 bool need_more_lines;
335 std::basic_string<Ch> data = read_string(text, &need_more_lines);
336 last->put_value(last->template get_value<std::basic_string<Ch> >() + data);
337 state = need_more_lines ? s_data_cont : s_key;
339 else
340 BOOST_PROPERTY_TREE_THROW(info_parser_error("expected \" after \\ in previous line", "", 0));
342 }; break;
344 // Should never happen
345 default:
346 BOOST_ASSERT(0);
352 // Check if stack has initial size, otherwise some {'s have not been closed
353 if (stack.size() != 1)
354 BOOST_PROPERTY_TREE_THROW(info_parser_error("unmatched {", "", 0));
357 catch (info_parser_error &e)
359 // If line undefined rethrow error with correct filename and line
360 if (e.line() == 0)
362 BOOST_PROPERTY_TREE_THROW(info_parser_error(e.message(), filename, line_no));
364 else
365 BOOST_PROPERTY_TREE_THROW(e);
371 } } }
373 #endif