Library: remould MatchSeq into a _Lumiera Forward Iterator_
MatchSeq was imported recently from the Yoshimi-testsuite, as supporting helper for the CSV table component. Actually this is just a thin wrapper on top of std::regex_iterator, which in turn has properties and behaviour very similar to Lumiera's »Forward Iterator« concept (in fact, it was a source of inspiration to generalise such a pattern). So this is an obvious round out and cleanup, as it requires just some minor additions and adjustments to allow processing a sequence of matches through a for-loop or some elaborate pipelining setup.
This commit is contained in:
parent
a2749adbc9
commit
9790feb822
4 changed files with 52 additions and 28 deletions
|
|
@ -127,6 +127,15 @@ namespace lib {
|
|||
friend ITER end (ITER const&) { return ITER(); } \
|
||||
using iterator_category = std::input_iterator_tag; \
|
||||
using difference_type = size_t;
|
||||
|
||||
/** define increment operator forwarding to baseclass but returning current */
|
||||
#define LIFT_PARENT_INCREMENT_OPERATOR(_BASECLASS_)\
|
||||
auto& \
|
||||
operator++() \
|
||||
{ \
|
||||
_BASECLASS_::operator++(); \
|
||||
return *this; \
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -48,7 +48,7 @@
|
|||
#define LIB_STAT_CSV_H
|
||||
|
||||
#include "lib/error.hpp"
|
||||
#include "lib/nocopy.hpp"
|
||||
#include "lib/null-value.hpp"
|
||||
#include "lib/format-string.hpp"
|
||||
#include "lib/format-obj.hpp"
|
||||
#include "lib/stat/regex.hpp"
|
||||
|
|
@ -140,35 +140,39 @@ namespace stat {
|
|||
* - dereference to get the field as string
|
||||
* - increment to move to the next field
|
||||
* @throws error::Invalid on CSV format violation
|
||||
* @todo 3/24 should be rewritten as Lumiera Forward Iterator
|
||||
*/
|
||||
class CsvLine
|
||||
: util::NonCopyable
|
||||
, util::MatchSeq
|
||||
: public util::RegexSearchIter
|
||||
{
|
||||
string const& line_;
|
||||
size_t field_;
|
||||
iterator curr_;
|
||||
size_t pos_;
|
||||
string const& line_{};
|
||||
size_t field_{0};
|
||||
size_t pos_{0};
|
||||
|
||||
util::RegexSearchIter const& curr() const { return *this; }
|
||||
util::RegexSearchIter end() const { return util::RegexSearchIter{}; }
|
||||
|
||||
public:
|
||||
CsvLine (string& line) // NOTE: string must exist elsewhere
|
||||
: MatchSeq(line, ACCEPT_FIELD)
|
||||
CsvLine()
|
||||
: line_{lib::NullValue<string>::get()}
|
||||
{ }
|
||||
|
||||
CsvLine (string& line) // NOTE: string and reg-exp must exist elsewhere
|
||||
: RegexSearchIter(line, ACCEPT_FIELD)
|
||||
, line_{line}
|
||||
, field_{0}
|
||||
, curr_{MatchSeq::begin()}
|
||||
, pos_{0}
|
||||
{ }
|
||||
|
||||
explicit operator bool() const
|
||||
{
|
||||
return isValid();
|
||||
}
|
||||
|
||||
ENABLE_USE_IN_STD_RANGE_FOR_LOOPS (CsvLine);
|
||||
|
||||
|
||||
string operator*() const
|
||||
{
|
||||
if (not isValid()) fail();
|
||||
auto& mat = *curr_;
|
||||
auto& mat = *curr();
|
||||
return mat[2].matched? mat[2]
|
||||
: mat[1];
|
||||
}
|
||||
|
|
@ -178,8 +182,8 @@ namespace stat {
|
|||
{
|
||||
if (not isValid())
|
||||
fail();
|
||||
pos_ = curr_->position() + curr_->length();
|
||||
++curr_;
|
||||
pos_ = curr()->position() + curr()->length();
|
||||
util::RegexSearchIter::operator ++();
|
||||
if (pos_ < line_.length() and not isValid())
|
||||
fail ();
|
||||
++field_;
|
||||
|
|
@ -194,22 +198,22 @@ namespace stat {
|
|||
bool
|
||||
isValid() const
|
||||
{
|
||||
return curr_ != end()
|
||||
and pos_ == size_t(curr_->position())
|
||||
and not curr_->empty();
|
||||
return curr() != end()
|
||||
and pos_ == size_t(curr()->position())
|
||||
and not curr()->empty();
|
||||
}
|
||||
|
||||
bool
|
||||
isParseFail() const
|
||||
{
|
||||
return curr_ != end()
|
||||
return curr() != end()
|
||||
and not isValid();
|
||||
}
|
||||
|
||||
void
|
||||
fail() const
|
||||
{
|
||||
if (curr_ == end())
|
||||
if (curr() == end())
|
||||
if (pos_ >= line_.length())
|
||||
throw error::Invalid{_Fmt{"Only %d data fields. Line:%s"}
|
||||
% field_ % line_};
|
||||
|
|
@ -217,7 +221,7 @@ namespace stat {
|
|||
throw error::Invalid{_Fmt{"Garbage after last field. Line:%s|↯|%s"}
|
||||
% line_.substr(0,pos_) % line_.substr(pos_)};
|
||||
else
|
||||
if (pos_ != size_t(curr_->position()))
|
||||
if (pos_ != size_t(curr()->position()))
|
||||
throw error::Invalid{_Fmt{"Garbage before field(%d):%s|↯|%s"}
|
||||
% (field_+1)
|
||||
% line_.substr(0,pos_) % line_.substr(pos_)};
|
||||
|
|
|
|||
|
|
@ -32,6 +32,8 @@
|
|||
#define LIB_STAT_REGEX_H
|
||||
|
||||
|
||||
#include "lib/iter-adapter.hpp"
|
||||
|
||||
#include <regex>
|
||||
#include <string>
|
||||
#include <optional>
|
||||
|
|
@ -45,16 +47,22 @@ namespace util {
|
|||
|
||||
|
||||
/** wrapped regex iterator to allow usage in foreach loops */
|
||||
struct MatchSeq
|
||||
struct RegexSearchIter
|
||||
: std::sregex_iterator
|
||||
{
|
||||
MatchSeq (string const& toParse, regex const& regex)
|
||||
RegexSearchIter() = default;
|
||||
|
||||
RegexSearchIter (string const& toParse, regex const& regex)
|
||||
: std::sregex_iterator{toParse.begin(), toParse.end(), regex}
|
||||
{ }
|
||||
|
||||
using iterator = std::sregex_iterator;
|
||||
iterator begin() const { return *this; }
|
||||
iterator end() const { return iterator(); }
|
||||
|
||||
operator bool() const { return isValid(); }
|
||||
|
||||
bool isValid () const { return (*this)->ready() and not (*this)->empty(); }
|
||||
bool empty () const { return not isValid(); }
|
||||
|
||||
LIFT_PARENT_INCREMENT_OPERATOR (std::sregex_iterator);
|
||||
ENABLE_USE_IN_STD_RANGE_FOR_LOOPS (RegexSearchIter);
|
||||
};
|
||||
|
||||
} // namespace util
|
||||
|
|
|
|||
|
|
@ -289,6 +289,9 @@ namespace test{
|
|||
|
||||
CHECK (not horror.isValid());
|
||||
CHECK (horror.isParseFail());
|
||||
|
||||
// CsvLine is a »Lumiera Forward Iterator«
|
||||
CHECK (meta::can_IterForEach<CsvLine>::value);
|
||||
}
|
||||
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue