Skip to content

Commit

Permalink
Add lexy::callback_with_state
Browse files Browse the repository at this point in the history
See #170.
  • Loading branch information
foonathan committed Aug 30, 2023
1 parent 083c9da commit d5041eb
Show file tree
Hide file tree
Showing 4 changed files with 63 additions and 3 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
* Add an overload of `fatal_error()` on scanners that allow construction of type-erased generic errors (#134).
* Add `lexy::buffer::release()` and `lexy::buffer::adopt()`.
* Add default argument to `lexy::dsl::flag()`.
* Add `lexy::callback_with_state`.
* Add missing `constexpr` to container callbacks and `lexy::as_string`.
* Fix swallowed errors from case-folding rules (#149).
* Fix `lexy::production_name` for productions in an anonymous namespace.
Expand Down
9 changes: 8 additions & 1 deletion docs/content/reference/callback/adapter.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
header: "lexy/callback/adapter.hpp"
entities:
"lexy::callback": callback
"lexy::callback_with_state": callback_with_state
"lexy::mem_fn": mem_fn
---
:toc: left
Expand All @@ -10,14 +11,17 @@ entities:
Adapt a function object or sink into a callback.

[#callback]
== Callback `lexy::callback`
== Callback `lexy::callback` and `lexy::callback_with_state`

{{% interface %}}
----
namespace lexy
{
template <typename ReturnType = _see-below_, typename ... Fns>
constexpr _callback_ auto callback(Fns&&... fns);
template <typename ReturnType = _see-below_, typename ... Fns>
constexpr _callback_ auto callback_with_state(Fns&&... fns);
}
----

Expand All @@ -31,6 +35,9 @@ The result must return an object of the specified `ReturnType`.
If no `ReturnType` is specified and all `fns` are itself _callbacks_, it will be the common type of all their return types.
Otherwise, it defaults to `void`.

`callback_with_state` will also pass the state passed via `operator[]` as the first argument to all calls to `fns`.
`lexy::callback_with_state(fn)` is equivalent to {{% docref "lexy::bind" %}}`(lexy::callback(fn), lexy::parse_state, lexy::values)`.

{{% godbolt-example "callback" "Build a callback from lambdas" %}}

[#callback-sink]
Expand Down
45 changes: 43 additions & 2 deletions include/lexy/callback/adapter.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,33 @@ struct _callback : _overloaded<Fns...>
constexpr explicit _callback(Fns... fns) : _overloaded<Fns...>(LEXY_MOV(fns)...) {}
};

template <typename ReturnType, typename... Fns>
struct _callback_with_state : _overloaded<Fns...>
{
using return_type = ReturnType;

template <typename State>
struct _with_state
{
const _callback_with_state& _cb;
State& _state;

template <typename... Args>
constexpr return_type operator()(Args&&... args) const&&
{
return _cb(_state, LEXY_FWD(args)...);
}
};

constexpr explicit _callback_with_state(Fns... fns) : _overloaded<Fns...>(LEXY_MOV(fns)...) {}

template <typename State>
constexpr auto operator[](State& state) const
{
return _with_state<State>{*this, state};
}
};

/// Creates a callback.
template <typename... Fns>
constexpr auto callback(Fns&&... fns)
Expand All @@ -26,14 +53,28 @@ constexpr auto callback(Fns&&... fns)
else
return _callback<void, std::decay_t<Fns>...>(LEXY_FWD(fns)...);
}

/// Creates a callback.
template <typename ReturnType, typename... Fns>
constexpr auto callback(Fns&&... fns)
{
return _callback<ReturnType, std::decay_t<Fns>...>(LEXY_FWD(fns)...);
}

/// Creates a callback that also receives the parse state.
template <typename... Fns>
constexpr auto callback_with_state(Fns&&... fns)
{
if constexpr ((lexy::is_callback<std::decay_t<Fns>> && ...))
return _callback_with_state<std::common_type_t<typename std::decay_t<Fns>::return_type...>,
std::decay_t<Fns>...>(LEXY_FWD(fns)...);
else
return _callback_with_state<void, std::decay_t<Fns>...>(LEXY_FWD(fns)...);
}
template <typename ReturnType, typename... Fns>
constexpr auto callback_with_state(Fns&&... fns)
{
return _callback_with_state<ReturnType, std::decay_t<Fns>...>(LEXY_FWD(fns)...);
}

template <typename Sink>
struct _cb_from_sink
{
Expand Down
11 changes: 11 additions & 0 deletions tests/lexy/callback/adapter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,17 @@ TEST_CASE("callback")
}
}

TEST_CASE("callback_with_state")
{
constexpr auto callback = lexy::callback_with_state<int>([](int a) { return a; },
[](int a, int b) { return a + b; });

CHECK(callback(1) == 1);

auto state = 2;
CHECK(callback[state](1) == 3);
}

TEST_CASE("callback from sink")
{
constexpr auto sink = lexy::fold_inplace<int>(0, [](int& result, int i) { result += i; });
Expand Down

0 comments on commit d5041eb

Please sign in to comment.