Skip to content

Commit

Permalink
Simplify implementation of dsl::parse_as
Browse files Browse the repository at this point in the history
This might fix #154.
  • Loading branch information
foonathan committed Jul 9, 2023
1 parent 8869a8b commit 5e2601b
Show file tree
Hide file tree
Showing 3 changed files with 49 additions and 33 deletions.
5 changes: 5 additions & 0 deletions docs/content/reference/grammar.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -196,6 +196,9 @@ This is used by {{% docref "lexy::dsl::whitespace" %}} during automatic whitespa
----
namespace lexy
{
template <_production_ Production, typename ParseState = void>
constexpr bool production_has_value_callback;
template <_production_ Production, typename ParseState = void>
class production_value_callback
{
Expand Down Expand Up @@ -224,6 +227,8 @@ It wraps an underlying callback/sink, which is determined as follows:
3. `ParseState` is void.
It will use `Production::value` as the underlying callback/sink.

If `state.value_of(Production{})` or `Production::value` is well-formed, `production_has_value_callback<Production, ParseState>` is `true`, `false` otherwise.

The behavior of the member functions depends on three cases:

1. If the underlying callback/sink is a link:{{< relref "callback#callback" >}}[callback], `return_type` is its return type, `.sink()` is ill-formed and `operator()` forwards to `Production::value.operator()`.
Expand Down
69 changes: 36 additions & 33 deletions include/lexy/dsl/parse_as.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
namespace lexyd
{
// Custom handler that forwards events but overrides the value callback.
template <typename T, typename CurProduction, typename Handler>
template <typename Handler>
struct _pas_handler
{
Handler& _handler;
Expand All @@ -26,31 +26,38 @@ struct _pas_handler
return static_cast<H&>(_handler);
}

// For child productions, use ::value to get a value.
// We use ::value to get a value.
// We can't use it unconditionally, as the initial production that contains the parse_as might
// not have one. So we silently fallback if that's the case - this might cause worse errors if
// the value is missing.
template <typename Production, typename State>
struct value_callback : lexy::production_value_callback<Production, State>
{
using lexy::production_value_callback<Production, State>::production_value_callback;
};
// For the production that contains parse_as, use lexy::construct.
template <typename State>
struct value_callback<CurProduction, State> : lexy::_construct<T>
using value_callback
= std::conditional_t<lexy::production_has_value_callback<Production, State>,
lexy::production_value_callback<Production, State>,
lexy::_detail::void_value_callback>;
};

struct _pas_final_parser
{
template <typename Context, typename Reader, typename T, typename... Args>
LEXY_PARSER_FUNC static bool parse(Context&, Reader&, lexy::_detail::lazy_init<T>& value,
Args&&... args)
{
constexpr value_callback() = default;
constexpr value_callback(State*) {}
};
value.emplace_result(lexy::construct<T>, LEXY_FWD(args)...);
return true;
}
};

template <typename T, typename CurProduction, typename Handler>
template <typename Handler>
constexpr auto _make_pas_handler(Handler& handler)
{
return _pas_handler<T, CurProduction, Handler>{handler};
return _pas_handler<Handler>{handler};
}
// Prevent infinite nesting when parse_as itself is recursive.
template <typename T, typename CurProduction, typename U, typename P, typename Handler>
constexpr auto _make_pas_handler(_pas_handler<U, P, Handler>& handler)
template <typename Handler>
constexpr auto _make_pas_handler(_pas_handler<Handler>& handler)
{
return _pas_handler<T, CurProduction, Handler>{handler._handler};
return handler;
}

template <typename T, typename Rule, bool Front = false>
Expand All @@ -77,17 +84,17 @@ struct _pas : _copy_base<Rule>
template <typename NextParser, typename Context, typename... Args>
LEXY_PARSER_FUNC bool finish(Context& context, Reader& reader, Args&&... args)
{
auto handler = _make_pas_handler<T, typename Context::production>(
context.control_block->parse_handler);
auto handler = _make_pas_handler(context.control_block->parse_handler);
lexy::_detail::parse_context_control_block cb(LEXY_MOV(handler), context.control_block);
using context_type
= lexy::_pc<decltype(handler), typename Context::state_type,
typename Context::production, typename Context::whitespace_production>;
context_type sub_context(&cb);
sub_context.handler = LEXY_MOV(context).handler;

auto result
= rule_parser.template finish<lexy::_detail::final_parser>(sub_context, reader);
lexy::_detail::lazy_init<T> value;
auto result
= rule_parser.template finish<_pas_final_parser>(sub_context, reader, value);

context.control_block->copy_vars_from(&cb);
context.handler = LEXY_MOV(sub_context).handler;
Expand All @@ -98,11 +105,9 @@ struct _pas : _copy_base<Rule>
// NOLINTNEXTLINE: clang-tidy wrongly thinks the branch is repeated.
return NextParser::parse(context, reader, LEXY_FWD(args)...);
else if constexpr (Front)
return NextParser::parse(context, reader, *LEXY_MOV(sub_context.value),
LEXY_FWD(args)...);
return NextParser::parse(context, reader, *LEXY_MOV(value), LEXY_FWD(args)...);
else
return NextParser::parse(context, reader, LEXY_FWD(args)...,
*LEXY_MOV(sub_context.value));
return NextParser::parse(context, reader, LEXY_FWD(args)..., *LEXY_MOV(value));
}
};

Expand All @@ -112,17 +117,17 @@ struct _pas : _copy_base<Rule>
template <typename Context, typename Reader, typename... Args>
LEXY_PARSER_FUNC static bool parse(Context& context, Reader& reader, Args&&... args)
{
auto handler = _make_pas_handler<T, typename Context::production>(
context.control_block->parse_handler);
auto handler = _make_pas_handler(context.control_block->parse_handler);
lexy::_detail::parse_context_control_block cb(LEXY_MOV(handler), context.control_block);
using context_type
= lexy::_pc<decltype(handler), typename Context::state_type,
typename Context::production, typename Context::whitespace_production>;
context_type sub_context(&cb);
sub_context.handler = LEXY_MOV(context).handler;

auto result
= lexy::parser_for<Rule, lexy::_detail::final_parser>::parse(sub_context, reader);
lexy::_detail::lazy_init<T> value;
auto result
= lexy::parser_for<Rule, _pas_final_parser>::parse(sub_context, reader, value);

context.control_block->copy_vars_from(&cb);
context.handler = LEXY_MOV(sub_context).handler;
Expand All @@ -133,11 +138,9 @@ struct _pas : _copy_base<Rule>
// NOLINTNEXTLINE: clang-tidy wrongly thinks the branch is repeated.
return NextParser::parse(context, reader, LEXY_FWD(args)...);
else if constexpr (Front)
return NextParser::parse(context, reader, *LEXY_MOV(sub_context.value),
LEXY_FWD(args)...);
return NextParser::parse(context, reader, *LEXY_MOV(value), LEXY_FWD(args)...);
else
return NextParser::parse(context, reader, LEXY_FWD(args)...,
*LEXY_MOV(sub_context.value));
return NextParser::parse(context, reader, LEXY_FWD(args)..., *LEXY_MOV(value));
}
};
};
Expand Down
8 changes: 8 additions & 0 deletions include/lexy/grammar.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -244,6 +244,9 @@ using _detect_value_of =
// qualify value_of() (it causes a hard error instead of going to ::value).
typename decltype(LEXY_DECLVAL(ParseState&).value_of(Production{}))::return_type;

template <typename Production>
using _detect_value = decltype(Production::value);

template <typename Production, typename Sink>
struct _sfinae_sink
{
Expand All @@ -270,6 +273,11 @@ struct _sfinae_sink
}
};

template <typename Production, typename ParseState = void>
constexpr bool production_has_value_callback
= lexy::_detail::is_detected<_detect_value_of, ParseState, Production>
|| lexy::_detail::is_detected<_detect_value, Production>;

template <typename Production, typename ParseState = void>
class production_value_callback
{
Expand Down

0 comments on commit 5e2601b

Please sign in to comment.