Spirit X3, два правила не компилируются после объединения в одно

В настоящее время я изучаю, как использовать x3. Как говорится в заголовке, мне удалось создать грамматику с несколькими простыми правилами, но при объединении двух из этих правил в одно код больше не компилируется. Вот код для части AST:

namespace x3 = boost::spirit::x3;

struct Expression;

struct FunctionExpression {
    std::string functionName;
    std::vector<x3::forward_ast<Expression>> inputs;
};

struct Expression: x3::variant<int, double, bool, FunctionExpression> {
    using base_type::base_type;
    using base_type::operator=;
};

Правила, которые я создал для синтаксического анализа, отформатированы как {rangeMin, rangeMax}:

rule<struct basic_exp_class, ast::Expression> const
    basic_exp = "basic_exp";
rule<struct exp_pair_class, std::vector<ast::Expression>> const 
    exp_pair = "exp_pair";
rule<struct range_class, ast::FunctionExpression> const 
    range = "range";

auto const basic_exp_def = double_ | int_ | bool_;
auto const exp_pair_def = basic_expr >> ',' >> basic_expr;
auto const range_def = attr("computeRange") >> '{' >> exp_pair >> '}';

BOOST_SPIRIT_DEFINE(basic_expr, exp_pair_def, range_def);

Этот код компилируется нормально. Однако, если я попытаюсь встроить правило exp_pair в правило range_def, вот так:

rule<struct basic_exp_class, ast::Expression> const
    basic_exp = "basic_exp";
rule<struct range_class, ast::FunctionExpression> const 
    range = "range";

auto const basic_exp_def = double_ | int_ | bool_;
auto const range_def = attr("computeRange") >> '{' >> (
    basic_exp >> ',' >> basic_exp
) >> '}';

BOOST_SPIRIT_DEFINE(basic_expr, range_def);

Код не компилируется с очень длинной ошибкой шаблона, заканчивающейся строкой:

spirit/include/boost/spirit/home/x3/operator/detail/sequence.hpp:149:9: error: static assertion failed: Size of the passed attribute is less than expected.
     static_assert(
     ^~~~~~~~~~~~~

Файл заголовка также включает этот комментарий над static_assert:

// If you got an error here, then you are trying to pass
// a fusion sequence with the wrong number of elements
// as that expected by the (sequence) parser.

Но я не понимаю, почему код должен дать сбой. Согласно составному , встроенная часть в скобках должна иметь атрибут типа vector<ast::Expression>, чтобы общее правило имело тип tuple<string, vector<ast::Expression>, чтобы оно было совместимо с ast::FunctionExpression. Та же логика применяется к более подробной версии с тремя правилами, с той лишь разницей, что я специально объявил правило для внутренней части и специально указал, что его атрибут должен иметь тип vector<ast::Expression>.


person CompileYourCake    schedule 22.11.2018    source источник
comment
Вероятно, он видит встроенное правило как два выражения вместо вектора «Ожидается», в другом ответе есть помощник as, чтобы вы могли указать тип. stackoverflow.com/questions/49932608/ не могу попробовать сейчас, поэтому не знаю, работает ли он.   -  person Bob Bills    schedule 22.11.2018
comment
@BobBills Спасибо, я собирался опубликовать ту же идею в качестве комментария :)   -  person sehe    schedule 22.11.2018
comment
@BobBills это именно то, что я искал. Включив лямбду as, я могу изменить правило на auto const range_def = attr("computeRange") >> '{' >> as<std::vector<ast::Expression>>(basic_exp >> ',' >> basic_exp) >> '}';, и оно успешно скомпилируется. Не могли бы вы изложить свое предложение в виде полного ответа, чтобы я мог его принять?   -  person CompileYourCake    schedule 22.11.2018
comment
Почему вы хотите встроить его? Вот для чего правила. Если вы передадите кортеж (FunctionExpression) анализатору последовательности (... >> ...), Spirit не будет творить никакой магии (не будет пытаться свернуть последовательность).   -  person Nikita Kniazev    schedule 23.11.2018


Ответы (1)


Spirit x3, вероятно, видит результат встроенного правила как два отдельных ast::Expression вместо std::vector<ast::Expression>, требуемых структурой ast::FunctionExpression.

Чтобы решить эту проблему, мы можем использовать вспомогательную лямбду as, как указано в другом ответе, чтобы указать тип возвращаемого значения подправила.

И измененный range_def станет:

auto const range_def = attr("computeRange") >> '{' >> as<std::vector<ast::Expression>>(basic_exp >> ',' >> basic_exp) >> '}';
person Bob Bills    schedule 23.11.2018