使用boost :: spirit以任何顺序解析命名参数
我正在为一种输入文件编写解析器。输入文件看起来像:使用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;
}
只是为了好玩/完整性我回顾了语法,并与下面的测试上来。
我已经做了一些改进意见左右(如OP的实时流见证),并将得到的编码,测试和输出的位置:
#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]
-------------------------
好吧。谢谢(另一个答案得到我的赞赏)。这是一个奖金** [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
(它也得到我的upvote) –
@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
您必须使用置换算符^
:
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");
我真的希望在发布问题之前我已经发现了该运营商。也就是说,这样做会混淆信息排序到我通过融合访问的结构中吗? –
稍后的简短测试表明它确实干扰了结构中信息的排序。事实上,如果我更改订单,解析失败,有没有办法解决这个问题? –
@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
你认为boost :: program_options? – ravenspoint
我没有,我不得不看看它。 boost :: program_options从长远来看可能会更简单,但现在我想弄明白这一点,因为它给了我这个问题,我想知道解决方案,并且因为学习精神会很方便,如果我有把手更棘手的事情。不过谢谢你的建议。 –
@ravenspoint我没有看到这将如何适用于此。我几乎可以看到Boost Property Tree如何成为一种选择。但我认为这两者都意味着改变输入格式 – sehe