Может ли Spirit X3 работать с BOOST_FUSION_ADAPT_ADT? - PullRequest
0 голосов
/ 14 мая 2018

Измените мои коды с QI на X3 и получите ошибку компиляции с BOOST_FUSION_ADAPT_ADT. Я пробовал повысить 1.64 и 1.67, ни один из них не работает. Я изменил пример Spirit X3 rexpr_min, добавив getter и setter к struct rexpr, изменив BOOST_FUSION_ADAPT_STRUCT на BOOST_FUSION_ADAPT_ADT, и также скомпилировал его.

Enviroment:

  • Ubuntu 16.04

  • G ++ 5.4, с флагом -std=c++17

  • повышение 1,67

Сообщение об ошибке:

boost/spirit/home/x3/core/detail/parse_into_container.hpp:142:35: error: invalid initialization of non-const reference of type ‘boost::fusion::extension::adt_attribute_proxy<client::ast::rexpr, 0, false>&’ from an rvalue of type ‘boost::fusion::extension::deref_impl<boost::fusion::struct_iterator_tag>::apply<boost::fusion::basic_iterator<boost::fusion::struct_iterator_tag, boost::fusion::random_access_traversal_tag, client::ast::rexpr, 0> >::type {aka boost::fusion::extension::adt_attribute_proxy<client::ast::rexpr, 0, false>}’
             return call_synthesize(parser, first, last, context, rcontext,

Я предполагаю, что fusion::front(attr) возвращает константную ссылку, а call_synthesize хочет неконстантную ссылку (в boost_1_64_0/boost/spirit/home/x3/core/detail/parse_into_container.hpp, строка 146). Но я не знаю, что делать.

Я погуглил и обнаружил некоторую регрессию QI, и они исправлены в последней версии. Но с X3 нет информации.

Оригинальный код Пример духа X3 rexpr_min, И моя модификация:

/*=============================================================================
    Copyright (c) 2001-2015 Joel de Guzman

    Distributed under the Boost Software License, Version 1.0. (See accompanying
    file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
=============================================================================*/
///////////////////////////////////////////////////////////////////////////////
//
//  A simple parser for X3 intended as a minimal starting point.
//  'rexpr' is a parser for a language resembling a minimal subset
//  of json, but limited to a dictionary (composed of key=value pairs)
//  where the value can itself be a string or a recursive dictionary.
//
//  Example:
//
//  {
//      "color" = "blue"
//      "size" = "29 cm."
//      "position" = {
//          "x" = "123"
//          "y" = "456"
//      }
//  }
//
///////////////////////////////////////////////////////////////////////////////

#include <boost/config/warning_disable.hpp>
#include <boost/spirit/home/x3.hpp>
#include <boost/spirit/home/x3/support/ast/variant.hpp>
#include <boost/fusion/include/adapt_adt.hpp>
#include <boost/fusion/include/std_pair.hpp>
#include <boost/fusion/include/io.hpp>

#include <iostream>
#include <fstream>
#include <string>
#include <map>

///////////////////////////////////////////////////////////////////////////////
//  Our AST
///////////////////////////////////////////////////////////////////////////////
namespace client { namespace ast
{
    namespace fusion = boost::fusion;
    namespace x3 = boost::spirit::x3;

    struct rexpr;

    struct rexpr_value : x3::variant<
            std::string
          , x3::forward_ast<rexpr>
        >
    {
        using base_type::base_type;
        using base_type::operator=;
    };

    typedef std::map<std::string, rexpr_value> rexpr_map;
    typedef std::pair<std::string, rexpr_value> rexpr_key_value;

    struct rexpr
    {
        rexpr_map i_entries;

        const rexpr_map& entries() const { return i_entries; }
        void entries(const rexpr_map& ent) { i_entries = ent; }
    };
}}

// We need to tell fusion about our rexpr struct
// to make it a first-class fusion citizen
BOOST_FUSION_ADAPT_ADT(client::ast::rexpr,
    (obj.entries(), obj.entries(val))
)

///////////////////////////////////////////////////////////////////////////////
//  AST processing
///////////////////////////////////////////////////////////////////////////////
namespace client { namespace ast
{
    ///////////////////////////////////////////////////////////////////////////
    //  Print out the rexpr tree
    ///////////////////////////////////////////////////////////////////////////
    int const tabsize = 4;

    struct rexpr_printer
    {
        typedef void result_type;

        rexpr_printer(int indent = 0)
          : indent(indent) {}

        void operator()(rexpr const& ast) const
        {
            std::cout << '{' << std::endl;
            for (auto const& entry : ast.entries())
            {
                tab(indent+tabsize);
                std::cout << '"' << entry.first << "\" = ";
                boost::apply_visitor(rexpr_printer(indent+tabsize), entry.second);
            }
            tab(indent);
            std::cout << '}' << std::endl;
        }

        void operator()(std::string const& text) const
        {
            std::cout << '"' << text << '"' << std::endl;
        }

        void tab(int spaces) const
        {
            for (int i = 0; i < spaces; ++i)
                std::cout << ' ';
        }

        int indent;
    };
}}

///////////////////////////////////////////////////////////////////////////////
//  Our rexpr grammar
///////////////////////////////////////////////////////////////////////////////
namespace client { namespace parser
{
    namespace x3 = boost::spirit::x3;
    namespace ascii = boost::spirit::x3::ascii;

    using x3::lit;
    using x3::lexeme;

    using ascii::char_;
    using ascii::string;

    x3::rule<class rexpr_value, ast::rexpr_value>
        rexpr_value = "rexpr_value";

    x3::rule<class rexpr, ast::rexpr>
        rexpr = "rexpr";

    x3::rule<class rexpr_key_value, ast::rexpr_key_value>
        rexpr_key_value = "rexpr_key_value";

    auto const quoted_string =
        lexeme['"' >> *(char_ - '"') >> '"'];

    auto const rexpr_value_def =
        quoted_string | rexpr;

    auto const rexpr_key_value_def =
        quoted_string >> '=' >> rexpr_value;

    auto const rexpr_def =
        '{' >> *rexpr_key_value >> '}';

    BOOST_SPIRIT_DEFINE(rexpr_value, rexpr, rexpr_key_value);
}}

///////////////////////////////////////////////////////////////////////////////
//  Main program
///////////////////////////////////////////////////////////////////////////////
int main(int argc, char **argv)
{
    char const* filename;
    if (argc > 1)
    {
        filename = argv[1];
    }
    else
    {
        std::cerr << "Error: No input file provided." << std::endl;
        return 1;
    }

    std::ifstream in(filename, std::ios_base::in);

    if (!in)
    {
        std::cerr << "Error: Could not open input file: "
            << filename << std::endl;
        return 1;
    }

    std::string storage; // We will read the contents here.
    in.unsetf(std::ios::skipws); // No white space skipping!
    std::copy(
        std::istream_iterator<char>(in),
        std::istream_iterator<char>(),
        std::back_inserter(storage));

    using client::parser::rexpr; // Our grammar
    client::ast::rexpr ast; // Our tree

    using boost::spirit::x3::ascii::space;
    std::string::const_iterator iter = storage.begin();
    std::string::const_iterator end = storage.end();
    bool r = phrase_parse(iter, end, rexpr, space, ast);

    if (r && iter == end)
    {
        std::cout << "-------------------------\n";
        std::cout << "Parsing succeeded\n";
        std::cout << "-------------------------\n";
        client::ast::rexpr_printer printer;
        printer(ast);
        return 0;
    }
    else
    {
        std::string::const_iterator some = iter+30;
        std::string context(iter, (some>end)?end:some);
        std::cout << "-------------------------\n";
        std::cout << "Parsing failed\n";
        std::cout << "stopped at: \": " << context << "...\"\n";
        std::cout << "-------------------------\n";
        return 1;
    }
}

1 Ответ

0 голосов
/ 15 мая 2018

Как я предупреждал людей раньше ¹ вы раздвигаете границы прямо на пересечении вещей, которые часто ломают механизмы Духа:

  1. одноэлементные последовательности слияния
  2. АДТ-адаптация в целом
  3. Постоянные ошибки с исправленным ADT в разработке ( после 1.67.0 релиза)

1. Одноэлементная головоломка

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

Давайте сделаем шаг в сторону, добавив фиктивное поле:

struct rexpr
{
    rexpr_map i_entries;

    const rexpr_map& entries() const { return i_entries; }
    rexpr_map& entries() { return i_entries; }

    void entries(const rexpr_map& ent) { i_entries = ent; }

    int i_dummy;
    int dummy() const { return i_dummy; }
    void dummy(int i) { i_dummy = i; }
};

// ... later:
BOOST_FUSION_ADAPT_ADT(client::ast::rexpr,
    (obj.entries(), obj.entries(val))
    (obj.dummy(), obj.dummy(val))
)

// ... even later:
auto const rexpr_def =
    '{' >> *rexpr_key_value >> '}' >> x3::attr(42);

2. ADT Proxy

Аппарат атрибута категории Spirit обнаруживает свойство entries в качестве атрибута контейнера (is_container<...>{} оценивается как true).

Однако требуемые свойства контейнера отсутствуют.

Более того, из-за ограниченного интерфейса, предоставляемого прокси-серверами ADT, значения свойств могут только заменять полную продажу, а это означает, что мы можем реализовать только очень неоптимальную версию:

namespace boost { namespace spirit { namespace x3 { namespace traits {

    template <typename T, auto... Other>
    struct container_value<boost::fusion::extension::adt_attribute_proxy<T, Other...> > 
        : container_value<typename boost::fusion::extension::adt_attribute_proxy<T, Other...>::type>
    { };

    template <typename T, auto... Other>
    struct push_back_container<boost::fusion::extension::adt_attribute_proxy<T, Other...> > 
    {
        using underlying_type = typename boost::fusion::extension::adt_attribute_proxy<T, Other...>::type;

        template <typename X, typename Y>
            static bool call(X& proxy, Y&& v) {
                auto u = proxy.get();
                bool b = push_back_container<underlying_type>::call(u, std::forward<Y>(v));
                proxy = u;
                return b;
            }
    };

} } } }

3. Сюрприз: старые ошибки исправлены после 1.67.0

Вы требуете коммитов:

commit ae78e1ec2431517a8b0580099aeba8f9122d8abb
Author: Nikita Kniazev <nok.raven@gmail.com>
Date:   Thu Mar 15 17:33:36 2018 +0300

    X3: sequence: Fixed reference to temporary bug

commit e7f31017ec7c0b5584d12ec1b718d8c415b26fa1
Author: Nikita Kniazev <nok.raven@gmail.com>
Date:   Wed Mar 14 18:54:35 2018 +0300

    Qi: Fixed ADT support by permutation and sequence_or operator

    This is follow-up to 0f2b3c49ce55a41a7d22cc5533e0f4ba59e491ae

Они более поздние, чем 1.67.0, и в настоящее время находятся в ветке develop. Они (частично) исправляют старую проблему: https://github.com/boostorg/spirit/pull/153#issuecomment-152879056. На текущее поведение также может повлиять коммит

commit a0df3c098ff4e42c0958796c4f47d4d72a20c164
Merge: f73b121 fac9dfa
Author: Nikita Kniazev <nok.raven@gmail.com>
Date:   Thu Mar 1 13:44:27 2018 +0300

    Merge pull request #370 from Kojoley/x3-pass-container-attribute-through-sequence

    X3: Pass container attribute through sequence

Трудно оценить, является ли воздействие положительным или отрицательным в этом ... турбулентном ландшафте.

Демо

Достаточно сказать, что если вы

  1. скомпилировать против ae78e1ec243151 или новее (develop)
  2. Примените оба обходные пути, описанные выше

затем Я вижу ожидаемый результат:

-------------------------
Parsing succeeded
-------------------------
{
    "color" = "blue"
    "position" = {
        "x" = "123"
        "y" = "456"
    }
    "size" = "29 cm."
}

(на основе ввода libs/spirit/example/x3/rexpr/rexpr_examples/a.rexpr).

Подведение итогов

Надеюсь, вы не думаете, что это "хорошо". Пожалуйста, рассмотрите вопрос о проблеме в списке рассылки / github. Также примите это во внимание:


¹ не говоря уже о моей неприязни к нему в большинстве случаев: Использование духа для разбора на классы?

² Проблема распространения атрибута Spirit Qi с одноэлементной структурой , X3, что такое attr_gen? , boost :: spirit :: x3 правила совместимости атрибутов, интуиция или код

...