如何使用Spirit x3将结果解析的结果移动到结构中 [英] How to move results parsed results into a struct using spirit x3

查看:87
本文介绍了如何使用Spirit x3将结果解析的结果移动到结构中的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我想将包含函数原型的输入缓冲区解析为函数原型的向量.我创建了一个具有3个成员的函数原型结构:returnType,名称和参数列表.我遇到编译器错误,抱怨它无法将已解析的结果移入结构.我想念什么吗?

I want to parse an input buffer containing function prototypes into a vector of function prototypes. I have created a function prototype struct that has 3 members: returnType, name and argument list. I am running into a compiler error that complains that it is unable to move the parsed results into the struct. Am I missing something?

//#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

推荐答案

融合适应

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

Fusion Adapt

One issue is the adaptations: you're supplying types but spelling them as forward declarations of types in the global namespace:

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版本,并让编译器找出类型:

It might be okay to fix by replacing with e.g. ast::identifier, but why bother? You can definitely use the c++11 variation and let the compiler figure out the types:

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)

RAW

第二个肚带是 x3 :: raw [] . raw 指令将迭代器范围公开为属性,并且与绑定的属性类型(例如 ast :: identifier )不兼容.

RAW

The second cinch is x3::raw[]. The raw directive exposes an iterator range as the attribute, and that's not compatible with the bound attribute type (e.g. ast::identifier).

在这种情况下,您正在解析(与我在上一个答案中的注释比较)解析,并且仅匹配).因此,您需要将综合属性与目标属性类型进行匹配:

In this particular case, you ARE parsing (compare with my remark from the previous answer, where you weren't parsing, and only matching). So you need to match up the synthesized attribute with the target attribute type:

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

单元素序列

Qi/X3存在一个挥之不去的问题,这导致单元素序列的属性传播变得混乱.在这种情况下,如果您只是将 identifier 解析为 std :: string (并且融合正确地将其分配给 ast),则将很有帮助:identifier 从此处开始):

Single-Element Sequences

There's a lingering issue with Qi/X3 that causes attribute propagation of single-element sequences to get confused. In this case it helps if you simply make identifier parse into std::string (and fusion will correctly assign that to ast::identifier from there):

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

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

Dropping the rest of the rawp[] directives makes it compile:

在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

SIMPLIFY

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

SIMPLIFY

As long as you don't share rules across translation units OR require recursive rules, there is no need for the define/macros. Instead simplify:

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[')'] >> ';');

这是使用非常简单的速记来键入规则attirbutes:

That's using a very simple shorthand to type the rule attirbutes:

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

查看 在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

这篇关于如何使用Spirit x3将结果解析的结果移动到结构中的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆