Limn  0.1
limn.h
Go to the documentation of this file.
1 
6 #pragma once
7 
8 #include <cctype>
9 #include <string_view>
10 #include <functional> // support match function call
11 
12 #ifndef _MSC_VER
13 #pragma GCC diagnostic push
14 #pragma GCC diagnostic ignored "-Wshadow"
15 #endif
16 
19 namespace lm {
20 
21  class Skipper
22  {
23  public:
25  virtual bool skip(std::string_view& sv) noexcept = 0;
26  };
27 
28  class SkipWhitespace : public Skipper
29  {
30  public:
32  bool skip(std::string_view& sv) noexcept {
33  // whitespace could be: [ \t\r\n]+, see below
34  // https://en.cppreference.com/w/cpp/string/byte/isspace
35  bool remove_char = false;
36  while (!sv.empty() && 0 != std::isspace(sv.front())) { // skip the whitespace chars
37  sv.remove_prefix(1);
38  remove_char = true;
39  }
40  return remove_char;
41  };
42  };
43 
44  class NoSkip : public Skipper
45  {
46  public:
47 
49  bool skip(std::string_view& sv) noexcept {
50  return true;
51  };
52  };
53 
54  static SkipWhitespace skws;
55  static NoSkip nosk;
56 
57 
58  namespace impl {
59 
60  template <typename Base>
61  struct parser_base {
62  constexpr auto operator*() const noexcept;
63  constexpr auto operator+() const noexcept;
64  constexpr auto operator[](std::string_view& output) const noexcept;
65  constexpr auto operator[](std::function<void(const std::string_view&)> callback) const noexcept;
66  };
67  }
68 
73  struct char_ final : public impl::parser_base<char_> {
76  constexpr explicit char_(const char ch) noexcept
77  : ch(ch)
78  {}
79 
85  constexpr inline auto operator!() const& noexcept {
86  struct not_ final : impl::parser_base<not_> {
87  constexpr explicit not_(const char ch) noexcept
88  : ch(ch)
89  {}
90 
91  constexpr inline bool visit(std::string_view& sv, Skipper& skipper) const& noexcept {
92  if (!sv.empty() && sv.front() != ch) {
93  sv.remove_prefix(1);
94  return true;
95  }
96  return false;
97  }
98 
99  private:
100  char ch;
101  };
102 
103  return not_(ch);
104  }
105 
106  constexpr inline bool visit(std::string_view& sv, Skipper& skipper) const& noexcept {
107  if (!sv.empty() && sv.front() == ch) {
108  sv.remove_prefix(1);
109  return true;
110  }
111  return false;
112  }
113 
114  private:
115  char ch;
116  };
117 
127  struct charset_ final : public impl::parser_base<charset_> {
130  constexpr explicit charset_(char const* set) noexcept
131  : set(set)
132  {}
133 
139  constexpr inline auto operator!() const& noexcept {
140  struct not_ final : impl::parser_base<not_> {
141  constexpr explicit not_(char const* set) noexcept
142  : set(set)
143  {}
144 
145  constexpr inline bool visit(std::string_view& sv, Skipper& skipper) const& noexcept {
146  if (!sv.empty()) {
147  for (char const* s = set; *s; ++s) {
148  if (*s == sv.front()) {
149  return false;
150  }
151  }
152  sv.remove_prefix(1);
153  return true;
154  }
155  return false;
156  }
157 
158  private:
159  char const* set;
160  };
161 
162  return not_(set);
163  }
164 
165  constexpr inline bool visit(std::string_view& sv, Skipper& skipper) const& noexcept {
166  if (!sv.empty()) {
167  for (char const* s = set; *s; ++s) {
168  if (*s == sv.front()) {
169  sv.remove_prefix(1);
170  return true;
171  }
172  }
173  }
174  return false;
175  }
176 
177  private:
178  char const* set;
179  };
180 
188  struct char_if_ final : public impl::parser_base<char_if_> {
192  constexpr explicit char_if_(bool(*pred)(char)) noexcept
193  : pred(pred)
194  {}
195 
196  constexpr inline bool visit(std::string_view& sv, Skipper& skipper) const& noexcept {
197  if (!sv.empty() && pred(sv.front())) {
198  sv.remove_prefix(1);
199  return true;
200  }
201  return false;
202  }
203 
204  private:
205  bool(*pred)(char);
206  };
207 
208  // These are not constexpr because the underlying functions in cctype aren't
209 
212  [[maybe_unused ]] static inline auto alnum_ = char_if_([](char const ch) noexcept -> bool { return 0 != std::isalnum(ch); });
213 
216  [[maybe_unused ]] static inline auto alpha_ = char_if_([](char const ch) noexcept -> bool { return 0 != std::isalpha(ch); });
217 
220  [[maybe_unused ]] static inline auto lower_ = char_if_([](char const ch) noexcept -> bool { return 0 != std::islower(ch); });
221 
224  [[maybe_unused ]] static inline auto upper_ = char_if_([](char const ch) noexcept -> bool { return 0 != std::isupper(ch); });
225 
228  [[maybe_unused ]] static inline auto digit_ = char_if_([](char const ch) noexcept -> bool { return 0 != std::isdigit(ch); });
229 
232  [[maybe_unused ]] static inline auto xdigit_ = char_if_([](char const ch) noexcept -> bool { return 0 != std::isxdigit(ch); });
233 
236  [[maybe_unused ]] static inline auto cntrl_ = char_if_([](char const ch) noexcept -> bool { return 0 != std::iscntrl(ch); });
237 
240  [[maybe_unused ]] static inline auto graph_ = char_if_([](char const ch) noexcept -> bool { return 0 != std::isgraph(ch); });
241 
244  [[maybe_unused ]] static inline auto space_ = char_if_([](char const ch) noexcept -> bool { return 0 != std::isspace(ch); });
245 
248  [[maybe_unused ]] static inline auto blank_ = char_if_([](char const ch) noexcept -> bool { return 0 != std::isblank(ch); });
249 
252  [[maybe_unused ]] static inline auto print_ = char_if_([](char const ch) noexcept -> bool { return 0 != std::isprint(ch); });
253 
256  [[maybe_unused ]] static inline auto punct_ = char_if_([](char const ch) noexcept -> bool { return 0 != std::ispunct(ch); });
257 
263  struct lit_ final : public impl::parser_base<lit_> {
266  constexpr lit_(std::string_view str) noexcept
267  : str(str)
268  {}
269 
270  constexpr inline bool visit(std::string_view& sv, Skipper& skipper) const& noexcept {
271  if (sv.substr(0, str.size()) == str) {
272  sv.remove_prefix(str.size());
273  return true;
274  }
275  return false;
276  }
277 
278  private:
279  std::string_view str;
280  };
281 
289  template <typename Func>
290  struct action_ final : public impl::parser_base<action_<Func>> {
311  constexpr explicit action_(Func&& func) noexcept
312  : func(std::forward<Func>(func))
313  {}
314 
315  constexpr inline bool visit(std::string_view& sv, Skipper& skipper) const& noexcept {
316  return func(sv); // func returns false to fail the parse
317  }
318 
319  private:
320  Func func;
321  };
322 
328  template <typename Base>
329  struct opt_ final : public impl::parser_base<opt_<Base>> {
330  constexpr explicit opt_(Base base) noexcept
331  : base(std::forward<Base>(base))
332  {}
333 
334  constexpr inline bool visit(std::string_view& sv, Skipper& skipper) const& noexcept {
335  skipper.skip(sv);
336  base.visit(sv, skipper);
337  return true;
338  }
339 
340  private:
341  Base base;
342  };
343 
347  template <typename Base>
348  struct lexeme_ final : public impl::parser_base<lexeme_<Base>> {
349  constexpr explicit lexeme_(Base base) noexcept
350  : base(std::forward<Base>(base))
351  {}
352 
353  constexpr inline bool visit(std::string_view& sv, Skipper& skipper) const& noexcept {
354  // skipper.skip(sv);
355  // nosk.skip(sv);
356  return base.visit(sv, nosk); // lexeme is atomic, so don't use passed skipper, use nosk instead;
357  }
358 
359  private:
360  Base base;
361  };
362 
363  namespace impl {
364  template <typename Left, typename Right>
365  struct seq_ final : public impl::parser_base<seq_<Left, Right>> {
366  constexpr explicit seq_(Left&& left, Right&& right) noexcept
367  : left(std::forward<Left>(left))
368  , right(std::forward<Right>(right))
369  {}
370 
371  constexpr inline bool visit(std::string_view& sv, Skipper& skipper) const& noexcept {
372  skipper.skip(sv);
373  bool left_result = left.visit(sv, skipper);
374  if (left_result)
375  {
376  skipper.skip(sv);
377  return right.visit(sv, skipper);
378  }
379  else
380  return false;
381  }
382 
383  private:
384  Left left;
385  Right right;
386  };
387 
388  template <typename Left, typename Right>
389  struct alt_ final : public impl::parser_base<alt_<Left, Right>> {
390  constexpr explicit alt_(Left&& left, Right&& right) noexcept
391  : left(std::forward<Left>(left))
392  , right(std::forward<Right>(right))
393  {}
394 
395  constexpr inline bool visit(std::string_view& sv, Skipper& skipper) const& noexcept {
396  skipper.skip(sv);
397  const std::string_view save = sv; // rewind the string_view if left failed, save should not be reference
398  return left.visit(sv, skipper) || right.visit(sv = save, skipper); // reset the sv when calling the right parser
399  }
400 
401  private:
402  Left left;
403  Right right;
404  };
405 
406  template <typename Base>
407  struct kleene_ final : public impl::parser_base<kleene_<Base>> {
408  constexpr explicit kleene_(Base base) noexcept
409  : base(std::move(base))
410  {}
411 
412  constexpr inline bool visit(std::string_view& sv, Skipper& skipper) const& noexcept {
413  skipper.skip(sv);
414  std::string_view save = sv;
415  // save != sv means we does step forward (base.visit(sv) consume some chars)
416  // the assignment save = sv means the sv get updated, so try next loop
417  // to see it goes forward again
418  while (base.visit(sv, skipper) && !sv.empty() && save != sv)
419  save = sv;
420  return true;
421  }
422 
423  private:
424  Base base;
425  };
426 
427  template <typename Base>
428  struct plus_ final : public impl::parser_base<plus_<Base>> {
429  constexpr explicit plus_(Base base) noexcept
430  : base(std::move(base))
431  {}
432 
433  constexpr inline bool visit(std::string_view& sv, Skipper& skipper) const& noexcept {
434  skipper.skip(sv);
435  if (!base.visit(sv, skipper)) {
436  return false;
437  }
438  std::string_view save = sv;
439  // save != sv means we does step forward (base.visit(sv) consume some chars)
440  // the assignment save = sv means the sv get updated, so try next loop
441  // to see it goes forward again
442  while (base.visit(sv, skipper) && !sv.empty() && save != sv)
443  save = sv;
444  return true;
445  }
446 
447  private:
448  Base base;
449  };
450 
451  template <typename Base>
452  struct match_ final : public impl::parser_base<match_<Base>> {
453  constexpr explicit match_(Base base, std::string_view& sv) noexcept
454  : base(std::move(base))
455  , out(sv)
456  {}
457 
458  constexpr inline bool visit(std::string_view& sv, Skipper& skipper) const& noexcept {
459  skipper.skip(sv);
460  std::string_view save = sv;
461  if (base.visit(sv, skipper)) {
462  out = save.substr(0, save.size() - sv.size());
463  return true;
464  }
465  return false;
466  }
467 
468  private:
469  Base base;
470  std::string_view& out;
471  };
472 
473  template <typename Base>
474  struct match_call_ final : public impl::parser_base<match_call_<Base>> {
475  constexpr explicit match_call_(Base base, std::function<void(const std::string_view&)> callback) noexcept
476  : base(std::move(base))
477  , callback(std::move(callback))
478  {}
479 
480  constexpr inline bool visit(std::string_view& sv, Skipper& skipper) const& noexcept {
481  skipper.skip(sv);
482  std::string_view save = sv;
483  if (base.visit(sv, skipper)) {
484  callback(save.substr(0, save.size() - sv.size()));
485  return true;
486  }
487  return false;
488  }
489 
490  private:
491  Base base;
492  std::function<void(const std::string_view&)> callback;
493  };
494 
495  struct endtype_ final : public impl::parser_base<endtype_> {
496  constexpr inline bool visit(std::string_view& sv, Skipper& skipper) const& noexcept {
497  return sv.empty();
498  }
499  };
500 
501  struct emptytype_ final : public impl::parser_base<emptytype_> {
502  constexpr inline bool visit(std::string_view& sv, Skipper& skipper) const& noexcept {
503  return true;
504  }
505  };
506  }
507 
516  [[maybe_unused]] constexpr static inline auto end_ = impl::endtype_();
517 
526  [[maybe_unused]] constexpr static inline auto empty_ = impl::emptytype_();
527 
541  template <typename Left, typename Right>
542  constexpr inline auto operator>>(Left&& left, Right&& right) noexcept {
543  return impl::seq_<Left, Right>(
544  std::forward<Left>(left),
545  std::forward<Right>(right)
546  );
547  }
548 
564  template <typename Left, typename Right>
565  constexpr inline auto operator|(Left&& left, Right&& right) noexcept {
566  return impl::alt_<Left, Right>(
567  std::forward<Left>(left),
568  std::forward<Right>(right)
569  );
570  }
571 
578  template <typename Base>
579  constexpr inline auto impl::parser_base<Base>::operator*() const noexcept {
580  return impl::kleene_<Base>(*static_cast<Base const*>(this));
581  }
582 
589  template <typename Base>
590  constexpr inline auto impl::parser_base<Base>::operator+() const noexcept {
591  return impl::plus_<Base>(*static_cast<Base const*>(this));
592  }
593 
607  template <typename Base>
608  constexpr inline auto impl::parser_base<Base>::operator[](std::string_view& ouput) const noexcept {
609  return impl::match_<Base>(*static_cast<Base const*>(this), ouput);
610  }
611 
628  template <typename Base>
629  constexpr inline auto impl::parser_base<Base>::operator[](std::function<void(const std::string_view&)> callback) const noexcept {
630  return impl:: match_call_<Base>(*static_cast<Base const*>(this), std::move(callback));
631  }
632 
649  template <typename Parser>
650  constexpr bool parse(std::string_view input, Parser const& parser, Skipper& skipper = skws) noexcept {
651  return parser.visit(input, skipper);
652  }
653 
663  template <typename Parser>
664  constexpr bool parse_ref(std::string_view& input, Parser const& parser, Skipper& skipper = skws) noexcept {
665  return parser.visit(input, skipper);
666  }
667 }
668 
669 #ifndef _MSC_VER
670 #pragma GCC diagnostic pop
671 #endif
constexpr charset_(char const *set) noexcept
Construct a charset_ parser.
Definition: limn.h:130
static auto blank_
Single character parser based on std::blank.
Definition: limn.h:248
Single character parser for any of several characters.
Definition: limn.h:127
constexpr bool parse_ref(std::string_view &input, Parser const &parser, Skipper &skipper=skws) noexcept
The parse function that takes input by reference.
Definition: limn.h:664
static auto alnum_
Single character parser based on std::isalnum.
Definition: limn.h:212
constexpr auto operator!() const &noexcept
Opposite parser.
Definition: limn.h:139
constexpr char_if_(bool(*pred)(char)) noexcept
Construct a char_if_ parser.
Definition: limn.h:192
static constexpr auto end_
End of input parser.
Definition: limn.h:516
constexpr lit_(std::string_view str) noexcept
Construct a lit_ parser.
Definition: limn.h:266
static auto cntrl_
Single character parser based on std::cntrl.
Definition: limn.h:236
constexpr action_(Func &&func) noexcept
Construct an action_ parser.
Definition: limn.h:311
constexpr auto operator>>(Left &&left, Right &&right) noexcept
The sequence parser combinator.
Definition: limn.h:542
constexpr bool parse(std::string_view input, Parser const &parser, Skipper &skipper=skws) noexcept
The parse function.
Definition: limn.h:650
constexpr char_(const char ch) noexcept
Construct a char_ parser.
Definition: limn.h:76
Customizable parser.
Definition: limn.h:290
Single character parser based on a char.
Definition: limn.h:73
Single character parser based on a function.
Definition: limn.h:188
Zero or one of the match parser, in another word it is an optional item.
Definition: limn.h:329
constexpr auto operator!() const &noexcept
Opposite parser.
Definition: limn.h:85
The namesapce for all Limn types, functions, and variables.
String literal parser.
Definition: limn.h:263
constexpr auto operator|(Left &&left, Right &&right) noexcept
The alternate parser combinator.
Definition: limn.h:565
static auto print_
Single character parser based on std::print.
Definition: limn.h:252
static auto digit_
Single character parser based on std::isdigit.
Definition: limn.h:228
static auto lower_
Single character parser based on std::islower.
Definition: limn.h:220
static auto space_
Single character parser based on std::space.
Definition: limn.h:244
static constexpr auto empty_
Empty parser.
Definition: limn.h:526
static auto alpha_
Single character parser based on std::isalpha.
Definition: limn.h:216
static auto xdigit_
Single character parser based on std::xdigit.
Definition: limn.h:232
static auto punct_
Single character parser based on std::punct.
Definition: limn.h:256
static auto graph_
Single character parser based on std::graph.
Definition: limn.h:240
static auto upper_
Single character parser based on std::isupper.
Definition: limn.h:224