深入解析:[RE2] Regexp对象 | shared_ptr | AST树

第2章:Regexp(正则表达式对象)

欢迎回来

第1章:RE2(API)中,我们了解到RE2类是C++中处理正则表达式的主要工具。

但当传入类似"(\\d+)-(\\d+)-(\\d+)"的模式时,RE2内部究竟如何"理解"这个字符串?

这就是**Regexp(正则表达式对象)**的职责所在——它是正则模式处理流程中的首个关键环节

Regexp解决什么问题?

想象你向建筑师描述梦想中的房屋。在施工前,建筑师需要绘制蓝图——这不是实体建筑,而是包含墙体位置、房间数量和屋顶类型等细节的结构化方案。

re2中,正则表达式字符串(如a+b*c)就是原始描述,而Regexp对象则是经过解析的、机器可理解的结构化蓝图。当调用RE2 phone_pattern("...")时,RE2对象通过内部解析器将字符串转换为Regexp对象,明确表达:

  • 哪些部分是字面量(如abc
  • 哪些部分是重复操作(如+表示"至少一次",*表示"零次或多次")
  • 这些部分如何组合(如拼接abc或选择a|b
  • 捕获组(...)或字符类[...]等特殊结构

这种结构化表示使得后续的优化和编译更加高效。

Regexp:内部蓝图

前文传送:[OP-Agent] docs | Rego策略语言 | AST语法树

Regexp对象本质上是抽象语法树(AST)。以简单模式(a|b)c为例,其Regexp结构如下:

在这里插入图片描述

Regexp对象核心特性:
  • 不可变性:创建后结构固定,确保线程安全
  • 引用计数:通过计数机制共享对象,避免内存泄漏
引用计数技术原理

引用计数通过为每个对象维护一个计数器,记录当前被引用的次数

  • 当对象新增引用时计数器加1,释放引用时减1。计数器归零时自动释放对象内存

  • 若存在循环引用(如A引用B,B又引用A),计数器无法归零会导致内存泄漏。

shared_ptr的设计:

shared_ptr采用引用计数实现智能内存管理

  • 构造函数递增目标对象计数
  • 析构函数递减计数并在归零时销毁对象
  • 支持拷贝构造/赋值操作,自动维护引用计数

循环引用解决方案:

使用weak_ptr打破循环引用链:

  • weak_ptr不增加引用计数
  • 通过lock()临时获取可用的shared_ptr
  • 典型场景:父子对象互相持有weak_ptr而非shared_ptr

相关前文传送:

解析流程

用户通常不直接创建Regexp对象,而是由RE2在初始化时自动完成解析:

在这里插入图片描述

代码实现

关键代码片段(简化自re2源码):

1. RE2初始化时触发解析

// re2/re2.cc
void RE2::Init(absl::string_view pattern) {
entire_regexp_ = Regexp::Parse(pattern, flags_, &status); // 核心解析调用
}

2. 字面量节点的创建

// re2/parse.cc
bool ParseState::PushLiteral(Rune r) {
Regexp* re = new Regexp(kRegexpLiteral, flags_);
re->rune_ = r; // 存储字符值
stacktop_ = re; // 压入解析栈
}

3. 树结构遍历工具

// re2/tostring.cc
string Regexp::ToString() {
ToStringWalker w(&result);
w.WalkExponential(this, max_depth); // 安全遍历AST
}

总结

Regexp对象是正则表达式处理流程中的结构化中间表示:

  • 将原始字符串转换为机器友好的树形结构
  • 支持语法检查与优化预处理
  • 为后续编译阶段提供清晰蓝图

下一章:Prog(编译程序)

posted on 2026-01-30 16:28  ljbguanli  阅读(0)  评论(0)    收藏  举报