2015-08-26 23 views
1

我使用boost :: spirit lex和qi來解析一些源代碼。qi :: skip爲什麼使用詞法分析器中的令牌失敗?

我已經使用詞法分析器從輸入字符串中跳過空格。我想要做的是根據解析器中的上下文切換跳過註釋。

這是一個基本的演示。見語法::語法()評論我的問題:

#include <boost/spirit/include/qi.hpp> 
#include <boost/spirit/include/lex_lexertl.hpp> 
#include <boost/spirit/include/phoenix.hpp> 

#include <iostream> 

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

typedef lex::lexertl::token<char const*, boost::mpl::vector<std::string>, boost::mpl::false_ > token_type; 
typedef lex::lexertl::actor_lexer<token_type> lexer_type; 

struct TokenId 
{ 
    enum type 
    { 
     INVALID_TOKEN_ID = lex::min_token_id, 
     COMMENT 
    }; 
}; 

struct Lexer : lex::lexer<lexer_type> 
{ 
public: 
    lex::token_def<std::string> comment; 
    lex::token_def<std::string> identifier; 
    lex::token_def<std::string> lineFeed; 
    lex::token_def<std::string> space; 

    Lexer() 
    { 
     comment = "\\/\\*.*?\\*\\/|\\/\\/[^\\r\\n]*"; 
     identifier = "[A-Za-z_][A-Za-z0-9_]*"; 
     space = "[\\x20\\t\\f\\v]+"; 
     lineFeed = "(\\r\\n)|\\r|\\n"; 

     this->self = space[lex::_pass = lex::pass_flags::pass_ignore]; 
     this->self += lineFeed[lex::_pass = lex::pass_flags::pass_ignore]; 
     this->self.add 
     (comment, TokenId::COMMENT) 
     (identifier) 
     (';') 
     ; 
    } 
}; 

typedef Lexer::iterator_type Iterator; 

void traceComment(const std::string& content) 
{ 
    std::cout << " comment: " << content << std::endl; 
} 

class Grammar : public qi::grammar<Iterator> 
{ 
    typedef token_type skipped_t; 

    qi::rule<Iterator, qi::unused_type, qi::unused_type> m_start; 
    qi::rule<Iterator, qi::unused_type, qi::unused_type, skipped_t> m_variable; 
    qi::rule<Iterator, std::string(), qi::unused_type> m_comment; 

public: 
    Lexer lx; 

public: 
    Grammar() : 
     Grammar::base_type(m_start) 
    { 
// This does not work (comments are not skipped in m_variable) 
     m_start = *(
      m_comment[phx::bind(&traceComment, qi::_1)] 
     | qi::skip(qi::token(TokenId::COMMENT))[m_variable] 
     ); 

     m_variable = lx.identifier >> lx.identifier >> ';'; 
     m_comment = qi::token(TokenId::COMMENT); 
/** But this works: 
     m_start = *(
     m_comment[phx::bind(&traceComment, qi::_1)] 
     | m_variable 
     ); 

     m_variable = qi::skip(qi::token(TokenId::COMMENT))[lx.identifier >> lx.identifier >> ';']; 
     m_comment = qi::token(TokenId::COMMENT); 
*/ 
    } 
}; 

void test(const char* code) 
{ 
    std::cout << code << std::endl; 

    Grammar parser; 
    const char* begin = code; 
    const char* end = code + strlen(code); 
    tokenize_and_parse(begin, end, parser.lx, parser); 

    if (begin == end) 
     std::cout << "-- OK --" << std::endl; 
    else 
     std::cout << "-- FAILED --" << std::endl; 
    std::cout << std::endl; 
} 

int main(int argc, char* argv[]) 
{ 
    test("/* kept */ int foo;"); 
    test("int /* ignored */ foo;"); 
    test("int foo /* ignored */;"); 
    test("int foo; // kept"); 
} 

輸出是:

/* kept */ int foo; 
    comment: /* kept */ 
-- OK -- 

int /* ignored */ foo; 
-- FAILED -- 

int foo /* ignored */; 
-- FAILED -- 

int foo; // kept 
    comment: // kept 
-- OK -- 

是否有任何問題與skipped_t?

回答

2

您描述的行爲是我期望從我的經驗。

當你寫

my_rule = qi::skip(ws) [ foo >> lit(',') >> bar >> lit('=') >> baz ]; 

這基本上是一樣的書寫

my_rule = *ws >> foo >> *ws >> lit(',') >> *ws >> bar >> *ws >> lit('=') >> *ws >> baz; 

(假設ws是沒有屬性的規則。如果它在你的語法屬性,該屬性是忽略,就好像使用qi::omit一樣)。

值得注意的是,在foo規則的內部,船長沒有得到傳播。因此,foo,barbaz在上面仍然可以是空白敏感的。跳過指令的作用是導致語法不關心此規則中的前導空格,或在此規則中圍繞',''='的空格。

此處瞭解詳情:http://boost-spirit.com/home/2010/02/24/parsing-skippers-and-skipping-parsers/


編輯:

另外,我不認爲skipped_t是做什麼你認爲它的存在。

當您使用自定義的隊長時,最直接的方法是指定一個解析器的實際實例作爲該規則的跳過解析器。當您使用類型而不是對象時,例如qi::skip(qi::blank_type),這是一種簡寫形式,其中標記類型qi::blank_type已通過之前的模板聲明鏈接到類型qi::blank,並且qi知道當它在某些地方看到qi::blank_type時,它應該實例化一個qi::blank解析器對象。

我沒有看到任何證據表明您已經設置了該機器,您只需鍵入skipped_ttoken_type即可。如果你想以這種方式工作(如果甚至有可能,我不知道),你應該做什麼,讀取關於qi定製點的信息,而是將qi::skipped_t聲明爲空的結構,通過一些模板鍋爐板鏈接到規則m_comment ,這大概是你想要跳過的東西。 (如果你跳過所有類型的所有令牌,那麼你不可能匹配任何東西,這是沒有意義的,所以我不知道你的意圖是什麼讓token_type船長。)

我的猜測是,當qi在您的參數列表中看到typedef token_type,它忽略它或將其解釋爲規則返回值的一部分或類似的東西,不確定它會做什麼。

+0

這聽起來對我來說很陌生。我有一些其他的代碼,我單獨使用qi(沒有lex),並且船長傳播到子規則。這就是爲什麼由於qi :: skip(...)[m_variable],規則必須用船長類型聲明:skipped_t – ZeeByeZon

+0

請注意,在您提到的頁面上,船長會遍歷所有規則... – ZeeByeZon

+0

「請注意,在您提到的頁面上,船長通過所有規則傳播......」不,它不會。這裏是哈特穆特的例子,在coliru上直播。 http://coliru.stacked-crooked.com/a/fd62add9e9ccb930或僅使用'skip'指令的變體:http://coliru.stacked-crooked.com/a/5068a53556449c01 –

相關問題