【问题标题】:Boost spirit lex write token value back to input streamBoost Spirit lex 将令牌值写回输入流
【发布时间】:2012-05-16 02:37:05
【问题描述】:

我想知道 boost::spirit::lex 中是否有办法将令牌值写回输入流(可能在编辑后)并再次重新扫描。我基本上在寻找的是 Flex 中 unput() 提供的功能。

谢谢!

【问题讨论】:

  • 你想达到什么目的?我的意思是,在什么情况下需要使用 unput()?如果您展示一个示例,我可能会向您展示我是如何做到的(可能使用 Lexer 状态)
  • 基本上,我需要词法分析器匹配一个标识符,直接后跟一个开放的括号“abc(”作为一个标记,并将其放回输入流中,括号位于字符串的开头像“(abc”)。下一步是让词法分析器再次扫描它,但作为两个单独的标记(一个paren标记,然后是一个标识符标记)。
  • 好的,我对此发表了我的看法,如果我对目标的理解有误,请告诉我。

标签: c++ boost lex boost-spirit boost-spirit-lex


【解决方案1】:

听起来你只是想接受不同顺序但含义相同的令牌。

事不宜迟,这里有一个完整的示例,展示了如何完成此操作,无论输入顺序如何都会公开标识符。输出:

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);
}

【讨论】:

  • 谢谢。不幸的是,在我的情况下,将问题导出到解析器不是一个好的选择,原因有两个:1)我需要区分开放括号直接跟随标识符的情况与它们被任何空白字符分隔的情况(其中情况下,解析器应该匹配不同的规则)。 2)还有许多其他关键字需要以相同的方式处理(不仅仅是标识符),因此这将使解析这些关键字所需的产生式数量增加一倍(一个当括号在关键字之前,一个在它之后)。
  • @HaithamGad 让我这么说:写一个质量问题很难。 就是为什么。我会给你的新约束一些想法
  • 是的,很抱歉 :) 感谢您的帮助!
【解决方案2】:

我最终实现了自己的 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() 为第一个匹配的标记调用的情况。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2017-01-03
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-02-15
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多