Есть ли способ сопоставить содержимое токена строки spirit::lex как литерал в грамматике spirit::qi

Я пишу DSL и использую лексер Boost Spirit для токенизации моего ввода. В моей грамматике мне нужно правило, подобное этому (где tok — лексер):

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

Вместо того, чтобы указывать зарезервированные слова для языка (например, «имя», «описание») и заниматься их синхронизацией между лексером и грамматикой, я хочу просто размечать все, что соответствует [a-zA-Z_]\w*, как один тип токена (например, tok.symbol), и пусть грамматика разберется. Если бы я не использовал лексический анализатор, я мог бы сделать что-то вроде этого:

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

С лексером в миксе я могу скомпилировать следующее правило, но, конечно, оно соответствует большему, чем мне нужно, оно не заботится о конкретных значениях символов «имя» и «описание»:

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

Я ищу что-то вроде этого:

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

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


person Tim Ruddick    schedule 16.07.2012    source источник
comment
Токены могут прекрасно раскрывать атрибуты. Если бы вы разместили свой класс лексера, мы могли бы показать вам, как это сделать.   -  person sehe    schedule 17.07.2012
comment
Мои токены раскрывают атрибуты. В данном случае tok.symbol равно lex::token_def<std::string>. Моя проблема заключается в сопоставлении этого строкового атрибута с использованием выражения в грамматике. Я могу опубликовать лексер, если это необходимо, но если это так, я бы предпочел составить небольшой тестовый пример.   -  person Tim Ruddick    schedule 17.07.2012


Ответы (1)


Если токен предоставляет std::string, вы должны просто сделать:

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

Если я вас правильно понял, это, более или менее, то, о чем вы спрашивали.

Пока вы этим занимаетесь, посмотрите qi::symbol<> и особенно изящное его применение, известное как Трюк с Набиалеком.


Бонусный материал

Если вы просто пытаетесь заставить существующую грамматику работать с лексером, вот что я только что сделал с calc_utree_ast.cpp, чтобы заставить его работать с лексером.

Это показывает

  • как вы можете напрямую использовать открытые атрибуты
  • как вы все еще можете анализировать на основе символьных литералов, если эти литералы символов зарегистрированы как (анонимные) токены
  • как (простая) гамма выражений была минимально изменена
  • как поведение пропуска было перенесено в лексер


///////////////////////////////////////////////////////////////////////////////
//
//  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<uint32_t> 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;
}

Для ввода

8*12312*(4+5)

Он печатает (без отладочной информации)

Parsing succeeded: ( * ( * 8 12312 ) ( + 4 5 ) )
person sehe    schedule 17.07.2012
comment
Спасибо @sehe! Я пытался избежать семантических действий и придерживаться чистой атрибутивной грамматики, но ваше предложение по-прежнему сохраняет основной смысл этого. А трюк с Набиалеком будет удобным инструментом в ящике с инструментами! - person Tim Ruddick; 17.07.2012
comment
@sehe, вы упоминаете здесь трюк с Набиалеком ... Я обнаружил, что очень сложно комбинировать qi::symbol с лексером. Я сделал это один раз, но это было несколько уродливо. Если у вас есть хороший совет, я начну новый вопрос :) - person Jeff Trull; 19.09.2013
comment
Сехе, ты мой новый герой. - person Liam M; 10.12.2014