解析不带引号的字符串时应用 to_upper

Apply to_upper when parsing unquoted strings

在 Boost.Spirit X3 项目的 foo_def.hpp 文件中,我有解析器:

auto const identifier_component_unrestricted =
    lexeme[(alpha | '_') >> *(alnum | '_')];

auto const identifier_component_def =
    ((identifier_component_unrestricted - reserved_words) |
    lexeme['"' >> identifier_component_unrestricted >> '"']);

identifier_component 被解析为 variant,但随后折叠为单个 std::string

如何在未加引号(变体中的第一种类型)时自动将已解析的 identifier_component 转换为全部大写,但在加引号时(变体中的第二种类型)保持大小写不变?

我尝试过使用语义操作,但没有成功获得 works/compiles。


编辑:感谢rmawatson提供以下解决方案。

添加文件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

在原来的 foo_def.hpp 中只需添加 #include "to_upper.hpp" 和:

// 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 >> '"'];

这两个都可以有一个 std::string 属性,不需要变体。

我认为最简单的方法可能是创建您自己的 all_caps 指令,并将引用的替代方法包装在其中。

有点像..

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{};

然后像这样使用它

auto const identifier_component_def =
    (identifier_component_unrestricted |
    all_caps[lexeme['"' >> identifier_component_unrestricted >> '"']]);

Demo

为了回应您对更简单的内容的评论,这里有一个语义动作版本。我觉得这不太清楚,我自己也不太好。

 auto all_caps = []( auto &ctx )
        {
            boost::to_upper( x3::_attr(ctx));
            x3::_val(ctx) = x3::_attr(ctx);
        };

并使用类似..

auto const identifier_component_def =
    (identifier_component_unrestricted |
    lexeme['"' >> identifier_component_unrestricted >> '"'][all_caps]);

Demo