boost :: spirit ленивый парсер с аргументами? - PullRequest
3 голосов
/ 20 мая 2019

Я не могу найти ничего по этому поводу, кроме зловещих намеков на то, что это может быть совершенно невозможно, но я не хочу просто верить этому, потому что ленивые парсеры в таком случае кажутся бесполезными. То, что я хочу сделать, это выбрать парсер во время разбора, в зависимости от результата какого-то предыдущего нетерминала. По сути это сводится к:

static rule<Constant *(Scope &)> &get_constant_parser(Typename type);

rule<Constant *(Scope &, Typename)> constant {
    lazy(phoenix::bind(&get_constant_parser, _r2))(_r1)
};

Таким образом, get_constant_parser возвращает синтаксический анализатор, соответствующий данному имени типа, однако , что синтаксическому анализатору требуется аргумент типа Scope &. Так что интуитивно я бы записал это, как указано выше, добавив аргумент в ленивый парсер. Однако это дает мне недопустимое выражение:

/usr/include/boost/spirit/home/qi/nonterminal/rule.hpp:177:13: error: static assertion failed: error_invalid_expression
             BOOST_SPIRIT_ASSERT_MATCH(qi::domain, Expr);
             ^~~~~~~~~~~~~~~~~~~~~~~~~

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

Извините, это неправильный MWE, сейчас я надеюсь, что кто-то уже делал это и знает ответ. Пожалуйста, дайте мне знать, если вы хотите активно расследовать и вам нужны MWE; -)

Ответы [ 2 ]

2 голосов
/ 20 мая 2019

Попытка совершить такое колдовство без понимания того, как на самом деле общаются Феникс и Дух, чрезвычайно трудна. Давайте попробуем покопаться в этом:

  1. Параметризация правила происходит через operator() из qi::rule, который создает экземпляр qi::parameterized_nonterminal синтаксического анализатора.
  2. Ленивый анализ парсера выполняется следующим образом: qi::lazy переносит phoenix::actor в proto::terminal, который позднее (метакомпилятором) преобразуется в qi::lazy_parser / qi::lazy_directive.

Итак, в вашем примере актер Phoenix преобразуется в Proto-терминал, а затем оператор вызова создает Proto-выражение, которое мета-компилятор Spirit не понимает.

Я предположил, что это должно быть lazy(phoenix::bind(&get_constant_parser, _r2)(_r1)), потому что вам нужно назвать это operator() для действующего правила, но Феникс не позволяет вам вызывать operator(), как это.

Что должно работать: lazy(phoenix::bind(phoenix::bind(&get_constant_parser, _r2), _r1)).


Давным-давно я попробовал что-то вроде того, что вы делаете, и тоже не получилось. Я также погуглил те темы, которые говорят, что это невозможно, и остановился на этом. Но ваш вопрос вызвал у меня интерес, и после коротких проб и ошибок (т. Е. Почесывая голову и копаясь в источниках Духа) я пришел к этому доказательству концепции:

#include <boost/spirit/include/qi.hpp>
#include <boost/spirit/include/phoenix.hpp>
#include <boost/spirit/include/support_argument.hpp>
#include <iostream>

namespace qi = boost::spirit::qi;
namespace phx = boost::phoenix;

int main()
{
    using passed_rule_t = qi::rule<char const*, int(int)>;
    qi::rule<char const*, int(int, passed_rule_t const&)> lazyinvoke
        = qi::lazy(phx::bind(qi::labels::_r2,   // binding is a way to call `operator()` lazily
                             qi::labels::_r1)); // non-lazy equivalent of this is `_r2(_r1)`
    int v;
    char const* s = nullptr;
    passed_rule_t inout = qi::attr(qi::labels::_r1);
    if (qi::parse(s, s, lazyinvoke(phx::val(123), phx::cref(inout)), v))
        std::cout << "OK: " << v << "\n";
    else
        std::cout << "Failed\n";
}

https://wandbox.org/permlink/m40DpeMikKRYyvH0

1 голос
/ 20 мая 2019

Мне жаль говорить, что я думаю, что это действительно невозможно [*]

Однако еще не все потеряно. Если вы можете использовать qi::locals вместо передачи «аргументов» ( унаследованных атрибутов ), тогда все будет в порядке.

В зависимости от вашей фактической цели, хотя вы можете обойти необходимость "lazy(arg)", позвонив non_lazy(symbols*). Моя догадка подсказала, что вы пытаетесь выполнить поиск в зависимости от пространства имен / домена. Смотрите, например


[*]

...