Boost X3。摆脱身份 lambda 将属性传播到值

问题描述 投票:0回答:1

我正在将代码从 Boost Qi 迁移到 X3。我有非终结规则

expression
正在编译为结构
ast::Expression
。对于最小复制示例,我留下了 2 个形成
expression
的语句。
value
创建新的,而
'(' expression ')'
应该允许使用括号并将
expression
的结果传播到父定义。

在 Qi 中,我有

%=
运算符将属性复制到值。所以问题是 - 如何在下面的代码中摆脱 lambda
e2e

#include <iostream>
#include <tuple>
#include <string>
#include <variant>
#include <vector>

#include <boost/spirit/home/x3.hpp>
#include <boost/spirit/home/x3/support/ast/variant.hpp>
#include <boost/fusion/include/adapt_struct.hpp>
#include <boost/fusion/adapted/std_tuple.hpp>

namespace x3 = boost::spirit::x3;

namespace ast
{
    enum class Cmd  : int
    {
        const_val = 42
    };
    using data_t = x3::variant<int, double>;
    struct Expression
    {
        Cmd _cmd;
        std::string _args;
    };
}//ast

BOOST_FUSION_ADAPT_STRUCT(ast::Expression, _cmd, _args)



inline std::ostream& operator << (std::ostream& os, const ast::Expression& val)
{
    os << "{" << (int)val._cmd << ':' << val._args << "}";
    return os;
}


namespace client
{
    namespace x3 = boost::spirit::x3;

    x3::rule<class expression, ast::Expression> const expression("expression_stmt");
    x3::rule<class value, ast::data_t> const value("value_stmt");

    auto const value_def =
        x3::int_
        | x3::double_
        ;

    // construct expression form plain constant
    auto val_to_expr = [](auto& ctx) {
        auto& a1 = _attr(ctx);
        _val(ctx) = ast::Expression{ 
            ast::Cmd::const_val, 
            std::to_string(boost::get<int>(a1)) 
        };
        };

    // copy attribute to result value
    auto e2e = [](auto& ctx) {
        auto& a1 = _attr(ctx);
        _val(ctx) = a1;
        };

    auto const expression_def =
        value[val_to_expr]
// !!!! why I need e2e to copy attr to value?
        | ('(' > expression > ')') [e2e]
        ;

    BOOST_SPIRIT_DEFINE(expression, value);
}//ns:client

auto parse_text(const std::string& s)
{

    namespace x3 = boost::spirit::x3;
    auto iter = s.cbegin();
    auto end_iter = s.cend();

    ast::Expression res;
    x3::ascii::space_type space;

    bool success = x3::phrase_parse(iter, end_iter,
        client::expression, space,
        res);
    if (success && iter == end_iter)
    {
        std::cout << "Success\n";
    }
    else {
        std::cout << "Parse failure\n{";
        while (iter != end_iter)
            std::cout << *iter++;
        std::cout << "}\n";
    }
    return res;
}

int main()
{
    auto test = +[](const std::string& s) {
            std::cout << "parsing " << s << '\n';
            auto res = parse_text(s);
            std::cout << "'" << res << "'\n";
        };
    ;
    test("123");
    test("(123)");
}
c++ boost-spirit boost-spirit-x3
1个回答
0
投票

您可以在

force_attribute
上设置
x3::rule
模板参数:

x3::rule<class expression, ast::Expression, true> const expression{"expression"};

但是,这意味着所有属性分配都将被强制,所以你必须提供便利

val_to_expr
。您可以轻松地使用相关的构造函数:

struct Expression {
    Cmd         _cmd;
    std::string _args;

    Expression(value_t v = {}) //
        : _cmd(Cmd::const_val)
        , _args(std::to_string(get<int>(v))) {}
};

此外,为了使用构造函数,您应该删除 Fusion 适配,无论如何您都没有使用它。现在一切都大约是代码大小的一半:

住在Coliru

#include <boost/spirit/home/x3.hpp>
#include <boost/spirit/home/x3/support/ast/variant.hpp>
#include <iomanip>
#include <iostream>
#include <optional>

namespace x3 = boost::spirit::x3;

namespace ast {
    using boost::get;
    enum class Cmd : int { const_val = 42 };
    using value_t = x3::variant<int, double>;

    struct Expression {
        Cmd         _cmd;
        std::string _args;

        Expression(value_t v = {}) //
            : _cmd(Cmd::const_val)
            , _args(std::to_string(get<int>(v))) {}

        friend std::ostream& operator<<(std::ostream& os, Expression const& e) {
            os << "{" << static_cast<int>(e._cmd) << ':' << e._args << "}";
            return os;
        }
    };
} // namespace ast

namespace client {
    x3::rule<class expression, ast::Expression, true> const expression{"expression"};
    x3::rule<class value, ast::value_t> const               value{"value"};

    auto const value_def      = x3::int_ | x3::double_;
    auto const expression_def = value | ('(' > expression > ')');

    BOOST_SPIRIT_DEFINE(expression, value)
} // namespace client

std::optional<ast::Expression> parse_text(std::string_view s) {
    if (ast::Expression res;
        phrase_parse(s.cbegin(), s.cend(), client::expression >> x3::eoi, x3::space, res))
        return res;
    else
        return std::nullopt;
}

int main() {
    for (std::string_view s : {"123", "(123)"})
        if (auto e = parse_text(s))
            std::cout << quoted(s) << " -> " << *e << "\n";
        else
            std::cout << quoted(s) << " failed\n";
}

印刷

"123" -> {42:123}
"(123)" -> {42:123}

奖金/警告

  1. 讽刺的是,到最后,你不再需要

    force_attribute
    ,因为你不使用任何语义动作(参见Boost Spirit:“语义动作是邪恶的”?)。

  2. 此外,产生式

    int_ | double_
    永远不会匹配双精度(参见例如为什么这个 boost::spirit::qi 规则不能成功解析?)。

  3. 看起来您正在制作经典的表达式解析器。因此,您非常期望值是 be 表达式:

    struct Command {
        enum Code : int { const_val = 42 };
        Code        _cmd;
        std::string _args;
    };
    
    using Expression = boost::variant<int, double, Command>;
    

    现在您可以享受无语义动作 Fusion 适应的幸福:

    x3::rule<class command, ast::Command>  const command{"command"};
    x3::rule<class expr,  ast::Expression> const expr{"expr"};
    
    auto const dbl         = x3::real_parser<double, x3::strict_real_policies<double>>{};
    auto const value       = dbl | x3::int_;
    auto const command_def = '(' > x3::attr(ast::Command::const_val) > x3::raw[value % ','] > ')';
    auto const expr_def    = command | value;
    

    打印在 Coliru 上直播

    "123" -> 123
    "(123)" -> {42:123}
    
  4. 到达这一点后,很明显可能没有必要将

    _args
    作为字符串。我打赌你宁愿用
    std::vector<Expression>
    进行递归?

    struct Command {
        enum Code : int { const_val = 42 };
        Code                    _cmd;
        std::vector<Expression> _args;
    };
    

    Command
    规则进行一个小改动:

    auto const command_def = '(' > x3::attr(ast::Command::const_val) > (expr % ',') > ')';
    

现在您可以解析任意嵌套的表达式,这些表达式甚至不会展平为 AST 中的字符串:

住在Coliru

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

namespace x3 = boost::spirit::x3;

namespace ast {
    struct Command;
    using Expression = boost::variant<int, double, boost::recursive_wrapper<Command>>;

    struct Command {
        enum Code : int { const_val = 42 };
        Code                    _cmd;
        std::vector<Expression> _args;

        friend std::ostream& operator<<(std::ostream& os, std::vector<Expression> const& ee) {
            for (auto sep ="["; auto& e : ee)
                os << std::exchange(sep, ",") << e;
            return ee.empty() ? os : os << "]";
        }
        friend std::ostream& operator<<(std::ostream& os, Command const& e) {
            os << "{" << e._cmd << ':' << e._args << "}";
            return os;
        }
        friend std::ostream& operator<<(std::ostream& os, Code const& c) {
            switch (c) {
                case const_val: return os << "const_val";
                default:        return os << "?";
            }
        }
    };

} // namespace ast

BOOST_FUSION_ADAPT_STRUCT(ast::Command, _cmd, _args)

namespace client {
    x3::rule<class command, ast::Command>  const command{"command"};
    x3::rule<class expr,  ast::Expression> const expr{"expr"};

    auto const dbl         = x3::real_parser<double, x3::strict_real_policies<double>>{};
    auto const value       = dbl | x3::int_;
    auto const command_def = '(' > x3::attr(ast::Command::const_val) > (expr % ',') > ')';
    auto const expr_def    = command | value;

    BOOST_SPIRIT_DEFINE(command, expr)
} // namespace client

std::optional<ast::Expression> parse_text(std::string_view s) {
    if (ast::Expression e;
        phrase_parse(s.cbegin(), s.cend(), client::expr >> x3::eoi, x3::space, e))
        return e;
    else
        return std::nullopt;
}

int main() {
    for (std::string_view s : {
             "123",
             "(123)",
             "(123, 234, 345)",
             "(123, 234, (345, 42.7e-3))",
         })
        if (auto e = parse_text(s))
            std::cout << std::setw(32) << quoted(s) << " -> " << *e << "\n";
        else
            std::cout << std::setw(32) << quoted(s) << " failed\n";
}

印刷:

                           "123" -> 123
                         "(123)" -> {const_val:[123]}
               "(123, 234, 345)" -> {const_val:[123,234,345]}
    "(123, 234, (345, 42.7e-3))" -> {const_val:[123,234,{const_val:[345,0.0427]}]}
最新问题
© www.soinside.com 2019 - 2024. All rights reserved.