diff --git a/src/core/fmpsparser.cpp b/src/core/fmpsparser.cpp index 25b4b4078..96379fdf9 100644 --- a/src/core/fmpsparser.cpp +++ b/src/core/fmpsparser.cpp @@ -21,13 +21,32 @@ #include -FMPSParser::FMPSParser() - : float_re_("\\s*([+-]?\\d+(?:\\.\\d+)?)\\s*(?:$|(?=::|;;))"), - string_re_("((?:[^\\\\;:]|(?:\\\\[\\\\:;]))+)(?:$|(?=::|;;))"), - escape_re_("\\\\([\\\\:;])") +FMPSParser::FMPSParser() : + // The float regex ends with (?:$|(?=::|;;)) to ensure it matches all the way + // up to the end of the value. Without it, it would match a string that + // starts with a number, like "123abc". + float_re_("\\s*([+-]?\\d+(?:\\.\\d+)?)\\s*(?:$|(?=::|;;))"), + + // Matches any character except unescaped slashes, colons and semicolons. + string_re_("((?:[^\\\\;:]|(?:\\\\[\\\\:;]))+)(?:$|(?=::|;;))"), + + // Used for replacing escaped characters. + escape_re_("\\\\([\\\\:;])") { } +// Parses a list of things (of type T) that are separated by two consecutive +// Separator characters. Each individual thing is parsed by the F function. +// For example, to parse this data: +// foo::bar::baz +// Use: +// QVariantList ret; +// ParseContainer<':'>(data, ParseValue, &ret); +// ret will then contain "foo", "bar", and "baz". +// Returns the number of characters that were consumed from data. +// +// You can parse lists of lists by using different separator characters: +// ParseContainer<';'>(data, ParseContainer<':'>, &ret); template static int ParseContainer(const QStringRef& data, F f, QList* ret) { ret->clear(); @@ -43,6 +62,7 @@ static int ParseContainer(const QStringRef& data, F f, QList* ret) { ret->append(value); pos += matched_len; + // Expect two separators in a row if (pos + 2 <= data.length() && data.at(pos) == Separator && data.at(pos+1) == Separator) { pos += 2; @@ -56,22 +76,23 @@ static int ParseContainer(const QStringRef& data, F f, QList* ret) { bool FMPSParser::Parse(const QString& data) { result_ = Result(); + + // Only return success if we matched the whole string return ParseListList(data, &result_) == data.length(); } -int FMPSParser::ParseValue(const QString& data, QVariant* ret) const { - return ParseValueRef(QStringRef(&data), ret); -} - int FMPSParser::ParseValueRef(const QStringRef& data, QVariant* ret) const { + // Try to match a float int pos = float_re_.indexIn(*data.string(), data.position()); if (pos == data.position()) { *ret = float_re_.cap(1).toDouble(); return float_re_.matchedLength(); } + // Otherwise try to match a string pos = string_re_.indexIn(*data.string(), data.position()); if (pos == data.position()) { + // Replace escape sequences with their actual characters QString value = string_re_.cap(1); value.replace(escape_re_, "\\1"); *ret = value; @@ -81,18 +102,24 @@ int FMPSParser::ParseValueRef(const QStringRef& data, QVariant* ret) const { return -1; } -int FMPSParser::ParseList(const QString& data, QVariantList* ret) const { - return ParseListRef(QStringRef(&data), ret); -} - +// Parses an inner list - a list of values int FMPSParser::ParseListRef(const QStringRef& data, QVariantList* ret) const { return ParseContainer<':'>(data, boost::bind(&FMPSParser::ParseValueRef, this, _1, _2), ret); } -int FMPSParser::ParseListList(const QString& data, Result* ret) const { - return ParseListListRef(QStringRef(&data), ret); -} - +// Parses an outer list - a list of lists int FMPSParser::ParseListListRef(const QStringRef& data, Result* ret) const { return ParseContainer<';'>(data, boost::bind(&FMPSParser::ParseListRef, this, _1, _2), ret); } + +// Convenience functions that take QStrings instead of QStringRefs. Use the +// QStringRef versions if possible, they're faster. +int FMPSParser::ParseValue(const QString& data, QVariant* ret) const { + return ParseValueRef(QStringRef(&data), ret); +} +int FMPSParser::ParseList(const QString& data, QVariantList* ret) const { + return ParseListRef(QStringRef(&data), ret); +} +int FMPSParser::ParseListList(const QString& data, Result* ret) const { + return ParseListListRef(QStringRef(&data), ret); +} diff --git a/src/core/fmpsparser.h b/src/core/fmpsparser.h index dca86bf8b..c422dbc3b 100644 --- a/src/core/fmpsparser.h +++ b/src/core/fmpsparser.h @@ -24,13 +24,20 @@ class FMPSParser { public: FMPSParser(); + // A FMPS result is a list of lists of values (where a value is a string or + // a float). typedef QList Result; + // Parses a FMPS value and returns true on success. bool Parse(const QString& data); + + // Gets the result of the last successful Parse. Result result() const { return result_; } + // Returns true if result() is empty. bool is_empty() const { return result().isEmpty() || result()[0].isEmpty(); } + // Internal functions, public for unit tests int ParseValue(const QString& data, QVariant* ret) const; int ParseValueRef(const QStringRef& data, QVariant* ret) const;