Mercurial > code
diff 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 |
line wrap: on
line diff
--- a/C++/modules/Ini/Ini.h Wed Oct 14 10:04:24 2015 +0200 +++ b/C++/modules/Ini/Ini.h Wed Oct 14 15:08:45 2015 +0200 @@ -21,43 +21,45 @@ /** * @file Ini.h - * @brief Configuration file parser + * @brief Configuration file parser. */ #include <algorithm> -#include <deque> -#include <stdexcept> +#include <exception> #include <string> +#include <vector> namespace ini { +class Document; + /** * @class Error * @brief Error in a file */ class Error : public std::exception { private: - int m_line; - int m_position; - std::string m_error; + int m_line; //!< line number + int m_column; //!< line column + std::string m_message; //!< error message public: /** - * Construct an error. + * Constructor. * - * @param line the line - * @param position the position - * @param error the error + * @param l the line + * @param c the column + * @param m the message */ - inline Error(int line, int position, std::string error) - : m_line(line) - , m_position(position) - , m_error(std::move(error)) + inline Error(int l, int c, std::string m) noexcept + : m_line{l} + , m_column{c} + , m_message{std::move(m)} { } /** - * Return the line number. + * Get the line number. * * @return the line */ @@ -67,29 +69,146 @@ } /** - * Return the position in the current line. + * Get the column number. * - * @return the position + * @return the column */ - inline int position() const noexcept + inline int column() const noexcept { - return m_position; + return m_column; } /** - * Get the error string. + * Return the raw error message (no line and column shown). * - * @return the string + * @return the error message */ - inline const char *what() const noexcept + const char *what() const noexcept override { - return m_error.c_str(); + return m_message.c_str(); } }; /** + * @class Token + * @brief Describe a token read in the .ini source + * + * This class can be used when you want to parse a .ini file yourself. + * + * @see Document::analyze + */ +class Token { +public: + /** + * @brief Token type + */ + enum Type { + Include, //!< include statement + Section, //!< [section] + Word, //!< word without quotes + QuotedWord, //!< word with quotes + Comment, //!< # comment like this + Assign, //!< = assignment + Space, //!< space or tabs + Line //!< '\n' + }; + +private: + Type m_type; + int m_line; + int m_column; + std::string m_value; + +public: + /** + * Construct a token. + * + * @param type the type + * @param line the line + * @param column the column + * @param value the value + */ + Token(Type type, int line, int column, std::string value = "") noexcept + : m_type{type} + , m_line{line} + , m_column{column} + { + switch (type) { + case Include: + m_value = "@include"; + break; + case Section: + case Word: + case QuotedWord: + m_value = value; + break; + case Comment: + m_value = "comment"; + break; + case Assign: + m_value = "="; + break; + case Line: + m_value = "<newline>"; + break; + case Space: + m_value = "<space>"; + break; + default: + break; + } + } + + /** + * Get the type. + * + * @return the type + */ + inline Type type() const noexcept + { + return m_type; + } + + /** + * Get the line. + * + * @return the line + */ + inline int line() const noexcept + { + return m_line; + } + + /** + * Get the column. + * + * @return the column + */ + inline int column() const noexcept + { + return m_column; + } + + /** + * Get the value. For words, quoted words and section, the value is the content. Otherwise it's the + * characters parsed. + * + * @return the value + */ + inline const std::string &value() const noexcept + { + return m_value; + } +}; + +/** + * List of tokens in order they are analyzed. + */ +using Tokens = std::vector<Token>; + +/** * @class Option - * @brief Option definition + * @brief Option definition. */ class Option { private: @@ -103,9 +222,9 @@ * @param key the key * @param value the value */ - inline Option(std::string key, std::string value) - : m_key(std::move(key)) - , m_value(std::move(value)) + inline Option(std::string key, std::string value) noexcept + : m_key{std::move(key)} + , m_value{std::move(value)} { } @@ -132,41 +251,34 @@ /** * @class Section - * @brief Section that contains one or more options + * @brief Section that contains one or more options. */ -class Section { +class Section : public std::vector<Option> { private: std::string m_key; - std::deque<Option> m_options; template <typename T> T find(const std::string &key) const { - auto it = std::find_if(m_options.begin(), m_options.end(), [&] (const Option &o) { + auto it = std::find_if(begin(), end(), [&] (const Option &o) { return o.key() == key; }); - if (it == m_options.end()) - throw std::out_of_range("option " + key + " not found"); + if (it == end()) { + throw std::out_of_range{"option " + key + " not found"}; + } return const_cast<T>(*it); } public: /** - * Default constructor has no sections and no values. - */ - Section() = default; - - /** - * Construct a section with a set of options. + * Construct a section with its name. * - * @param key the section name - * @param options the list of options + * @param key the key */ - inline Section(std::string key, std::deque<Option> options = {}) noexcept - : m_key(std::move(key)) - , m_options(std::move(options)) + inline Section(std::string key) noexcept + : m_key{std::move(key)} { } @@ -181,117 +293,14 @@ } /** - * Get an iterator to the beginning. - * - * @return the iterator - */ - inline auto begin() noexcept - { - return m_options.begin(); - } - - /** - * Overloaded function. + * Check if the section contains a specific option. * - * @return the iterator - */ - inline auto begin() const noexcept - { - return m_options.begin(); - } - - /** - * Overloaded function. - * - * @return the iterator + * @param key the option key + * @return true if the option exists */ - inline auto cbegin() const noexcept - { - return m_options.cbegin(); - } - - /** - * Get an iterator to the end. - * - * @return the iterator - */ - inline auto end() noexcept - { - return m_options.end(); - } - - /** - * Overloaded function. - * - * @return the iterator - */ - inline auto end() const noexcept - { - return m_options.end(); - } - - /** - * Overloaded function. - * - * @return the iterator - */ - inline auto cend() const noexcept + inline bool contains(const std::string &key) const noexcept { - return m_options.cend(); - } - - /** - * Append an option. - * - * @param option the option to add - */ - inline void push_back(Option option) - { - m_options.push_back(std::move(option)); - } - - /** - * Push an option to the beginning. - * - * @param option the option to add - */ - inline void push_front(Option option) - { - m_options.push_front(std::move(option)); - } - - /** - * Get the number of options in that section. - * - * @return the size - */ - inline unsigned size() const noexcept - { - return m_options.size(); - } - - /** - * Access an option at the specified index. - * - * @param index the index - * @return the option - * @warning No bounds checking is performed - */ - inline Option &operator[](int index) noexcept - { - return m_options[index]; - } - - /** - * Access an option at the specified index. - * - * @param index the index - * @return the option - * @warning No bounds checking is performed - */ - inline const Option &operator[](int index) const noexcept - { - return m_options[index]; + return std::find_if(begin(), end(), [&] (const auto &opt) { return opt.key() == key; }) != end(); } /** @@ -317,155 +326,140 @@ { return find<const Option &>(key); } + + /** + * Inherited operators. + */ + using std::vector<Option>::operator[]; +}; + +/** + * @class File + * @brief Source for reading .ini files. + */ +class File { +public: + /** + * Path to the file. + */ + std::string path; + + /** + * Load the file into the document. + * + * @param doc the document + * @throw Error on errors + */ + void load(Document &doc); +}; + +/** + * @class Buffer + * @brief Source for reading ini from text. + * @note the include statement is not supported with buffers. + */ +class Buffer { +public: + /** + * The ini content. + */ + std::string text; + + /** + * Load the file into the document. + * + * @param doc the document + * @throw Error on errors + */ + void load(Document &doc); }; /** * @class Document * @brief Ini config file loader */ -class Document { +class Document : public std::vector<Section> { private: - std::deque<Section> m_sections; + std::string m_path; template <typename T> T find(const std::string &key) const { - auto it = std::find_if(m_sections.begin(), m_sections.end(), [&] (const Section &s) { + auto it = std::find_if(begin(), end(), [&] (const Section &s) { return s.key() == key; }); - if (it == m_sections.end()) - throw std::out_of_range("section " + key + " not found"); + if (it == end()) { + throw std::out_of_range{"section " + key + " not found"}; + } return const_cast<T>(*it); } public: /** - * Default constructor with an empty configuration. + * Analyze a file and extract tokens. If the function succeeds, that does not mean the content is valid, + * it just means that there are no syntax error. + * + * For example, this class does not allow adding options under no sections and this function will not + * detect that issue. + * + * @param file the file to read + * @return the list of tokens + * @throws Error on errors */ - Document() = default; + static Tokens analyze(const File &file); /** - * Open the path as the configuration file. + * Overloaded function for buffers. * - * @param path the path - * @throw Error on any error + * @param buffer the buffer to read + * @return the list of tokens + * @throws Error on errors */ - Document(const std::string &path); - - /** - * Get an iterator to the beginning. - * - * @return the iterator - */ - inline auto begin() noexcept - { - return m_sections.begin(); - } + static Tokens analyze(const Buffer &buffer); /** - * Overloaded function. + * Show all tokens and their description. * - * @return the iterator + * @param tokens the tokens */ - inline auto begin() const noexcept - { - return m_sections.begin(); - } + static void dump(const Tokens &tokens); + + /** + * Construct a document from a file. + * + * @param file the file to read + * @throws Error on errors + */ + Document(const File &file); /** - * Overloaded function. + * Overloaded constructor for buffers. * - * @return the iterator + * @param buffer the buffer to read + * @throws Error on errors */ - inline auto cbegin() const noexcept - { - return m_sections.cbegin(); - } + Document(const Buffer &buffer); /** - * Get an iterator to the end. + * Get the current document path, only useful when constructed from File source. * - * @return the iterator + * @return the path */ - inline auto end() noexcept + inline const std::string &path() const noexcept { - return m_sections.end(); + return m_path; } /** - * Overloaded function. + * Check if a document has a specific section. * - * @return the iterator - */ - inline auto end() const noexcept - { - return m_sections.end(); - } - - /** - * Overloaded function. - * - * @return the iterator + * @param key the key */ - inline auto cend() const noexcept - { - return m_sections.cend(); - } - - /** - * Get the number of sections in the configuration. - * - * @return the size - */ - inline unsigned size() const noexcept - { - return m_sections.size(); - } - - /** - * Append a section to the end. - * - * @param section the section to add - */ - inline void push_back(Section section) + inline bool contains(const std::string &key) const noexcept { - m_sections.push_back(std::move(section)); - } - - /** - * Add a section to the beginning. - * - * @param section the section to add - */ - inline void push_front(Section section) - { - m_sections.push_front(std::move(section)); - } - - /** - * Access a section at the specified index. - * - * @param index the index - * @return the section - * @warning No bounds checking is performed - */ - inline Section &operator[](int index) noexcept - { - return m_sections[index]; - } - - /** - * Access a section at the specified index. - * - * @param index the index - * @return the section - * @warning No bounds checking is performed - */ - inline const Section &operator[](int index) const noexcept - { - return m_sections[index]; + return std::find_if(begin(), end(), [&] (const auto &sc) { return sc.key() == key; }) != end(); } /** @@ -491,8 +485,13 @@ { return find<Section &>(key); } + + /** + * Inherited operators. + */ + using std::vector<Section>::operator[]; }; } // !ini -#endif // !_INI_H_ +#endif // !_INI_H_ \ No newline at end of file