Spirit-Qi: Como posso escrever um analisador não-terminal?

Quero escrever um analisador (como uma extensão qi) que pode ser usado viamy_parser(p1, p2, ...) Ondep1, p2, ... são expressões do analisador qi.

Na verdade, eu quero implementar umbest_matchanalisador que funciona como a alternativa qi, mas seleciona não a primeira regra correspondente, mas a regra que 'explica' a maior parte da entrada.

Dadas duas regrassimple_id = +(qi::alpha) ecomplex_id = simple_id >> *(qi::string("::") > simple_id) selecionariacomplex_id na entradawilly::anton. E não produziria atributos intermediários ao fazê-lo. Esses benefícios são pagos com o tempo de execução porque a análise de lookahead é necessária.

Na minha opinião, existem vários casos de uso para essa construção de analisador.benchmark(p1, ...) como um analisador de suporte para otimização, apenas um exemplo. Eu fornecerei isso, uma vez que eu saiba como fazer isso.

Este analisador seria um não terminal. Eu tentei (difícil), mas não consigo encontrar respostas para o meu problema no qi. Meu palpite é que os mecanismos C ++ são tão fortemente integrados ao c qi, que não há um ponto de entrada compreensível, pelo menos para mim.

É muito fácil implementar o que eu quero introduzir um operador. Anexo a solução atual abaixo. Compila conforme o esperado, mas está incompleto.

Um operador de qi obtém uma fusão :: vetor / sequência (o que for) pronta e age sobre ela. Parece não haver ofertas de bibliotecas para resolver meu problema. Atémake_nary_composite já espera que os argumentos sejam compilados paraElements.

Eu tentei muito, tudo falhou, então não quero aborrecê-lo com isso.

Uma solução alternativa que posso imaginar poderia ser fornecer um operador com estado,, o que tornariap1, ... a uma expressão legal de qi. Em seguida, implemente um unary_parserbest_match ou diretiva para processar essa expressão. o, obteria dois modos: obter o comprimento da entrada que o analisador atual (bem-sucedido) consome e, na verdade, analisar o selecionado da fase 1. O wrapper que executa esse analisador de vírgula primeiro no modo 1 e depois no modo 2. Pode ser feio, mas poderia trabalhar.

A implementação do operadorp1 |= p2 |= ... é o mais caro em termos de tempo de execução para n> 2. Eu ficaria feliz em contornar isso.

As despesas gerais mínimas (mas mesmo assim razoáveis) teriambest_match(p1, ...). Isso é sequer possível?

Por não ter muita experiência com boost :: fusion, adicionei algumas perguntas ao código (como fazer em relação à fusão).

Parece errado pressionar algo que na verdade é um analisador não terminal não obrigatório no esquema de análise unária. Mas, com meu entendimento ruim, parece ser a única maneira de fazê-lo. Eu apreciaria muito qualquer ajuda para me tirar disso.

Meu ambiente: Boost 1.61, MSVC 2015 Upd 2, alvo win32console.

PROGRESSO:

Eu acho que estou conseguindo lenta mas seguramente. Fiquei muito feliz em ver umerror_invalid_expression. Falha na compilação devido à seguinte expressão proto:

boost::proto::exprns_::expr<
    boost::proto::tagns_::tag::function,boost::proto::argsns_::list5<
        const boost::proto::exprns_::expr<
            boost::proto::tagns_::tag::terminal,boost::proto::argsns_::term<
                mxc::qitoo::tag::best_match
            >
        ,0> &
        ,const boost::spirit::qi::rule<
            iterator_type,std::string (void),
            boost::spirit::standard::space_type,
            boost::spirit::unused_type,boost::spirit::unused_type> &
        ,const boost::spirit::qi::rule<
            iterator_type,std::string (void),
            boost::spirit::standard::space_type,
            boost::spirit::unused_type,
            boost::spirit::unused_type> &
        , const boost::spirit::qi::rule<
            iterator_type,std::string (void),
            boost::spirit::standard::space_type,
            boost::spirit::unused_type,
            boost::spirit::unused_type> &
        ,const boost::spirit::qi::rule<
            iterator_type,std::string (void),
            boost::spirit::standard::space_type,
            boost::spirit::unused_type,
            boost::spirit::unused_type> &
        >
    ,5>

Na verdade, essa é a expressão que descreve meu uso no estilo de função do meu analisador best_match. Minha regra de teste parece

qitoo::best_match(id, qualified_id, id, qualified_id)

Ondeid equalified_id são as regras mencionadas acima. Tudo o que eu quero. O erro ocorre porque esta expressão não tem um membroparse. OK. Indo mais fundo, descobri que o meta-compilador do qi suporta analisadores unários, binários e nários (o que significa variável). Mas ele não suporta sintaxe de estilo de função. O Qi parece utilizar unário, binário e nário apenas para operadores. E embora o tipo nary seja suportado, é mais ou menos obsoleto, porque os operadores de qi são binários máx.

EDITAR PROGRESSO EDIT

#include <boost/spirit/home/qi.hpp>

namespace boost {
    namespace spirit
    {
        ///////////////////////////////////////////////////////////////////////////
        // Enablers
        ///////////////////////////////////////////////////////////////////////////
        template <>
        struct use_operator<qi::domain, proto::tag::bitwise_or_assign> // enables |=
            : mpl::true_ {};

        template <>
        struct flatten_tree<qi::domain, proto::tag::bitwise_or_assign> // flattens |=
            : mpl::true_ {};
    }
}

namespace mxc {
    namespace qitoo {

        namespace spirit = boost::spirit;
        namespace qi = spirit::qi;
        namespace fusion = boost::fusion;

        template <typename Elements>
        struct best_match_parser : qi::nary_parser<mxc::qitoo::best_match_parser<Elements>>
        {
            // This one does a lookahead parse of each qi expression to find out which rule matches 
            // most of the input 
            template <typename Iterator, typename Context, typename Skipper>
            struct best_function
            {
                best_function(Iterator& first, Iterator const& last, Context& context,
                    Skipper const& skipper, int& best, int& idx, int& size)
                    : first(first), last(last), context(context), skipper(skipper), best(best),
                    idx(idx), size(size) {};

                template <typename Component>
                void operator()(Component& component) const
                {
                    Iterator f = first;
                    if (component.parse(f, last, context, skipper, qi::unused)) {
                        // please have a look on how I transport best, idx and size
                        // into the parser context
                        //
                        // I guess, this is a nasty hack and could be done better
                        // Any advice would be highliy appreciated

                        int l = f - first;
                        if (l > size) {
                            size = l;
                            best = idx++;
                        }
                        idx++;
                    }
                }

            private:
                int& best;
                int& idx;
                int& size;

                Iterator&       first;
                Iterator const& last;
                Context&        context;
                Skipper const&  skipper;
            };

            template <typename Context, typename Iterator>
            struct attribute
            {
                // Put all the element attributes in a tuple
                typedef typename spirit::traits::build_attribute_sequence <
                    Elements, Context, spirit::traits::alternative_attribute_transform
                    , Iterator, qi::domain
                >::type all_attributes;

                // Ok, now make a variant over the attribute sequence. Note that
                // build_variant makes sure that 1) all attributes in the variant
                // are unique 2) puts the unused attribute, if there is any, to
                // the front and 3) collapses single element variants, variant<T>
                // to T.
                typedef typename
                    spirit::traits::build_variant<all_attributes>::type
                    type;
            };

            best_match_parser(Elements const& elements_) : elements(elements_), size(0), idx(0), best(-1) {}


            template <typename Iterator, typename Context, typename Skipper, typename Attribute>
            bool parse(Iterator& first, Iterator const& last
                , Context& context, Skipper const& skipper
                , Attribute& attr_) const
            {
                best_function<Iterator, Context, Skipper>  f(first, last, context, skipper, best, idx, size);

                // find out which parser matches most of the input
                fusion::for_each(elements, f);

                // best >= 0 if at least one parser was successful
                if (best >= 0) {
                    // now that I have the best parser identified, how do I access it?
                    // I understand that the next line can not work, but I'm looking for something like that

                    // --> auto v = fusion::at<boost::mpl::int_<best>>(elements);
                };

                return false;
            }

            template <typename Context>
            qi::info what(Context& context) const
            {
                qi::info result("best_match");
                fusion::for_each(elements,
                    spirit::detail::what_function<Context>(result, context));

                return result;
            }

            Elements elements;
            mutable int  best;
            mutable int  idx;
            mutable int  size;

        };
    }
}

namespace boost {
    namespace spirit {
        namespace qi {

            ///////////////////////////////////////////////////////////////////////////
            // Parser generators: make_xxx function (objects)
            ///////////////////////////////////////////////////////////////////////////
            template <typename Elements, typename Modifiers>
            struct make_composite<proto::tag::bitwise_or_assign, Elements, Modifiers>
                : make_nary_composite < Elements, mxc::qitoo::best_match_parser >
            {};
        }

        namespace traits {
            ///////////////////////////////////////////////////////////////////////////
            template <typename Elements>
            struct has_semantic_action<mxc::qitoo::best_match_parser<Elements> >
                : nary_has_semantic_action<Elements> {};

            ///////////////////////////////////////////////////////////////////////////
            template <typename Elements, typename Attribute, typename Context
                , typename Iterator>
                struct handles_container<mxc::qitoo::best_match_parser<Elements>, Attribute, Context
                , Iterator>
                : nary_handles_container<Elements, Attribute, Context, Iterator> {};
        }
    }
}
namespace qi = boost::spirit::qi;
namespace qitoo = mxc::qitoo;

using iterator_type = std::string::const_iterator;
using result_type = std::string;

template<typename Parser>
void parse(const std::string message, const std::string& input, const std::string& rule, const Parser& parser) {
    iterator_type iter = input.begin(), end = input.end();

    std::vector<result_type> parsed_result;

    std::cout << "-------------------------\n";
    std::cout << message << "\n";
    std::cout << "Rule: " << rule << std::endl;
    std::cout << "Parsing: \"" << input << "\"\n";

    bool result = qi::phrase_parse(iter, end, parser, qi::space, parsed_result);
    if (result)
    {
        std::cout << "Parser succeeded.\n";
        std::cout << "Parsed " << parsed_result.size() << " elements:";
        for (const auto& str : parsed_result)
            std::cout << "[" << str << "]";
        std::cout << std::endl;
    }
    else
    {
        std::cout << "Parser failed" << std::endl;
    }
    if (iter == end) {
        std::cout << "EOI reached." << std::endl;
    }
    else {
        std::cout << "EOI not reached. Unparsed: \"" << std::string(iter, end) << "\"" << std::endl;
    }
    std::cout << "-------------------------\n";

}


int main()
{
    namespace qi = boost::spirit::qi;

    qi::rule < iterator_type, std::string(), qi::space_type>
        id = (qi::alpha | qi::char_('_')) >> *(qi::alnum | qi::char_('_'));

    qi::rule < iterator_type, std::string(), qi::space_type>
        qualified_id = id >> *(qi::string("::") > id);


    namespace qitoo = mxc::qitoo;
    namespace qi = boost::spirit::qi;

    parse("best match operator, select second rule"
        , "willy::anton::lutz"
        , "id |= qualified_id"
        , id |= qualified_id);
}

questionAnswers(1)

yourAnswerToTheQuestion