2015-10-29 1 views
3

Можно ли назвать выражение в Boost Spirit без его назначения правилу?Выражения имени в 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"); 

Так что исключение будет сказать, что она ожидает «цифру» в положении 2, если он получил вход как «3а» (это на самом деле не важно, что это позиция 2).

Альтернативный способ выразить это будет:

using boost::spirit::standard::char_; 
boost::spirit::qi::rule<> digit = char_("0-9"); 
digit.name("digit"); 
boost::spirit::qi::rule<> twoDigits = digit > digit; 

Я уже проверил источник и обнаружил, что выражение есть функция называется то, что(), который возвращает повышающий :: дух :: объект информации из которого можно получить строковое представление. Но я не смог переписать это, поскольку я не familier с Boost Proto и внутренностями Boost Spirit.

ответ

3

Ниже приведен подход с использованием специальной директивы. Вы можете увидеть очень хорошее объяснение того, как сделать что-то подобное (это парсер, а не директива) here.

Процесс создания пользовательского парсера/директиву можно разделить на четыре части:

  1. Определение/создание терминала для использования в выражении духа. Обычно вы используете BOOST_SPIRIT_TERMINAL, если вам не нужен ваш синтаксический анализатор/указатель формы parser(whatever)/directive(whatever)[subject]. В этом случае вам нужно будет использовать BOOST_SPIRIT_TERMINAL_EX. Этот случай особенно странен в том, что вам нужно состояние, связанное с вашим терминалом, и поэтому вам нужно определить структуру, полученную из terminal<tag::stateful_tag,...>. Обычно это не требуется. Я решил поместить все это в пространство имен custom_directive, но вы также можете поместить его в boost::spirit::qi.
  2. Включение вашего парсера/директивы. Здесь вы должны упомянуть либо use_terminal (для парсеров), либо use_directive (для директив) внутри пространства имен boost::spirit.
  3. Создание фактического анализатора/директивы. Этот синтаксический анализатор/директива требует трех вещей: связанного метафайла attribute<Context,Iterator>::type, в котором указывается, что такое атрибут вашего синтаксического анализатора (в этом случае я просто передал атрибут анализатора субъекта); a parse функция-член с соответствующей подписью, которая выполняет реальный синтаксический анализ (опять же я отложил до анализатора темы) и функцию-член what, что мы действительно интересуем модификацией, которая возвращает все, что вы связывали с терминалом при построении. Опять же я решил использовать пространство имен custom_directive, но вы также можете поместить его в boost::spirit::qi.
  4. Подключение терминала к вашему фактическому парсеру/директиве. Это должно быть внутри boost::spirit::qi и требует, что вы специализируетесь либо на make_directive, либо на make_primitive с вашим тегом терминала и устанавливаете фактический парсер/директиву.

Live on coliru

#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); 

} 
+1

Очень хороший пример и объяснение. Это почти дало мне синтаксис, который я хотел. См. Http://coliru.stacked-crooked.com/a/2f9a72bcbb6702ac с небольшими корректировками кода, близкого к синтаксису, в примере моего вопроса. – user2525536

+0

+1 Безумие. Интересно, что именно это покупает OP (например, на самом деле это на самом деле более легкий вес? Или просто меньше ввода - что приводит к более длинным определениям правил) – sehe

+0

Не уверен, что теперь это более легкий вес, но он позволяет вам называть части выражения без необходимости для определения правила для этого. В Windows я получил более крупный исполняемый файл размером 1KiB, чем использование правил при компиляции с GCC 5.2.0 в 32-битном режиме. Это решение получается в меньшем исполняемом файле 1KiB, когда я компилирую его в 64-битном режиме с теми же параметрами компилятора и сборки. – user2525536

0

Вы можете группировать, используя auto. Но имена отладки могут быть привязаны только к не-терминалам: qi::rule<> и qi::grammar<> (другие «группировки» не прослеживаются в любом случае, даже с attr_cast<>, который семантически близок к семантике распространения атрибутов в rule<>).


Существует большое предостережение, связанные с использованием auto:

выражение Прото деревья должны быть глубоко скопированы. Последние Qi определяют qi::copy(), чтобы сделать именно это.

+0

Это на самом деле не о трассировке. Это больше касается обработки обхода, где исключение выбрасывается в том месте, где ожидалось определенное выражение. Исключенное исключение содержит в том() элемент информации, который представляет ожидаемое целое выражение. Это можно было подробно рассказать пользователю о синтаксических ошибках, и вы также можете прекратить синтаксический анализ на ранней стадии. Я думал, что можно будет перезаписать эту информационную ценность какой-то директивой. Это не обязательно означает, что мне не нужно писать свою собственную директиву для этого, но я не мог этого сделать. – user2525536

+0

@ user2525536 Я попытался написать настраиваемую директиву, которая, как я думаю, делает то, что вы хотите. – llonesmiz