3 votes

Existe-t-il un moyen de faire correspondre le contenu d'un jeton de chaîne spirit::lex en tant que littéral dans une grammaire spirit::qi ?

J'écris un DSL et j'utilise un lexer Boost Spirit pour tokeniser mon entrée. Dans ma grammaire, je veux une règle similaire à celle-ci (où tok est le lexer) :

header_block =
    tok.name >> ':' >> tok.stringval > ';' >>
    tok.description >> ':' >> tok.stringval > ';'
  ;

Plutôt que de spécifier des mots réservés pour la langue (par exemple, "nom", "description") et de s'occuper de leur synchronisation entre le lexeur et la grammaire, je veux simplement tokeniser tout ce qui correspond à la langue [a-zA-Z_]\w* en tant que type de jeton unique (par ex. tok.symbol ), et laissez la grammaire s'en charger. Si je n'étaient pas en utilisant un lexer, je pourrais faire quelque chose comme ça :

stringval = lexeme['"' >> *(char_ - '"') >> '"'];
header_block =
    lit("name") >> ':' >> stringval > ';' >>
    lit("description") >> ':' >> stringval > ';'
  ;

Avec un lexer dans le mélange, je peux compiler la règle suivante, mais bien sûr, elle correspond à plus de choses que je ne le souhaite - elle ne se soucie pas des valeurs de symbole particulières "name" et "description" :

header_block =
    tok.symbol >> ':' >> tok.stringval > ';' >>
    tok.symbol >> ':' >> tok.stringval > ';'
  ;

Ce que je cherche, c'est quelque chose comme ça :

header_block =
    specific_symbol_matcher("name") >> ':' >> tok.stringval > ';' >>
    specific_symbol_matcher("description") >> ':' >> tok.stringval > ';'
  ;

Est-ce que Qi fournit quelque chose que je peux utiliser à la place de mon specific_symbol_matcher de la main, là ? Je préfère ne pas écrire mon propre matcher si je peux m'en approcher en utilisant les éléments fournis. Si je dois écrire mon propre matcher, quelqu'un peut-il me suggérer comment le faire ?

5voto

sehe Points 123151

Si le jeton expose une std::string, vous devriez pouvoir le faire :

 statement =
      ( tok.keyword [ qi::_pass = (_1 == "if")   ] >> if_stmt )
    | ( tok.keyword [ qi::_pass = (_1 == "while) ] >> while_stmt );

Si je vous ai bien compris, c'est, plus ou moins, ce que vous demandiez.

Pendant que vous y êtes, regardez qi::symbol<> et une application particulièrement astucieuse de celle-ci, connue sous le nom de Le truc de Nabialek .


Bonus material

Au cas où vous auriez du mal à faire fonctionner une grammaire existante avec un lexer, voici ce que j'ai fait avec la fonction calc_utree_ast.cpp exemple pour le faire fonctionner avec un lexer.

Il montre

  • comment consommer directement les attributs exposés
  • comment vous pouvez toujours effectuer une analyse syntaxique basée sur des caractères littéraux, à condition que ces caractères littéraux soient enregistrés comme des jetons (anonymes).
  • comment la gammar (simple) expression a été modifiée de façon minimale
  • comment le comportement de saut a été déplacé dans le lexer

    /////////////////////////////////////////////////////////////////////////////// // // Plain calculator example demonstrating the grammar. The parser is a // syntax checker only and does not do any semantic evaluation. // // [ JDG May 10, 2002 ] spirit1 // [ JDG March 4, 2007 ] spirit2 // [ HK November 30, 2010 ] spirit2/utree // [ SH July 17, 2012 ] use a lexer // ///////////////////////////////////////////////////////////////////////////////

    define BOOST_SPIRIT_DEBUG

    include <boost/config/warning_disable.hpp>

    include <boost/spirit/include/lex_lexertl.hpp>

    include <boost/spirit/include/support_utree.hpp>

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

    include <boost/spirit/include/phoenix_operator.hpp>

    include <boost/spirit/include/phoenix_function.hpp>

    include <iostream>

    include <string>

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

    // base iterator type typedef std::string::const_iterator BaseIteratorT;

    // token type typedef lex::lexertl::token<BaseIteratorT, boost::mpl::vector<char, uint32_t> > TokenT;

    // lexer type typedef lex::lexertl::actor_lexer<TokenT> LexerT;

    template <typename LexerT> struct Tokens: public lex::lexer<LexerT> { Tokens() { // literals uint_ = "[0-9]+"; space = " \t\r\n";

      // literal rules
      this->self += uint_;
      this->self += '+';
      this->self += '-';
      this->self += '*';
      this->self += '/';
      this->self += '(';
      this->self += ')';
    
      using lex::_pass;
      using lex::pass_flags;
      this->self += space [ _pass = pass_flags::pass_ignore ];

    }

    lex::token_def<uint32t> uint; lex::token_def<lex::omit> space; };

    namespace client { namespace qi = boost::spirit::qi; namespace ascii = boost::spirit::ascii; namespace spirit = boost::spirit;

    struct expr
    {
        template <typename T1, typename T2 = void>
        struct result { typedef void type; };
    
        expr(char op) : op(op) {}
    
        void operator()(spirit::utree& expr, spirit::utree const& rhs) const
        {
            spirit::utree lhs;
            lhs.swap(expr);
            expr.push_back(spirit::utf8_symbol_range_type(&op, &op+1));
            expr.push_back(lhs);
            expr.push_back(rhs);
        }
    
        char const op;
    };
    boost::phoenix::function<expr> const plus   = expr('+');
    boost::phoenix::function<expr> const minus  = expr('-');
    boost::phoenix::function<expr> const times  = expr('*');
    boost::phoenix::function<expr> const divide = expr('/');
    
    struct negate_expr
    {
        template <typename T1, typename T2 = void>
        struct result { typedef void type; };
    
        void operator()(spirit::utree& expr, spirit::utree const& rhs) const
        {
            char const op = '-';
            expr.clear();
            expr.push_back(spirit::utf8_symbol_range_type(&op, &op+1));
            expr.push_back(rhs);
        }
    };
    boost::phoenix::function<negate_expr> neg;
    
    ///////////////////////////////////////////////////////////////////////////////
    //  Our calculator grammar
    ///////////////////////////////////////////////////////////////////////////////
    template <typename Iterator>
    struct calculator : qi::grammar<Iterator, spirit::utree()>
    {
        template <typename Tokens>
        calculator(Tokens const& toks) : calculator::base_type(expression)
        {
            using qi::_val;
            using qi::_1;
    
            expression =
                term                            [_val = _1]
                >> *(   ('+' >> term            [plus(_val, _1)])
                    |   ('-' >> term            [minus(_val, _1)])
                    )
                ;
    
            term =
                factor                          [_val = _1]
                >> *(   ('*' >> factor          [times(_val, _1)])
                    |   ('/' >> factor          [divide(_val, _1)])
                    )
                ;
    
            factor =
                    toks.uint_                  [_val = _1]
                |   '(' >> expression           [_val = _1] >> ')'
                |   ('-' >> factor              [neg(_val, _1)])
                |   ('+' >> factor              [_val = _1])
                ;
    
            BOOST_SPIRIT_DEBUG_NODE(expression);
            BOOST_SPIRIT_DEBUG_NODE(term);
            BOOST_SPIRIT_DEBUG_NODE(factor);
        }
    
        qi::rule<Iterator, spirit::utree()> expression, term, factor;
    };

    }

    /////////////////////////////////////////////////////////////////////////////// // Main program /////////////////////////////////////////////////////////////////////////////// int main() { std::cout << "/////////////////////////////////////////////////////////\n\n"; std::cout << "Expression parser...\n\n"; std::cout << "/////////////////////////////////////////////////////////\n\n"; std::cout << "Type an expression...or [q or Q] to quit\n\n";

    using boost::spirit::utree;
    typedef std::string::const_iterator iterator_type;
    typedef Tokens<LexerT>::iterator_type IteratorT;
    typedef client::calculator<IteratorT> calculator;
    
    Tokens<LexerT> l;
    calculator calc(l); // Our grammar
    
    std::string str;
    while (std::getline(std::cin, str))
    {
        if (str.empty() || str[0] == 'q' || str[0] == 'Q')
            break;
    
        std::string::const_iterator iter = str.begin();
        std::string::const_iterator end  = str.end();
        utree ut;
        bool r = lex::tokenize_and_parse(iter, end, l, calc, ut);
    
        if (r && iter == end)
        {
            std::cout << "-------------------------\n";
            std::cout << "Parsing succeeded: " << ut << "\n";
            std::cout << "-------------------------\n";
        }
        else
        {
            std::string rest(iter, end);
            std::cout << "-------------------------\n";
            std::cout << "Parsing failed\n";
            std::cout << "stopped at: \"" << rest << "\"\n";
            std::cout << "-------------------------\n";
        }
    }
    
    std::cout << "Bye... :-) \n\n";
    return 0;

    }

Pour l'entrée

8*12312*(4+5)

Il imprime (sans info de débogage)

Parsing succeeded: ( * ( * 8 12312 ) ( + 4 5 ) )

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X