2013-04-10 89 views
1

我是一個精神初學者使用boost精神(qi)解析英制值

我想使用spirit將一個英制字符串值解析爲一個結構體。

輸入應該接受以下語法:

 
    5'3"1/2 
    5'1/2 
    3"1/2 

struct imp_constant看起來是這樣,請注意以下流運算符,因爲這符不,我會打印結果:

struct imp_constant 
{ 
    explicit imp_constant(unsigned int feet=0 
         ,unsigned int inch=0 
         ,unsigned int fracn=0 
         ,unsigned int fracd=1) 
     :feet_(feet),inches_(inch),fracn_(fracn),fracd_(fracd){} 
    unsigned int feet_,inches_,fracn_,fracd_; 
}; 
std::ostream& operator<<(std::ostream& os, imp_constant const& cst) 
{ 
    if (cst.feet_) 
     os << cst.feet_ << '\''; 
    if (cst.inches_)  
     os << cst.inches_ << '"'; 
    if (cst.fracn_) 
     os << cst.fracn_ << '/' << cst.fracd_; 
    return os; 
} 

我的僞語法是非常簡單的,看起來像這樣:

myrule = (
    (
     (qi::uint_ >> L'\'') 
     || 
     (qi::uint_ >> L'"') 
    ) 
    >> -(
      qi::uint_ 
      >> L'/' >> 
      qi::uint_ 
     ) 
    ); 

這裏是我的相當VE首先嚐試填充我的結構:

我添加BOOST_FUSION_ADAPT_STRUCT宏來我struct imp_constant然後試圖以下語法:

qi::rule<std::string::const_iterator, imp_constant()> 
    impconst = qi::lexeme[ //not sure this is required since no skipper precised 
     (
      (qi::uint_[phx::at_c<0>(qi::_val)=qi::_1] >> L'\'') 
      || 
      (qi::uint_[phx::at_c<1>(qi::_val)=qi::_1] >> L'"') 
     ) 
     >> -(
      qi::uint_[phx::at_c<2>(qi::_val)=qi::_1] 
      >> L'/' >> 
      qi::uint_[phx::at_c<3>(qi::_val)=qi::_1] 
      ) 
     ]; 

結果是:

input:5'3"1/2 ==> output:5'3"1/2 (ok) 
input:5'1/2 ==> output:5'1"1/2 (__nok__) 

我想我不瞭解如何在這種情況下佔位符的行爲。

由於我在精神的世界初學者,任何意見,歡迎

非常感謝您

下面是完整的代碼,這應該有助於

#define BOOST_SPIRIT_DONT_USE_MPL_ASSERT_MSG 1 
//#define BOOST_SPIRIT_DEBUG << uncomment to enable debug 
#include <boost/spirit/include/qi.hpp> 
#include <boost/spirit/include/phoenix.hpp> 
#include <boost/variant/recursive_wrapper.hpp> 
#include <boost/fusion/adapted.hpp> 

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

struct imp_constant 
{ 
    explicit imp_constant(unsigned int feet=0 
         ,unsigned int inch=0 
         ,unsigned int fracn=0 
         ,unsigned int fracd=1) 
     :feet_(feet),inches_(inch),fracn_(fracn),fracd_(fracd){} 
    unsigned int feet_,inches_,fracn_,fracd_; 
}; 

std::ostream& operator<<(std::ostream& os, imp_constant const& cst) 
{ 
    if (cst.feet_) 
     os << cst.feet_ << '\''; 
    if (cst.inches_)  
     os << cst.inches_ << '"'; 
    if (cst.fracn_) 
     os << cst.fracn_ << '/' << cst.fracd_; 
    return os; 
} 

BOOST_FUSION_ADAPT_STRUCT(imp_constant, 
          (unsigned int, feet_) 
          (unsigned int, inches_) 
          (unsigned int, fracn_) 
          (unsigned int, fracd_)) 

int _tmain(int argc, _TCHAR* argv[]) 
{ 
    std::string input; 

    std::cout << "\n----------------------\n> "; 
    while (std::getline(std::cin, input)) 
    { 
     if (input.empty() || input[0] == 'q' || input[0] == 'Q') 
      break; 

     std::string::const_iterator f(input.begin()),l(input.end()); 
     try 
     { 
      imp_constant result; 
      std::cout << "parsing: " << input << "\n"; 
      bool ok; 


      qi::rule<std::string::const_iterator, imp_constant()> 
       impconst = qi::lexeme[ //not sure this is required since 
          //no skipper precised 
        (
         (qi::uint_[phx::at_c<0>(qi::_val)=qi::_1] 
         >> L'\'') 
         || 
         (qi::uint_[phx::at_c<1>(qi::_val)=qi::_1] 
         >> L'"') 
         ) 
         >> -(
         qi::uint_[phx::at_c<2>(qi::_val)=qi::_1] 
        >> L'/' >> 
         qi::uint_[phx::at_c<3>(qi::_val)=qi::_1] 
        ) 
       ]; 

      ok=qi::phrase_parse(f,l,impconst ,qi::space,result); 

      if (!ok) 
       std::cerr << "invalid input\n"; 
      else 
      { 

       std::cout << "\n---------------------------\n"; 
       std::cout << "result="<< result; 
      } 
     } 
     catch (const qi::expectation_failure<const char *>& e) 
     { 
      std::cerr << "expect failure at '" 
         << std::string(e.first, e.last) << "'\n"; 
     } 
     catch (...) 
     { 
      std::cerr << "parse error\n"; 
     } 
     if (f!=l) std::cerr << "unparsed: '" << std::string(f,l) << "'\n"; 
     std::cout << "\n-----------------------\n> "; 
    } 
    std::getchar(); 
    return 0; 
} 

回答

4

以替代sehe的回答(你應該接受):

qi::rule<std::string::const_iterator, imp_constant()> 
     impconst = 
       (
        (qi::uint_ >> L'\'')[phx::at_c<0>(qi::_val)=qi::_1] 
        || 
        (qi::uint_ >> L'"')[phx::at_c<1>(qi::_val)=qi::_1] 
       ) 
       >> 
        -(qi::uint_ >> L'/' >> qi::uint_) 
        [phx::at_c<2>(qi::_val)=qi::_1,phx::at_c<3>(qi::_val)=qi::_2] 
     ; 

如何:

你的解決辦法,如果你只是改變了你的規則才能起作用我會這樣做:

稍微改變你的imp_constant

struct fraction 
{ 
    unsigned int n_,d_; 
}; 

struct imp_constant 
{ 
    unsigned int feet_,inches_; 
    fraction frac_; 
}; 

BOOST_FUSION_ADAPT_STRUCT(fraction, 
          (unsigned int, n_) 
          (unsigned int, d_) 
) 

BOOST_FUSION_ADAPT_STRUCT(imp_constant, 
          (unsigned int, feet_) 
          (unsigned int, inches_) 
          (fraction , frac_) 

然後該規則將是:

qi::rule<std::string::const_iterator,unsigned int()> feet = (qi::uint_ >> L'\'') | qi::attr(0); 
qi::rule<std::string::const_iterator,unsigned int()> inches = (qi::uint_ >> L'"') | qi::attr(0); 
qi::rule<std::string::const_iterator,fraction()> fract = (qi::uint_ >> L'/' >> qi::uint_) | (qi::attr(0)>> qi::attr(1)); 
qi::rule<std::string::const_iterator, imp_constant()> impconst=feet>>inches>>fract; 

Example on LWS

+0

非常感謝,它工作得很好!我會記住'| qi :: attr(...)'作爲可選的默認值技巧。 – loic 2013-04-10 13:42:16

+0

+1也來自我:)我玩這個主意,但選擇保持接近原始並解釋發生了什麼。 (@loic屬性技巧實際上就是我簡單地用「_explicit defaults_或qi :: hold可以修復它」的方式暗示的) – sehe 2013-04-10 18:52:04

+0

@sehe的確,user2266005找到的解決方案對我來說非常封閉!對於錯過「明確默認」提示的道歉,我幾天前發現了精神(順便說一句,我歡迎自己在這個新的世界:),儘管我已經閱讀了很多,但我還是不太滿意概念!感謝你們的幫助,你們在這裏和其他許多職位的貢獻讓精神對我更具吸引力。 – loic 2013-04-11 07:23:16

2

你在看回溯。

5'1/2中的'1'首先在可能的英寸分支中被解析爲qi::uint_,導致屬性被分配。

只有然後是遇到的'/',導致從'英寸分支'回溯。但屬性已經設置好了。在這種情況下,明確的默認值或qi::hold可以解決它。

這裏的東西喜歡的方式我可能短語的事情自己:

qi::rule<std::string::const_iterator, unsigned(char)> 
    component = qi::hold [ qi::uint_ >> qi::lit(qi::_r1) ]; 

qi::rule<std::string::const_iterator, imp_constant()> 
    impconst = 
     component(L'\'') || 
     component(L'"') || 
     component(L'/') || 
     qi::uint_ 
; 

BOOST_SPIRIT_DEBUG_NODE(component); 
BOOST_SPIRIT_DEBUG_NODE(impconst); 

我認爲這可能是一個很好的起點?

其它注意事項:

  1. 測試f!=l檢測未解析其餘輸入
  2. 的完整代碼樣本中的語義動作是多餘的
  3. 的確,lexeme是多餘的存在

看到它住在這裏:http://liveworkspace.org/code/Rpydy$0

測試輸出:

---------------------- 
> parsing: 5'1/2 
<impconst> 
    <try>5'1/2</try> 
    <component> 
    <try>5'1/2</try> 
    <success>1/2</success> 
    <attributes>[5, ']</attributes> 
    </component> 
    <component> 
    <try>1/2</try> 
    <fail/> 
    </component> 
    <component> 
    <try>1/2</try> 
    <success>2</success> 
    <attributes>[1, /]</attributes> 
    </component> 
    <success></success> 
    <attributes>[[5, 0, 1, 2]]</attributes> 
</impconst> 

--------------------------- 
result=5'1/2 
----------------------- 
> 
+0

嗨sehe,首先,非常感謝你的快速和完整的答案(和btw所有其他答案你發佈在stackoverflow - 我已經學會了太多的閱讀)。確實看起來不錯。不幸的是,hold指令抱怨'無法從swap中'unsigned int''推斷boost :: spirit :: multi_pass <...>的模板參數。測試vs9和vs10,有什麼想法?再次感謝 – loic 2013-04-10 12:48:34

+0

特別感謝解釋什麼hapsed – loic 2013-04-10 13:35:30

+0

@loic啊。我發現'qi :: lazy'與舊的鳳凰代碼顯然是破壞了clang/libC++(也可能是MSVC)。在全部包括'#define BOOST_SPIRIT_USE_PHOENIX_V3'之前添加包括解決Clang問題。我建議你添加它,看看它是如何爲你。參見[http://liveworkspace.org/code/Rydydy$1](http://liveworkspace.org/code/Rydydy$1),在那裏你可以驗證它適用於GCC和Clang – sehe 2013-04-10 18:49:50