Pandoc Markdown 花括号引用键语法(`@{...}`)深度解析:从命令测试到源码实现
2026/9/20 20:41:30 网站建设 项目流程
  • 文档
  • 开发工具
  • CLI

【免费下载链接】pandoc

Universal markup converter

项目地址:https://gitcode.com/gh_mirrors/pa/pandoc
点击查看免费下载

导读

本篇技术指南围绕 Pandoc 官方命令测试用例 test/command/6026.md 展开,系统讲解 Markdown 读者(reader)中花括号包裹的引用键语法(curly-brace citation key syntax,即@{...}形式)。你不仅会掌握该语法在-t native-t markdown两种输出下的精确行为,还会从 Text.Pandoc.Parsing.Citations 的citeKey实现和 Markdown 读者的引用解析器 两个层面理解它为何存在、如何解析,以及如何利用它承载 URL 等含特殊字符的引用键。

测试用例背景:什么是命令测试(command test)

在深入研究语法之前,先明确 test/command/6026.md 在整个测试体系中的角色。Pandoc 采用"命令测试"(command test)机制来验证命令行行为,其定义位于 test/Tests/Command.hs:

  • 测试文件是以.md结尾的 Markdown 文件,存放于 test/command 目录;
  • 文件中的每个代码块(code block)即一个独立测试用例;
  • 代码块第一行以%开头,后面是要执行的 shell 命令;
  • 接下来的若干行是作为 stdin 传给该命令的输入;
  • 输入以单独一行^D结束;
  • ^D之后的内容是期望的 stdout 输出;若期望 stderr 输出,则每行需以2>前缀标记;
  • 若期望非零退出码,最后一行应为=>后接退出码。

测试运行时,Tests.Command 的测试树 会遍历command目录下所有.md文件,extractCommandTest读取文件并用 Pandoc 解析出其中的代码块,随后逐块执行命令并与期望输出比对(执行与比对逻辑)。6026 号测试正是针对"花括号引用键语法"的回归测试(regression test),由 changelog.md 可以确认该语法是在对应版本引入的:Implement curly-brace syntax for Markdown citation keys (#6026)——测试文件编号 6026 正是 GitHub issue 编号,这也是 Pandoc 测试文件命名的惯例。

花括号引用键语法是什么

Pandoc 的 Markdown 引用语法以@开头,紧跟引用键(citation key)。标准引用键有严格约束,而花括号语法@{...}提供了一种把任意字符串作为引用键的方式。从 MANUAL.txt 的权威说明可知:

  • 引用键必须以字母、数字或_开头;
  • 只能包含字母数字和单个内部标点字符(:.#$%&-+?<>~/);
  • 若不满足上述约束,就必须用花括号包裹,且花括号本身不属于键的一部分
  • 例如@Foo_bar.baz.的键是Foo_bar.baz(末尾句点不是内部标点,故不纳入键);而@{Foo_bar.baz.}的键则是完整的Foo_bar.baz.
  • @Foo_bar--baz的键只有Foo_bar,因为重复的内部标点会终止键;
  • 当你使用 URL 作为引用键时,花括号语法是推荐做法[@{https://example.com/bib?name=foobar&date=2000}, p. 33]

这正是 6026 测试的用意:用@{https://openreview.net/forum?id=HkwoSDPgg}来验证以完整 URL 作为引用键的可行性。

逐行拆解测试用例

第一个用例:-t native输出

% pandoc -t native @{https://openreview.net/forum?id=HkwoSDPgg} @https://openreview.net/forum?id=HkwoSDPgg ^D

输入包含两段:第一段使用花括号语法,第二段直接裸写 URL。期望输出如下两个Para块:

  1. 花括号形式解析为一个Cite,其中包含citationId = "https://openreview.net/forum?id=HkwoSDPgg"(完整 URL),citationMode = AuthorInText,说明@{...}被视为文内引用(author-in-text),同时保留了原文@https://openreview.net/forum?id=HkwoSDPgg作为引用的文字内容。
  2. 裸 URL 形式则被解析为键https://openreview.net/forum?id——注意?=的截断——剩下的=HkwoSDPgg变成了普通文本Str

这一对比清晰地表明:裸 URL 中?不是合法键字符(不在内部标点集合内),因此解析器在?处截断键;而花括号形式能完整保留整个 URL 作为键。

第二个用例:-t markdown输出

% pandoc -t markdown @{https://openreview.net/forum?id=HkwoSDPgg} @https://openreview.net/forum?id=HkwoSDPgg ^D @{https://openreview.net/forum?id=HkwoSDPgg} @https://openreview.net/forum?id=HkwoSDPgg

第二用例验证往返一致性(round-trip):Markdown 读者解析后再由 Markdown 写出器(writer)输出,输入与输出完全一致——说明写出器能正确地把含特殊字符的引用键重新用花括号包裹输出。这保证了"解析→写出"不会破坏引用键的完整性。

源码级解析原理

citeKey:花括号键的入口

两种形式的键解析都汇聚在Text.Pandoc.Parsing.Citations模块的citeKey函数:

citeKey :: ... => Bool -> ParsecT s st m (Bool, Text) citeKey allowBraced = try $ do guard =<< notAfterString suppress_author <- option False (True <$ char '-') char '@' key <- simpleCiteIdentifier <|> if allowBraced then charsInBalanced '{' '}' (T.singleton <$> (satisfy (not . isSpace))) else mzero return (suppress_author, key)

关键点:

  • 布尔参数allowBraced控制是否启用@{...}扩展语法。在 Markdown 读者的textualCitecitation调用处 均传入True,因此 Markdown 读者支持花括号形式;
  • charsInBalanced '{' '}'会匹配花括号内任意非空白字符satisfy (not . isSpace)),支持嵌套花括号的平衡匹配,这也是为什么 changelog 示例中@{foo_bar{x}'}能解析出键foo_bar{x}
  • 花括号内的空白会被排除,因此 URL 中的?=&等字符都能被完整保留;
  • 同时返回的布尔值表示-前缀(@-key表示 SuppressAuthor,即压制作者)。

与之相对,simpleCiteIdentifier实现标准键的约束:首字符必须是字母数字或_(或*,用于@*通配 nocite),后续字符仅允许字母数字、_以及单个内部标点:.#$%&-+?<>~/,重复的内部标点会终止键。这解释了裸 URL 为何在?处截断。

Markdown 读者中的引用解析流程

cite是 Markdown 读者中引用解析的顶层入口,受Ext_citations扩展开关控制(guardEnabled Ext_citations),并且只有在非脚注上下文(stateInNote)才递增stateNoteNumber(该值仅用于citationNoteNum赋值,不影响非脚注样式):

  • textualCite处理@key文内引用:通过citeKey True取得键;若后续跟[...]括号内容,则与normalCite组合;最终构建Citation记录时,citationMode依据suppressAuthorSuppressAuthorAuthorInTextcitationId即花括号解析出的完整键。
  • citation处理方括号列表中的单条引用(如[see @doe99, pp. 33]),同样通过citeKey True获取键,并支持-前缀压制作者(SuppressAuthor),否则为NormalCitation

这两条路径产出的Citation记录都包含citationHash = 0与递增的citationNoteNum,与 6026 测试的 native 输出完全吻合。

为什么需要花括号语法

从 6026 测试的输出差异可以看到问题的本质:裸引用键语法与 URL 字符集冲突。URL 包含?=&%等字符,其中?=不是simpleCiteIdentifier允许的内部标点,会导致键被截断甚至产生歧义。而引用数据库(如 BibTeX、CSL JSON)允许任意字符串作为键,尤其许多工作流直接用 DOI 或 URL 作为键。

花括号语法在保持原有@语法习惯的同时,做到了两件事:

  1. 允许键包含任意特殊字符?=&.{}等均可安全地出现在键中;
  2. 消除键与紧随文本的歧义:如 changelog 所注,@{foo}A能将foo与紧随的A明确分离,因为@fooA会被整体当作键的一部分;同样地,@{key}.中的末尾句点不再被当作键的结束标志而被吞掉,而是明确保留在键内。

这两点正是 changelog.md 中对该特性的官方描述:提供一种使用包含特殊字符(标准引用键语法无法使用)的引用键的途径,同时支持将引用键与紧跟其后的文本分隔开。

完整语法示例与实战建议

结合 MANUAL.txt 的规范、changelog 的示例与 6026 测试的验证,汇总可复制、可运行的用法:

输入解析出的引用键说明
@doe99doe99标准键,最常用
@Foo_bar.baz.Foo_bar.baz末尾句点被排除(非内部标点)
@{Foo_bar.baz.}Foo_bar.baz.花括号保留末尾句点
@Foo_bar--bazFoo_bar重复内部标点终止键
@{foo_bar{x}'}foo_bar{x}键可含嵌套花括号与引号(changelog 示例)
@{foo}Afoo键与紧随文本明确分离
@{https://example.com/bib?name=foobar&date=2000}完整 URL官方推荐 URL 作键的写法
@{https://openreview.net/forum?id=HkwoSDPgg}完整 URL6026 测试验证

实战建议:

  • 以 URL/DOI 为引用键时务必使用花括号,这是 MANUAL.txt 的明确推荐,也是 6026 测试的核心场景;
  • 花括号内不能包含空白字符(解析器使用satisfy (not . isSpace)),因此键内部不要留空格;
  • 花括号支持嵌套与平衡匹配,但请保持键可读性,避免过度复杂;
  • 文内引用@{key}会得到AuthorInText模式,方括号列表中的@{key}NormalCitation模式,压制作者时使用@-{key}
  • 在 MANUAL.txt 中描述的引用键解析规则适用于所有支持citations扩展的 Markdown 方言。

如何验证与运行该测试

如果你想在本地验证 6026 测试的行为,可以直接运行两条命令:

# 方式一:按测试原样运行(stdin 输入,^D 结束) pandoc -t native # 输入: # @{https://openreview.net/forum?id=HkwoSDPgg} # # @https://openreview.net/forum?id=HkwoSDPgg # ^D # 方式二:单行验证花括号键解析 printf '@{https://openreview.net/forum?id=HkwoSDPgg}\n' | pandoc -t native # 方式三:验证 markdown 写出器的往返一致性 printf '@{https://openreview.net/forum?id=HkwoSDPgg}\n' | pandoc -t markdown

若在源码构建环境中运行,也可通过测试框架执行全部命令测试(test/Tests/Command.hs会自动收集 test/command 目录下的用例),6026 用例即作为其中一个回归项被验证。

结语

test/command/6026.md 虽只是一个十余行的命令测试文件,却精准刻画了 Pandoc 花括号引用键语法的完整行为边界:-t native用例证明了含 URL 特殊字符的键能被完整解析并保持AuthorInText模式,-t markdown用例证明了写出的往返一致性。结合 citeKey 的平衡括号匹配实现、Markdown 读者的引用解析器 的调用链,以及 MANUAL.txt 的官方语法规范,你现在已具备在 Markdown 文档中安全使用 URL/DOI 等特殊字符引用键的完整知识与验证手段。

  • 文档
  • 开发工具
  • CLI

【免费下载链接】pandoc

Universal markup converter

项目地址:https://gitcode.com/gh_mirrors/pa/pandoc
点击查看免费下载

相关推荐

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询