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:
Fischlurch 2024-03-21 22:50:18 +01:00
parent a2749adbc9
commit 9790feb822
4 changed files with 52 additions and 28 deletions

View file

@ -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; \
}

View file

@ -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_)};

View file

@ -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

View file

@ -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);
}