Boost Spirit lex записывает значение токена обратно во входной поток

Мне интересно, есть ли в boost::spirit::lex способ записать значение токена обратно во входной поток (возможно, после редактирования) и снова выполнить повторное сканирование. То, что я в основном ищу, - это функциональность, подобная той, которую предлагает unput() во Flex.

Спасибо!


person Haitham Gad    schedule 16.05.2012    source источник
comment
Чего вы пытаетесь достичь? Я имею в виду, в каком контексте вам нужно использовать unput()? Если вы покажете пример, я смогу показать вам, как я это сделаю (возможно, используя состояния Lexer)   -  person sehe    schedule 16.05.2012
comment
По сути, мне нужно, чтобы лексер соответствовал идентификатору, за которым непосредственно следует открытый парен abc( как один токен, и помещал его обратно во входной поток с парнем, находящимся в начале строки, например (abc . Следующий шаг будет для лексер, чтобы просмотреть его снова, но как два отдельных токена (токен парен, а затем токен идентификатора).   -  person Haitham Gad    schedule 16.05.2012
comment
Хорошо, я опубликовал свое мнение по этому поводу, дайте мне знать, если я неправильно понял цель.   -  person sehe    schedule 17.05.2012


Ответы (2)


Похоже, вы просто хотите принимать токены в разном порядке, но с тем же значением.

Без дальнейших церемоний, вот полный пример, который показывает, как это можно сделать, раскрывая идентификатор независимо от порядка ввода. Выход:

Input 'abc(' Parsed as: '(abc'
Input '(abc' Parsed as: '(abc'

Код

#include <boost/spirit/include/qi.hpp>
#include <boost/spirit/include/lex_lexertl.hpp>
#include <iostream>
#include <string>

using namespace boost::spirit;

///// LEXER
template <typename Lexer>
struct tokens : lex::lexer<Lexer>
{
    tokens()
    {
        identifier = "[a-zA-Z][a-zA-Z0-9]*";
        paren_open = '(';

        this->self.add
            (identifier)
            (paren_open)
            ;
    }

    lex::token_def<std::string> identifier;
    lex::token_def<lex::omit> paren_open;
};

///// GRAMMAR
template <typename Iterator>
struct grammar : qi::grammar<Iterator, std::string()>
{
    template <typename TokenDef>
        grammar(TokenDef const& tok) : grammar::base_type(ident_w_parenopen)
    {
        ident_w_parenopen = 
              (tok.identifier >> tok.paren_open)
            | (tok.paren_open >> tok.identifier) 
            ;
    }
  private:
    qi::rule<Iterator, std::string()> ident_w_parenopen;
};

///// DEMONSTRATION
typedef std::string::const_iterator It;

template <typename T, typename G>
void DoTest(std::string const& input, T const& tokens, G const& g)
{
    It first(input.begin()), last(input.end());

    std::string parsed;
    bool r = lex::tokenize_and_parse(first, last, tokens, g, parsed);

    if (r) {
        std::cout << "Input '" << input << "' Parsed as: '(" << parsed << "'\n";
    }
    else {
        std::string rest(first, last);
        std::cerr << "Parsing '" << input << "' failed\n" << "stopped at: \"" << rest << "\"\n";
    }
}

int main(int argc, char* argv[])
{
    typedef lex::lexertl::token<It, boost::mpl::vector<std::string> > token_type;
    typedef lex::lexertl::lexer<token_type> lexer_type;
    typedef tokens<lexer_type>::iterator_type iterator_type;

    tokens<lexer_type> tokens;
    grammar<iterator_type> g (tokens);

    DoTest("abc(", tokens, g);
    DoTest("(abc", tokens, g);
}
person sehe    schedule 16.05.2012
comment
Спасибо сехе. К сожалению, экспортировать проблему в парсер в моем случае не лучший вариант по двум причинам: 1) Мне нужно отличать случай, когда открывающая скобка следует непосредственно за идентификатором, от случая, когда они разделены каким-либо пробельным символом (в котором случае синтаксический анализатор должен соответствовать другому правилу). 2) Есть много других ключевых слов, которые необходимо обрабатывать таким же образом (не только идентификаторы), поэтому это удвоит количество производств, необходимых для анализа этих ключевых слов (один, когда скобка предшествует ключевому слову, и один, когда он следует за ним). - person Haitham Gad; 17.05.2012
comment
@HaithamGad Позвольте мне сказать следующее: написать качественный вопрос сложно. Вот вот почему. Я подумаю о ваших новых ограничениях - person sehe; 17.05.2012
comment
Да, извините за это :) и спасибо за вашу помощь! - person Haitham Gad; 17.05.2012

В итоге я реализовал свою собственную функцию unput() следующим образом:

   struct unputImpl
   {
      template <typename Iter1T, typename Iter2T, typename StrT>
      struct result {
         typedef void type;
      };

      template <typename Iter1T, typename Iter2T, typename StrT>
      typename result<Iter1T, Iter2T, StrT>::type operator()(Iter1T& start, Iter2T& end, StrT str) const {
         start -= (str.length() - std::distance(start, end));
         std::copy(str.begin(), str.end(), start);
         end = start;
      }
   };

   phoenix::function<unputImpl> const unput = unputImpl();

Затем это можно использовать как:

   this->self += lex::token_def<lex::omit>("{SYMBOL}\\(")
        [
           unput(_start, _end, "(" + construct<string>(_start, _end - 1) + " "),
           _pass = lex::pass_flags::pass_ignore
        ];

Если длина не введенной строки больше, чем длина совпадающего токена, она переопределит некоторые из ранее проанализированных входных данных. Вам нужно позаботиться о том, чтобы в самом начале входной строки было достаточно пустого места, чтобы обработать случай, когда unput() вызывается для самого первого совпадающего токена.

person Haitham Gad    schedule 28.06.2012