
Pandoc DocBook 读取器如何解析有序列表的编号样式与内嵌标题以 test/command/10594.md 命令测试为例【免费下载链接】pandocUniversal markup converter项目地址: https://gitcode.com/gh_mirrors/pa/pandoctest/command/10594.md是 pandoc 项目中的一个命令行级command-level回归测试它以一段包含内嵌title的 DocBookorderedlist为输入通过pandoc -f docbook -t native输出内部 AST验证 DocBook 读取器对有序列表numeration编号样式的映射、列表内title元素的 Div 化处理以及listitem/simpara的块级转换行为。读完本文你将能看懂这类test/command/*.md测试文件的格式约定掌握 DocBook 列表在 pandoc 中的 AST 表示并能对照 DocBook 读取器源码 复现与扩展验证。一、测试文件全景一个最小可复现的命令测试test/command/10594.md全文是一个用反引号包裹的代码块其内容遵循 pandoc 命令测试command tests的标准格式包含三部分% pandoc -f docbook -t native orderedlist numerationloweralpha titleheader inside listing/title // not rendered in any output format! listitem simparafirst step/simpara /listitem /orderedlist ^D [ Div ( , [] , [] ) ... ]第一行%声明要执行的 pandoc 命令行这里是pandoc -f docbook -t native即把输入当作 DocBook 解析并以 native 格式pandoc 内部 AST 的文本表示输出^D之前喂给命令的标准输入heredoc 形式^D模拟 EOF^D之后期望的标准输出即 golden 结果测试框架会把实际输出与它逐字节比对。这类测试由 test/Tests/Command.hs 驱动测试发现器会扫描test/command/目录下所有以.md结尾的文件test/Tests/Command.hs逐个解析出命令、输入与期望输出并通过goldenTest做 golden 比对。值得注意的实现细节是实际执行时命令中的pandoc会被替换为test-pandoc --emulatetest/Tests/Command.hs从而使用与发布版 pandoc 行为一致的测试专用二进制。从命名规律可以推断这类编号文件通常对应 pandoc 历史上的 issue/PR 编号10594即为此用例的回归编号。二、输入 DocBook 片段逐段拆解测试输入是一段结构清晰的 DocBook 5 文档片段orderedlist numerationloweralpha titleheader inside listing/title // not rendered in any output format! listitem simparafirst step/simpara /listitem /orderedlist各元素含义如下元素/属性含义orderedlistDocBook 有序列表numerationloweralpha指定编号样式为小写字母title列表的可选标题测试中用//注释注明它在各输出格式中通常不会被渲染listitem列表项容器simparasimple paragraph只含文本与内联标记、不含块级元素的段落注意title是直接嵌在orderedlist内部、而非位于listinfo中——这正是本用例的焦点读取器需要把列表标题作为一种可选的块级前置内容捕获下来。三、native 输出解读Div 嵌套与 OrderedList 属性期望输出揭示了 DocBook 读取器生成的内部 AST[ Div ( , [] , [] ) [ Div ( , [ title ] , [] ) [ Plain [ Str header, Space, Str inside, Space, Str listing ] ] , OrderedList ( 1 , LowerAlpha , DefaultDelim ) [ [ Para [ Str first, Space, Str step ] ] ] ] ]对照源码可以逐层还原外层Div ( , [] , [])整个orderedlist被包装为 Divid、class、键值属性均为空本例未设置id也没有role等属性内层Div ( , [title] , [])title的文本 header inside listing 被解析为行内内容并以Plain块呈现随后被打包成带titleclass 的 DivOrderedList (1 , LowerAlpha , DefaultDelim)元组三个分量分别是起始编号、编号样式、分隔符类型——起始号为 1样式为LowerAlpha小写字母分隔符为DefaultDelimDocBook 本身不编码分隔符信息因此固定取默认值[ [ Para [ Str first, Space, Str step ] ] ]唯一的listitem解析为一个列表项其内部的simpara被转换为Para块。测试注释说该titlenot rendered in any output format在输出格式中通常不渲染但 native 输出恰恰证明了它在 AST 层面是被保留的——这是保留信息、渲染交给 writer的典型设计。四、源码实现一orderedlist 分支与 numeration 映射orderedlist的解析逻辑位于 src/Text/Pandoc/Readers/DocBook.hsorderedlist - withOptionalTitle $ do let listStyle case attrValue numeration e of arabic - Decimal loweralpha - LowerAlpha upperalpha - UpperAlpha lowerroman - LowerRoman upperroman - UpperRoman _ - Decimal let start fromMaybe 1 $ safeRead $ attrValue startingnumber e orderedListWith (start,listStyle,DefaultDelim) . handleCompact $ listitems这段代码揭示了完整的编号样式映射关系DocBooknumeration属性值pandoc 列表样式arabic及未识别值默认Decimal十进制数字loweralphaLowerAlpha小写字母 a, b, c…upperalphaUpperAlpha大写字母 A, B, C…lowerromanLowerRoman小写罗马数字 i, ii, iii…upperromanUpperRoman大写罗马数字 I, II, III…起始编号则读取startingnumber属性缺省时回退为 1分隔符固定为DefaultDelim。测试用例中的numerationloweralpha因此精确命中LowerAlpha分支验证了这条映射链。与之相邻的列表类元素解析同样值得对照itemizedlist走bulletListvariablelist走definitionListprocedure与substeps直接使用默认样式的orderedListsrc/Text/Pandoc/Readers/DocBook.hs。这些标签以及title、listitem、simpara等都会先经过读取器的元素白名单检查参见 src/Text/Pandoc/Readers/DocBook.hs未列入白名单的标签将被跳过。五、源码实现二withOptionalTitle 与 title 的 Div 化列表项内容的收集很直观listitems mapM getBlocks $ filterChildren (named listitem) esrc/Text/Pandoc/Readers/DocBook.hs即把每个listitem子元素递归解析成块列表而simpara通过parseMixed para被解析为Para块src/Text/Pandoc/Readers/DocBook.hs。真正有意思的是title的处理。在getBlocks的分支表中顶层出现title时直接返回mempty注释写明handled in parent element由父元素处理src/Text/Pandoc/Readers/DocBook.hs。也就是说title是否被消费完全取决于父元素是否调用withOptionalTitle。其实现如下src/Text/Pandoc/Readers/DocBook.hswithOptionalTitle p do mbt - getTitle b - p case mbt of Nothing - return b Just t - return $ divWith (attrValue id e, [], getRoleAttr e) (divWith (, [title], []) (plain t) b)getTitle用filterChild (named title) e查找直接子级title取其行内内容若存在则把标题包装为divWith (, [title], []) (plain t)再与列表主体b拼接外包一层带元素id与 role 属性的 Div若不存在则原样返回列表内容不产生额外 Div。这正是 native 输出中两层 Div 的由来外层 Div 的 id 取自orderedlist的id属性本测试未设置故为空内层titleDiv 则是标题的固定容器。同一个withOptionalTitle也被calloutlist、itemizedlist等复用而表格与图表的标题走的是另一条title/caption处理路径。六、补充机制compact 紧凑列表orderedListWith ... . handleCompact中的handleCompact由spacing属性控制src/Text/Pandoc/Readers/DocBook.hscompactSpacing case attrValue spacing e of compact - True _ - False handleCompact if compactSpacing then map (fmap paraToPlain) else id当列表声明spacingcompact时每个列表项内的Para会被降级为Plain紧凑呈现否则保持Para不变。10594 用例未设置spacing因此simpara生成的Para原样保留——这也解释了为什么期望输出中列表项内容是Para而非Plain。七、如何本地复现与验证在已构建 pandoc 的环境中可以直接用 heredoc 复现该测试结果应与^D后的 golden 输出完全一致pandoc -f docbook -t native EOF orderedlist numerationloweralpha titleheader inside listing/title listitem simparafirst step/simpara /listitem /orderedlist EOF也可以运行整个命令测试套件来验证该用例cabal test --test-options-p #10594-p的匹配串来自 test/Tests/Command.hs 中的testname # show num即每个.md文件名去掉扩展名后即为测试名。若实际输出与 golden 不一致测试框架会给出--- test/command/10594.md与 pandoc -f docbook -t native形式的 diff方便定位读取器行为变化test/Tests/Command.hs。八、小结从一条测试看 pandoc 的回归测试方法论test/command/10594.md虽只有二十余行却浓缩了 pandoc 三个层面的工程实践读取器语义numeration→ListNumberStyle的六路映射、startingnumber→ 起始编号、spacingcompact→Para/Plain切换以及title由父元素按需消费的handled in parent element设计AST 约定可选列表标题被编码为带titleclass 的 Div这一约定被withOptionalTitle统一实现并被 itemizedlist、calloutlist 等列表类元素共享测试基建.md即用例、%/^D即输入边界、golden 比对与test-pandoc --emulate替身机制构成了覆盖读者与写者行为的低成本回归体系。理解这一条测试等于掌握了阅读test/command/目录下数百个用例的通用钥匙——每个文件都是一段可直接复现的命令 输入 期望输出三元组随时可以对照 DocBook 读取器 或 命令测试驱动 深入验证。【免费下载链接】pandocUniversal markup converter项目地址: https://gitcode.com/gh_mirrors/pa/pandoc创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考