/Src/Dependencies/Boost/boost/property_tree/detail/info_parser_read.hpp
http://hadesmem.googlecode.com/ · C++ Header · 380 lines · 295 code · 43 blank · 42 comment · 115 complexity · 3a32103ca8b54fa783a9d04e789494c4 MD5 · raw file
- // ----------------------------------------------------------------------------
- // Copyright (C) 2002-2006 Marcin Kalicinski
- //
- // Distributed under the Boost Software License, Version 1.0.
- // (See accompanying file LICENSE_1_0.txt or copy at
- // http://www.boost.org/LICENSE_1_0.txt)
- //
- // For more information, see www.boost.org
- // ----------------------------------------------------------------------------
- #ifndef BOOST_PROPERTY_TREE_DETAIL_INFO_PARSER_READ_HPP_INCLUDED
- #define BOOST_PROPERTY_TREE_DETAIL_INFO_PARSER_READ_HPP_INCLUDED
- #include "boost/property_tree/ptree.hpp"
- #include "boost/property_tree/detail/info_parser_error.hpp"
- #include "boost/property_tree/detail/info_parser_utils.hpp"
- #include <iterator>
- #include <string>
- #include <stack>
- #include <fstream>
- #include <cctype>
- namespace boost { namespace property_tree { namespace info_parser
- {
- // Expand known escape sequences
- template<class It>
- std::basic_string<typename std::iterator_traits<It>::value_type>
- expand_escapes(It b, It e)
- {
- typedef typename std::iterator_traits<It>::value_type Ch;
- std::basic_string<Ch> result;
- while (b != e)
- {
- if (*b == Ch('\\'))
- {
- ++b;
- if (b == e)
- {
- BOOST_PROPERTY_TREE_THROW(info_parser_error(
- "character expected after backslash", "", 0));
- }
- else if (*b == Ch('0')) result += Ch('\0');
- else if (*b == Ch('a')) result += Ch('\a');
- else if (*b == Ch('b')) result += Ch('\b');
- else if (*b == Ch('f')) result += Ch('\f');
- else if (*b == Ch('n')) result += Ch('\n');
- else if (*b == Ch('r')) result += Ch('\r');
- else if (*b == Ch('t')) result += Ch('\t');
- else if (*b == Ch('v')) result += Ch('\v');
- else if (*b == Ch('"')) result += Ch('"');
- else if (*b == Ch('\'')) result += Ch('\'');
- else if (*b == Ch('\\')) result += Ch('\\');
- else
- BOOST_PROPERTY_TREE_THROW(info_parser_error(
- "unknown escape sequence", "", 0));
- }
- else
- result += *b;
- ++b;
- }
- return result;
- }
- // Advance pointer past whitespace
- template<class Ch>
- void skip_whitespace(const Ch *&text)
- {
- using namespace std;
- while (isspace(*text))
- ++text;
- }
- // Extract word (whitespace delimited) and advance pointer accordingly
- template<class Ch>
- std::basic_string<Ch> read_word(const Ch *&text)
- {
- using namespace std;
- skip_whitespace(text);
- const Ch *start = text;
- while (!isspace(*text) && *text != Ch(';') && *text != Ch('\0'))
- ++text;
- return expand_escapes(start, text);
- }
- // Extract line (eol delimited) and advance pointer accordingly
- template<class Ch>
- std::basic_string<Ch> read_line(const Ch *&text)
- {
- using namespace std;
- skip_whitespace(text);
- const Ch *start = text;
- while (*text != Ch('\0') && *text != Ch(';'))
- ++text;
- while (text > start && isspace(*(text - 1)))
- --text;
- return expand_escapes(start, text);
- }
- // Extract string (inside ""), and advance pointer accordingly
- // Set need_more_lines to true if \ continuator found
- template<class Ch>
- std::basic_string<Ch> read_string(const Ch *&text, bool *need_more_lines)
- {
- skip_whitespace(text);
- if (*text == Ch('\"'))
- {
- // Skip "
- ++text;
- // Find end of string, but skip escaped "
- bool escaped = false;
- const Ch *start = text;
- while ((escaped || *text != Ch('\"')) && *text != Ch('\0'))
- {
- escaped = (!escaped && *text == Ch('\\'));
- ++text;
- }
- // If end of string found
- if (*text == Ch('\"'))
- {
- std::basic_string<Ch> result = expand_escapes(start, text++);
- skip_whitespace(text);
- if (*text == Ch('\\'))
- {
- if (!need_more_lines)
- BOOST_PROPERTY_TREE_THROW(info_parser_error(
- "unexpected \\", "", 0));
- ++text;
- skip_whitespace(text);
- if (*text == Ch('\0') || *text == Ch(';'))
- *need_more_lines = true;
- else
- BOOST_PROPERTY_TREE_THROW(info_parser_error(
- "expected end of line after \\", "", 0));
- }
- else
- if (need_more_lines)
- *need_more_lines = false;
- return result;
- }
- else
- BOOST_PROPERTY_TREE_THROW(info_parser_error(
- "unexpected end of line", "", 0));
- }
- else
- BOOST_PROPERTY_TREE_THROW(info_parser_error("expected \"", "", 0));
- }
- // Extract key
- template<class Ch>
- std::basic_string<Ch> read_key(const Ch *&text)
- {
- skip_whitespace(text);
- if (*text == Ch('\"'))
- return read_string(text, NULL);
- else
- return read_word(text);
- }
- // Extract data
- template<class Ch>
- std::basic_string<Ch> read_data(const Ch *&text, bool *need_more_lines)
- {
- skip_whitespace(text);
- if (*text == Ch('\"'))
- return read_string(text, need_more_lines);
- else
- {
- *need_more_lines = false;
- return read_word(text);
- }
- }
- // Build ptree from info stream
- template<class Ptree, class Ch>
- void read_info_internal(std::basic_istream<Ch> &stream,
- Ptree &pt,
- const std::string &filename,
- int include_depth)
- {
- typedef std::basic_string<Ch> str_t;
- // Possible parser states
- enum state_t {
- s_key, // Parser expects key
- s_data, // Parser expects data
- s_data_cont // Parser expects data continuation
- };
- unsigned long line_no = 0;
- state_t state = s_key; // Parser state
- Ptree *last = NULL; // Pointer to last created ptree
- // Define line here to minimize reallocations
- str_t line;
- // Initialize ptree stack (used to handle nesting)
- std::stack<Ptree *> stack;
- stack.push(&pt); // Push root ptree on stack initially
- try {
- // While there are characters in the stream
- while (stream.good()) {
- // Read one line from stream
- ++line_no;
- std::getline(stream, line);
- if (!stream.good() && !stream.eof())
- BOOST_PROPERTY_TREE_THROW(info_parser_error(
- "read error", filename, line_no));
- const Ch *text = line.c_str();
- // If directive found
- skip_whitespace(text);
- if (*text == Ch('#')) {
- // Determine directive type
- ++text; // skip #
- std::basic_string<Ch> directive = read_word(text);
- if (directive == convert_chtype<Ch, char>("include")) {
- // #include
- if (include_depth > 100) {
- BOOST_PROPERTY_TREE_THROW(info_parser_error(
- "include depth too large, "
- "probably recursive include",
- filename, line_no));
- }
- str_t s = read_string(text, NULL);
- std::string inc_name =
- convert_chtype<char, Ch>(s.c_str());
- std::basic_ifstream<Ch> inc_stream(inc_name.c_str());
- if (!inc_stream.good())
- BOOST_PROPERTY_TREE_THROW(info_parser_error(
- "cannot open include file " + inc_name,
- filename, line_no));
- read_info_internal(inc_stream, *stack.top(),
- inc_name, include_depth + 1);
- } else { // Unknown directive
- BOOST_PROPERTY_TREE_THROW(info_parser_error(
- "unknown directive", filename, line_no));
- }
- // Directive must be followed by end of line
- skip_whitespace(text);
- if (*text != Ch('\0')) {
- BOOST_PROPERTY_TREE_THROW(info_parser_error(
- "expected end of line", filename, line_no));
- }
- // Go to next line
- continue;
- }
- // While there are characters left in line
- while (1) {
- // Stop parsing on end of line or comment
- skip_whitespace(text);
- if (*text == Ch('\0') || *text == Ch(';')) {
- if (state == s_data) // If there was no data set state to s_key
- state = s_key;
- break;
- }
- // Process according to current parser state
- switch (state)
- {
- // Parser expects key
- case s_key:
- {
- if (*text == Ch('{')) // Brace opening found
- {
- if (!last)
- BOOST_PROPERTY_TREE_THROW(info_parser_error("unexpected {", "", 0));
- stack.push(last);
- last = NULL;
- ++text;
- }
- else if (*text == Ch('}')) // Brace closing found
- {
- if (stack.size() <= 1)
- BOOST_PROPERTY_TREE_THROW(info_parser_error("unmatched }", "", 0));
- stack.pop();
- last = NULL;
- ++text;
- }
- else // Key text found
- {
- std::basic_string<Ch> key = read_key(text);
- last = &stack.top()->push_back(
- std::make_pair(key, Ptree()))->second;
- state = s_data;
- }
- }; break;
- // Parser expects data
- case s_data:
- {
-
- // Last ptree must be defined because we are going to add data to it
- BOOST_ASSERT(last);
-
- if (*text == Ch('{')) // Brace opening found
- {
- stack.push(last);
- last = NULL;
- ++text;
- state = s_key;
- }
- else if (*text == Ch('}')) // Brace closing found
- {
- if (stack.size() <= 1)
- BOOST_PROPERTY_TREE_THROW(info_parser_error("unmatched }", "", 0));
- stack.pop();
- last = NULL;
- ++text;
- state = s_key;
- }
- else // Data text found
- {
- bool need_more_lines;
- std::basic_string<Ch> data = read_data(text, &need_more_lines);
- last->data() = data;
- state = need_more_lines ? s_data_cont : s_key;
- }
- }; break;
- // Parser expects continuation of data after \ on previous line
- case s_data_cont:
- {
-
- // Last ptree must be defined because we are going to update its data
- BOOST_ASSERT(last);
-
- if (*text == Ch('\"')) // Continuation must start with "
- {
- bool need_more_lines;
- std::basic_string<Ch> data = read_string(text, &need_more_lines);
- last->put_value(last->template get_value<std::basic_string<Ch> >() + data);
- state = need_more_lines ? s_data_cont : s_key;
- }
- else
- BOOST_PROPERTY_TREE_THROW(info_parser_error("expected \" after \\ in previous line", "", 0));
- }; break;
- // Should never happen
- default:
- BOOST_ASSERT(0);
- }
- }
- }
- // Check if stack has initial size, otherwise some {'s have not been closed
- if (stack.size() != 1)
- BOOST_PROPERTY_TREE_THROW(info_parser_error("unmatched {", "", 0));
- }
- catch (info_parser_error &e)
- {
- // If line undefined rethrow error with correct filename and line
- if (e.line() == 0)
- {
- BOOST_PROPERTY_TREE_THROW(info_parser_error(e.message(), filename, line_no));
- }
- else
- BOOST_PROPERTY_TREE_THROW(e);
- }
- }
- } } }
- #endif