我要做的是在运行时从ABNF语法文件创建解析器。我已经在qi :: grammar中实现了所有ABNF规则,就像这两个:
typedef /*qi::rule or struct containing qi::rule*/ parserRule
[...] //all other ABNF rules according to RFC 5234
rule =
(
rulename[qi::_a = qi::_1] >>
definedAs >>
elements[qi::_b = qi::_1] >>
cNl
)[qi::_val = px::bind(&AbnfParserFactory::fromRule, &factory, qi::_a, qi::_b)];
rulelist =
+(
rule[px::push_back(qi::_a, qi::_1)] |
(*cWsp >> cNl)
) >>
eps[qi::_val = px::bind(&AbnfParserFactory::fromRulelist, &factory, qi::_a)];
qi::rule<Iterator, std::map<std::string, parserRule>(), qi::locals<std::vector<parserRule> > > rulelist;
qi::rule<Iterator, parserRule(), qi::locals<std::string>, qi::locals<parserRule> > rule;
[...] // all other ABNF rules
在ParserFactory
内,根据读入的语法创建一个新的qi ::规则:
std::map<std::string, ReturnType> fromRulelist(std::vector<ReturnType> &rules)
{
// return a map with <rulename, rule>
};
parserRule fromRule(std::string &name, parserRule &rule)
{
//name the rule an return it
rule.name(name);
return rule;
};
问题是关于parserRule
的类型。
如果我使用qi :: rule作为类型(就像我通常所希望的那样),我将丢失在ParserFactory
中分配的每个规则名称(如fromRule
中所示)。我猜这是由精神在内部工作的方式引起的(=
运算符总是创建一个新的,未命名的规则。=
用于分配px :: bind函数结果)
但是,如果我尝试将qi :: rule包装到结构中以避免此问题,我将无法再使用精灵调试来编译我的代码。这是我试过的:
typedef qi::rule<std::string::const_iterator, std::string()> FactoryRuleType;
struct parserRule
{
FactoryRuleType mRule;
};
BOOST_FUSION_ADAPT_STRUCT(
parserRule,
(FactoryRuleType, mRule)
)
[...] //rule definitions like above
debug(rule);
debug(rulelist);
[...] //debug all other rules
这会给我一大堆编译错误(这是很长的时间来发布)。我已经搜索了几天试图解决这个问题,但没有运气。我希望我提到足够的细节。
感谢任何帮助。
编译输出摘录:
/usr/include/boost/proto/operators.hpp:295:9: note: template argument deduction/substitution failed:
/usr/include/boost/proto/operators.hpp: In substitution of ‘template<class Left, class Right> const typename boost::proto::detail::enable_binary<boost::proto::domainns_::deduce_domain, boost::proto::detail::not_a_grammar, boost::mpl::or_<boost::proto::is_extension<Arg>, boost::proto::is_extension<Right> >, boost::proto::tagns_::tag::shift_left, Left&, Right&>::type boost::proto::exprns_::operator<<(Left&, Right&) [with Left = std::basic_ostream<char>; Right = const boost::spirit::qi::reference<const boost::spirit::qi::rule<__gnu_cxx::__normal_iterator<const char*, std::basic_string<char> >, std::basic_string<char>()> >]’:
/usr/include/boost/spirit/home/support/attributes.hpp:1226:17: required from ‘static void boost::spirit::traits::print_attribute_debug<Out, T, Enable>::call_impl3(Out&, const T_&, mpl_::false_) [with T_ = boost::spirit::qi::reference<const boost::spirit::qi::rule<__gnu_cxx::__normal_iterator<const char*, std::basic_string<char> >, std::basic_string<char>()> >; Out = std::basic_ostream<char>; T = boost::spirit::qi::reference<const boost::spirit::qi::rule<__gnu_cxx::__normal_iterator<const char*, std::basic_string<char> >, std::basic_string<char>()> >; Enable = void; mpl_::false_ = mpl_::bool_<false>]’
/usr/include/boost/spirit/home/support/attributes.hpp:1242:67: required from ‘static void boost::spirit::traits::print_attribute_debug<Out, T, Enable>::call_impl2(Out&, const T_&, mpl_::false_) [with T_ = boost::spirit::qi::reference<const boost::spirit::qi::rule<__gnu_cxx::__normal_iterator<const char*, std::basic_string<char> >, std::basic_string<char>()> >; Out = std::basic_ostream<char>; T = boost::spirit::qi::reference<const boost::spirit::qi::rule<__gnu_cxx::__normal_iterator<const char*, std::basic_string<char> >, std::basic_string<char>()> >; Enable = void; mpl_::false_ = mpl_::bool_<false>]’
/usr/include/boost/spirit/home/support/attributes.hpp:1277:52: required from ‘static void boost::spirit::traits::print_attribute_debug<Out, T, Enable>::call_impl(Out&, const T_&, mpl_::true_) [with T_ = boost::spirit::qi::reference<const boost::spirit::qi::rule<__gnu_cxx::__normal_iterator<const char*, std::basic_string<char> >, std::basic_string<char>()> >; Out = std::basic_ostream<char>; T = boost::spirit::qi::reference<const boost::spirit::qi::rule<__gnu_cxx::__normal_iterator<const char*, std::basic_string<char> >, std::basic_string<char>()> >; Enable = void; mpl_::true_ = mpl_::bool_<true>]’
/usr/include/boost/spirit/home/support/attributes.hpp:1283:52: required from ‘static void boost::spirit::traits::print_attribute_debug<Out, T, Enable>::call(Out&, const T&) [with Out = std::basic_ostream<char>; T = boost::spirit::qi::reference<const boost::spirit::qi::rule<__gnu_cxx::__normal_iterator<const char*, std::basic_string<char> >, std::basic_string<char>()> >; Enable = void]’
/usr/include/boost/spirit/home/support/attributes.hpp:1303:53: required from ‘void boost::spirit::traits::print_attribute(Out&, const T&) [with Out = std::basic_ostream<char>; T = boost::spirit::qi::reference<const boost::spirit::qi::rule<__gnu_cxx::__normal_iterator<const char*, std::basic_string<char> >, std::basic_string<char>()> >]’
/usr/include/boost/spirit/home/support/attributes.hpp:1196:57: [ skipping 34 instantiation contexts, use -ftemplate-backtrace-limit=0 to disable ]
/usr/include/boost/function/function_template.hpp:722:7: required from ‘boost::function4<R, T1, T2, T3, T4>::function4(Functor, typename boost::enable_if_c<boost::type_traits::ice_not<boost::is_integral<Functor>::value>::value, int>::type) [with Functor = boost::spirit::qi::debug_handler<__gnu_cxx::__normal_iterator<const char*, std::basic_string<char> >, boost::spirit::context<boost::fusion::cons<FactoryReturnType&, boost::fusion::nil>, boost::fusion::vector0<> >, boost::spirit::unused_type, boost::spirit::qi::simple_trace>; R = bool; T0 = __gnu_cxx::__normal_iterator<const char*, std::basic_string<char> >&; T1 = const __gnu_cxx::__normal_iterator<const char*, std::basic_string<char> >&; T2 = boost::spirit::context<boost::fusion::cons<FactoryReturnType&, boost::fusion::nil>, boost::fusion::vector0<> >&; T3 = const boost::spirit::unused_type&; typename boost::enable_if_c<boost::type_traits::ice_not<boost::is_integral<Functor>::value>::value, int>::type = int]’
/usr/include/boost/function/function_template.hpp:1069:16: required from ‘boost::function<R(T0, T1, T2, T3)>::function(Functor, typename boost::enable_if_c<boost::type_traits::ice_not<boost::is_integral<Functor>::value>::value, int>::type) [with Functor = boost::spirit::qi::debug_handler<__gnu_cxx::__normal_iterator<const char*, std::basic_string<char> >, boost::spirit::context<boost::fusion::cons<FactoryReturnType&, boost::fusion::nil>, boost::fusion::vector0<> >, boost::spirit::unused_type, boost::spirit::qi::simple_trace>; R = bool; T0 = __gnu_cxx::__normal_iterator<const char*, std::basic_string<char> >&; T1 = const __gnu_cxx::__normal_iterator<const char*, std::basic_string<char> >&; T2 = boost::spirit::context<boost::fusion::cons<FactoryReturnType&, boost::fusion::nil>, boost::fusion::vector0<> >&; T3 = const boost::spirit::unused_type&; typename boost::enable_if_c<boost::type_traits::ice_not<boost::is_integral<Functor>::value>::value, int>::type = int]’
/usr/include/boost/function/function_template.hpp:1124:5: required from ‘typename boost::enable_if_c<boost::type_traits::ice_not<boost::is_integral<Functor>::value>::value, boost::function<R(T0, T1, T2, T3)>&>::type boost::function<R(T0, T1, T2, T3)>::operator=(Functor) [with Functor = boost::spirit::qi::debug_handler<__gnu_cxx::__normal_iterator<const char*, std::basic_string<char> >, boost::spirit::context<boost::fusion::cons<FactoryReturnType&, boost::fusion::nil>, boost::fusion::vector0<> >, boost::spirit::unused_type, boost::spirit::qi::simple_trace>; R = bool; T0 = __gnu_cxx::__normal_iterator<const char*, std::basic_string<char> >&; T1 = const __gnu_cxx::__normal_iterator<const char*, std::basic_string<char> >&; T2 = boost::spirit::context<boost::fusion::cons<FactoryReturnType&, boost::fusion::nil>, boost::fusion::vector0<> >&; T3 = const boost::spirit::unused_type&; typename boost::enable_if_c<boost::type_traits::ice_not<boost::is_integral<Functor>::value>::value, boost::function<R(T0, T1, T2, T3)>&>::type = boost::function<bool(__gnu_cxx::__normal_iterator<const char*, std::basic_string<char> >&, const __gnu_cxx::__normal_iterator<const char*, std::basic_string<char> >&, boost::spirit::context<boost::fusion::cons<FactoryReturnType&, boost::fusion::nil>, boost::fusion::vector0<> >&, const boost::spirit::unused_type&)>&]’
/usr/include/boost/spirit/home/qi/nonterminal/debug_handler.hpp:122:13: required from ‘void boost::spirit::qi::debug(boost::spirit::qi::rule<Iterator, T1, T2, T3, T4>&) [with Iterator = __gnu_cxx::__normal_iterator<const char*, std::basic_string<char> >; T1 = FactoryReturnType(); T2 = boost::spirit::unused_type; T3 = boost::spirit::unused_type; T4 = boost::spirit::unused_type]’
../src/AbnfReader.hpp:350:14: required from ‘AbnfRules<Iterator>::AbnfRules() [with Iterator = __gnu_cxx::__normal_iterator<const char*, std::basic_string<char> >]’
../src/AbnfReader.cpp:27:12: required from here
/usr/include/boost/proto/operators.hpp:295:9: error: no type named ‘type’ in ‘struct boost::proto::detail::enable_binary<boost::proto::domainns_::deduce_domain, boost::proto::detail::not_a_grammar, boost::mpl::or_<boost::proto::is_extension<std::basic_ostream<char> >, boost::proto::is_extension<const boost::spirit::qi::reference<const boost::spirit::qi::rule<__gnu_cxx::__normal_iterator<const char*, std::basic_string<char> >, std::basic_string<char>()> > >, mpl_::bool_<false>, mpl_::bool_<false>, mpl_::bool_<false> >, boost::proto::tagns_::tag::shift_left, std::basic_ostream<char>&, const boost::spirit::qi::reference<const boost::spirit::qi::rule<__gnu_cxx::__normal_iterator<const char*, std::basic_string<char> >, std::basic_string<char>()> >&>’
make: *** [src/AbnfReader.o] Error 1
答案 0 :(得分:1)
嗯,这很尴尬。在尝试编写像sehe建议的SSCCE时,我发现我的方法实际上一直在工作。我在试图解决这个问题时遇到了很多麻烦......:C
以下是我尝试做的一个工作示例。虽然它有效但行为仍然有点奇怪,这就是我认为它不能以这种方式工作的原因。
#include <boost/spirit/include/qi.hpp>
#include <boost/spirit/include/phoenix.hpp>
namespace qi = boost::spirit::qi;
namespace px = boost::phoenix;
namespace ascii = boost::spirit::ascii;
typedef qi::rule<std::string::const_iterator, std::string()> parserRule;
namespace std
{
std::ostream& operator<<(std::ostream& stream, const parserRule& val);
}
std::ostream& std::operator<<(std::ostream& stream, const parserRule& val)
{
stream << "RULE( " << val.name();
stream << " )";
return stream;
}
class Factory
{
public:
Factory(){}
parserRule createParser(std::string str)
{
parserRule r;
r = qi::char_(str);
r.name("FactoryRule");
std::cout << r.name() << " now parses >" + str + "<" << std::end;
return r;
}
};
template <typename Iterator>
struct TestGrammar : qi::grammar<Iterator, parserRule()>
{
TestGrammar() : TestGrammar::base_type(start, "Test grammar")
{
start = stringRule.alias();
stringRule = (
ascii::char_("'") >>
*(ascii::char_ - ascii::char_("'"))[qi::_a += qi::_1] >>
ascii::char_("'")
)[qi::_val = px::bind(&Factory::createParser, &factory, qi::_a)];
start.name("Start");
stringRule.name("StringRule");
qi::debug(start); // shows "RULE( unnamed-rule )"
qi::debug(stringRule); // shows "RULE( unnamed-rule )"
}
qi::rule<Iterator, parserRule() > start;
qi::rule<Iterator, parserRule(), qi::locals<std::string> > stringRule;
Factory factory;
};
int main()
{
typedef std::string::const_iterator iterator_type;
typedef TestGrammar<iterator_type> TGrammar;
TGrammar test_parser;
std::string test = "parse THIS!";
std::string input = "'"+test+"'";
parserRule result;
std::string::const_iterator iter = input.begin();
std::string::const_iterator end = input.end();
bool r = parse(iter, end, test_parser, result);
if (r && iter == end)
{
std::cout << "-------------------------\n";
std::cout << "1st Parsing succeeded\n";
std::cout << "-------------------------\n";
}
else
{
std::string rest(iter, end);
std::cout << "-------------------------\n";
std::cout << "1st Parsing failed\n";
std::cout << "stopped at: \"" << rest << "\"\n";
std::cout << "-------------------------\n";
}
iterator_type first(test.begin()), last(test.end());
qi::debug(result); //shows correct rule name
r = qi::phrase_parse(first, last, result, boost::spirit::ascii::space);
if (r && iter == end)
{
std::cout << "-------------------------\n";
std::cout << "2nd Parsing succeeded\n";
std::cout << "-------------------------\n";
}
else
{
std::string rest(first, last);
std::cout << "-------------------------\n";
std::cout << "2nd Parsing failed\n";
std::cout << "stopped at: \"" << rest << "\"\n";
std::cout << "-------------------------\n";
}
}
这是输出:
FactoryRule now parses >parse THIS!<
<Start>
<try>'parse THIS!'</try>
<StringRule>
<try>'parse THIS!'</try>
<success></success>
<attributes>[[RULE( unnamed-rule )]]</attributes><locals>(parse THIS!)</locals>
</StringRule>
<success></success>
<attributes>[[RULE( unnamed-rule )]]</attributes>
</Start>
-------------------------
1st Parsing succeeded
-------------------------
<FactoryRule>
<try>parse THIS!</try>
<success>arse THIS!</success>
<attributes>[[p]]</attributes>
</FactoryRule>
-------------------------
2nd Parsing succeeded
-------------------------
如您所见,调试规则start
和stringRule
将始终显示&#34; RULE(未命名规则)&#34;作为属性。这使我相信,在分配=
结果时,由于使用了px::bind
- 运算符,已分配的规则名称已丢失。由于调试result
规则显示正确的名称,我假设&#34;未命名规则&#34;输出只是没有显示正确的FINAL合成属性,而是显示当前的&#34;空&#34;之一。
在我看到&#34;未命名的规则&#34;时,我会立刻感到恐慌。并花费很多天试图解决这个问题,甚至在这里询问,而不是通过调试result
规则来实际测试整个事情。
感谢您无论如何都想帮助我。我希望这至少对其他人有用。
答案 1 :(得分:0)
这里有两个问题,仅仅是经验,没有进一步的代码:
单元素适应结构构成问题,请参阅:[link coming]
调试信息需要专门调试打印特征(可以追溯到ostream
流式传输)。显然,qi::rule
尚未做到这一点。所以,要么添加它。
也许,也许你可以让包含结构iostream能够避免它(但我担心融合适应可能会优先考虑。这是一次尝试)
Q值。是的,你是对的。我必须在运行时动态编写语法。为了确保这是可能的,我已经编写了一堆按预期工作的测试片段。
非常生硬:精神不是那种工具。 Spirit是一种工具,可以从“praser表达式”生成静态编译的语法,这些表达式是表达式模板。整个目的是成为编译时并使用静态多态与完全优化。我建议可能创建一组运行时 - 多态解析器组件(你可以反过来,使用Spirit实现:))并用它们组成你的动态语法。
一种可能的中间立场是使用qi::lazy
和一组预定义的不可变“原子”规则(因此不是动态组合),并通过引用使用这些“原子”规则组合你的解析器。 / p>
基本上,只要您不尝试动态撰写表达式模板(而是现成的类型擦除非终端),您应该没事。
如果您需要更多,您很快就会进入UB-land。有些时候你可以使用boost::proto::deep_copy
来解决问题,但是有一些限制。