In a foo_def.hpp
file for a Boost.Spirit X3 project, I have parsers:
auto const identifier_component_unrestricted =
lexeme[(alpha | '_') >> *(alnum | '_')];
auto const identifier_component_def =
((identifier_component_unrestricted - reserved_words) |
lexeme['"' >> identifier_component_unrestricted >> '"']);
The identifier_component
is parsed as a variant
but then collapses to a single std::string
.
How can I automatically convert the parsed identifier_component
into ALL CAPS when it is unquoted (first type in the variant) but keep the case untouched when it is quoted (second type in the variant)?
I've tried using semantic actions but haven't succeeded in getting something that works/compiles.
Edit: Thanks to rmawatson for the following solution.
Add file to_upper.hpp
:
#pragma once
#include <boost/algorithm/string.hpp>
namespace parser {
using namespace boost::spirit::x3;
template <typename Subject>
struct ToUpperDirective : unary_parser<Subject, ToUpperDirective<Subject>> {
using base_type = unary_parser<Subject, ToUpperDirective<Subject>>;
using attribute_type = typename extension::as_parser<Subject>::value_type;
static bool const has_attribute = true;
using subject_type = Subject;
ToUpperDirective(Subject const& subject) : base_type(subject) {}
template <typename Iterator, typename Context, typename RContext,
typename Attribute>
bool parse(Iterator& first,
Iterator const& last,
Context const& context,
RContext& rcontext,
Attribute& attr) const {
auto result = this->subject.parse(first, last, context, rcontext, attr);
boost::to_upper(attr);
return result;
}
};
struct ToUpper {
template <typename Subject>
ToUpperDirective<typename extension::as_parser<Subject>::value_type>
operator[](Subject const& subject) const {
return {as_parser(subject)};
}
};
ToUpper const to_upper;
} // namespace parser
In the original foo_def.hpp
just add the #include "to_upper.hpp"
and:
// Convert unquoted identifier_components to upper case; keep quoted unchanged.
auto const identifier_component_def =
to_upper[identifier_component_unrestricted - reserved_words] |
lexeme['"' >> identifier_component_unrestricted >> '"'];
Both of these could just have a std::string
attribute, without the need for the variant.
I think the easiest way is probably to create your own all_caps
directive and just wrap the quoted alternative in this.
Something like..
template <typename Subject>
struct all_caps_directive : x3::unary_parser<Subject, all_caps_directive<Subject>>
{
using base_type = x3::unary_parser<Subject, all_caps_directive<Subject> >;
using attribute_type = typename x3::extension::as_parser<Subject>::value_type;
static bool const has_attribute = true;
using subject_type = Subject;
all_caps_directive(Subject const& subject)
: base_type(subject) {}
template <typename Iterator, typename Context, typename RContext,typename Attribute>
bool parse(Iterator& first, Iterator const& last
, Context const& context, RContext& rcontext, Attribute& attr) const
{
auto result = this->subject.parse(first, last, context, rcontext, attr);
boost::to_upper(attr);
return result;
}
};
struct all_caps_gen
{
template <typename Subject>
all_caps_directive<typename x3::extension::as_parser<Subject>::value_type>
operator[](Subject const& subject) const
{
return { as_parser(subject) };
}
};
auto const all_caps = all_caps_gen{};
Then use it like
auto const identifier_component_def =
(identifier_component_unrestricted |
all_caps[lexeme['"' >> identifier_component_unrestricted >> '"']]);
In response to your comment for something simpler, here is a semantic action version. I think this is less clear and not quite as nice myself.
auto all_caps = []( auto &ctx )
{
boost::to_upper( x3::_attr(ctx));
x3::_val(ctx) = x3::_attr(ctx);
};
and use like..
auto const identifier_component_def =
(identifier_component_unrestricted |
lexeme['"' >> identifier_component_unrestricted >> '"'][all_caps]);