当前位置: 首页 > news >正文

使用组合子构建抽象语法树

引言

组合子(Combinator)是一种函数式编程中的概念,它允许我们通过组合简单的函数来构建复杂的逻辑。在解析器和抽象语法树(AST)的构建中,组合子提供了一种简洁且模块化的方法。本文将介绍如何使用组合子来构建一个简单的表达式解析器,并生成对应的抽象语法树。

组合子基础

组合子的核心思想是通过函数的组合来表达复杂的逻辑,而不必显式地使用变量。在C++中,我们可以使用函数对象(Functor)来实现组合子。

基本组合子

以下是一些常见的组合子:

  • Sequence: 顺序执行两个解析器,并返回组合后的结果。
  • Choice: 尝试第一个解析器,如果失败则尝试第二个解析器。
  • Repeat: 重复执行一个解析器,直到无法匹配为止。

示例代码

#include <string>
#include <vector>
#include <functional>
#include <variant>using namespace std;// 定义一个结果类型,用于存储解析结果
template <typename T>
struct Result {bool success;T value;size_t position;Result(bool s, T v, size_t p) : success(s), value(v), position(p) {}
};// 定义一个解析器类型,输入为字符串,输出为结果
template <typename T>
using Parser = function<Result<T>(const string&, size_t)>;

构建解析器

基本解析器

数字解析器

Parser<int> number() {return [](const string& input, size_t pos) -> Result<int> {size_t end = pos;while (end < input.size() && isdigit(input[end])) {end++;}if (end == pos) {return {false, 0, pos};}int val = stoi(input.substr(pos, end - pos));return {true, val, end};};
}

操作符解析器

Parser<char> operator_parser() {return [](const string& input, size_t pos) -> Result<char> {if (pos < input.size() && (input[pos] == '+' || input[pos] == '-')) {return {true, input[pos], pos + 1};}return {false, ' ', pos};};
}

组合子实现

Sequence 组合子

template <typename A, typename B>
Parser<pair<A, B>> sequence(Parser<A> a, Parser<B> b) {return [=](const string& input, size_t pos) -> Result<pair<A, B>> {auto a_result = a(input, pos);if (!a_result.success) {return {false, {}, pos};}auto b_result = b(input, a_result.position);if (!b_result.success) {return {false, {}, pos};}return {true, {a_result.value, b_result.value}, b_result.position};};
}

Choice 组合子

template <typename T>
Parser<T> choice(Parser<T> a, Parser<T> b) {return [=](const string& input, size_t pos) -> Result<T> {auto a_result = a(input, pos);if (a_result.success) {return a_result;}return b(input, pos);};
}

Repeat 组合子

template <typename T>
Parser<vector<T>> repeat(Parser<T> p) {return [=](const string& input, size_t pos) -> Result<vector<T>> {vector<T> result;size_t current_pos = pos;while (true) {auto p_result = p(input, current_pos);if (p_result.success) {result.push_back(p_result.value);current_pos = p_result.position;} else {break;}}if (result.empty()) {return {false, {}, pos};}return {true, result, current_pos};};
}

生成抽象语法树

AST节点定义

struct ASTNode {virtual ~ASTNode() = default;virtual string toString() const = 0;
};struct NumberNode : ASTNode {int value;NumberNode(int v) : value(v) {}string toString() const override {return to_string(value);}
};struct BinaryOperationNode : ASTNode {char op;unique_ptr<ASTNode> left;unique_ptr<ASTNode> right;BinaryOperationNode(char o, unique_ptr<ASTNode> l, unique_ptr<ASTNode> r): op(o), left(move(l)), right(move(r)) {}string toString() const override {return "(" + left->toString() + " " + op + " " + right->toString() + ")";}
};

解析器与AST生成

表达式解析器

Parser<unique_ptr<ASTNode>> expression() {// 实现一个简单的表达式解析器,支持加减法auto term = number();auto operator_term = sequence(operator_parser(), term);auto expression_parser = sequence(term, repeat(operator_term));return [=](const string& input, size_t pos) -> Result<unique_ptr<ASTNode>> {auto expr_result = expression_parser(input, pos);if (!expr_result.success) {return {false, nullptr, pos};}auto& [term_result, operator_terms] = expr_result.value;unique_ptr<ASTNode> ast = make_unique<NumberNode>(term_result);for (auto& [op, operand] : operator_terms) {ast = make_unique<BinaryOperationNode>(op, move(ast), make_unique<NumberNode>(operand));}return {true, move(ast), expr_result.position};};
}

测试与示例

int main() {string input = "123+45-67";auto parser = expression();auto result = parser(input, 0);if (result.success) {cout << "Parsing successful!" << endl;cout << "AST: " << result.value->toString() << endl;} else {cout << "Parsing failed!" << endl;}return 0;
}

图表说明

为了更直观地理解组合子的工作原理和抽象语法树的结构,我们可以添加以下图表:

组合子工作流程图

数字解析器
操作符解析器
数字解析器
表达式解析器

抽象语法树结构图

123
+
45
-
67

总结

通过组合子方法,我们可以模块化地构建解析器,并生成对应的抽象语法树。这种方法不仅简洁,而且易于扩展和维护。本文通过一个简单的表达式解析器示例,展示了如何使用组合子来构建解析器,并生成AST。

进一步阅读

  • 《Parsing with Combinators》
  • 《Crafting Interpreters》
http://www.xdnf.cn/news/1411039.html

相关文章:

  • vsgCs显示谷歌全球倾斜模型-数据转换
  • 打工人日报#20250831
  • pyinstaller打包后失败问题记录
  • 贝叶斯分类(Bayes Classify)
  • Java面试-微服务(spring cloud篇)
  • 网络:相比于HTTP,HTTPS协议到底安全在哪?
  • 【HarmonyOS】天气预报 UI 的基本实现
  • 基于Echarts+HTML5可视化数据大屏展示-惠民服务平台
  • 一文理清TCP协议的通讯流程
  • 【车载开发系列】CAN与CANFD下篇
  • Linux-驱动积累
  • docker安装tomcat
  • 1.2 操作系统发展历程
  • dify docker compose操作命令指南
  • 【不懂就问】-手机相关学习
  • 内核等待队列以及用户态的类似机制
  • 基于Spring Cloud Sleuth与Zipkin的分布式链路追踪实战指南
  • 机器学习基础-day01-机器学习介绍
  • syn与quote的简单使用——实现debug
  • 萌宝喂养日志-我用AI做喂养记录小程序1-原型设计
  • 中科大少年班记
  • 编程与数学 03-004 数据库系统概论 10_数据库的实施
  • 【GaussDB】排查应用高可用切换出现数据库整体卡顿及报错自治事务无法创建的问题
  • 基于JavaScript的智能合约平台(Agoric)
  • join怎么用
  • Spring Boot单体项目整合Nacos
  • STAR法则
  • C/C++ 高阶数据结构 —— 二叉搜索树(二叉排序树)
  • 【Linux】系统部分——ELF文件格式与动态库加载
  • 【系统分析师】高分论文:论大数据架构的应用