c++ - 为什么我无法访问语义操作中的值?

标签 c++ boost-spirit boost-spirit-qi

我正在尝试编写一个解析器来使用 boost::spirit 创建 AST。作为第一步,我尝试将数值包装在 AST 节点中。这是我正在使用的代码:

AST_NodePtr make_AST_NodePtr(const int& i) {
    return std::make_shared<AST_Node>(i);
}

namespace qi = boost::spirit::qi;
namespace ascii = boost::spirit::ascii;
namespace l = qi::labels;

 template<typename Iterator>
    struct test_grammar : qi::grammar<Iterator, AST_NodePtr(), ascii::space_type> {
        test_grammar() : test_grammar::base_type(test) {

            test = qi::int_ [qi::_val = make_AST_NodePtr(qi::_1)];
        }
        qi::rule<Iterator, AST_NodePtr(), ascii::space_type> test;

    }; 

据我从文档中理解,q::_1 应该包含由 qi::int_ 解析的值,但是上面的代码总是给我一个错误

invalid initialization of reference of type ‘const int&’ from expression of type ‘const _1_type {aka const boost::phoenix::actor<boost::spirit::argument<0> >}

即使 qi::_1 应该保存解析值,为什么这不起作用?我还能如何将输入解析为自定义 AST?

最佳答案

您在语义操作中使用常规函数。

这意味着在构造函数中编译器将尝试使用提供的参数调用 make_AST_NodePtr 函数:qi::_1

Q. Why does this not work even though qi::_1 is supposed to hold the parsed valued?

A. qi::_1保存解析的值。它代表(is-a-placeholder-for)函数调用中的第一个未绑定(bind)参数

这/显然/永远不会起作用。该函数需要一个整数。

那么什么给出了呢?


您需要创建一个“惰性”或“延迟”函数以在语义操作中使用。仅使用预先提供的 Boost Phoenix 仿函数,您可以拼写出来:

test  = qi::int_ [ qi::_val = px::construct<AST_NodePtr>(px::new_<AST_Node>(qi::_1)) ];

这样你就不需要辅助函数了。但结果既丑陋又不理想。所以,让我们做得更好!

使用 Phoenix 函数包装器

struct make_shared_f {
    std::shared_ptr<AST_Node> operator()(int v) const {
        return std::make_shared<AST_Node>(v);
    }
};
px::function<make_shared_f> make_shared_;

定义此定义后,您可以将语义操作简化为:

test  = qi::int_ [ qi::_val = make_shared_(qi::_1) ];

实际上,如果您使其通用,您可以将其重用于多种类型:

template <typename T>
    struct make_shared_f {
        template <typename... Args>
            std::shared_ptr<T> operator()(Args&&... args) const {
                return std::make_shared<T>(std::forward<Args>(args)...);
            }
    };
px::function<make_shared_f<AST_Node> > make_shared_;

演示

这是一个独立的示例,显示了该过程中的一些样式修复:

Live On Coliru

#include <boost/spirit/include/qi.hpp>
#include <boost/spirit/include/phoenix.hpp>
#include <memory>

struct AST_Node {
    AST_Node(int v) : _value(v) {}
    int value() const { return _value; }
  private:
    int _value;
};

using AST_NodePtr = std::shared_ptr<AST_Node>;

AST_NodePtr make_AST_NodePtr(const int& i) {
    return std::make_shared<AST_Node>(i);
}

namespace qi    = boost::spirit::qi;
namespace px    = boost::phoenix;

template<typename Iterator>
struct test_grammar : qi::grammar<Iterator, AST_NodePtr()> {
    test_grammar() : test_grammar::base_type(start) {
        using boost::spirit::ascii::space;

        start = qi::skip(space) [ test ];

        test  = qi::int_ [ qi::_val = make_shared_(qi::_1) ];
    }
  private:
    struct make_shared_f {
        std::shared_ptr<AST_Node> operator()(int v) const {
            return std::make_shared<AST_Node>(v);
        }
    };
    px::function<make_shared_f> make_shared_;
    //
    qi::rule<Iterator, AST_NodePtr()> start;
    qi::rule<Iterator, AST_NodePtr(), boost::spirit::ascii::space_type> test;
}; 

int main() {
AST_NodePtr parsed;

    std::string const input ("42");
    auto f = input.begin(), l = input.end();
    test_grammar<std::string::const_iterator> g;
    bool ok = qi::parse(f, l, g, parsed);

    if (ok) {
        std::cout << "Parsed: " << (parsed? std::to_string(parsed->value()) : "nullptr") << "\n";
    } else {
        std::cout << "Failed\n";
    }

    if (f!=l)
    {
        std::cout << "Remaining input: '" << std::string(f, l) << "'\n";
    }
}

打印

Parsed: 42

奖励:使用 BOOST_PHOENIX_ADAPT_FUNCTION 的替代方案

如果您愿意,您实际上可以使用免费函数,并按如下方式使用它:

Live On Coliru

#include <boost/spirit/include/qi.hpp>
#include <boost/spirit/include/phoenix.hpp>
#include <memory>

struct AST_Node {
    AST_Node(int v) : _value(v) {}
    int value() const { return _value; }
  private:
    int _value;
};

using AST_NodePtr = std::shared_ptr<AST_Node>;

AST_NodePtr make_AST_NodePtr(int i) {
    return std::make_shared<AST_Node>(i);
}

BOOST_PHOENIX_ADAPT_FUNCTION(AST_NodePtr, make_AST_NodePtr_, make_AST_NodePtr, 1)

namespace qi    = boost::spirit::qi;
namespace px    = boost::phoenix;

template<typename Iterator>
struct test_grammar : qi::grammar<Iterator, AST_NodePtr()> {
    test_grammar() : test_grammar::base_type(start) {
        using boost::spirit::ascii::space;

        start = qi::skip(space) [ test                            ] ;
        test  = qi::int_        [ qi::_val = make_AST_NodePtr_(qi::_1) ] ;
    }
  private:
    qi::rule<Iterator, AST_NodePtr()> start;
    qi::rule<Iterator, AST_NodePtr(), boost::spirit::ascii::space_type> test;
}; 

int main() {
AST_NodePtr parsed;

    std::string const input ("42");
    auto f = input.begin(), l = input.end();
    test_grammar<std::string::const_iterator> g;
    bool ok = qi::parse(f, l, g, parsed);

    if (ok) {
        std::cout << "Parsed: " << (parsed? std::to_string(parsed->value()) : "nullptr") << "\n";
    } else {
        std::cout << "Failed\n";
    }

    if (f!=l)
    {
        std::cout << "Remaining input: '" << std::string(f, l) << "'\n";
    }
}

关于c++ - 为什么我无法访问语义操作中的值?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/32854007/

相关文章:

c++ - 如何使用 API 将 map<string,string> 传递给 py?

c++ - 如何在 Spirit X3 中正确指定锚定条件?

c++ - 如何使用 qi::hold[] 解析器指令。 (boost::swap 的属性类型问题)

c++ - Boost Spirit 换行和输入结束

c++ - rand() 仍然给出相同的值

c++ - 返回指向局部函数变量的指针

c++ - 在 C++ 中将不同的键映射到相同的值

c++ - 使用 Boost Spirit 匹配字符串

c++ - 在 Boost Spirit Qi 中,我如何将每个字符匹配到下一个空格(带预跳过)

c++ - 支持具有固定数组的对象的 BOOST_FUSION_ADAPT_STRUCT?