2014-05-07 89 views
2

我可能會問一個非常微不足道的問題,但我的腦子裏並沒有阻塞它來破解它。 試圖解析像where子句如下所示,使用boost ::精神::氣生成對無法解析SQL類型,其中條件使用boost :: spirit :: qi

std::string input = "book.author_id = '1234' and book.isbn = 'xy99' and book.type = 'abc' and book.lang = 'Eng'" 

我已經通過以下線程消失的載體,但仍無法做到這一點:-(Thread5一個SQL Thread4Thread3 Thread2 Thread1

[Thread1][6] 
[Thread2][7] 
[Thread3][8] 
[Thread4][9] 
[Thread5][10] 

我真誠的請求,請幫助我瞭解如何實現這一目標?可能是我還沒有完全給我的100%,但是請善待....作爲第一步,我只是檢查是否可以在Vector中獲取所有的令牌,然後解析每個Vector元素以生成另一個std向量: :對

#include <boost/fusion/adapted.hpp> 
#include <boost/spirit/include/qi.hpp> 
#include <map> 
#include <vector> 

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

typedef std::string str_t; 
typedef std::pair<str_t, str_t> pair_t; 
typedef std::vector<pair_t> pairs_t; 

typedef std::vector<str_t> strings_t; 
//typedef std::map<std::string, std::string> map_t; 
//typedef std::vector<map_t> maps_t; 

template <typename It, typename Skipper = qi::space_type> 
    //struct parser : qi::grammar<It, pairs_t(), Skipper> 
    struct parser : qi::grammar<It, strings_t(), Skipper> 
{ 
    parser() : parser::base_type(start) 
    { 
     using namespace qi; 

     cond = lexeme [ *(char_) ]; 
     conds = *(char_) >> cond % (lit("and")); 

     //conds = *(char_ - lit("and")) >>(cond % lit("and")); 
     /*cond = lexeme [ *(char_ - lit("and")) ]; 
     cond = key >> "=" >> value; 
     key  = *(char_ - "="); 
     value = ('\'' >> *(~char_('\'')) >> '\''); 
     kv_pair = key >> value;*/ 
     start = conds; 
     //cond = key >> "=" >> value; 
     //key  = *(char_ - "="); 
     //value = ('\'' >> *(~char_('\'')) >> '\''); 
    //  kv_pair = key >> value; 
    //  start = kv_pair; 
    } 

    private: 
    qi::rule<It, str_t(), Skipper> cond; 
    qi::rule<It, strings_t(), Skipper> conds; 
    //qi::rule<It, std::string(), Skipper> key, value;//, cond; 
    //qi::rule<It, pair_t(), Skipper> kv_pair; 
    //qi::rule<It, pairs_t(), Skipper> start; 
    qi::rule<It, strings_t(), Skipper> start; 
}; 

template <typename C, typename Skipper> 
    bool doParse(const C& input, const Skipper& skipper) 
{ 
    auto f(std::begin(input)), l(std::end(input)); 

    parser<decltype(f), Skipper> p; 
    strings_t data; 

    try 
    { 
     bool ok = qi::phrase_parse(f,l,p,skipper,data); 
     if (ok) 
     { 
      std::cout << "parse success\n"; 
      std::cout << "No Of Key-Value Pairs= "<<data.size()<<"\n"; 
     } 
     else std::cerr << "parse failed: '" << std::string(f,l) << "'\n"; 
     return ok; 
    } 
    catch(const qi::expectation_failure<decltype(f)>& e) 
    { 
     std::string frag(e.first, e.last); 
     std::cerr << e.what() << "'" << frag << "'\n"; 
    } 

    return false; 
} 

int main() 
{ 
    std::cout<<"Pair Test \n"; 
    const std::string input = "book.author_id = '1234' and book.isbn = 'xy99' and book.type = 'abc' and book.lang = 'Eng'"; 
    bool ok = doParse(input, qi::space); 
    std::cout<< input <<"\n"; 
    return ok? 0 : 255; 
} 

OUTPUT:

Pair Test 
parse success 
No Of Key-Value Pairs= 2 
book.author_id = '1234' and book.isbn = 'xy99' and book.type = 'abc' and book.lang = 'Eng' 

這我預計4 ...因爲有4個條件!

由於提前 問候, 維韋克

一些示例工作OUT- live on coliru

+0

也許你也會感興趣:[使用Boost.Spirit解析C++中的SQL查詢](http://stackoverflow.com/a/12631146/85371)和[試圖用Boost-Spirit解析SQL語句](http ://stackoverflow.com/a/10339716/85371)。 – sehe

回答

1

對不起打破它給你,但你的語法是遠遠比你想象的更壞了。

conds = *(char_) // ... 

就在這裏,你基本上只是將所有輸入解析成單個字符串,並跳過空格。事實上,加入

for (auto& el : data) 
     std::cout << "'" << el << "'\n"; 

解析打印後:

Pair Test 
parse success 
No Of Key-Value Pairs= 2 
'book.author_id='1234'andbook.isbn='xy99'andbook.type='abc'andbook.lang='Eng'' 
'' 

正如你所看到的,第一個元素是字符串*char_解析,你可以免費獲得一個空元素由於事實在空輸入上匹配condscond

我強烈建議你開始簡單。我的意思是,簡單得多。

慢慢地從地面建立你的語法。 Spirit是用測試驅動開發解決問題的一個非常好的工具(編譯時間除外,但是,嘿,你有更多的時間思考!)。

這裏的東西,我只是做了,從最初的積木開始思考,在indent ifier和工作我的方式達到更高級別的元素:

// lexemes (no skipper) 
ident  = +char_("a-zA-Z."); 
op  = no_case [ lit("=") | "<>" | "LIKE" | "IS" ]; 
nulllit = no_case [ "NULL" ]; 
and_  = no_case [ "AND" ]; 
stringlit = "'" >> *~char_("'") >> "'"; 

// other productions 
field  = ident; 
value  = stringlit | nulllit; 
condition = field >> op >> value; 

conjunction = condition % and_; 
start  = conjunction; 

這些都是接近最簡單我認爲可以解析你的語法(用幾個創造性的左右註釋,它們看起來不太侵入)。

UPDATE因此,這是我在20分鐘內得到了:

我總是先有自​​己繪製我想要的規則,暴露類型:

namespace ast 
{ 
    enum op { op_equal, op_inequal, op_like, op_is }; 

    struct null { }; 

    typedef boost::variant<null, std::string> value; 

    struct condition 
    { 
     std::string _field; 
     op _op; 
     value _value; 
    }; 

    typedef std::vector<condition> conditions; 
} 

只有condition不能「自然「在沒有適應的Spirit語法中使用:

BOOST_FUSION_ADAPT_STRUCT(ast::condition, (std::string,_field)(ast::op,_op)(ast::value,_value)) 

現在語法本身來了:

// lexemes (no skipper) 
    ident  = +char_("a-zA-Z._"); 
    op_token.add 
     ("=", ast::op_equal) 
     ("<>", ast::op_inequal) 
     ("like", ast::op_like) 
     ("is", ast::op_is); 
    op   = no_case [ op_token ]; 
    nulllit  = no_case [ "NULL" >> attr(ast::null()) ]; 
    and_  = no_case [ "AND" ]; 
    stringlit = "'" >> *~char_("'") >> "'"; 

    //// other productions 
    field  = ident; 
    value  = stringlit | nulllit; 
    condition = field >> op >> value; 

    whereclause = condition % and_; 
    start  = whereclause; 

你可以看到輕微的偏離我原來的草圖,這很有趣:

  • 添加_來標識字符
  • 移動op_token到符號匹配(因爲這是比較容易映射枚舉值)

看到這一切Live And Working On Coliru,輸出:

Pair Test 
parse success 
No Of Key-Value Pairs= 4 
([book.author_id] = 1234) 
([book.isbn] LIKE xy99) 
([book.type] = abc) 
([book.lang] IS NULL) 

book.author_id = '1234' and book.isbn liKE 'xy99' and book.type = 'abc' and book.lang IS null 
+0

非常感謝Sehe對於細節的探索...我想我已經做出了非常非常非常幼稚的努力,你的文章是讓人大開眼界的......會更加專心地思考,並且努力構建一個更清潔,更簡單的語法...關注Vivek –

+0

赦免再次..但實施無法解析LIKE或IS如果提供而不是'='!而如果我將令牌分爲「and_」,則使用「|」在「領域>>(op_like | op_is)>>價值」那麼它的工作..是否有任何隱藏的故障..關注Vivek –

+0

發表評論請求** [在郵件列表](http:// boost .2283326.n4.nabble.com/no-case-symbols-lt-gt-not-matching-Spirit-V-2-td4662098.html)**(包括最小再現器)並更新了答案。 – sehe

相關問題