comparison C++/modules/Ini/Ini.h @ 427:aa9cc55338be

Ini: rewrite the parse/analyze process
author David Demelier <markand@malikania.fr>
date Wed, 14 Oct 2015 15:08:45 +0200
parents 30e4a93f86c7
children c2b02b5f32a9
comparison
equal deleted inserted replaced
426:cee5c74c1c83 427:aa9cc55338be
19 #ifndef _INI_H_ 19 #ifndef _INI_H_
20 #define _INI_H_ 20 #define _INI_H_
21 21
22 /** 22 /**
23 * @file Ini.h 23 * @file Ini.h
24 * @brief Configuration file parser 24 * @brief Configuration file parser.
25 */ 25 */
26 26
27 #include <algorithm> 27 #include <algorithm>
28 #include <deque> 28 #include <exception>
29 #include <stdexcept>
30 #include <string> 29 #include <string>
30 #include <vector>
31 31
32 namespace ini { 32 namespace ini {
33
34 class Document;
33 35
34 /** 36 /**
35 * @class Error 37 * @class Error
36 * @brief Error in a file 38 * @brief Error in a file
37 */ 39 */
38 class Error : public std::exception { 40 class Error : public std::exception {
39 private: 41 private:
42 int m_line; //!< line number
43 int m_column; //!< line column
44 std::string m_message; //!< error message
45
46 public:
47 /**
48 * Constructor.
49 *
50 * @param l the line
51 * @param c the column
52 * @param m the message
53 */
54 inline Error(int l, int c, std::string m) noexcept
55 : m_line{l}
56 , m_column{c}
57 , m_message{std::move(m)}
58 {
59 }
60
61 /**
62 * Get the line number.
63 *
64 * @return the line
65 */
66 inline int line() const noexcept
67 {
68 return m_line;
69 }
70
71 /**
72 * Get the column number.
73 *
74 * @return the column
75 */
76 inline int column() const noexcept
77 {
78 return m_column;
79 }
80
81 /**
82 * Return the raw error message (no line and column shown).
83 *
84 * @return the error message
85 */
86 const char *what() const noexcept override
87 {
88 return m_message.c_str();
89 }
90 };
91
92 /**
93 * @class Token
94 * @brief Describe a token read in the .ini source
95 *
96 * This class can be used when you want to parse a .ini file yourself.
97 *
98 * @see Document::analyze
99 */
100 class Token {
101 public:
102 /**
103 * @brief Token type
104 */
105 enum Type {
106 Include, //!< include statement
107 Section, //!< [section]
108 Word, //!< word without quotes
109 QuotedWord, //!< word with quotes
110 Comment, //!< # comment like this
111 Assign, //!< = assignment
112 Space, //!< space or tabs
113 Line //!< '\n'
114 };
115
116 private:
117 Type m_type;
40 int m_line; 118 int m_line;
41 int m_position; 119 int m_column;
42 std::string m_error; 120 std::string m_value;
43 121
44 public: 122 public:
45 /** 123 /**
46 * Construct an error. 124 * Construct a token.
47 * 125 *
126 * @param type the type
48 * @param line the line 127 * @param line the line
49 * @param position the position 128 * @param column the column
50 * @param error the error 129 * @param value the value
51 */ 130 */
52 inline Error(int line, int position, std::string error) 131 Token(Type type, int line, int column, std::string value = "") noexcept
53 : m_line(line) 132 : m_type{type}
54 , m_position(position) 133 , m_line{line}
55 , m_error(std::move(error)) 134 , m_column{column}
56 { 135 {
57 } 136 switch (type) {
58 137 case Include:
59 /** 138 m_value = "@include";
60 * Return the line number. 139 break;
140 case Section:
141 case Word:
142 case QuotedWord:
143 m_value = value;
144 break;
145 case Comment:
146 m_value = "comment";
147 break;
148 case Assign:
149 m_value = "=";
150 break;
151 case Line:
152 m_value = "<newline>";
153 break;
154 case Space:
155 m_value = "<space>";
156 break;
157 default:
158 break;
159 }
160 }
161
162 /**
163 * Get the type.
164 *
165 * @return the type
166 */
167 inline Type type() const noexcept
168 {
169 return m_type;
170 }
171
172 /**
173 * Get the line.
61 * 174 *
62 * @return the line 175 * @return the line
63 */ 176 */
64 inline int line() const noexcept 177 inline int line() const noexcept
65 { 178 {
66 return m_line; 179 return m_line;
67 } 180 }
68 181
69 /** 182 /**
70 * Return the position in the current line. 183 * Get the column.
71 * 184 *
72 * @return the position 185 * @return the column
73 */ 186 */
74 inline int position() const noexcept 187 inline int column() const noexcept
75 { 188 {
76 return m_position; 189 return m_column;
77 } 190 }
78 191
79 /** 192 /**
80 * Get the error string. 193 * Get the value. For words, quoted words and section, the value is the content. Otherwise it's the
81 * 194 * characters parsed.
82 * @return the string 195 *
83 */ 196 * @return the value
84 inline const char *what() const noexcept 197 */
85 { 198 inline const std::string &value() const noexcept
86 return m_error.c_str(); 199 {
87 } 200 return m_value;
88 }; 201 }
202 };
203
204 /**
205 * List of tokens in order they are analyzed.
206 */
207 using Tokens = std::vector<Token>;
89 208
90 /** 209 /**
91 * @class Option 210 * @class Option
92 * @brief Option definition 211 * @brief Option definition.
93 */ 212 */
94 class Option { 213 class Option {
95 private: 214 private:
96 std::string m_key; 215 std::string m_key;
97 std::string m_value; 216 std::string m_value;
101 * Construct an option. 220 * Construct an option.
102 * 221 *
103 * @param key the key 222 * @param key the key
104 * @param value the value 223 * @param value the value
105 */ 224 */
106 inline Option(std::string key, std::string value) 225 inline Option(std::string key, std::string value) noexcept
107 : m_key(std::move(key)) 226 : m_key{std::move(key)}
108 , m_value(std::move(value)) 227 , m_value{std::move(value)}
109 { 228 {
110 } 229 }
111 230
112 /** 231 /**
113 * Get the option key. 232 * Get the option key.
130 } 249 }
131 }; 250 };
132 251
133 /** 252 /**
134 * @class Section 253 * @class Section
135 * @brief Section that contains one or more options 254 * @brief Section that contains one or more options.
136 */ 255 */
137 class Section { 256 class Section : public std::vector<Option> {
138 private: 257 private:
139 std::string m_key; 258 std::string m_key;
140 std::deque<Option> m_options;
141 259
142 template <typename T> 260 template <typename T>
143 T find(const std::string &key) const 261 T find(const std::string &key) const
144 { 262 {
145 auto it = std::find_if(m_options.begin(), m_options.end(), [&] (const Option &o) { 263 auto it = std::find_if(begin(), end(), [&] (const Option &o) {
146 return o.key() == key; 264 return o.key() == key;
147 }); 265 });
148 266
149 if (it == m_options.end()) 267 if (it == end()) {
150 throw std::out_of_range("option " + key + " not found"); 268 throw std::out_of_range{"option " + key + " not found"};
269 }
151 270
152 return const_cast<T>(*it); 271 return const_cast<T>(*it);
153 } 272 }
154 273
155 public: 274 public:
156 /** 275 /**
157 * Default constructor has no sections and no values. 276 * Construct a section with its name.
158 */ 277 *
159 Section() = default; 278 * @param key the key
160 279 */
161 /** 280 inline Section(std::string key) noexcept
162 * Construct a section with a set of options. 281 : m_key{std::move(key)}
163 *
164 * @param key the section name
165 * @param options the list of options
166 */
167 inline Section(std::string key, std::deque<Option> options = {}) noexcept
168 : m_key(std::move(key))
169 , m_options(std::move(options))
170 { 282 {
171 } 283 }
172 284
173 /** 285 /**
174 * Get the section key. 286 * Get the section key.
179 { 291 {
180 return m_key; 292 return m_key;
181 } 293 }
182 294
183 /** 295 /**
184 * Get an iterator to the beginning. 296 * Check if the section contains a specific option.
185 * 297 *
186 * @return the iterator 298 * @param key the option key
187 */ 299 * @return true if the option exists
188 inline auto begin() noexcept 300 */
189 { 301 inline bool contains(const std::string &key) const noexcept
190 return m_options.begin(); 302 {
191 } 303 return std::find_if(begin(), end(), [&] (const auto &opt) { return opt.key() == key; }) != end();
192 304 }
193 /** 305
194 * Overloaded function. 306 /**
195 * 307 * Access an option at the specified key.
196 * @return the iterator 308 *
197 */ 309 * @param key the key
198 inline auto begin() const noexcept
199 {
200 return m_options.begin();
201 }
202
203 /**
204 * Overloaded function.
205 *
206 * @return the iterator
207 */
208 inline auto cbegin() const noexcept
209 {
210 return m_options.cbegin();
211 }
212
213 /**
214 * Get an iterator to the end.
215 *
216 * @return the iterator
217 */
218 inline auto end() noexcept
219 {
220 return m_options.end();
221 }
222
223 /**
224 * Overloaded function.
225 *
226 * @return the iterator
227 */
228 inline auto end() const noexcept
229 {
230 return m_options.end();
231 }
232
233 /**
234 * Overloaded function.
235 *
236 * @return the iterator
237 */
238 inline auto cend() const noexcept
239 {
240 return m_options.cend();
241 }
242
243 /**
244 * Append an option.
245 *
246 * @param option the option to add
247 */
248 inline void push_back(Option option)
249 {
250 m_options.push_back(std::move(option));
251 }
252
253 /**
254 * Push an option to the beginning.
255 *
256 * @param option the option to add
257 */
258 inline void push_front(Option option)
259 {
260 m_options.push_front(std::move(option));
261 }
262
263 /**
264 * Get the number of options in that section.
265 *
266 * @return the size
267 */
268 inline unsigned size() const noexcept
269 {
270 return m_options.size();
271 }
272
273 /**
274 * Access an option at the specified index.
275 *
276 * @param index the index
277 * @return the option 310 * @return the option
278 * @warning No bounds checking is performed 311 * @warning No bounds checking is performed
279 */ 312 */
280 inline Option &operator[](int index) noexcept 313 inline Option &operator[](const std::string &key)
281 { 314 {
282 return m_options[index]; 315 return find<Option &>(key);
283 } 316 }
284 317
285 /** 318 /**
286 * Access an option at the specified index. 319 * Access an option at the specified key.
287 * 320 *
288 * @param index the index 321 * @param key the key
289 * @return the option 322 * @return the option
290 * @warning No bounds checking is performed 323 * @warning No bounds checking is performed
291 */ 324 */
292 inline const Option &operator[](int index) const noexcept
293 {
294 return m_options[index];
295 }
296
297 /**
298 * Access an option at the specified key.
299 *
300 * @param key the key
301 * @return the option
302 * @warning No bounds checking is performed
303 */
304 inline Option &operator[](const std::string &key)
305 {
306 return find<Option &>(key);
307 }
308
309 /**
310 * Access an option at the specified key.
311 *
312 * @param key the key
313 * @return the option
314 * @warning No bounds checking is performed
315 */
316 inline const Option &operator[](const std::string &key) const 325 inline const Option &operator[](const std::string &key) const
317 { 326 {
318 return find<const Option &>(key); 327 return find<const Option &>(key);
319 } 328 }
329
330 /**
331 * Inherited operators.
332 */
333 using std::vector<Option>::operator[];
334 };
335
336 /**
337 * @class File
338 * @brief Source for reading .ini files.
339 */
340 class File {
341 public:
342 /**
343 * Path to the file.
344 */
345 std::string path;
346
347 /**
348 * Load the file into the document.
349 *
350 * @param doc the document
351 * @throw Error on errors
352 */
353 void load(Document &doc);
354 };
355
356 /**
357 * @class Buffer
358 * @brief Source for reading ini from text.
359 * @note the include statement is not supported with buffers.
360 */
361 class Buffer {
362 public:
363 /**
364 * The ini content.
365 */
366 std::string text;
367
368 /**
369 * Load the file into the document.
370 *
371 * @param doc the document
372 * @throw Error on errors
373 */
374 void load(Document &doc);
320 }; 375 };
321 376
322 /** 377 /**
323 * @class Document 378 * @class Document
324 * @brief Ini config file loader 379 * @brief Ini config file loader
325 */ 380 */
326 class Document { 381 class Document : public std::vector<Section> {
327 private: 382 private:
328 std::deque<Section> m_sections; 383 std::string m_path;
329 384
330 template <typename T> 385 template <typename T>
331 T find(const std::string &key) const 386 T find(const std::string &key) const
332 { 387 {
333 auto it = std::find_if(m_sections.begin(), m_sections.end(), [&] (const Section &s) { 388 auto it = std::find_if(begin(), end(), [&] (const Section &s) {
334 return s.key() == key; 389 return s.key() == key;
335 }); 390 });
336 391
337 if (it == m_sections.end()) 392 if (it == end()) {
338 throw std::out_of_range("section " + key + " not found"); 393 throw std::out_of_range{"section " + key + " not found"};
394 }
339 395
340 return const_cast<T>(*it); 396 return const_cast<T>(*it);
341 } 397 }
342 398
343 public: 399 public:
344 /** 400 /**
345 * Default constructor with an empty configuration. 401 * Analyze a file and extract tokens. If the function succeeds, that does not mean the content is valid,
346 */ 402 * it just means that there are no syntax error.
347 Document() = default; 403 *
348 404 * For example, this class does not allow adding options under no sections and this function will not
349 /** 405 * detect that issue.
350 * Open the path as the configuration file. 406 *
351 * 407 * @param file the file to read
352 * @param path the path 408 * @return the list of tokens
353 * @throw Error on any error 409 * @throws Error on errors
354 */ 410 */
355 Document(const std::string &path); 411 static Tokens analyze(const File &file);
356 412
357 /** 413 /**
358 * Get an iterator to the beginning. 414 * Overloaded function for buffers.
359 * 415 *
360 * @return the iterator 416 * @param buffer the buffer to read
361 */ 417 * @return the list of tokens
362 inline auto begin() noexcept 418 * @throws Error on errors
363 { 419 */
364 return m_sections.begin(); 420 static Tokens analyze(const Buffer &buffer);
365 } 421
366 422 /**
367 /** 423 * Show all tokens and their description.
368 * Overloaded function. 424 *
369 * 425 * @param tokens the tokens
370 * @return the iterator 426 */
371 */ 427 static void dump(const Tokens &tokens);
372 inline auto begin() const noexcept 428
373 { 429 /**
374 return m_sections.begin(); 430 * Construct a document from a file.
375 } 431 *
376 432 * @param file the file to read
377 /** 433 * @throws Error on errors
378 * Overloaded function. 434 */
379 * 435 Document(const File &file);
380 * @return the iterator 436
381 */ 437 /**
382 inline auto cbegin() const noexcept 438 * Overloaded constructor for buffers.
383 { 439 *
384 return m_sections.cbegin(); 440 * @param buffer the buffer to read
385 } 441 * @throws Error on errors
386 442 */
387 /** 443 Document(const Buffer &buffer);
388 * Get an iterator to the end. 444
389 * 445 /**
390 * @return the iterator 446 * Get the current document path, only useful when constructed from File source.
391 */ 447 *
392 inline auto end() noexcept 448 * @return the path
393 { 449 */
394 return m_sections.end(); 450 inline const std::string &path() const noexcept
395 } 451 {
396 452 return m_path;
397 /** 453 }
398 * Overloaded function. 454
399 * 455 /**
400 * @return the iterator 456 * Check if a document has a specific section.
401 */ 457 *
402 inline auto end() const noexcept 458 * @param key the key
403 { 459 */
404 return m_sections.end(); 460 inline bool contains(const std::string &key) const noexcept
405 } 461 {
406 462 return std::find_if(begin(), end(), [&] (const auto &sc) { return sc.key() == key; }) != end();
407 /** 463 }
408 * Overloaded function. 464
409 * 465 /**
410 * @return the iterator 466 * Access a section at the specified key.
411 */ 467 *
412 inline auto cend() const noexcept 468 * @param key the key
413 {
414 return m_sections.cend();
415 }
416
417 /**
418 * Get the number of sections in the configuration.
419 *
420 * @return the size
421 */
422 inline unsigned size() const noexcept
423 {
424 return m_sections.size();
425 }
426
427 /**
428 * Append a section to the end.
429 *
430 * @param section the section to add
431 */
432 inline void push_back(Section section)
433 {
434 m_sections.push_back(std::move(section));
435 }
436
437 /**
438 * Add a section to the beginning.
439 *
440 * @param section the section to add
441 */
442 inline void push_front(Section section)
443 {
444 m_sections.push_front(std::move(section));
445 }
446
447 /**
448 * Access a section at the specified index.
449 *
450 * @param index the index
451 * @return the section 469 * @return the section
452 * @warning No bounds checking is performed 470 * @warning No bounds checking is performed
453 */ 471 */
454 inline Section &operator[](int index) noexcept 472 inline Section &operator[](const std::string &key)
455 { 473 {
456 return m_sections[index]; 474 return find<Section &>(key);
457 } 475 }
458 476
459 /** 477 /**
460 * Access a section at the specified index. 478 * Access a section at the specified key.
461 * 479 *
462 * @param index the index 480 * @param key the key
463 * @return the section 481 * @return the section
464 * @warning No bounds checking is performed 482 * @warning No bounds checking is performed
465 */ 483 */
466 inline const Section &operator[](int index) const noexcept 484 inline const Section &operator[](const std::string &key) const
467 {
468 return m_sections[index];
469 }
470
471 /**
472 * Access a section at the specified key.
473 *
474 * @param key the key
475 * @return the section
476 * @warning No bounds checking is performed
477 */
478 inline Section &operator[](const std::string &key)
479 { 485 {
480 return find<Section &>(key); 486 return find<Section &>(key);
481 } 487 }
482 488
483 /** 489 /**
484 * Access a section at the specified key. 490 * Inherited operators.
485 * 491 */
486 * @param key the key 492 using std::vector<Section>::operator[];
487 * @return the section
488 * @warning No bounds checking is performed
489 */
490 inline const Section &operator[](const std::string &key) const
491 {
492 return find<Section &>(key);
493 }
494 }; 493 };
495 494
496 } // !ini 495 } // !ini
497 496
498 #endif // !_INI_H_ 497 #endif // !_INI_H_