PageRenderTime 105ms CodeModel.GetById 75ms app.highlight 24ms RepoModel.GetById 1ms app.codeStats 0ms

/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 | 111 complexity | 3a32103ca8b54fa783a9d04e789494c4 MD5 | raw file
  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
 12
 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>
 21
 22namespace boost { namespace property_tree { namespace info_parser
 23{
 24
 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)
 29    {
 30        typedef typename std::iterator_traits<It>::value_type Ch;
 31        std::basic_string<Ch> result;
 32        while (b != e)
 33        {
 34            if (*b == Ch('\\'))
 35            {
 36                ++b;
 37                if (b == e)
 38                {
 39                    BOOST_PROPERTY_TREE_THROW(info_parser_error(
 40                        "character expected after backslash", "", 0));
 41                }
 42                else if (*b == Ch('0')) result += Ch('\0');
 43                else if (*b == Ch('a')) result += Ch('\a');
 44                else if (*b == Ch('b')) result += Ch('\b');
 45                else if (*b == Ch('f')) result += Ch('\f');
 46                else if (*b == Ch('n')) result += Ch('\n');
 47                else if (*b == Ch('r')) result += Ch('\r');
 48                else if (*b == Ch('t')) result += Ch('\t');
 49                else if (*b == Ch('v')) result += Ch('\v');
 50                else if (*b == Ch('"')) result += Ch('"');
 51                else if (*b == Ch('\'')) result += Ch('\'');
 52                else if (*b == Ch('\\')) result += Ch('\\');
 53                else
 54                    BOOST_PROPERTY_TREE_THROW(info_parser_error(
 55                        "unknown escape sequence", "", 0));
 56            }
 57            else
 58                result += *b;
 59            ++b;
 60        }
 61        return result;
 62    }
 63
 64    // Advance pointer past whitespace
 65    template<class Ch>
 66    void skip_whitespace(const Ch *&text)
 67    {
 68        using namespace std;
 69        while (isspace(*text))
 70            ++text;
 71    }
 72
 73    // Extract word (whitespace delimited) and advance pointer accordingly
 74    template<class Ch>
 75    std::basic_string<Ch> read_word(const Ch *&text)
 76    {
 77        using namespace std;
 78        skip_whitespace(text);
 79        const Ch *start = text;
 80        while (!isspace(*text) && *text != Ch(';') && *text != Ch('\0'))
 81            ++text;
 82        return expand_escapes(start, text);
 83    }
 84
 85    // Extract line (eol delimited) and advance pointer accordingly
 86    template<class Ch>
 87    std::basic_string<Ch> read_line(const Ch *&text)
 88    {
 89        using namespace std;
 90        skip_whitespace(text);
 91        const Ch *start = text;
 92        while (*text != Ch('\0') && *text != Ch(';'))
 93            ++text;
 94        while (text > start && isspace(*(text - 1)))
 95            --text;
 96        return expand_escapes(start, text);
 97    }
 98
 99    // Extract string (inside ""), and advance pointer accordingly
100    // Set need_more_lines to true if \ continuator found
101    template<class Ch>
102    std::basic_string<Ch> read_string(const Ch *&text, bool *need_more_lines)
103    {
104        skip_whitespace(text);
105        if (*text == Ch('\"'))
106        {
107
108            // Skip "
109            ++text;
110
111            // Find end of string, but skip escaped "
112            bool escaped = false;
113            const Ch *start = text;
114            while ((escaped || *text != Ch('\"')) && *text != Ch('\0'))
115            {
116                escaped = (!escaped && *text == Ch('\\'));
117                ++text;
118            }
119
120            // If end of string found
121            if (*text == Ch('\"'))
122            {
123                std::basic_string<Ch> result = expand_escapes(start, text++);
124                skip_whitespace(text);
125                if (*text == Ch('\\'))
126                {
127                    if (!need_more_lines)
128                        BOOST_PROPERTY_TREE_THROW(info_parser_error(
129                            "unexpected \\", "", 0));
130                    ++text;
131                    skip_whitespace(text);
132                    if (*text == Ch('\0') || *text == Ch(';'))
133                        *need_more_lines = true;
134                    else
135                        BOOST_PROPERTY_TREE_THROW(info_parser_error(
136                            "expected end of line after \\", "", 0));
137                }
138                else
139                    if (need_more_lines)
140                        *need_more_lines = false;
141                return result;
142            }
143            else
144                BOOST_PROPERTY_TREE_THROW(info_parser_error(
145                    "unexpected end of line", "", 0));
146
147        }
148        else
149            BOOST_PROPERTY_TREE_THROW(info_parser_error("expected \"", "", 0));
150    }
151
152    // Extract key
153    template<class Ch>
154    std::basic_string<Ch> read_key(const Ch *&text)
155    {
156        skip_whitespace(text);
157        if (*text == Ch('\"'))
158            return read_string(text, NULL);
159        else
160            return read_word(text);
161    }
162
163    // Extract data
164    template<class Ch>
165    std::basic_string<Ch> read_data(const Ch *&text, bool *need_more_lines)
166    {
167        skip_whitespace(text);
168        if (*text == Ch('\"'))
169            return read_string(text, need_more_lines);
170        else
171        {
172            *need_more_lines = false;
173            return read_word(text);
174        }
175    }
176
177    // Build ptree from info stream
178    template<class Ptree, class Ch>
179    void read_info_internal(std::basic_istream<Ch> &stream,
180                            Ptree &pt,
181                            const std::string &filename,
182                            int include_depth)
183    {
184        typedef std::basic_string<Ch> str_t;
185        // Possible parser states
186        enum state_t {
187            s_key,              // Parser expects key
188            s_data,             // Parser expects data
189            s_data_cont         // Parser expects data continuation
190        };
191
192        unsigned long line_no = 0;
193        state_t state = s_key;          // Parser state
194        Ptree *last = NULL;             // Pointer to last created ptree
195        // Define line here to minimize reallocations
196        str_t line;
197
198        // Initialize ptree stack (used to handle nesting)
199        std::stack<Ptree *> stack;
200        stack.push(&pt);                // Push root ptree on stack initially
201
202        try {
203            // While there are characters in the stream
204            while (stream.good()) {
205                // Read one line from stream
206                ++line_no;
207                std::getline(stream, line);
208                if (!stream.good() && !stream.eof())
209                    BOOST_PROPERTY_TREE_THROW(info_parser_error(
210                        "read error", filename, line_no));
211                const Ch *text = line.c_str();
212
213                // If directive found
214                skip_whitespace(text);
215                if (*text == Ch('#')) {
216                    // Determine directive type
217                    ++text;     // skip #
218                    std::basic_string<Ch> directive = read_word(text);
219                    if (directive == convert_chtype<Ch, char>("include")) {
220                        // #include
221                        if (include_depth > 100) {
222                            BOOST_PROPERTY_TREE_THROW(info_parser_error(
223                                "include depth too large, "
224                                "probably recursive include",
225                                filename, line_no));
226                        }
227                        str_t s = read_string(text, NULL);
228                        std::string inc_name =
229                            convert_chtype<char, Ch>(s.c_str());
230                        std::basic_ifstream<Ch> inc_stream(inc_name.c_str());
231                        if (!inc_stream.good())
232                            BOOST_PROPERTY_TREE_THROW(info_parser_error(
233                                "cannot open include file " + inc_name,
234                                filename, line_no));
235                        read_info_internal(inc_stream, *stack.top(),
236                                           inc_name, include_depth + 1);
237                    } else {   // Unknown directive
238                        BOOST_PROPERTY_TREE_THROW(info_parser_error(
239                            "unknown directive", filename, line_no));
240                    }
241
242                    // Directive must be followed by end of line
243                    skip_whitespace(text);
244                    if (*text != Ch('\0')) {
245                        BOOST_PROPERTY_TREE_THROW(info_parser_error(
246                            "expected end of line", filename, line_no));
247                    }
248
249                    // Go to next line
250                    continue;
251                }
252
253                // While there are characters left in line
254                while (1) {
255
256                    // Stop parsing on end of line or comment
257                    skip_whitespace(text);
258                    if (*text == Ch('\0') || *text == Ch(';')) {
259                        if (state == s_data)    // If there was no data set state to s_key
260                            state = s_key;
261                        break;
262                    }
263
264                    // Process according to current parser state
265                    switch (state)
266                    {
267
268                        // Parser expects key
269                        case s_key:
270                        {
271
272                            if (*text == Ch('{'))   // Brace opening found
273                            {
274                                if (!last)
275                                    BOOST_PROPERTY_TREE_THROW(info_parser_error("unexpected {", "", 0));
276                                stack.push(last);
277                                last = NULL;
278                                ++text;
279                            }
280                            else if (*text == Ch('}'))  // Brace closing found
281                            {
282                                if (stack.size() <= 1)
283                                    BOOST_PROPERTY_TREE_THROW(info_parser_error("unmatched }", "", 0));
284                                stack.pop();
285                                last = NULL;
286                                ++text;
287                            }
288                            else    // Key text found
289                            {
290                                std::basic_string<Ch> key = read_key(text);
291                                last = &stack.top()->push_back(
292                                    std::make_pair(key, Ptree()))->second;
293                                state = s_data;
294                            }
295
296                        }; break;
297
298                        // Parser expects data
299                        case s_data:
300                        {
301                            
302                            // Last ptree must be defined because we are going to add data to it
303                            BOOST_ASSERT(last);
304                            
305                            if (*text == Ch('{'))   // Brace opening found
306                            {
307                                stack.push(last);
308                                last = NULL;
309                                ++text;
310                                state = s_key;
311                            }
312                            else if (*text == Ch('}'))  // Brace closing found
313                            {
314                                if (stack.size() <= 1)
315                                    BOOST_PROPERTY_TREE_THROW(info_parser_error("unmatched }", "", 0));
316                                stack.pop();
317                                last = NULL;
318                                ++text;
319                                state = s_key;
320                            }
321                            else    // Data text found
322                            {
323                                bool need_more_lines;
324                                std::basic_string<Ch> data = read_data(text, &need_more_lines);
325                                last->data() = data;
326                                state = need_more_lines ? s_data_cont : s_key;
327                            }
328
329
330                        }; break;
331
332                        // Parser expects continuation of data after \ on previous line
333                        case s_data_cont:
334                        {
335                            
336                            // Last ptree must be defined because we are going to update its data
337                            BOOST_ASSERT(last);
338                            
339                            if (*text == Ch('\"'))  // Continuation must start with "
340                            {
341                                bool need_more_lines;
342                                std::basic_string<Ch> data = read_string(text, &need_more_lines);
343                                last->put_value(last->template get_value<std::basic_string<Ch> >() + data);
344                                state = need_more_lines ? s_data_cont : s_key;
345                            }
346                            else
347                                BOOST_PROPERTY_TREE_THROW(info_parser_error("expected \" after \\ in previous line", "", 0));
348
349                        }; break;
350
351                        // Should never happen
352                        default:
353                            BOOST_ASSERT(0);
354
355                    }
356                }
357            }
358
359            // Check if stack has initial size, otherwise some {'s have not been closed
360            if (stack.size() != 1)
361                BOOST_PROPERTY_TREE_THROW(info_parser_error("unmatched {", "", 0));
362
363        }
364        catch (info_parser_error &e)
365        {
366            // If line undefined rethrow error with correct filename and line
367            if (e.line() == 0)
368            {
369                BOOST_PROPERTY_TREE_THROW(info_parser_error(e.message(), filename, line_no));
370            }
371            else
372                BOOST_PROPERTY_TREE_THROW(e);
373
374        }
375
376    }
377
378} } }
379
380#endif