块解析
pulldown-cmark 分两遍解析。第一遍在 Parser::new_ext 里一次做完:从头到尾读一遍输入,认出所有的块(段落、标题、代码块、列表、引用块、表格……),建成一棵存在 Vec 里的树;块里的行内内容这时只扫一遍、标出可能是行内标记的位置,留给第二遍。这一部分讲第一遍,也就是块主线:例子文档的 21 行怎么变成一个 70 项的节点数组(含下标 0 的占位)。
代码分布(按 tag v0.13.5 统计):firstpass.rs 2849 行(其中最后约 270 行是只在 x86_64 开 simd 时编译的查表实现),tree.rs 298 行,行首和各种块标记的扫描器在 scanners.rs(1566 行),scan_containers 和 Item / ItemBody 的定义在 parse.rs。每页都对照 CommonMark 0.31.2 规范(仓库里的 third_party/CommonMark/spec.txt)的对应章节。
- 扁平数组上的树:
Tree只有nodes、spine、cur三个字段,节点用从 1 开始的NonZeroUsize下标相连,Node<Item>实测 48 字节;第二遍用同一个 spine 当显式栈走树。 - 一行一行:
parse_block一次处理一个块,LineStart按 4 列制表位扫行首,scan_containers判断哪些容器还开着,惰性续行在段落循环里处理;0.13.5 修掉了深层列表加空行的平方复杂度。 - 叶子块:段落怎么在下一行变成 Setext 标题,ATX 标题、分隔线、两种代码块、七类 HTML 块、元数据块;0.13.5 与规范的两处出入。
- 容器块:引用块与 GFM 提示块,列表与列表项的紧凑和松散,任务列表,定义列表。
- 表格与脚注:表格要等第二行的分隔行才成立,对齐存在
Allocations;两种风格的脚注定义;链接引用定义在第一遍收进refdefs。
flowchart TB new["Parser new_ext"] --> rfp["run_first_pass"] rfp --> pb["parse_block 循环"] pb --> sc["scan_containers"] sc --> cont["容器块"] sc --> leaf["叶子块"] leaf --> pl["parse_line 标占位"] cont --> tree["Tree of Item"] pl --> tree pb --> allocs["Allocations"] tree --> next["Parser next"] allocs --> next
第一遍产出的是树和 Allocations 两样东西:树里只有字节范围和小下标,链接定义、info string、表格对齐、标题属性这些变长数据在 Allocations 里(一次解析分配了什么)。第二遍怎么沿着树把占位节点解析成强调、链接,见按需解析;两遍连起来走一份文档,见一份文档。
💬 关联讨论
发起讨论加载中...