LPeter1997 / CppCmb

A generic C++17 parser-combinator library with a natural grammar notation.
MIT License
123 stars 8 forks source link

Matching N times #8

Open gotnone opened 4 years ago

gotnone commented 4 years ago

I am trying to write a parser using CppCmb that will match N characters. I see that there are operators for matching zero or many (*) and one or many (+). I did not see the equivalent {N} operator that many grep languages afford.

My use case is to actually match up to N characters or the first null '\0' character from an input stream. E.g. if N is 3 and "\0" is the null character escape code, matching strings would be:

INPUT NOTES
"123" "123" is captured
"ABCD" "ABC" is captured, "D" remains and may be used for a different match
"12\0" "12" or "12\0" as captures are fine, as long as the "\0" is consumed
"\0" "" or "\0" are acceptable captures, as long as the "\0" is consumed

Examples of not matching strings would be:

INPUT NOTES
"12" where there is no "\0" following the "2"
"" an empty stream with no "\0"

I tried to see if I could use a template with a non type parameter to recursively apply a match where N is decremented each call, but It didn't seem to work correctly with your cppcmb_decl / def macro system.

Here is a function that will match up to N characters or the first NULL character given a filter predicate and a begin and end iterator. It returns a pair containing a boolean of the match outcome and the iterator pointing to the furthest character in the stream that was parsed.

template <size_t N, typename F, typename I>
constexpr auto parseNor0(F&& filter, I begin, I end) {
  if constexpr (N == 0) {
    return std::make_pair(false, begin);
  }

  if (*begin == '\0') {
    return std::make_pair(true, begin);
  }

  if (filter(*begin)) {
    if constexpr (N == 1) {
      return std::make_pair(true, begin); 
    }
    if constexpr (N > 1) {
      if (++begin != end) {
        //call recursively
        return parseNor0<N-1>(filter, begin, end);
      }
    }
  }

  return std::make_pair(false, begin);
}

Do you have any suggestions on how to match N times?