Boost Spirit 中的名称表达式没有分配给规则
Name expressions in Boost Spirit without assignment to a rule
是否可以在 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 有一个 "digit"(位置 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() 的函数,它 returns 一个 boost::spirit::info 可以从中检索字符串表示形式的对象。但我无法覆盖它,因为我不熟悉 Boost Proto 和 Boost Spirit 的内部结构。
您可以使用 auto
分组。但是调试名称只能附加到非终端:qi::rule<>
和 qi::grammar<>
(其他 "groupings" 无论如何都不会被跟踪,甚至 attr_cast<>
也不会被跟踪,这在语义上是接近的rule<>
).
的属性传播语义
关于使用 auto
有一个很大的警告:
Proto表达式树需要深拷贝。最近的齐定义qi::copy()
来做到这一点。
这是一种使用自定义指令的方法。你可以看到一个很好的解释如何做类似的事情(它是一个解析器,而不是一个指令)here。
创建自定义的过程 parser/directive 可以分为四个部分:
- Defining/creating 在 Spirit 表达式中使用的终端。 通常你会使用
BOOST_SPIRIT_TERMINAL
除非你需要你的 parser/directive parser(whatever)
/directive(whatever)[subject]
的形式。在这种情况下,您需要使用 BOOST_SPIRIT_TERMINAL_EX
。这种情况特别奇怪,因为您需要与终端关联的状态,因此您需要定义一个从 terminal<tag::stateful_tag,...>
派生的结构。这通常不是必需的。我决定将所有这些都放在命名空间 custom_directive
中,但您也可以将其放在 boost::spirit::qi
中。
- 启用您的 parser/directive. 这里您需要在命名空间内专门化
use_terminal
(对于解析器)或 use_directive
(对于指令) boost::spirit
.
- 创建实际的 parser/directive. 这个 parser/directive 需要三件事:一个
attribute<Context,Iterator>::type
关联的元函数,它说明了解析器的属性是(在这种情况下,我只是通过了主题解析器的属性);一个 parse
具有适当签名的成员函数进行真正的解析(我再次推迟到主题解析器),以及一个 what
成员函数,我们真正有兴趣修改 returns 与建设中的终端相关的任何内容。我再次决定使用命名空间 custom_directive
但你也可以将它放在 boost::spirit::qi
. 中
- 将终端与您的实际 parser/directive 连接。 这需要在
boost::spirit::qi
内并且需要您专门化 make_directive
或 make_primitive
与您的终端标签并实例化您的实际 parser/directive。
#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);
}
是否可以在 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 有一个 "digit"(位置 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() 的函数,它 returns 一个 boost::spirit::info 可以从中检索字符串表示形式的对象。但我无法覆盖它,因为我不熟悉 Boost Proto 和 Boost Spirit 的内部结构。
您可以使用 auto
分组。但是调试名称只能附加到非终端:qi::rule<>
和 qi::grammar<>
(其他 "groupings" 无论如何都不会被跟踪,甚至 attr_cast<>
也不会被跟踪,这在语义上是接近的rule<>
).
关于使用 auto
有一个很大的警告:
Proto表达式树需要深拷贝。最近的齐定义qi::copy()
来做到这一点。
这是一种使用自定义指令的方法。你可以看到一个很好的解释如何做类似的事情(它是一个解析器,而不是一个指令)here。
创建自定义的过程 parser/directive 可以分为四个部分:
- Defining/creating 在 Spirit 表达式中使用的终端。 通常你会使用
BOOST_SPIRIT_TERMINAL
除非你需要你的 parser/directiveparser(whatever)
/directive(whatever)[subject]
的形式。在这种情况下,您需要使用BOOST_SPIRIT_TERMINAL_EX
。这种情况特别奇怪,因为您需要与终端关联的状态,因此您需要定义一个从terminal<tag::stateful_tag,...>
派生的结构。这通常不是必需的。我决定将所有这些都放在命名空间custom_directive
中,但您也可以将其放在boost::spirit::qi
中。 - 启用您的 parser/directive. 这里您需要在命名空间内专门化
use_terminal
(对于解析器)或use_directive
(对于指令)boost::spirit
. - 创建实际的 parser/directive. 这个 parser/directive 需要三件事:一个
attribute<Context,Iterator>::type
关联的元函数,它说明了解析器的属性是(在这种情况下,我只是通过了主题解析器的属性);一个parse
具有适当签名的成员函数进行真正的解析(我再次推迟到主题解析器),以及一个what
成员函数,我们真正有兴趣修改 returns 与建设中的终端相关的任何内容。我再次决定使用命名空间custom_directive
但你也可以将它放在boost::spirit::qi
. 中
- 将终端与您的实际 parser/directive 连接。 这需要在
boost::spirit::qi
内并且需要您专门化make_directive
或make_primitive
与您的终端标签并实例化您的实际 parser/directive。
#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);
}