如何解析末尾带有可选分隔符的列表?
How to parse a list with an optional separator at the end?
我正在编写一个解析器来处理带有枚举和结构的简单 C 头文件。我已经使用 Boost Spirit Qi 编写了一个解析器,几乎可以完成任务。我遇到了一个可以通过 hack 解决的问题,但我很好奇是否有可能更准确地解决它。
我正在处理的枚举很简单。这是一个例子:
enum <optional enum name>
{
VALUE1,
VALUE2 = 222,
VALUE3
}
解析此类枚举的代码片段:
IdParser %= lexeme[(alpha | '_') >> *(alnum | '_')];
EnumExprParser %= lexeme[+(char_ - (lit(",") | lit("}")))];
EnumValueParser %= IdParser >> -('=' >> EnumExprParser);
EnumParser %= lit("enum") >> -IdParser >> lit("{") >> (EnumValueParser % lit(",")) >> lit("}") >> -lit(";");
注意,我将枚举值解析为以逗号分隔的列表。但有时最后一个枚举值也以逗号结尾:VALUE3,
。我的脏解决方案如下:*(EnumValueParser >> -lit(","))
但这允许在没有分隔符的情况下解析多个枚举值。这对我来说是可以接受的,但我对更干净的解决方案感兴趣。我正在将枚举解析为以下结构:
struct EnumValue
{
std::string Name;
boost::optional<std::string> Value;
};
struct Enum
{
boost::optional<std::string> Name;
std::vector<EnumValue> Values;
};
非常感谢!
一个快速修复方法是替换
EnumBody = '{' >> EnumValue % "," >> '}';
与
EnumBody = '{' >> -EnumValue % "," >> '}';
虽然这很草率,因为它也允许 enum X { a,,,b }
。所以,这会更准确:
EnumBody = '{' >> EnumValue % "," >> -lit(',') >> '}';
注意 还有一个问题你还没有发现,那就是空枚举主体也应该被允许 (enum X {}
),所以让我们修复 那也是:
EnumBody = '{' >> -(EnumValue % ",") >> -lit(',') >> '}';
演示
#include <boost/fusion/adapted/std_pair.hpp>
#include <boost/fusion/adapted/struct.hpp>
#include <boost/spirit/include/qi.hpp>
#include <iostream>
namespace qi = boost::spirit::qi;
namespace ast {
using Id = std::string;
using EnumEntry = std::pair<Id, std::string>;
using EnumBody = std::vector<EnumEntry>;
struct EnumDef {
Id name;
EnumBody members;
};
}
BOOST_FUSION_ADAPT_STRUCT(ast::EnumDef, name, members)
template <typename It, typename Skipper = qi::space_type>
struct Parser : qi::grammar<It, ast::EnumDef(), Skipper> {
Parser() : Parser::base_type(Enum) {
using namespace qi;
Id = raw [(alpha | '_') >> *(alnum | '_')];
EnumExpr = +~char_(",}");
EnumValue = Id >> -('=' >> EnumExpr);
EnumBody = '{' >> -(EnumValue % ",") >> -lit(',') >> '}';
Enum = "enum" >> -Id >> EnumBody >> -lit(';');
}
private:
qi::rule<It, ast::EnumEntry(), Skipper> EnumValue;
qi::rule<It, ast::EnumBody(), Skipper> EnumBody;
qi::rule<It, ast::EnumDef(), Skipper> Enum;
// lexemes:
qi::rule<It, ast::Id()> Id, EnumExpr;
};
int main() {
using It = boost::spirit::istream_iterator;
It f(std::cin >> std::noskipws), l;
bool ok = qi::phrase_parse(f, l, Parser<It>(), qi::space);
if (ok) {
std::cout << "Parse success\n";
} else {
std::cout << "Parse failed\n";
}
if (f != l)
std::cout << "Remaining input: '" << std::string(f,l) << "'\n";
}
为输入
enum NAME
{
VALUE1,
VALUE2 = 222,
VALUE3,
}
版画
Parse success
我正在编写一个解析器来处理带有枚举和结构的简单 C 头文件。我已经使用 Boost Spirit Qi 编写了一个解析器,几乎可以完成任务。我遇到了一个可以通过 hack 解决的问题,但我很好奇是否有可能更准确地解决它。
我正在处理的枚举很简单。这是一个例子:
enum <optional enum name>
{
VALUE1,
VALUE2 = 222,
VALUE3
}
解析此类枚举的代码片段:
IdParser %= lexeme[(alpha | '_') >> *(alnum | '_')];
EnumExprParser %= lexeme[+(char_ - (lit(",") | lit("}")))];
EnumValueParser %= IdParser >> -('=' >> EnumExprParser);
EnumParser %= lit("enum") >> -IdParser >> lit("{") >> (EnumValueParser % lit(",")) >> lit("}") >> -lit(";");
注意,我将枚举值解析为以逗号分隔的列表。但有时最后一个枚举值也以逗号结尾:VALUE3,
。我的脏解决方案如下:*(EnumValueParser >> -lit(","))
但这允许在没有分隔符的情况下解析多个枚举值。这对我来说是可以接受的,但我对更干净的解决方案感兴趣。我正在将枚举解析为以下结构:
struct EnumValue
{
std::string Name;
boost::optional<std::string> Value;
};
struct Enum
{
boost::optional<std::string> Name;
std::vector<EnumValue> Values;
};
非常感谢!
一个快速修复方法是替换
EnumBody = '{' >> EnumValue % "," >> '}';
与
EnumBody = '{' >> -EnumValue % "," >> '}';
虽然这很草率,因为它也允许 enum X { a,,,b }
。所以,这会更准确:
EnumBody = '{' >> EnumValue % "," >> -lit(',') >> '}';
注意 还有一个问题你还没有发现,那就是空枚举主体也应该被允许 (enum X {}
),所以让我们修复 那也是:
EnumBody = '{' >> -(EnumValue % ",") >> -lit(',') >> '}';
演示
#include <boost/fusion/adapted/std_pair.hpp>
#include <boost/fusion/adapted/struct.hpp>
#include <boost/spirit/include/qi.hpp>
#include <iostream>
namespace qi = boost::spirit::qi;
namespace ast {
using Id = std::string;
using EnumEntry = std::pair<Id, std::string>;
using EnumBody = std::vector<EnumEntry>;
struct EnumDef {
Id name;
EnumBody members;
};
}
BOOST_FUSION_ADAPT_STRUCT(ast::EnumDef, name, members)
template <typename It, typename Skipper = qi::space_type>
struct Parser : qi::grammar<It, ast::EnumDef(), Skipper> {
Parser() : Parser::base_type(Enum) {
using namespace qi;
Id = raw [(alpha | '_') >> *(alnum | '_')];
EnumExpr = +~char_(",}");
EnumValue = Id >> -('=' >> EnumExpr);
EnumBody = '{' >> -(EnumValue % ",") >> -lit(',') >> '}';
Enum = "enum" >> -Id >> EnumBody >> -lit(';');
}
private:
qi::rule<It, ast::EnumEntry(), Skipper> EnumValue;
qi::rule<It, ast::EnumBody(), Skipper> EnumBody;
qi::rule<It, ast::EnumDef(), Skipper> Enum;
// lexemes:
qi::rule<It, ast::Id()> Id, EnumExpr;
};
int main() {
using It = boost::spirit::istream_iterator;
It f(std::cin >> std::noskipws), l;
bool ok = qi::phrase_parse(f, l, Parser<It>(), qi::space);
if (ok) {
std::cout << "Parse success\n";
} else {
std::cout << "Parse failed\n";
}
if (f != l)
std::cout << "Remaining input: '" << std::string(f,l) << "'\n";
}
为输入
enum NAME
{
VALUE1,
VALUE2 = 222,
VALUE3,
}
版画
Parse success