c++ - 如何使用 spirit x3 将结果解析结果移动到结构中

标签 c++ parsing boost-spirit

我想将包含函数原型(prototype)的输入缓冲区解析为函数原型(prototype) vector 。我创建了一个具有 3 个成员的函数原型(prototype)结构:returnType、名称和参数列表。我遇到了一个编译器错误,提示它无法将解析的结果移动到结构中。我错过了什么吗?

//#define BOOST_SPIRIT_X3_DEBUG
#include <boost/spirit/home/x3.hpp>
#include <boost/fusion/adapted.hpp>
#include <boost/fusion/adapted/struct/adapt_struct.hpp>
#include <boost/config/warning_disable.hpp>
#include <boost/fusion/include/io.hpp>
#include <boost/spirit/home/x3/support/ast/variant.hpp>
#include <list>
#include <iostream>

namespace x3 = boost::spirit::x3;
namespace ascii = boost::spirit::x3::ascii;
using boost::spirit::x3::ascii::space;

namespace ast {
struct identifier {
    std::string name;
};

struct argument {
    identifier typeName;
    identifier value;
};

struct function_call {
    identifier name;
    std::list<argument> arguments;
};

struct function_prototype {
    identifier returnType;
    identifier name;
    std::list<argument> arguments;
};
} // namespace ast

BOOST_FUSION_ADAPT_STRUCT(ast::identifier, (std::string, name))
BOOST_FUSION_ADAPT_STRUCT(ast::argument, (struct identifier, typeName) (struct identifier, value))
BOOST_FUSION_ADAPT_STRUCT(ast::function_call, (struct identifier, name) (struct identifier, arguments))
BOOST_FUSION_ADAPT_STRUCT(ast::function_prototype, (struct identifier, returnType) (struct identifier, name) (std::list<struct argument>, arguments))

namespace parser
{
    struct identifier_class;
    typedef x3::rule<identifier_class, ast::identifier> identifier_type;
    identifier_type const identifier = "identifier";
    auto const identifier_def = x3::raw[x3::lexeme[(x3::alpha | '_') >> *(x3::alnum | '_')]];
    BOOST_SPIRIT_DEFINE(identifier)

    struct argument_class;
    typedef x3::rule<argument_class, ast::argument> argument_type;
    argument_type const argument = "argument";
    auto const argument_def = x3::raw[identifier >> identifier];
    BOOST_SPIRIT_DEFINE(argument)

    struct function_call_class;
    typedef x3::rule<function_call_class, ast::function_call> function_call_type;
    function_call_type const function_call = "function_call";
    auto const function_call_def = x3::raw[identifier >> '(' > -(argument % ',') > ')'];
    BOOST_SPIRIT_DEFINE(function_call)

    struct function_prototype_class;
    typedef x3::rule<function_prototype_class, ast::function_prototype> function_prototype_class_type;
    function_prototype_class_type const function_prototype = "function_prototype";
    auto const function_prototype_def =
    x3::raw[identifier >> identifier >> '(' > -(argument % ',') > ')'];
    BOOST_SPIRIT_DEFINE(function_prototype)

    auto const functionProtos = function_prototype >> *(function_prototype);
}

namespace Application {
class Parser {

  public:
    void functionParser(const std::string& input) {
        std::vector<ast::function_call> output;
        x3::phrase_parse(input.begin(), input.end(), parser::functionProtos, x3::space, output);
        std::cout << "success\n";
    }
};
} // namespace parser

最佳答案

融合适应

一个问题是改编:您提供类型但将它们拼写为全局命名空间中类型的前向声明:

BOOST_FUSION_ADAPT_STRUCT(ast::argument, (struct identifier, typeName) (struct identifier, value))
BOOST_FUSION_ADAPT_STRUCT(ast::function_call, (struct identifier, name) (struct identifier, arguments))
BOOST_FUSION_ADAPT_STRUCT(ast::function_prototype, (struct identifier, returnType) (struct identifier, name) (std::list<struct argument>, arguments))

可能可以通过替换为例如来修复ast::identifier,但何必呢?您绝对可以使用 c++11 变体并让编译器找出类型:

BOOST_FUSION_ADAPT_STRUCT(ast::identifier, name)
BOOST_FUSION_ADAPT_STRUCT(ast::argument, typeName, value)
BOOST_FUSION_ADAPT_STRUCT(ast::function_call, name, arguments)
BOOST_FUSION_ADAPT_STRUCT(ast::function_prototype, returnType, name, arguments)

原始

第二个问题是 x3::raw[]raw 指令将迭代器范围公开为属性,这与绑定(bind)的属性类型(例如 ast::identifier)不兼容。

在这种特殊情况下,您正在解析(与我在 the previous answer 中的评论相比,您没有解析,只是匹配)。因此,您需要将合成属性与目标属性类型相匹配:

auto const identifier_def = x3::lexeme[(x3::alpha | x3::char_('_')) >> *(x3::alnum | x3::char_('_'))];

单元素序列

Qi/X3 存在一个挥之不去的问题,导致单元素序列的属性传播变得困惑。在这种情况下,如果您简单地将 identifier 解析为 std::string(并且 fusion 正确地将其分配给 ast,它会有所帮助: :identifier 从那里):

auto const identifier_def = x3::lexeme[(x3::alpha | x3::char_('_')) >> *(x3::alnum | x3::char_('_'))];

删除其余的 rawp[] 指令使其编译:

Live On Coliru

#define BOOST_SPIRIT_X3_DEBUG
#include <boost/spirit/home/x3.hpp>
#include <boost/fusion/adapted.hpp>
#include <boost/fusion/include/io.hpp>
#include <boost/spirit/home/x3/support/ast/variant.hpp>
#include <list>
#include <iostream>

namespace x3 = boost::spirit::x3;
namespace ascii = boost::spirit::x3::ascii;
using boost::spirit::x3::ascii::space;

namespace ast {
struct identifier {
    std::string name;
};

struct argument {
    identifier typeName;
    identifier value;
};

struct function_call {
    identifier name;
    std::list<argument> arguments;
};

struct function_prototype {
    identifier returnType;
    identifier name;
    std::list<argument> arguments;
};
} // namespace ast

BOOST_FUSION_ADAPT_STRUCT(ast::identifier, name)
BOOST_FUSION_ADAPT_STRUCT(ast::argument, typeName, value)
BOOST_FUSION_ADAPT_STRUCT(ast::function_call, name, arguments)
BOOST_FUSION_ADAPT_STRUCT(ast::function_prototype, returnType, name, arguments)

namespace parser
{
    struct identifier_class;
    typedef x3::rule<identifier_class, std::string> identifier_type;
    identifier_type const identifier = "identifier";
    auto const identifier_def = x3::lexeme[(x3::alpha | x3::char_('_')) >> *(x3::alnum | x3::char_('_'))];
    BOOST_SPIRIT_DEFINE(identifier)

    struct argument_class;
    typedef x3::rule<argument_class, ast::argument> argument_type;
    argument_type const argument = "argument";
    auto const argument_def = identifier >> identifier;
    BOOST_SPIRIT_DEFINE(argument)

    struct function_call_class;
    typedef x3::rule<function_call_class, ast::function_call> function_call_type;
    function_call_type const function_call = "function_call";
    auto const function_call_def = identifier >> '(' > -(argument % ',') > ')';
    BOOST_SPIRIT_DEFINE(function_call)

    struct function_prototype_class;
    typedef x3::rule<function_prototype_class, ast::function_prototype> function_prototype_class_type;
    function_prototype_class_type const function_prototype = "function_prototype";
    auto const function_prototype_def =
        identifier >> identifier >> '(' > -(argument % ',') >> x3::expect[')'] >> ';';
    BOOST_SPIRIT_DEFINE(function_prototype)

    auto const functionProtos = +function_prototype;
}

namespace Application {
class Parser {

  public:
    void functionParser(const std::string& input) {
        if (0) {
            ast::identifier output;
            x3::phrase_parse(input.begin(), input.end(), parser::identifier, x3::space, output);
        }
        if (0) {
            ast::argument output;
            x3::phrase_parse(input.begin(), input.end(), parser::argument, x3::space, output);
        }
        if (0) {
            ast::function_call output;
            x3::phrase_parse(input.begin(), input.end(), parser::function_call, x3::space, output);
        }
        if (0) {
            ast::function_prototype output;
            x3::phrase_parse(input.begin(), input.end(), parser::function_prototype, x3::space, output);
        }
        {
            std::vector<ast::function_prototype> output;
            x3::phrase_parse(input.begin(), input.end(), parser::functionProtos, x3::space, output);
            std::cout << "success: " << output.size() << " prototypes parsed\n";
        }
    }
};
} // namespace parser

int main()
{
    Application::Parser p;
    p.functionParser("void foo(int a); float bar(double b, char c);");
}

打印

success: 2 prototypes parsed

简化

只要您不跨翻译单元共享规则或需要递归规则,就不需要定义/宏。而是简化:

auto const identifier         = as<std::string>("identifier", x3::lexeme[(x3::alpha | x3::char_('_')) >> *(x3::alnum | x3::char_('_'))]);
auto const argument           = as<ast::argument>("argument", identifier >> identifier);
auto const function_call      = as<ast::function_call>("function_call", identifier >> '(' > -(argument % ',') > ')');
auto const function_prototype = as<ast::function_prototype>("function_prototype",
    identifier >> identifier >> '(' > -(argument % ',') >> x3::expect[')'] >> ';');

这是使用一个非常简单的速记来键入规则属性:

template <typename T> auto as = [](auto name, auto p) { return x3::rule<struct _, T> {name} = p; };

查看 Live On Coliru

//#define BOOST_SPIRIT_X3_DEBUG
#include <boost/spirit/home/x3.hpp>
#include <boost/fusion/adapted.hpp>
#include <list>
#include <iostream>

namespace x3 = boost::spirit::x3;
using boost::spirit::x3::ascii::space;

namespace ast {
    struct identifier {
        std::string name;
    };

    struct argument {
        identifier typeName;
        identifier value;
    };

    struct function_call {
        identifier name;
        std::list<argument> arguments;
    };

    struct function_prototype {
        identifier returnType;
        identifier name;
        std::list<argument> arguments;
    };
} // namespace ast

BOOST_FUSION_ADAPT_STRUCT(ast::identifier, name)
BOOST_FUSION_ADAPT_STRUCT(ast::argument, typeName, value)
BOOST_FUSION_ADAPT_STRUCT(ast::function_call, name, arguments)
BOOST_FUSION_ADAPT_STRUCT(ast::function_prototype, returnType, name, arguments)

namespace parser
{
    template <typename T> auto as = [](auto name, auto p) { return x3::rule<struct _, T> {name} = p; };

    auto const identifier         = as<std::string>("identifier", x3::lexeme[(x3::alpha | x3::char_('_')) >> *(x3::alnum | x3::char_('_'))]);
    auto const argument           = as<ast::argument>("argument", identifier >> identifier);
    auto const function_call      = as<ast::function_call>("function_call", identifier >> '(' > -(argument % ',') > ')');
    auto const function_prototype = as<ast::function_prototype>("function_prototype",
        identifier >> identifier >> '(' > -(argument % ',') >> x3::expect[')'] >> ';');

    auto const functionProtos = +function_prototype;
}

namespace Application {
    class Parser {

        public:
            void functionParser(const std::string& input) {
                std::vector<ast::function_prototype> output;
                x3::phrase_parse(input.begin(), input.end(), parser::functionProtos, x3::space, output);
                std::cout << "success: " << output.size() << " prototypes parsed\n";
            }
    };
} // namespace parser

int main()
{
    Application::Parser p;
    p.functionParser("void foo(int a); float bar(double b, char c);");
}

同时打印

success: 2 prototypes parsed

关于c++ - 如何使用 spirit x3 将结果解析结果移动到结构中,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/56157223/

相关文章:

c++ - 在 C++ 中交换多维数组

c++ - 我可以返回一个 vector 以使用 C++11 扩展现有 vector 吗?

c++ - `VirtualAllocEx`指定不同的起始地址时返回相同的地址?

c++ - 在循环中将字符串和运行索引连接到字符串中

Java解析XML日期-排除时间

c++ - 用 libclang : getting CXX_BASE_SPECIFIER cursors when base types unknown 解析

c++ - 将类转换为结构

boost - 哪个boost库包含boost spirit?

c++ - qi::rule<It, std::string ()> 不解析输入字符串

boost - 使用 Boost Spirit X3 编写解析器对 future 有多安全?