gpt4 book ai didi

c++ - 如何使Boost.Spirit.Lex token 值成为匹配序列的子字符串(最好是正则表达式匹配组)

转载 作者:塔克拉玛干 更新时间:2023-11-03 07:14:39 24 4
gpt4 key购买 nike

我正在编写一个简单的表达式解析器。它基于基于Boost.Spirit.Lex token (Boost在1.56版中)的Boost.Spirit.Qi语法构建。

token 的定义如下:

using namespace boost::spirit;

template<
typename lexer_t
>
struct tokens
: lex::lexer<lexer_t>
{
tokens()
: /* ... */,
variable("%(\\w+)")
{
this->self =
/* ... */ |
variable;
}

/* ... */
lex::token_def<std::string> variable;
};

现在,我希望 variable token 值只是没有前缀 (\\w+)符号的名称(匹配组 %)。我怎么做?

单独使用匹配组无济于事。静止值是完整字符串,包括前缀 %

有什么方法可以强制使用匹配组?

还是至少在 token 操作中以某种方式引用了它?

我也尝试使用以下操作:
variable[lex::_val = std::string(lex::_start + 1, lex::_end)]

但无法编译。错误声称 std::string构造函数重载均不能匹配参数:
(const boost::phoenix::actor<Expr>, const boost::spirit::lex::_end_type)

更简单
variable[lex::_val = std::string(lex::_start, lex::_end)]

编译失败。由于类似的原因,现在只有第一个参数类型是 boost::spirit::lex::_start_type

最后,我尝试了此方法(即使看起来很浪费):
lex::_val = std::string(lex::_val).erase(0, 1)

但这也无法编译。这次编译器无法从 const boost::spirit::lex::_val_type转换为 std::string

有什么办法可以解决这个问题?

最佳答案

简单的解决方案

构造std::string属性值的正确形式如下:

variable[lex::_val = boost::phoenix::construct<std::string>(lex::_start + 1, lex::_end)]

完全按照 jv_在他(或她)的 comment中的建议。
boost::phoenix::construct<boost/phoenix/object/construct.hpp> header 提供。或使用 <boost/phoenix.hpp>

正则表达式解决方案

但是,上述解决方案仅在简单情况下才有效。并且排除了从外部(尤其是从配置数据)提供模式的可能性。由于将模式更改为例如 %(\\w+)%,因此需要更改值构造代码。

这就是为什么最好能从定义 token 的正则表达式中引用捕获组的原因。

现在请注意,这仍然不是完美的,因为像 %(\\w+)%(\\w+)%这样的怪异案例仍然需要更改代码才能正确处理。不仅可以通过为 token 配置正则表达式来解决此问题,还可以通过从匹配范围中形成值来解决。但这超出了问题的范围。在许多情况下,直接使用捕获组似乎足够灵活。

sehe在其他地方的 comment中表示,无法使用 token 的正则表达式中的捕获组。更不用说 token 实际上仅支持正则表达式的子集。 (例如,在显着差异中,缺少对命名捕获组的命名或忽略它们的支持!)。

我在这方面的实验也对此提供了支持。遗憾的是,没有办法使用捕获组。但是,有一种解决方法-您只需要在操作中重新应用正则表达式即可。

Action 获取范围

为了使它有点模块化,让我们从一个最简单的任务开始-一个操作,该操作返回对应于指定捕获的 token 匹配的 boost::iterator_range部分。
template<typename Attribute, typename Char, typename Idtype>
class basic_get_capture
{
public:
typedef lex::token_def<Attribute, Char, Idtype> token_type;
typedef boost::basic_regex<Char> regex_type;

explicit basic_get_capture(token_type const& token, int capture_index = 1)
: token(token),
regex(),
capture_index(capture_index)
{
}

template<typename Iterator, typename IdType, typename Context>
boost::iterator_range<Iterator> operator ()(Iterator& first, Iterator& last, lex::pass_flags& /*flag*/, IdType& /*id*/, Context& /*context*/)
{
typedef boost::match_results<Iterator> match_results_type;

match_results_type results;
regex_match(first, last, results, get_regex());
typename match_results_type::const_reference capture = results[capture_index];
return boost::iterator_range<Iterator>(capture.first, capture.second);
}

private:
regex_type& get_regex()
{
if(regex.empty())
{
token_type::string_type const& regex_text = token.definition();
regex.assign(regex_text);
}
return regex;
}

token_type const& token;
regex_type regex;
int capture_index;
};

template<typename Attribute, typename Char, typename Idtype>
basic_get_capture<Attribute, Char, Idtype> get_capture(lex::token_def<Attribute, Char, Idtype> const& token, int capture_index = 1)
{
return basic_get_capture<Attribute, Char, Idtype>(token, capture_index);
}

该操作使用 Boost.Regex(包括 <boost/regex.hpp>)。

Action 获取捕获为字符串

现在,由于捕获范围很不错,因为它没有为字符串分配任何新的内存,所以毕竟这是我们最终想要的字符串。因此,这里采取的另一项行动是在前一项行动的基础上进行的。
template<typename Attribute, typename Char, typename Idtype>
class basic_get_capture_as_string
{
public:
typedef basic_get_capture<Attribute, Char, Idtype> basic_get_capture_type;
typedef typename basic_get_capture_type::token_type token_type;

explicit basic_get_capture_as_string(token_type const& token, int capture_index = 1)
: get_capture_functor(token, capture_index)
{
}

template<typename Iterator, typename IdType, typename Context>
std::basic_string<Char> operator ()(Iterator& first, Iterator& last, lex::pass_flags& flag, IdType& id, Context& context)
{
boost::iterator_range<Iterator> const& capture = get_capture_functor(first, last, flag, id, context);
return std::basic_string<Char>(capture.begin(), capture.end());
}

private:
basic_get_capture_type get_capture_functor;
};

template<typename Attribute, typename Char, typename Idtype>
basic_get_capture_as_string<Attribute, Char, Idtype> get_capture_as_string(lex::token_def<Attribute, Char, Idtype> const& token, int capture_index = 1)
{
return basic_get_capture_as_string<Attribute, Char, Idtype>(token, capture_index);
}

这里没有魔术。我们只是从简单操作返回的范围中制作一个 std::basic_string

从捕获中分配值的操作

返回值的 Action 对我们没有多大用处。最终目标是从捕获中设置 token 值。这是通过最后一个 Action 完成的。
template<typename Attribute, typename Char, typename Idtype>
class basic_set_val_from_capture
{
public:
typedef basic_get_capture_as_string<Attribute, Char, Idtype> basic_get_capture_as_string_type;
typedef typename basic_get_capture_as_string_type::token_type token_type;

explicit basic_set_val_from_capture(token_type const& token, int capture_index = 1)
: get_capture_as_string_functor(token, capture_index)
{
}

template<typename Iterator, typename IdType, typename Context>
void operator ()(Iterator& first, Iterator& last, lex::pass_flags& flag, IdType& id, Context& context)
{
std::basic_string<Char> const& capture = get_capture_as_string_functor(first, last, flag, id, context);
context.set_value(capture);
}

private:
basic_get_capture_as_string_type get_capture_as_string_functor;
};

template<typename Attribute, typename Char, typename Idtype>
basic_set_val_from_capture<Attribute, Char, Idtype> set_val_from_capture(lex::token_def<Attribute, Char, Idtype> const& token, int capture_index = 1)
{
return basic_set_val_from_capture<Attribute, Char, Idtype>(token, capture_index);
}

讨论区

这些 Action 的用法如下:
variable[set_val_from_capture(variable)]

(可选)您可以提供第二个参数,即要使用的捕获索引。它默认为 1,在大多数情况下似乎合适。

创建函数
set_val_from_capture(或分别为 get_capture_as_stringget_capture)是辅助功能,用于自动从 token_def推导模板参数。特别地,我们需要的是 Char类型来进行相应的正则表达式。

我不确定是否可以合理地避免这种情况,即使这样,也会使调用运算符显着复杂化(特别是如果我们要努力缓存正则表达式对象而不是每次都重新构建它)。我的疑惑主要来自于不确定 Chartoken_def类型是否必须与标记化序列字符类型相同。我以为它们不必相同。

重复 token

Action 的绝对不愉快的部分是需要提供 token 本身作为重复的参数。

但是,如上所述, Char类型需要 token ,并且要获取正则表达式!

在我看来,至少从理论上讲,我们可以基于操作的 id参数(我们目前暂时忽略)以某种方式“在运行时”获得 token 。但是,无论从 token_def参数还是词法分析器本身(可以通过创建函数作为 context传递给操作),我都找不到任何方法可以基于 token 的标识符获取 this

可重用性

由于这些是 Action ,因此在更复杂的场景中它们并不是真正可重用的(开箱即用的)。例如,如果您不仅要获取捕获并将其转换为某个数值,您就必须以这种方式编写另一个操作,而不是对 token 进行复杂的操作。

起初,我试图实现以下目标:
variable[lex::_val = get_capture_as_string(variable)]

似乎更灵活,因为您可以轻松地在其周围添加更多代码-例如,将其包装在某些转换函数中。

但是我没有实现。尽管我觉得自己没有尽力。了解有关 Boost.Phoenix的更多信息肯定会对您有所帮助。

双工

所有这些解决方法都不能阻止我们做双重工作。两者都在正则表达式解析后匹配。但是,如开头所述,似乎没有更好的方法(不更改Boost.Spirit本身)。

关于c++ - 如何使Boost.Spirit.Lex token 值成为匹配序列的子字符串(最好是正则表达式匹配组),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/35476454/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com