gpt4 book ai didi

c++ - 使用 boost::spirit 以任意顺序解析命名参数

转载 作者:太空狗 更新时间:2023-10-29 20:02:55 24 4
gpt4 key购买 nike

我正在为一种输入文件编写解析器。输入文件类似于:

[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] 开头的 block ,然后在其中包含一组命名参数。命名参数可以用',''\n' 字符分隔。

使用我在上面给出的不完整的输入文件,我想为它编写一个解析器,作为一个更完整的输入文件的起点。我这样做了,但是解析器有一个我不确定如何解决的弱点。它不是独立于参数顺序的。例如,如果用户将参数 PartialWaveConv= 10E-8 放在 SMatConv=10E-8 之前,它将失败。

我曾短暂地考虑过在一个 block 中枚举每个可能的参数顺序,但我放弃了它,因为有 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 在直播中看到的那样),结果代码、测试和输出如下:

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]
-------------------------

关于c++ - 使用 boost::spirit 以任意顺序解析命名参数,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/33555643/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com