是否可以在没有分配给规则的情况下在 Boost Spirit 中命名表达式?
我知道您可以通过分配规则来命名它,例如:
using boost::spirit::standard::char_;
boost::spirit::qi::rule<> number = char_("0") | (char_("1-9") >> *char_("0-9"));
number.name("number");
这使得调试语法错误变得更加容易,因为您已经可以以正确的方式命名特定部分。
但是有可能以这种方式内联吗?
using boost::spirit::standard::char_;
boost::spirit::qi::rule<> twoDigits = char_("0-9") > name(char_("0-9"), "digit");
因此,如果它得到像“3a”这样的输入,那么异常会说它期望位置 2 有一个“数字”(这里它是位置 2 并不重要)。
另一种表达方式是:
using boost::spirit::standard::char_;
boost::spirit::qi::rule<> digit = char_("0-9");
digit.name("digit");
boost::spirit::qi::rule<> twoDigits = digit > digit;
我已经检查了源代码并发现该表达式有一个名为 what() 的函数,它返回一个 boost::spirit::info 对象,从中可以检索字符串表示形式。但我无法覆盖它,因为我不熟悉 Boost Proto 和 Boost Spirit 的内部结构。
最佳答案
这是一种使用自定义指令的方法。你可以看到关于如何做类似事情的很好的解释(它是一个解析器,而不是一个指令)here .
创建自定义解析器/指令的过程可以分为四个部分:
- 定义/创建终端以在 Spirit 表达式中使用。通常您会使用
BOOST_SPIRIT_TERMINAL
除非你需要你的解析器/指令是parser(whatever)
的形式/directive(whatever)[subject]
.在这种情况下,您需要使用BOOST_SPIRIT_TERMINAL_EX
.这种情况特别奇怪,因为您需要与终端关联的状态,因此您需要定义一个派生自terminal<tag::stateful_tag,...>
的结构。 .这通常不是必需的。我决定将所有这些放在命名空间custom_directive
中但你也可以把它放在boost::spirit::qi
里面. - 启用你的解析器/指令。在这里你需要专门化
use_terminal
(对于解析器)或use_directive
(对于指令)在命名空间内boost::spirit
. - 创建实际的解析器/指令。这个解析器/指令需要三样东西:
attribute<Context,Iterator>::type
关联的元函数,说明你的解析器的属性是什么(在这种情况下,我只是通过了主题解析器的属性);一个parse
具有进行真正解析的适当签名的成员函数(我再次推迟到主题解析器)和what
成员函数,这是我们真正感兴趣的修改,它返回您在构建时与终端关联的任何内容。我再次决定使用命名空间custom_directive
但你也可以把它放在boost::spirit::qi
里面. - 将终端与您的实际解析器/指令连接。这需要在
boost::spirit::qi
内并要求您专攻make_directive
或make_primitive
使用您的终端标签并实例化您的实际解析器/指令。
#include <iostream>
#include <boost/spirit/include/qi.hpp>
#include <boost/spirit/include/phoenix.hpp>
//START OF expression_renamer.hpp
namespace custom_directive
{
BOOST_SPIRIT_TERMINAL(rename_expression);
struct expression_renamer: boost::spirit::terminal<boost::spirit::tag::stateful_tag<std::string, tag::rename_expression> >
{
typedef boost::spirit::tag::stateful_tag<std::string, tag::rename_expression> tag_type;
expression_renamer(std::string const& p) : boost::spirit::terminal<tag_type>(p) {}
};
}
namespace boost { namespace spirit
{
template <>
struct use_directive<qi::domain, boost::spirit::tag::stateful_tag<std::string, custom_directive::tag::rename_expression> > // enables expression_renamer[p]
: mpl::true_ {};
}}
namespace custom_directive
{
template <typename Subject, typename Data>
struct rename_directive : boost::spirit::qi::unary_parser<rename_directive<Subject,Data> >
{
typedef Subject subject_type;
rename_directive(Subject const& subject_, Data const& data_)
: subject(subject_),data(data_) {}
template <typename Context, typename Iterator>
struct attribute
{
typedef typename
boost::spirit::traits::attribute_of<subject_type, Context, Iterator>::type
type;
};
template <typename Iterator, typename Context
, typename Skipper, typename Attribute>
bool parse(Iterator& first, Iterator const& last, Context& context, Skipper const& skipper, Attribute& attr_) const
{
return subject.parse(first, last, context, skipper, attr_);
}
template <typename Context>
boost::spirit::info what(Context& context) const
{
return boost::spirit::info(data);
}
Subject subject;
Data data;
};
}
// instantiation of the parser
namespace boost { namespace spirit { namespace qi
{
template<typename Data, typename Subject,typename Modifiers>
struct make_directive<tag::stateful_tag<Data, custom_directive::tag::rename_expression>, Subject, Modifiers>
{
typedef custom_directive::rename_directive<Subject,Data> result_type;
template<typename Terminal>
result_type operator()(Terminal& term, Subject const& subject, unused_type) const
{
typedef tag::stateful_tag<Data,
custom_directive::tag::rename_expression> tag_type;
using spirit::detail::get_stateful_data;
return result_type(subject,get_stateful_data<tag_type>::call(term));
}
};
}}}
//END OF expression_renamer.hpp
template <typename Parser>
void parse(std::string const& str, Parser const& parser)
{
std::cout << "Parsing: \"" << str << "\"" << " with `digit` `point` `digit`" << std::endl;
std::string::const_iterator iter=str.begin(),end=str.end();
boost::spirit::qi::parse(iter,end,parser);
}
int main()
{
custom_directive::expression_renamer point("point");
custom_directive::expression_renamer digit("digit");
boost::spirit::qi::char_type char_;
boost::spirit::qi::rule<std::string::const_iterator> twoDigitsWithPoint = char_("0-9") > point[char_('.')] > digit[char_("0-9")];
boost::spirit::qi::on_error<boost::spirit::qi::fail>
(
twoDigitsWithPoint
, std::cout
<< boost::phoenix::val("Error! Expecting ")
<< boost::spirit::qi::_4 // what failed?
<< std::endl
);
parse("33",twoDigitsWithPoint);
parse("3.a",twoDigitsWithPoint);
}
关于c++ - Boost Spirit 中的名称表达式没有分配给规则,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/33424589/