From 7d5db60962938976da17e25e612d6d3882509f1b Mon Sep 17 00:00:00 2001 From: Mohsen Mirkarimi Date: Sun, 23 Nov 2025 21:08:53 +0100 Subject: [PATCH] feat: add peek parser combinator --- include/parsi/fn/peek.hpp | 26 ++++++++++++++++++++++++++ include/parsi/parsi.hpp | 14 ++++++++++++++ tests/parsers.cpp | 14 ++++++++++++++ 3 files changed, 54 insertions(+) create mode 100644 include/parsi/fn/peek.hpp diff --git a/include/parsi/fn/peek.hpp b/include/parsi/fn/peek.hpp new file mode 100644 index 0000000..b71f5af --- /dev/null +++ b/include/parsi/fn/peek.hpp @@ -0,0 +1,26 @@ +#ifndef PARSI_FN_PEEK_HPP +#define PARSI_FN_PEEK_HPP + +#include + +#include "parsi/base.hpp" + +namespace parsi::fn { + +/** + * A parser combinator that checks whether the given parser is valid or not + * without changing the stream cursor. + */ +template +struct Peek { + std::remove_cvref_t parser; + + [[nodiscard]] constexpr auto operator()(Stream stream) const noexcept -> Result + { + return Result{stream, parser(stream).is_valid()}; + }; +}; + +} // namespace parsi::fn + +#endif // PARSI_FN_PEEK_HPP diff --git a/include/parsi/parsi.hpp b/include/parsi/parsi.hpp index c6d977c..3da3d74 100644 --- a/include/parsi/parsi.hpp +++ b/include/parsi/parsi.hpp @@ -10,6 +10,7 @@ #include "parsi/fn/expect.hpp" #include "parsi/fn/extract.hpp" #include "parsi/fn/optional.hpp" +#include "parsi/fn/peek.hpp" #include "parsi/fn/repeated.hpp" #include "parsi/fn/sequence.hpp" #include "parsi/internal/optimizer.hpp" @@ -196,6 +197,19 @@ template return internal::optimize(fn::Optional>{std::forward(parser)}); } +/** + * Creates a parser that only checks whether + * the given `parser` can parse the current + * stream, but does not move the stream forward. + * + * @see fn::Peek + */ +template +[[nodiscard]] constexpr auto peek(F&& parser) noexcept +{ + return internal::optimize(fn::Peek>{std::forward(parser)}); +} + } // namespace parsi #endif // PARSI_PARSI_HPP diff --git a/tests/parsers.cpp b/tests/parsers.cpp index bae4f4f..0882193 100644 --- a/tests/parsers.cpp +++ b/tests/parsers.cpp @@ -129,6 +129,20 @@ TEST_CASE("extract") [](std::string_view str) { return str == "not test"; })("test")); } +TEST_CASE("peek") +{ + auto parser = pr::peek(pr::expect("x")); + + CHECK(parser("x")); + CHECK(parser("x").stream().as_string_view() == "x"); + + CHECK(parser("xxa")); + CHECK(parser("xxa").stream().as_string_view() == "xxa"); + + CHECK(not parser("a")); + CHECK(parser("a").stream().as_string_view() == "a"); +} + TEST_CASE("complex composition") { auto parser = pr::sequence(