如何用Boost Spirit X3解析嵌套成员访问生成嵌套Get节点AST?
使用Boost Spirit X3生成嵌套Get节点AST解析HLSL链式成员访问
完全可以用Boost Spirit X3实现这个需求,核心是利用X3对左递归语法的支持,把链式成员访问的扁平序列转化为嵌套的Get节点结构,和《Crafting Interpreters》里的AST结构对齐。
1. 定义AST节点结构
首先模仿书中的结构,定义表示表达式的类型,包含标识符和成员访问(Get)两种节点:
#include <boost/spirit/home/x3.hpp> #include <boost/fusion/include/adapt_struct.hpp> #include <string> #include <variant> #include <memory> namespace ast { struct Expr; using ExprPtr = std::unique_ptr<Expr>; struct Identifier { std::string name; }; struct Get { ExprPtr object; std::string name; }; struct Expr { std::variant<Identifier, Get> value; }; } // 适配Boost Fusion,让X3能自动填充AST结构 BOOST_FUSION_ADAPT_STRUCT(ast::Identifier, name) BOOST_FUSION_ADAPT_STRUCT(ast::Get, object, name)
2. 编写X3解析规则
利用X3的左递归规则能力,先定义基础的标识符规则,再定义成员访问的递归规则,每次匹配到.标识符时,将之前的表达式作为Get节点的object,新标识符作为name,生成嵌套结构:
namespace parser { namespace x3 = boost::spirit::x3; // 声明左递归规则 x3::rule<class expr_rule, ast::ExprPtr> const expr = "expr"; // 标识符规则:匹配HLSL合法标识符(字母、下划线开头,后跟字母、数字、下划线) auto const identifier = x3::lexeme[(x3::alpha | '_') >> *(x3::alnum | '_')]; // 成员访问规则:基础标识符 + 重复的 .标识符 auto const member_access = identifier >> *( '.' >> identifier // 每次匹配到 .标识符,将之前的Expr包装成Get节点 [](auto& ctx) { auto member_name = x3::_attr(ctx); auto& prev_expr = x3::_val(ctx); x3::_val(ctx) = std::make_unique<ast::Expr>( ast::Get{std::move(prev_expr), member_name} ); } ) // 初始标识符包装成Identifier节点 [](auto& ctx) { auto id_name = x3::_attr(ctx); x3::_val(ctx) = std::make_unique<ast::Expr>(ast::Identifier{id_name}); }; // 绑定左递归规则的定义 auto const expr_def = member_access; BOOST_SPIRIT_DEFINE(expr); }
3. 测试解析逻辑
编写测试代码验证解析结果,比如解析first.Second.third会生成三层嵌套的Get节点:
#include <iostream> #include <iomanip> // 递归打印AST结构,用于验证 void print_ast(const ast::Expr& expr, int indent = 0) { std::visit([&](const auto& node) { using T = std::decay_t<decltype(node)>; if constexpr (std::is_same_v<T, ast::Identifier>) { std::cout << std::setw(indent) << "" << "Identifier: " << node.name << "\n"; } else if constexpr (std::is_same_v<T, ast::Get>) { std::cout << std::setw(indent) << "" << "Get:\n"; std::cout << std::setw(indent + 4) << "" << "Object:\n"; print_ast(*node.object, indent + 8); std::cout << std::setw(indent + 4) << "" << "Name: " << node.name << "\n"; } }, expr.value); } int main() { std::string input = "first.Second.third"; ast::ExprPtr result; auto iter = input.begin(); auto end = input.end(); bool success = boost::spirit::x3::parse(iter, end, parser::expr, result); if (success && iter == end) { std::cout << "解析成功,AST结构:\n"; print_ast(*result); } else { std::cout << "解析失败,剩余未解析部分: " << std::string(iter, end) << "\n"; } return 0; }
输出结果
运行后会输出嵌套的AST结构:
解析成功,AST结构: Get: Object: Get: Object: Identifier: first Name: Second Name: third
这完全符合《Crafting Interpreters》中嵌套Get节点的AST结构,后续可以扩展这个规则,整合到完整的HLSL解析器中。
内容的提问来源于stack exchange,提问作者Daniel Dokipen Elliott
相关产品推荐
相关产品推荐

