Mercurial > code
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_ |