当前位置: 首页 > 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.dtcms.com/a/360653.html

相关文章:

  • 24数学建模国赛C
  • Linux性能调试工具之ftrace
  • 【开题答辩全过程】以 基于Java的城市公交查询系统设计与实现为例,包含答辩的问题和答案
  • 元宇宙与旅游产业:虚实融合的文旅新体验
  • 【代码随想录day 21】 力扣 216.组合总和III
  • 【代码随想录day 22】 力扣 39. 组合总和
  • 2025年跨领域职业发展证书选择指南
  • 设计模式:外观模式(Facade Pattern)
  • [线上问题排查]深度剖析:一条MySQL慢查询的全面优化实战
  • 操作文件 File类
  • Linux网络编程04:网络基础(万字图文解析)
  • Day19_【机器学习—线性回归 (2)】
  • 【大模型记忆-Mem0详解-1】概述
  • springboot整合minio实现上传下载搭建minio
  • 【CVPR24-工业异常检测】InCTRL:少样本基于上下文残差学习的通才异常检测
  • 安装pthread man手册
  • 决策思维研究体系主要构成
  • B 站 “成分” 检测工具,深挖历史记录,秒测二次元浓度
  • OWASP Top 10漏洞详解
  • MCP(Model Context Protocol,模型上下文协议)介绍
  • 图像质量评价——结构相似度
  • CVPR上的多模态检索+视频理解,LLM助力提效翻倍
  • 基于Basilisk库实现三种姿态的切换
  • 雪花算法是什么,时钟回拨问题怎么解决?
  • 大厂文章学习《DDD在大众点评交易系统演进中的应用》
  • 【数据分享】安徽省四份土地利用矢量shp数据
  • C++ 数据结构之哈希表及其相关容器
  • LeetCode 3459.最长 V 形对角线段的长度:记忆化搜索——就一步步试
  • 【开题答辩全过程】以 家庭理财管理系统的设计与实现为例,包含答辩的问题和答案
  • mit6.031 2023spring 软件构造 笔记 Testing