2014-12-03 2 views
1

То, что я пытаюсь сделать, это создать парсер из файла грамматики ABNF во время выполнения. Я уже выполнил все правила ABNF внутри ци :: грамматика, как эти два:boost :: spirit: qi :: rule или struct, содержащее qi :: rule как результат синтаксического анализа

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, новая ци :: правила создается в соответствии с прочитанным в грамматике:

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)

Если я, тем не менее, пытаюсь обернуть мой ци: : rule в struct, чтобы избежать этой проблемы, я больше не могу скомпилировать свой код с отладкой спирта. Вот что я пробовал:

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 

Это даст мне кучу ошибок компиляции (что WAY долго размещать здесь). Я искал свою задницу в течение нескольких дней, пытаясь решить эту проблему, но без везения. Надеюсь, я упомянул достаточно подробностей.

Любая помощь приветствуется.

Compile выход выдержка:

/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

Я не уверен, что вы пытаетесь сказать. Можете ли вы включить SSCCE, который демонстрирует вашу проблему в сжатом виде? Кроме того, я получаю половину вибрации, что вы хотите динамически составлять грамматики. Эта дорога чревата опасностью, поскольку шаблоны выражений Boost Proto/Boost Phoenix не предназначены для семантики стоимости (они предпочитают жить только как временные). Вы можете найти мои ответы для ключевого слова 'deep_copy', чтобы попробовать множество проблем, с которыми вы столкнетесь. – sehe

+0

Да, вы правы. Я должен динамически составлять грамматики во время выполнения. Чтобы убедиться, что это будет возможно, я уже написал кучу тестовых фрагментов, которые работали по назначению. Поэтому я думаю, что мой подход возможен (хотя это может быть и не самое лучшее). Я посмотрю ваши ответы и попытаюсь убрать мой вопрос, если у меня будет время. спасибо – Bert

ответ

1

Ну, это неудобно. Пытаясь написать 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 всегда будут отображать "ПРАВИЛО (безымянные-правила)" в качестве атрибутов. Это заставило меня поверить, что присвоенное имя правила было потеряно из-за использования = -оператора при назначении результата px::bind. Поскольку отладка правила result показывает правильное имя, я предполагаю, что вывод «неназванный-правило» просто не показывает правильный, FINAL синтезированный атрибут, а скорее текущий, «пустой».

BIG SHAME ON ME для мгновенного паники, когда вы видите «неназванное правило» и тратите WAY на многие дни, пытаясь решить эту проблему и даже спрашивая здесь БЕЗ фактического тестирования всего этого, отлаживая правило result.

Спасибо за попытку помочь мне в любом случае. Надеюсь, это может быть по крайней мере полезно для кого-то другого.

0

два вопроса может быть в игре здесь, от всего опыта, без дополнительного кода:

  1. Одноэлементных приспособленных Структур создают проблемы, см: [ссылка coming]

  2. Отладочная информация требует специализации отпечатков (отходит на ostream). Очевидно, это не было сделано для qi::rule. Итак, добавьте это.

    Может быть, просто, может быть, вы могли бы сделать, содержащий структура iostream-состоянии, чтобы избежать этого (но я боюсь, что адаптация фьюжн может иметь приоритет. Это worh попробовать)


Q . Да вы правы. Я должен динамически составлять грамматики во время выполнения. Чтобы убедиться, что это будет возможно, я уже написал кучу тестовых фрагментов, которые работали по назначению.

  • Чтобы быть очень тупой: Дух не инструмент для этого. Дух - это инструмент, который генерирует статически скомпилированный грамматик из «праймерных выражений», которые являются шаблонами выражений. Вся цель заключается в компиляции и использовании статического полиморфизма с полной оптимизацией. Я бы предположил, возможно, создать набор компонентов времени полиморфизма парсера (который вы, в свою очередь, можете реализовать с помощью Spirit :)) и вместо этого выложите из них свои динамические грамматики.

    В качестве возможного среднего уровня используется qi::lazy с набором предопределенных неизменных «атомных» правил (так не составленных «на лету») и составить ваш синтаксический анализатор, используя эти правила «атома» по ссылке.

  • В принципе, до тех пор, как вы не пытаются динамически создавать выражения шаблонов (а скорее, готовые типа стертых нетерминалы), вы должны быть в порядке.

    Если вам нужно больше, вы будете в UB-land в кратчайшие сроки. Некоторое время вы сможете бороться с выходом boost::proto::deep_copy, но есть ограничения на это.