What I'm trying to do is creating a parser from a ABNF grammar file at runtime. I already implemented all ABNF rules inside a qi::grammar like these two:
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
Inside the ParserFactory
, a new qi::rule is created according to the read in grammar:
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;
};
The question is about the type of parserRule
.
If I use qi::rule as type (like I usually intended), I will loose every rule name that was assigned in the ParserFactory
(like in fromRule
). I guess this is caused by the way spirit works internally (=
operator always creates a new, unnamed rule. And the =
is used to assign the px::bind function result)
If I however try to wrap my qi::rule into a struct to avoid this issue, I am no longer able to compile my code with spirit debugging. Here is what i tried:
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
This will give me a load of compile errors (which is WAY to long to post here). I have searched my ass of for days trying to solve this, but without any luck. I hope I mentioned enough details.
Any help is appreciated.
Compile output excerpt:
/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
Well, this is awkward. While trying to write a SSCCE like sehe suggested, I discovered that my approach was actually working all along. And I went through so much trouble trying to solve this... :C
Here is a working example of what I was trying to do. Although it works, the behaviour is still a bit odd, which is the reason I believed it does not work this way.
#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";
}
}
Here is the output:
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
-------------------------
As you can see, debugging the rules start
and stringRule
will always display "RULE( unnamed-rule )" as attributes. This made me believe that the assigned rule name was lost due to the use of the =
-operator when assigning the px::bind
result. Since debugging the result
rule shows the correct name, I assume that the "unnamed-rule" output simply does not show the correct, FINAL synthesized attribute, but rather the current, "empty" one.
BIG SHAME ON ME for immediately panicking when seeing the "unnamed-rule" and spending WAY to many days trying to solve this and even asking here WITHOUT actually testing the whole thing by debugging the result
rule.
Thanks for trying to help me anyway. I hope this might at least be useful for somebody else.