我正在为一种输入文件编写一个解析器。输入文件看起来像:
[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]
然后里面有一组命名参数。命名参数可以用以下分隔符分隔','
or '\n'
人物。
使用上面给出的不完整输入文件,我想为其编写一个解析器,作为更完整输入文件的起点。我这样做了但是解析器有一个弱点,我不知道如何解决。它与参数顺序无关。例如,如果用户要输入参数PartialWaveConv= 10E-8
before SMatConv=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;
}