c++ - 如何让 spirit 的 qi::as_string 与 repeat 一起工作?

标签 c++ c++11 boost-spirit boost-spirit-qi boost-spirit-lex

出于某些奇怪的原因,我无法让 qi::as_string[]repeat()[] 一起工作。

解析 std::string str = { "{ +100S+++ ;\n }"};,我得到以下 OUTPUT

PLUS OR MINUS+
THREE PLUS OR MINUS
PARSED FINE
-------------------------
Parsing succeeded
-------------------------

这表明解析没问题,捕获了第一个+,但没有捕获到随后的三个+++

注意 我只是想让three_plus_or_minus 将一到三个连续的加号或减号捕获为一个字符串。不使用 as_string[] 的替代解决方案也将受到赞赏。

我为长列表道歉,但我需要在我的实际代码中同时使用词法分析器和解析器。

代码

// --------------  Third Party  --------------
#include <boost/config/warning_disable.hpp>
#include <boost/spirit/include/qi.hpp>
#include <boost/spirit/include/lex_lexertl.hpp>
#include <boost/spirit/include/phoenix_operator.hpp>

// --------------  C++ stdlib   --------------
#include <iostream>
#include <fstream>
#include <string>

using namespace boost::spirit;
using boost::phoenix::val;

enum token_ids
{
    ID_CONSTANT = 1000,
        ID_INTEGER,
        ID_TAG,
    ID_IDENTIFIER
};

template <typename Lexer>
struct example6_tokens : lex::lexer<Lexer>
{
    example6_tokens()
    {
        identifier = "[a-zA-Z_][a-zA-Z0-9_]*";
        constant   = "[0-9]+";

        tag           = "sl|s|l|tl|SL|S|L|TSL|"    
                                    "z|r|i|Z|R|I|"              
                                    "<>|><|<<>>|>><<|><><|<><>";

        this->self = lex::token_def<>('(') | ')' | '{' | '}' 
            | '=' | ';' | ':' | '+' | '-';

        this->self.add
        (constant,        ID_CONSTANT       )
        (tag,             ID_TAG            )
        (identifier,      ID_IDENTIFIER     )
        ;

        this->self("WS")
            =   lex::token_def<>("[ \\t\\n]+")
                |   "\\/\\*[^*]*\\*+([^/*][^*]*\\*+)*\\/"
                |   "\\/\\/[^\n]*"
                ;
    }
    lex::token_def<std::string>   identifier, tag;
    lex::token_def<unsigned int>  constant;
};

// ----------------------------------------------------------------------------
template <typename Iterator, typename Lexer>
struct example6_grammar
        : qi::grammar<Iterator, qi::in_state_skipper<Lexer> >
{
    template <typename TokenDef>
    example6_grammar(TokenDef const& tok)
        : example6_grammar::base_type(program)
    {
        using boost::spirit::_val;

        program
            =  +block
               ;

        block
            =   '{' >> *slsltl_stmt >> '}'
                ;

        plus_or_minus
                %=   ( qi::as_string[ qi::lit( '+' ) ] | qi::as_string[ '-' ])
                    [
                 std::cout << val("PLUS OR MINUS") << val( _1 ) << "\n"
                    ]
                        ;

        three_plus_or_minus
                %=   ( qi::as_string[ repeat(1,3)['+'] ] | qi::as_string[ repeat(1,3)['-'] ] )
                    [
                 std::cout << val("THREE PLUS OR MINUS") << val( _1 ) << "\n"
                    ]
                        ;

        slsltl_stmt
        =  (  - plus_or_minus
                    >> token(ID_CONSTANT) 
                    >> token(ID_TAG)
                    >> three_plus_or_minus
                    >> ';'
                )
                        [
                    std::cout << val("PARSED FINE") << "\n"
                        ]
                ;

        expression
            =   tok.identifier [ _val = _1 ]
                |   tok.constant   [ _val = _1 ]
                ;
    }

    typedef boost::variant<unsigned int, std::string> expression_type;

    qi::rule<Iterator, qi::in_state_skipper<Lexer> > program, block;
    qi::rule<Iterator, std::string(), qi::in_state_skipper<Lexer> > 
        plus_or_minus, three_plus_or_minus;
    qi::rule<Iterator, std::string(), qi::in_state_skipper<Lexer> > slsltl_stmt;
    qi::rule<Iterator, expression_type(), qi::in_state_skipper<Lexer> >  expression;
};

int
main( int argv, char* argc[] )
{
    typedef std::string::iterator base_iterator_type;
    typedef lex::lexertl::token<
    base_iterator_type, boost::mpl::vector<unsigned int, std::string>
    > token_type;

    typedef lex::lexertl::lexer<token_type> lexer_type;
    typedef example6_tokens<lexer_type> example6_tokens;
    typedef example6_tokens::iterator_type iterator_type;
    typedef example6_grammar<iterator_type, example6_tokens::lexer_def> example6_grammar;

    example6_tokens tokens;                         // Our lexer
    example6_grammar calc(tokens);                  // Our parser
    std::string str = { "{ +100S+++ ;\n }" };

    std::string::iterator it = str.begin();
    iterator_type iter = tokens.begin(it, str.end());
    iterator_type end = tokens.end();

    std::string ws("WS");
    bool r = qi::phrase_parse(iter, end, calc, qi::in_state(ws)[tokens.self]);
    if (r && iter == end)
    {
        std::cout << "-------------------------\n";
        std::cout << "Parsing succeeded\n";
        std::cout << "-------------------------\n";
    }
    else
    {
        std::cout << "-------------------------\n";
        std::cout << "Parsing failed\n";
        std::cout << "-------------------------\n";
    }
}

最佳答案

尝试

    slsltl_stmt %=  /*....*/;

代替 slsltl_stmt =。语义 Action 的使用禁用了自动属性传播。

(我还没有看过你的其余代码。可能还有更多地方需要调整这个/其他东西)


编辑

我做了更多测试,认为这符合您的预期:

three_plus_or_minus
        =   (qi::as_string[ repeat(1,3)[qi::char_('+')] | repeat(1,3)[qi::char_('-')] ])
            [ std::cout << val("THREE PLUS OR MINUS") << _1 << "\n" ]
                ;

但有一个开箱即用的问题:您为什么要使用词法分析器?为 +++ 设置一个 token 不是很有意义吗?

关于c++ - 如何让 spirit 的 qi::as_string 与 repeat 一起工作?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/14084769/

相关文章:

c++ - 在 std::cout 和 std::endl 的上下文中使用输出流缓冲区

pointers - vector <list <T >>是否保证元素地址保持不变?

c++ - 默认模板类型可以作为通用引用吗?

c++ - 如何处理异常以便我的程序仍然运行?

c++ - 使用 fscanf 解析文件

c++ - Detours 3.0 钩子(Hook) GetProcAddresss()

c++ - 未捕获的 std::exception 在核心中不正确的堆栈跟踪

c++ - 如何在不先将整个文件读入内存的情况下使用 Boost::Spirit::Lex 对文件进行 lex?

c++ - Boost spirit lex 将 token 值写回输入流

c++11 - 如何定义此提升精神规则的AST?