diff --git a/CHANGELOG.adoc b/CHANGELOG.adoc index c8b6aee69..8c2e74835 100644 --- a/CHANGELOG.adoc +++ b/CHANGELOG.adoc @@ -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. diff --git a/docs/content/reference/callback/adapter.adoc b/docs/content/reference/callback/adapter.adoc index 1bcb9e134..38689da6e 100644 --- a/docs/content/reference/callback/adapter.adoc +++ b/docs/content/reference/callback/adapter.adoc @@ -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 @@ -10,7 +11,7 @@ entities: Adapt a function object or sink into a callback. [#callback] -== Callback `lexy::callback` +== Callback `lexy::callback` and `lexy::callback_with_state` {{% interface %}} ---- @@ -18,6 +19,9 @@ namespace lexy { template constexpr _callback_ auto callback(Fns&&... fns); + + template + constexpr _callback_ auto callback_with_state(Fns&&... fns); } ---- @@ -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] diff --git a/include/lexy/callback/adapter.hpp b/include/lexy/callback/adapter.hpp index 29b2750d6..d81dd2faf 100644 --- a/include/lexy/callback/adapter.hpp +++ b/include/lexy/callback/adapter.hpp @@ -16,6 +16,33 @@ struct _callback : _overloaded constexpr explicit _callback(Fns... fns) : _overloaded(LEXY_MOV(fns)...) {} }; +template +struct _callback_with_state : _overloaded +{ + using return_type = ReturnType; + + template + struct _with_state + { + const _callback_with_state& _cb; + State& _state; + + template + constexpr return_type operator()(Args&&... args) const&& + { + return _cb(_state, LEXY_FWD(args)...); + } + }; + + constexpr explicit _callback_with_state(Fns... fns) : _overloaded(LEXY_MOV(fns)...) {} + + template + constexpr auto operator[](State& state) const + { + return _with_state{*this, state}; + } +}; + /// Creates a callback. template constexpr auto callback(Fns&&... fns) @@ -26,14 +53,28 @@ constexpr auto callback(Fns&&... fns) else return _callback...>(LEXY_FWD(fns)...); } - -/// Creates a callback. template constexpr auto callback(Fns&&... fns) { return _callback...>(LEXY_FWD(fns)...); } +/// Creates a callback that also receives the parse state. +template +constexpr auto callback_with_state(Fns&&... fns) +{ + if constexpr ((lexy::is_callback> && ...)) + return _callback_with_state::return_type...>, + std::decay_t...>(LEXY_FWD(fns)...); + else + return _callback_with_state...>(LEXY_FWD(fns)...); +} +template +constexpr auto callback_with_state(Fns&&... fns) +{ + return _callback_with_state...>(LEXY_FWD(fns)...); +} + template struct _cb_from_sink { diff --git a/tests/lexy/callback/adapter.cpp b/tests/lexy/callback/adapter.cpp index 01363b99c..7d6852f5a 100644 --- a/tests/lexy/callback/adapter.cpp +++ b/tests/lexy/callback/adapter.cpp @@ -86,6 +86,17 @@ TEST_CASE("callback") } } +TEST_CASE("callback_with_state") +{ + constexpr auto callback = lexy::callback_with_state([](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(0, [](int& result, int i) { result += i; });