Изменение порядка подправил внутри правила в грамматике boost :: spirit приводит к ошибке segfault

Предупреждение; пока я пытался сократить код до минимума. Мне еще нужно было включить совсем немного, чтобы обеспечить наличие необходимой информации.

Этот код компилирует файлы и запускается, что приводит к синтаксической ошибке;

name = simple_name      [ qi::_val = qi::_1 ]
     | qualified_name   [ qi::_val = qi::_1 ]
     ;

Хотя это;

name = qualified_name   [ qi::_val = qi::_1 ]
     | simple_name      [ qi::_val = qi::_1 ]
     ;

Приводит к SIGSEGV, ошибка сегментации;

boost::detail::function::function_obj_invoker4<boost::spirit::qi::detail::parser_binder<boost::spirit::qi::alternative<boost::fusion::cons<boost::spirit::qi::action<boost::spirit::qi::reference<boost::spirit::qi::rule<boost::spirit::lex::lexertl::iterator<boost::spirit::lex::lexertl::functor<boost::spirit::lex::lexertl::token<__gnu_cxx::__normal_iterator<char*, std::string>, boost::mpl::vector<std::string, mpl_::na, mpl_::na, mpl_::na, mpl_::na, mpl_::na, mpl_::na, mpl_::na, mpl_::na, mpl_::na, mpl_::na, mpl_::na, mpl_::na, mpl_::na, mpl_::na, mpl_::na, mpl_::na, mpl_::na, mpl_::na, mpl_::na>, mpl_::bool_<false>, unsigned long>, boost::spirit::lex::lexertl::detail::data, __gnu_cxx::__normal_iterator<char*, std::string>, mpl_::bool_<true>, mpl_::bool_<false> > >, Ast::name* (), boost::spirit::unused_type, boost::spirit::unused_type, boost::spirit::unused_type> const>, boost::phoenix::actor<boost::proto::exprns_::expr<boost::proto::tagns_::tag::assign, boost::proto::argsns_::list2<boost::proto::exprns_::expr<boost::proto::tagns_::tag::terminal, boost::proto::argsns_::term<boost::spirit::attribute<0> >,0l>,boost::phoenix::actor<boost::spirit::argument<0> > >, 2l> > >,boost::fusion::cons<boost::spirit::qi::action<boost::spirit::qi::reference<boost::spirit::qi::rule<boost::spirit::lex::lexertl::iterator<boost::spirit::lex::lexertl::functor<boost::spirit::lex::lexertl::token<__gnu_cxx::__normal_iterator<char*, std::string>,boost::mpl::vector<std::string, mpl_::na, mpl_::na, mpl_::na, mpl_::na, mpl_::na, mpl_::na, mpl_::na, mpl_::na, mpl_::na, mpl_::na, mpl_::na, mpl_::na, mpl_::na, mpl_::na, mpl_::na, mpl_::na, mpl_::na, mpl_::na, mpl_::na>, mpl_::bool_<false>,unsigned long>, boost::spirit::lex::lexertl::detail::data, __gnu_cxx::__normal_iterator<char*,std::string>, mpl_::bool_<true>, mpl_::bool_<false> > >, Ast::name* (), ... more to come ...

Где;

simple_name = (tok.identifier) [ qi::_val = build_simple_name_(qi::_1) ];

А также;

qualified_name = (name >> qi::raw_token(DOT) >> tok.identifier) [ qi::_val = build_qualified_name_(qi::_1, qi::_2) ] ;

Все эти правила возвращают Ast::name*();

qi::rule<Iterator, Ast::name*()> name;
qi::rule<Iterator, Ast::name*()> simple_name;
qi::rule<Iterator, Ast::name*()> qualified_name;

Вспомогательные функции определены как;

Ast::name* build_simple_name(std::string str)
{
    return (new Ast::name_simple(Ast::identifier(str)));
}
BOOST_PHOENIX_ADAPT_FUNCTION(Ast::name*, build_simple_name_, build_simple_name, 1)

А также;

Ast::name* build_qualified_name(Ast::name* name, std::string str)
{
    std::list<Ast::identifier> qualified_name = Ast::name_to_identifier_list(name);
    qualified_name.push_back(Ast::identifier(str));

    return (new Ast::name_qualified(qualified_name));
}
BOOST_PHOENIX_ADAPT_FUNCTION(Ast::name*, build_qualified_name_, build_qualified_name, 2)

Используемые определения лексера определены как;

lex::token_def<std::string> identifier = "{JAVA_LETTER}{JAVA_LETTER_OR_DIGIT}*";

А также;

('.', DOT)

Где шаблоны {JAVA_LETTER} и {JAVA_LETTER_OR_DIGIT} определены как;

("DIGIT",           "[0-9]")
("LATIN1_LETTER",   "[A-Z]|[a-z]")
("JAVA_LETTER",     "{LATIN1_LETTER}|$|_")
("JAVA_LETTER_OR_DIGIT", "{JAVA_LETTER}|{DIGIT}")

Мой ввод - простая строка;

package a.D;

Какие лексы к токенам;

Keywords : package
Identifier : a
Delimiters : .
Identifier : D
Delimiters : ;

Если первый пример (с первым именем simple_name) выдает синтаксическую ошибку как;

Syntax Error at line 1:
package a.D;
          ^^

И последний пример просто выдает segfault с ошибкой, опубликованной ранее.

Ясно, что второй пример - это то, что я хочу, поскольку он должен попытаться сопоставить сложное выражение перед простым.

Кто-нибудь понимает, почему происходит сбой кода, или как я могу это выяснить? - Также должно ли это быть при проверке кода?


person Skeen    schedule 04.09.2013    source источник
comment
Вы оставили рекурсию, имя зависит от квалифицированного_имя, которое зависит от имени и так далее.   -  person llonesmiz    schedule 04.09.2013
comment
Это незаконно? - Кроме того, если это так, как я могу получить тот же эффект, то есть как я могу его реорганизовать?   -  person Skeen    schedule 04.09.2013


Ответы (1)


Проблема в том, что у вас есть леворекурсивная грамматика, которую нельзя использовать с Boost.Spirit. В основном у вас есть:

name = identifier | name >> dot >> identifier;

Как вы можете видеть здесь, в чтобы удалить левую рекурсию, когда у вас есть что-то вроде:

A = A >> alpha | beta;

Вам необходимо создать 2 новых «правила»:

A = beta >> A_tail;
A_tail = eps | alpha >> A_tail;

В твоем случае:

A := name
alpha := dot >> identifier
beta := identifier

Итак, ваши «правила» будут такими:

name = identifier >> name_tail;
name_tail = eps | dot >> identifier >> A_tail;

Если вы внимательно посмотрите на name_tail, вы увидите, что это буквально означает: либо ничего, либо dot >> identifier, за которым следует либо ничего, либо dot >> identifier и так далее. Это означает, что name_tail:

name_tail = *(dot >> identifier);

Итак, наконец, ваше name правило будет таким:

name = identifier >> *(dot >> identifier);

Все это правильно, но есть очень большая вероятность, что это не сработает с вашими атрибутами.

person Community    schedule 04.09.2013
comment
Хм, у меня на самом деле есть вопрос, поскольку кажется, что существует детерминированный способ удаления левой рекурсии, есть ли конкретная причина, по которой boost :: spirit не «просто» выполняет предварительную обработку грамматики для ее устранения. - Это невозможно, невыполнимо или просто функция, которую никто не реализовал? - person Skeen; 04.09.2013
comment
Понятия не имею, я действительно пытался реализовать именно это, но безуспешно. Думаю, это было бы что-то действительно крутое. Удалось ли вам изменить свои семантические действия, чтобы приспособиться к этому, или я должен попытаться что-то с этим сделать? - person llonesmiz; 04.09.2013
comment
@Skeen это невозможно, потому что у вас могут быть собственные парсеры (не говоря уже о семантических действиях), которые нарушают законы ассоциативности, которые это предполагает. Конечно, они могут позволить использовать черты характера, но гораздо проще позволить человеку думать: / - person sehe; 04.09.2013
comment
Как правило, я не думаю, что в Spirit Qi есть какие-либо преобразования выражений (кроме, возможно, неявного вычитания char_ классов) - person sehe; 04.09.2013
comment
@cv_and_he: Я успешно изменил свои семантические действия, чтобы адаптировать это, и это работает довольно хорошо! Однако есть один вопрос: могу ли я внутри своей функции-строителя каким-то образом вызвать сбой синтаксического анализа? (В настоящее время я просто взломал его вместе с assert(false), но для меня это не очень хорошее решение). - person Skeen; 04.09.2013
comment
@sehe: Совершенно очевидно, что этого нельзя делать, когда вы начинаете делать что-то, выходящее за рамки контекстно-свободной грамматики. Точно так же, как мой код нельзя было бы автоматически ре-факторизовать, это также потребовало бы изменения моих «злых» семантических действий. - person Skeen; 04.09.2013
comment
На противоположном конце спектра создание статических таблиц лексера (обычно) является чисто детерминированным, поэтому это обычное дело (CoCo / R, ANTLR, flex / bison, lex / yacc и т. д., включая Spirit Lex) - person sehe; 04.09.2013
comment
@Skeen Я уверен, что вы можете, я не уверен, что этот способ сработает: вам нужно добавить параметр bool& pass в свою функцию построителя, а затем вы должны вызывать его так qi::_val = build_simple_name_(qi::_1, qi::_pass) (или, возможно, используя phx::ref(qi::_pass)). Вам нужно будет установить для этой переменной pass значение false, если вы хотите, чтобы синтаксический анализ завершился неудачно. - person llonesmiz; 04.09.2013
comment
@cv_and_he: А затем назначьте его false, чтобы синтаксический анализ не удался, верно? - person Skeen; 04.09.2013
comment
О, ага, я пропустил этот вопрос. Да [_pass = false] (например, qi::eps(false)) вызовет несоответствие. Кроме того, SA-указатели на функцию по умолчанию принимают параметр bool& pass. - person sehe; 04.09.2013
comment
@cv_and_he: Ах, прекрасно, я считаю, что я действительно нашел время, чтобы прочитать это, по крайней мере, в документации. - person Skeen; 04.09.2013
comment
@sehe: указатель на функцию Семантические действия, это то, что я использую? - или это то, что вы получаете, просто помещая указатель на функцию в фигурные скобки «семантическое действие»? - person Skeen; 04.09.2013
comment
@Skeen Он имел в виду последнее, например, здесь. - person llonesmiz; 04.09.2013