2015-11-05 44 views
3

我正在爲一種輸入文件編寫解析器。輸入文件看起來像:使用boost :: spirit以任何順序解析命名參數

[CalculationBlock] 
CalculationTitle="Test Parser Input System" , MatchingRadius=25.0, StepSize=0.01,ProblemType=RelSchroedingerEqn 
MaxPartialWaveJ=800, SMatConv=10E-8 
PartialWaveConv= 10E-8, SmallValueLimit = 10E-8 
PotentialRadType=HeavyIon 
[end] 

本質上,它被劃分成與[BlockName]開始,然後有一個集中的命名參數塊。命名參數可以用',''\n'字符分隔。

使用上面給出的不完整的輸入文件,我想爲它編寫一個解析器,它將作爲更完整輸入文件的跳轉點。我這樣做,但解析器有一個弱點,我不知道如何解決。它不是獨立的參數順序。例如,如果用戶在SMatConv=10E-8之前放置參數PartialWaveConv= 10E-8,它將會失敗。

我簡要地考慮過枚舉一個塊中參數的每個可能的順序,但是我丟棄了它,因爲有n個參數值對的n!排列。 所以我的問題是:有什麼辦法讓解析器獨立於參數排序嗎?

玩具分析器我寫了下面的是,我很抱歉,如果是業餘的,這是我首次涉足boost,更不用說boost.spirit

#include<string> 
#include<iostream> 
#include<cstdlib> 
#include<fstream> 
#include<boost/config/warning_disable.hpp> 
#include<boost/spirit/include/qi.hpp> 
#include<boost/spirit/include/phoenix_core.hpp> 
#include<boost/spirit/include/phoenix_operator.hpp> 
#include<boost/spirit/include/phoenix_object.hpp> 
#include<boost/fusion/include/adapt_struct.hpp> 
#include<boost/fusion/include/io.hpp> 
#include<boost/spirit/include/support_istream_iterator.hpp> 

namespace blocks 
{ 
namespace qi = boost::spirit::qi; 
namespace ascii = boost::spirit::ascii; 
namespace phoenix = boost::phoenix; 

struct CalcBlock 
{ 
    std::string calculationTitle; 
    float matchingRad; 
    float stepSize; 
    std::string problemType; 
    int maxPartialWaveJ; 
    float sMatrixConvergenceValue; 
    float partialWaveConvergenceValue; 
    float smallValueLimit; 
    std::string potentialRadType; 
}; 

} 

//tell fusion about the block structure 
BOOST_FUSION_ADAPT_STRUCT(blocks::CalcBlock, 
         (std::string, calculationTitle) 
         (float, matchingRad) 
         (float, stepSize) 
         (std::string, problemType) 
         (int, maxPartialWaveJ) 
         (float, sMatrixConvergenceValue) 
         (float, partialWaveConvergenceValue) 
         (float, smallValueLimit) 
         (std::string, potentialRadType) 
) 

namespace blocks 
{ 

template <typename Iterator> 
struct CalcBlockParser : qi::grammar<Iterator, CalcBlock(), boost::spirit::ascii::blank_type> 
{ 
    CalcBlockParser() : CalcBlockParser::base_type(start) 
    { 
     using qi::int_; 
     using qi::lit; 
     using qi::float_; 
     using qi::lexeme; 
     using ascii::char_; 

     quotedString %= lexeme['"' >> +(char_ - '"' - '\n') >> '"']; 
     plainString %= lexeme[ +(char_ - ' ' - ',' - '\n') ]; 

     start %= 
      lit("[CalculationBlock]") >> '\n' 
      >> lit("CalculationTitle") >> '=' >> quotedString >> (lit(',') | lit('\n')) 
      >> lit("MatchingRadius") >> '=' >> float_ >> (lit(',') | lit('\n')) 
      >> lit("StepSize") >> '=' >> float_ >> (lit(',') | lit('\n')) 
      >> lit("ProblemType") >> '=' >> plainString >> (lit(',') | lit('\n')) 
      >> lit("MaxPartialWaveJ") >> '=' >> int_ >> (lit(',') | lit('\n')) 
      >> lit("SMatConv") >> '=' >> float_ >> (lit(',') | lit('\n')) 
      >> lit("PartialWaveConv") >> '=' >> float_ >> (lit(',') | lit('\n')) 
      >> lit("SmallValueLimit") >> '=' >> float_ >> (lit(',') | lit('\n')) 
      >> lit("PotentialRadType") >> '=' >> plainString 
      >> lit("\n[end]\n"); 
    } 

    qi::rule<Iterator, std::string(), boost::spirit::ascii::blank_type> quotedString; 
    qi::rule<Iterator, std::string(), boost::spirit::ascii::blank_type> plainString; 
    qi::rule<Iterator, CalcBlock(), boost::spirit::ascii::blank_type> start; 
}; 

} 

using std::cout; 
using std::endl; 
namespace spirit = boost::spirit; 
int main(int argc, char *argv[]) 
{ 
    if (argc != 2) 
    { 
     cout << "\nUsage:\n\t./echos InputFileName\n" << endl; 
     return EXIT_FAILURE; 
    } 

    std::string inputFileName(argv[1]); 
    cout << "Reading input from the file: " << inputFileName << endl; 
    std::ifstream input(inputFileName); 
    input.unsetf(std::ios::skipws); 

    spirit::istream_iterator start(input); 
    spirit::istream_iterator stop; 

    typedef blocks::CalcBlockParser<spirit::istream_iterator> CalcBlockParser; 

    CalcBlockParser cbParser; 

    blocks::CalcBlock cb; 

    bool success = phrase_parse(start, stop, cbParser, boost::spirit::ascii::blank, cb); 

    if (success && start == stop) 
    { 
     std::cout << boost::fusion::tuple_open('['); 
     std::cout << boost::fusion::tuple_close(']'); 
     std::cout << boost::fusion::tuple_delimiter(", "); 

     std::cout << "-------------------------\n"; 
     std::cout << "Parsing succeeded\n"; 
     std::cout << "got: " << boost::fusion::as_vector(cb) << std::endl; 
     std::cout << "\n-------------------------\n"; 
    } 
    else 
    { 
     std::cout << boost::fusion::tuple_open('['); 
     std::cout << boost::fusion::tuple_close(']'); 
     std::cout << boost::fusion::tuple_delimiter(", "); 

     std::cout << "-------------------------\n"; 
     std::cout << "Parsing failed\n"; 
     std::cout << "got: " << boost::fusion::as_vector(cb) << std::endl; 
     std::cout << "\n-------------------------\n"; 
    } 

    return EXIT_SUCCESS; 
} 
+0

你認爲boost :: program_options? – ravenspoint

+0

我沒有,我不得不看看它。 boost :: program_options從長遠來看可能會更簡單,但現在我想弄明白這一點,因爲它給了我這個問題,我想知道解決方案,並且因爲學習精神會很方便,如果我有把手更棘手的事情。不過謝謝你的建議。 –

+0

@ravenspoint我沒有看到這將如何適用於此。我幾乎可以看到Boost Property Tree如何成爲一種選擇。但我認爲這兩者都意味着改變輸入格式 – sehe

回答

3

只是爲了好玩/完整性我回顧了語法,並與下面的測試上來。

我已經做了一些改進意見左右(如OP的實時流見證),並將得到的編碼,測試和輸出的位置:

Live On Coliru

#include <boost/fusion/include/adapt_struct.hpp> 
#include <boost/spirit/include/qi.hpp> 
#include <fstream> 
#include <iostream> 

namespace blocks { 
    struct CalcBlock { 
     std::string calculationTitle; 
     float  matchingRad; 
     float  stepSize; 
     std::string problemType; 
     int   maxPartialWaveJ; 
     float  sMatrixConvergenceValue; 
     float  partialWaveConvergenceValue;  
     float  smallValueLimit; 
     std::string potentialRadType; 
    }; 
} 

BOOST_FUSION_ADAPT_STRUCT(blocks::CalcBlock, // Boost 1.58+ style adapt-struct 
     calculationTitle, matchingRad, stepSize, problemType, maxPartialWaveJ, 
     sMatrixConvergenceValue, partialWaveConvergenceValue, smallValueLimit, 
     potentialRadType) 

namespace blocks { 

    namespace qi = boost::spirit::qi; 

    template <typename Iterator> 
    struct CalcBlockParser : qi::grammar<Iterator, CalcBlock()> { 

     CalcBlockParser() : CalcBlockParser::base_type(start) { 

      using namespace qi; 
      auto eol_ = copy((',' >> *eol) | +eol); // http://stackoverflow.com/a/26411266/85371 (!) 

      quotedString = '"' >> +~char_("\"\n") >> '"'; 
      plainString = +~char_(" ,\n"); 

      start  = skip(blank) [cbRule]; 

      cbRule  = lexeme["[CalculationBlock]"] >> eol 
       >> (
         (lexeme["CalculationTitle"] >> '=' >> quotedString >> eol_) 
        ^(lexeme["MatchingRadius"] >> '=' >> float_  >> eol_) 
        ^(lexeme["StepSize"]   >> '=' >> float_  >> eol_) 
        ^(lexeme["ProblemType"]  >> '=' >> plainString >> eol_) 
        ^(lexeme["MaxPartialWaveJ"] >> '=' >> int_   >> eol_) 
        ^(lexeme["SMatConv"]   >> '=' >> float_  >> eol_) 
        ^(lexeme["PartialWaveConv"] >> '=' >> float_  >> eol_) 
        ^(lexeme["SmallValueLimit"] >> '=' >> float_  >> eol_) 
        ^(lexeme["PotentialRadType"] >> '=' >> plainString >> eol_) 
       ) 
      >> lexeme["[end]"] 
      >> *eol 
      >> eoi; 
     } 

     private: 
     qi::rule<Iterator, CalcBlock()> start; 
     qi::rule<Iterator, CalcBlock(), qi::blank_type> cbRule; 
     // lexemes: 
     qi::rule<Iterator, std::string()> quotedString, plainString; 
    }; 
} 

using boost::fusion::as_vector; 
typedef boost::spirit::istream_iterator It; 

int main(int argc, char **argv) { 
    if (argc != 2) { 
     std::cout << "Usage:\n\t" << argv[0] << " InputFileName" << std::endl; 
     return 1; 
    } 

    std::string inputFileName(argv[1]); 
    std::cout << "Reading input from the file: " << inputFileName << std::endl; 
    std::ifstream input(inputFileName); 
    input.unsetf(std::ios::skipws); 

    It start(input), stop; 

    blocks::CalcBlock cb; 
    blocks::CalcBlockParser<It> cbParser; 

    bool success = parse(start, stop, cbParser, cb); 

    { 
     using namespace boost::fusion; 
     std::cout << tuple_open('[') << tuple_close(']') << tuple_delimiter(", "); 
    } 

    std::cout << "-------------------------\n"; 
    std::cout << "Parsing " << (success?"succeeded":"failed") << "\n"; 
    std::cout << "got: " << as_vector(cb)     << "\n"; 
    std::cout << "-------------------------\n"; 
} 

輸入:

[CalculationBlock] 
CalculationTitle="Test Parser Input System" 


SMatConv=10E-8, 


PartialWaveConv= 10E-8, MaxPartialWaveJ=800, SmallValueLimit = 10E-8 

PotentialRadType=HeavyIon , MatchingRadius=25.0, StepSize=0.01,ProblemType=RelSchroedingerEqn 

[end] 

輸出:

Reading input from the file: input.txt 
------------------------- 
Parsing succeeded 
got: [Test Parser Input System, 25, 0.01, RelSchroedingerEqn, 800, 1e-07, 1e-07, 1e-07, HeavyIon] 
------------------------- 
+0

好吧。謝謝(另一個答案得到我的讚賞)。這是一個獎金** [Spirit X3解析器](http://coliru.stacked-crooked.com/a/c84aae191247c937)**。如果你想要整個戰爭故事:[流部分1](https://www.livecoding.tv/video/stateful-semantic-actions-in-spirit-x3-part1/)和[部分2](https: //www.livecoding.tv/video/stateful-semantic-actions-in-spirit-x3-part2/)([experiment](http://chat.stackoverflow.com/transcript/10?m=24182469#24182469) ) – sehe

+0

(它也得到我的upvote) –

+0

@JamesMatta既然你刪除了你的問題,這是它的錯誤報告擴展的X3版本:** [Live Live Coliru](http://coliru.stacked-crooked.com/a/ c2db66e432ea9b72)**。請注意引入[eps]的實例[解決此錯誤](http://boost.2283326.n4.nabble.com/Single-element-attributes-in-X3-quot-still-quot-broken-td4681549。 HTML)。 (見鬥爭:[part#1](http://tinyurl.com/o2ne8nr),[part#2](http://tinyurl.com/omfvov6),[part#3](http:// tinyurl的.com/o449nbe)) – sehe

3

您必須使用置換算符^

start %= 
     lit("[CalculationBlock]") >> '\n' >> 
     (
     (lit("CalculationTitle") >> '=' >> quotedString >> (lit(',') | lit)('\n'))) 
     ^(lit("MatchingRadius") >> '=' >> float_ >> (lit(',') | lit('\n'))) 
     ^(lit("StepSize") >> '=' >> float_ >> (lit(',') | lit('\n'))) 
     ^(lit("ProblemType") >> '=' >> plainString >> (lit(',') | lit('\n'))) 
     ^(lit("MaxPartialWaveJ") >> '=' >> int_ >> (lit(',') | lit('\n'))) 
     ^(lit("SMatConv") >> '=' >> float_ >> (lit(',') | lit('\n'))) 
     ^(lit("PartialWaveConv") >> '=' >> float_ >> (lit(',') | lit('\n'))) 
     ^(lit("SmallValueLimit") >> '=' >> float_ >> (lit(',') | lit('\n'))) 
     ^(lit("PotentialRadType") >> '=' >> plainString >> (lit(',') | lit('\n'))) 
     ) 
     >> lit("\n[end]\n"); 
+0

我真的希望在發佈問題之前我已經發現了該運營商。也就是說,這樣做會混淆信息排序到我通過融合訪問的結構中嗎? –

+0

稍後的簡短測試表明它確實干擾了結構中信息的排序。事實上,如果我更改訂單,解析失敗,有沒有辦法解決這個問題? –

+0

@JamesMatta你的簡短測試是錯誤的。此外,它只是記錄:http://www.boost.org/doc/libs/1_59_0/libs/spirit/doc/html/spirit/qi/reference/operator/permutation.html#spirit。qi.reference.operator.permutation.attributes – sehe

相關問題