内容

名称

perlpodspec - 纯文本文档:格式规范和说明

描述

本文档是关于 Pod 标记语言的详细说明。大多数人只需要阅读 perlpod 即可了解如何编写 Pod,但本文档可能会回答一些关于解析和渲染 Pod 的偶然问题。

在本文件中,“必须”/“禁止”,“应该”/“不应该”和“可以”具有其惯例意义(参见 RFC 2119):“X 必须做 Y”意味着如果 X 不做 Y,则违反本规范,应予以修复。“X 应该做 Y”意味着建议这样做,但如果存在正当理由,X 可以不做 Y。“X 可以做 Y”仅仅表示 X 可以随意做 Y(尽管由读者判断“我认为如果 X 做 Y 会很好”与“如果 X 做 Y 我不会介意”之间的任何含义)。

值得注意的是,当我提到“解析器应该做 Y”时,如果调用应用程序明确要求解析器做 Y,则解析器可能不做 Y。我通常将其表述为“解析器默认情况下应该做 Y”。这并不要求解析器提供关闭 Y 功能的选项(例如,在逐字段落中扩展制表符),尽管这意味着可能提供此类选项。

Pod 定义

Pod 嵌入在文件中,通常是 Perl 源代码文件中,但您也可以编写一个只包含 Pod 的文件。

文件中的由零个或多个非换行符组成,以换行符或文件结尾符结束。

换行符序列通常是一个平台相关的概念,但 Pod 解析器应该理解它表示 CR(ASCII 13)、LF(ASCII 10)或 CRLF(ASCII 13 后紧跟 ASCII 10),以及任何其他系统特定的含义。文件中的第一个 CR/CRLF/LF 序列可用于识别用于解析文件其余部分的换行符序列。

空行是仅包含零个或多个空格(ASCII 32)或制表符(ASCII 9)且以换行符或文件结尾符结束的行。非空行是包含一个或多个除空格或制表符以外的字符(并以换行符或文件结尾符结束)的行。

注意:许多较旧的 Pod 解析器不接受由空格/制表符和换行符组成的行作为空行。它们认为只有不包含任何字符且以换行符结束的行才是空行。)

空白在本文件中用作空格、制表符和换行符序列的统称。(就其本身而言,此术语通常是指字面空白。也就是说,Pod 源代码中的空白字符序列,而不是“E<32>”,它是一个表示空白字符的格式化代码。)

Pod 解析器是一个用于解析 Pod 的模块(无论这是否涉及调用回调、构建解析树或直接格式化它)。Pod 格式化器(或Pod 翻译器)是一个模块或程序,它将 Pod 转换为其他格式(HTML、纯文本、TeX、PostScript、RTF)。Pod 处理器可能是一个格式化器或翻译器,也可能是一个对 Pod 执行其他操作的程序(例如,统计字数、扫描索引点等)。

Pod 内容包含在Pod 块中。Pod 块以匹配m/\A=[a-zA-Z]/的行开头,并一直持续到下一行匹配m/\A=cut/的行,或者如果不存在m/\A=cut/行,则持续到文件末尾。

请注意,解析器不应区分看起来像 Pod 但在引号字符串中的内容,例如 here 文档。

在 Pod 块中,存在Pod 段落。Pod 段落由非空文本行组成,这些行之间用一个或多个空行隔开。

出于 Pod 处理的目的,Pod 块中存在四种类型的段落。

例如:考虑以下段落

# <- that's the 0th column

=head1 Foo

Stuff

  $foo->bar

=cut

这里,“=head1 Foo”和“=cut”是命令段落,因为每行的第一行都匹配m/\A=[a-zA-Z]/。“[空格][空格]$foo->bar”是一个逐字段落,因为它的第一行以字面空格字符开头(并且周围没有“=begin”...“=end”区域)。

如果标识符不以冒号开头,“=begin 标识符”...“=end 标识符”命令会阻止它们包围的段落被解析为普通段落或逐字段落。这将在“关于数据段落和“=begin/=end”区域”部分中详细讨论。

Pod 命令

本节旨在补充和澄清“perlpod 中的命令段落”中的讨论。这些是当前识别的 Pod 命令

“=head1”,“=head2”,“=head3”,“=head4”,“=head5”,“=head6”

此命令表示段落其余部分中的文本是标题。该文本可能包含格式代码。示例

=head1 Object Attributes

=head3 What B<Not> to Do!

=head5=head6都在 2020 年添加,可能不受所有 Pod 解析器的支持。Pod::Simple 3.41 于 2020 年 10 月发布,支持这两者,为所有基于Pod::Simple的 Pod 解析器提供支持。

“=pod”

此命令表示此段落开始一个 Pod 块。(如果我们已经在 Pod 块的中间,此命令没有任何效果。)如果此命令段落中在“=pod”之后有任何文本,则必须忽略它。示例

=pod

This is a plain Pod paragraph.

=pod This text is ignored.
“=cut”

此命令表示此行是先前启动的 Pod 块的结束。如果此行在“=cut”之后有任何文本,则必须忽略它。示例

=cut

=cut The documentation ends here.

=cut
# This is the first line of program text.
sub foo { # This is the second.

尝试使用“=cut”命令启动 Pod 块是错误的。在这种情况下,Pod 处理器必须停止解析输入文件,并且默认情况下必须发出警告。

“=over”

此命令表示这是一个列表/缩进区域的开始。如果在“=over”之后有任何文本,它必须仅包含一个非零正数。此数字的语义将在下面进一步的“关于 =over...=back 区域”部分中解释。格式代码不会扩展。示例

=over 3

=over 3.5

=over
"=item"

此命令表示列表中的一个项目从这里开始。格式化代码将被处理。本段剩余部分中(可选)文本的语义将在下面更详细的 "关于 =over...=back 区域" 部分中解释。示例

=item

=item *

=item      *    

=item 14

=item   3.

=item C<< $thing->stuff(I<dodad>) >>

=item For transporting us beyond seas to be tried for pretended
offenses

=item He is at this time transporting large armies of foreign
mercenaries to complete the works of death, desolation and
tyranny, already begun with circumstances of cruelty and perfidy
scarcely paralleled in the most barbarous ages, and totally
unworthy the head of a civilized nation.
"=back"

此命令表示这是由最近的 "=over" 命令开始的区域的结束。它不允许在 "=back" 命令之后有任何文本。

"=begin formatname"
"=begin formatname parameter"

这将以下段落(直到匹配的 "=end formatname")标记为某种特殊类型的处理。除非 "formatname" 以冒号开头,否则包含的非命令段落是数据段落。但是,如果 "formatname" *确实* 以冒号开头,那么非命令段落是普通段落或数据段落。这将在 "关于数据段落和 "=begin/=end" 区域" 部分中详细讨论。

建议 formatname 匹配正则表达式 m/\A:?[-a-zA-Z0-9_]+\z/。formatname 后的空格后的所有内容都是一个参数,格式化程序在处理此区域时可能会使用它。此参数不能在 "=end" 段落中重复。实现者应该预料到 "=begin"/"=end"/"=for" 的第一个参数的语义和语法在未来会扩展。

"=end formatname"

这标志着由匹配的 "=begin formatname" 区域打开的区域的结束。如果 "formatname" 不是最近打开的 "=begin formatname" 区域的 formatname,那么这是一个错误,并且必须生成错误消息。这将在 "关于数据段落和 "=begin/=end" 区域" 部分中详细讨论。

"=for formatname text..."

这与以下内容同义

=begin formatname

text...

=end formatname

也就是说,它创建一个由单个段落组成的区域;如果 "formatname" 以冒号开头,则该段落将被视为普通段落;如果 "formatname" *不* 以冒号开头,那么 "text..." 将构成数据段落。无法使用 "=for formatname text..." 来表达 "text..." 作为逐字段落。

"=encoding encodingname"

此命令应该出现在文档的早期(至少在任何非 US-ASCII 数据之前!),声明此文档以编码 encodingname 编码,它必须是 Encode 识别的编码名称。(Encode 支持的编码列表,在 Encode::Supported 中,在这里很有用。)如果 Pod 解析器无法解码声明的编码,它应该发出警告,并且可能会完全中止解析文档。

如果一个文档包含多个“=encoding”行,则应视为错误。Pod 处理器可能会默默地容忍这种情况,如果非第一个“=encoding”行只是第一个行的重复(例如,如果存在“=encoding utf8”行,之后又出现另一个“=encoding utf8”行)。但如果同一个文档中存在矛盾的“=encoding”行(例如,如果文档开头有“=encoding utf8”,后面有“=encoding big5”),Pod 处理器应该报错。识别 BOM 的 Pod 处理器也可能会报错,如果它们看到与 BOM 矛盾的“=encoding”行(例如,如果一个带有 UTF-16LE BOM 的文档包含“=encoding shiftjis”行)。

如果 Pod 处理器看到任何除上面列出的命令之外的命令(如“=head”,“=haed1”,“=stuff”,“=cuttlefish”,“=w123”),则该处理器默认应将其视为错误。它不能处理以该命令开头的段落,默认情况下应将其作为错误警告,并可能中止解析。Pod 解析器可以允许特定应用程序将上述已知命令列表扩展,并为每个附加命令指定是否应处理格式化代码。

本规范的未来版本可能会添加其他命令。

Pod 格式化代码

(注意,在本规范和 perlpod 的早期草案中,格式化代码被称为“内部序列”,这个术语可能仍然存在于 Pod 解析器的文档中,以及 Pod 处理器的错误消息中。)

格式化代码有两种语法

在解析 Pod 时,一个特别棘手的部分是正确解析(可能嵌套的!)格式化代码。实现者应该参考 Pod::Parser 中的 parse_text 例程中的代码,作为正确实现的示例。

I<text> -- 斜体文本

请参阅 "perlpod 中的格式化代码" 中的简要讨论。

B<text> -- 粗体文本

请参阅 "perlpod 中的格式化代码" 中的简要讨论。

C<code> -- 代码文本

请参阅 "perlpod 中的格式化代码" 中的简要讨论。

F<filename> -- 文件名样式

请参阅 "perlpod 中的格式化代码" 中的简要讨论。

X<topic name> -- 索引条目

请参阅 "perlpod 中的格式化代码" 中的简要讨论。

此代码不同寻常,因为大多数格式化程序会完全丢弃此代码及其内容。其他格式化程序将使用不可见的代码来渲染它,这些代码可用于构建当前文档的索引。

Z<> -- 空(零效果)格式化代码

"perlpod 中的格式化代码" 中简要讨论。

此代码不同寻常,因为它不应该有任何内容。也就是说,如果处理器看到 Z<potatoes>,它可能会报错。无论它是否报错,potatoes 文本都应该被忽略。

此代码的复杂语法在 "perlpod 中的格式化代码" 中进行了详细讨论,实现细节将在下面的 "关于 L<...> 代码" 中讨论。解析 L<content> 的内容很棘手。值得注意的是,必须检查内容是否看起来像 URL,或者是否必须在文字 "|" 和/或 "/"(按正确的顺序!)上拆分,等等, E<...> 代码解析之前。

E<escape> -- 字符转义

参见 "perlpod 中的格式化代码",以及 "关于实现 Pod 处理器的注意事项" 中的几个要点。

S<text> -- 文本包含不间断空格

此格式化代码语法简单,但语义复杂。它的含义是,此代码的可打印内容中的每个空格都代表一个不间断空格。

考虑

C<$x ? $y    :  $z>

S<C<$x ? $y     :  $z>>

两者都表示由 "$x"、一个空格、"?"、一个空格、":"、一个空格和 "$z" 组成的等宽字体(c[ode] 风格)文本。区别在于,在后者中,使用 S 代码,这些空格不是“普通”空格,而是不间断空格。

如果 Pod 处理器看到任何不在上述列表中的格式化代码(如 "N<...>" 或 "Q<...>" 等),则该处理器默认情况下必须将其视为错误。Pod 解析器可能允许特定应用程序将上述已知格式化代码列表扩展;Pod 解析器甚至可能允许为每个附加命令指定是否需要某种形式的特殊处理,就像 L<...> 一样。

此规范的未来版本可能会添加其他格式化代码。

历史说明:一些较旧的 Pod 处理器不会将 ">" 视为 "C<" 代码的结束,如果 ">" 紧接在 "-" 之前。这是为了使

C<$foo->bar>

解析为等效于

C<$foo-E<gt>bar>

而不是等效于包含 "C" 格式化代码的 "$foo-",以及 "C" 格式化代码之外的 "bar>"。这个问题已通过添加以下语法解决

C<< $foo->bar >>

符合标准的解析器不得将 "->" 视为特殊字符。

格式化代码绝对不能跨段落。如果在一个段落中打开一个代码,并且在该段落结束时没有找到结束代码,则 Pod 解析器必须关闭该格式化代码,并应发出警告(如“段落 123 行开始处的 I 代码未终止:'时间对象不是...'”)。因此,这两个段落

I<I told you not to do this!

Don't make me say it again!>

...应解析为两个斜体段落(I 代码在一个段落中开始,在另一个段落中开始)。相反,第一个段落应该生成一个警告,但除此之外,上述代码必须解析为

I<I told you not to do this!>

Don't make me say it again!E<gt>

(在 SGMLish 行话中,所有 Pod 命令都类似于块级元素,而所有 Pod 格式化代码都类似于内联级元素。)

关于实现 Pod 处理器的注意事项

以下是关于 Pod 处理的各种要求和建议。

关于 L<...> 代码

正如您从浏览 perlpod 可以看出,L<...> 代码是 Pod 格式化代码中最复杂的代码。以下几点将有助于澄清它的含义以及处理器如何处理它。

关于 =over...=back 区域

"=over"..."=back" 区域用于各种列表状结构。(我在这里使用“区域”一词仅仅作为从“=over”到匹配的“=back”的所有内容的总称。)

关于数据段落和“=begin/=end”区域

数据段落通常用于内联非 Pod 数据,这些数据将在将文档渲染到特定格式时使用(通常是传递)。

=begin rtf

\par{\pard\qr\sa4500{\i Printed\~\chdate\~\chtime}\par}

=end rtf

顺便说一下,可以使用单个“=for”段落来实现完全相同的效果。

=for rtf \par{\pard\qr\sa4500{\i Printed\~\chdate\~\chtime}\par}

(尽管这在形式上不是数据段落,但它与数据段落具有相同的含义,Pod 解析器可能会将其解析为数据段落。)

数据段落的另一个示例

=begin html

I like <em>PIE</em>!

<hr>Especially pecan pie!

=end html

如果这些是普通段落,Pod 解析器会尝试将“E</em>” (在第一个段落中)扩展为格式化代码,就像“E<lt>” 或“E<eacute>” 一样。但由于这是在“=begin identifier”...”=end identifier”区域中,并且标识符“html”没有以“:”为前缀,因此该区域的内容将存储为数据段落,而不是作为普通段落进行处理(或者如果它们以空格和/或制表符开头,则作为逐字段落进行处理)。

再举一个例子:在撰写本文时,不支持“biblio”标识符,但假设编写了一些处理器来识别它作为(例如)表示书目参考文献(必然包含普通段落中的格式化代码)的一种方式。每个“biblio”标识符前面都带有冒号,这将表明“biblio”段落是用于普通处理的。

=begin :biblio

Wirth, Niklaus.  1976.  I<Algorithms + Data Structures =
Programs.>  Prentice-Hall, Englewood Cliffs, NJ.

=end :biblio

这将向解析器发出信号,表明此 begin...end 区域中的段落将受到正常处理,作为普通/逐字段落(尽管仍然标记为仅供理解“biblio”标识符的处理器使用)。相同的效果可以通过

=for :biblio
Wirth, Niklaus.  1976.  I<Algorithms + Data Structures =
Programs.>  Prentice-Hall, Englewood Cliffs, NJ.

这些标识符上的“:” 仅仅意味着“正常处理这些内容,即使结果是针对某个特殊目标”。我建议解析器 API 将“biblio”报告为目标标识符,但也报告它有一个“:” 前缀。(同样,对于上面的“html”,将“html”报告为目标标识符,并注意没有“:” 前缀。)

请注意,一个 "=begin identifier"..."=end identifier" 区域,其中identifier 以冒号开头,可以包含命令。例如

=begin :biblio

Wirth's classic is available in several editions, including:

=for comment
 hm, check abebooks.com for how much used copies cost.

=over

=item

Wirth, Niklaus.  1975.  I<Algorithmen und Datenstrukturen.>
Teubner, Stuttgart.  [Yes, it's in German.]

=item

Wirth, Niklaus.  1976.  I<Algorithms + Data Structures =
Programs.>  Prentice-Hall, Englewood Cliffs, NJ.

=back

=end :biblio

但是请注意,一个 "=begin identifier"..."=end identifier" 区域,其中identifier 以冒号开头,不应该直接包含 "=head1" ... "=head4" 命令,也不应该包含 "=over"、"=back" 或 "=item"。例如,这可能被认为是无效的

=begin somedata

This is a data paragraph.

=head1 Don't do this!

This is a data paragraph too.

=end somedata

一个 Pod 处理器可能会发出信号,表明上面的内容(特别是 "=head1" 段落)是一个错误。但是请注意,以下内容不应该被视为错误

=begin somedata

This is a data paragraph.

=cut

# Yup, this isn't Pod anymore.
sub excl { (rand() > .5) ? "hoo!" : "hah!" }

=pod

This is a data paragraph too.

=end somedata

这也同样有效

=begin someformat

This is a data paragraph.

  And this is a data paragraph.

=begin someotherformat

This is a data paragraph too.

  And this is a data paragraph too.

=begin :yetanotherformat

=head2 This is a command paragraph!

This is an ordinary paragraph!

  And this is a verbatim paragraph!

=end :yetanotherformat

=end someotherformat

Another data paragraph!

=end someformat

上面 "=begin :yetanotherformat" ... "=end :yetanotherformat" 区域的内容不是数据段落,因为直接包含区域的标识符(":yetanotherformat")以冒号开头。在实践中,大多数包含数据段落的区域将包含数据段落;但是,上面的嵌套在语法上是有效的 Pod,即使它很少见。但是,某些格式(如“html”)的处理程序将只接受数据段落,而不是嵌套区域;如果它们看到(针对它们的)嵌套区域或命令(除了 "=end"、"=pod" 和 "=cut"),它们可能会抱怨。

还要考虑这种有效的结构

=begin :biblio

Wirth's classic is available in several editions, including:

=over

=item

Wirth, Niklaus.  1975.  I<Algorithmen und Datenstrukturen.>
Teubner, Stuttgart.  [Yes, it's in German.]

=item

Wirth, Niklaus.  1976.  I<Algorithms + Data Structures =
Programs.>  Prentice-Hall, Englewood Cliffs, NJ.

=back

Buy buy buy!

=begin html

<img src='wirth_spokesmodeling_book.png'>

<hr>

=end html

Now now now!

=end :biblio

在那里,"=begin html"..."=end html" 区域嵌套在更大的 "=begin :biblio"..."=end :biblio" 区域内。请注意,"=begin html"..."=end html" 区域的内容是数据段落,因为直接包含区域的标识符("html")以冒号开头。

Pod 解析器在处理一系列数据段落(在一个区域内)时,应该将它们视为一个包含空行的大的数据段落。因此,上面 "=begin html"..."=end html" 的内容可能被存储为两个数据段落(一个由 "<img src='wirth_spokesmodeling_book.png'>\n" 组成,另一个由 "<hr>\n" 组成),但应该被存储为一个数据段落(由 "<img src='wirth_spokesmodeling_book.png'>\n\n<hr>\n" 组成)。

Pod 处理器应该容忍空的 "=begin something"..."=end something" 区域,空的 "=begin :something"..."=end :something" 区域,以及无内容的 "=for something" 和 "=for :something" 段落。也就是说,这些应该被容忍。

=for html

=begin html

=end html

=begin :biblio

=end :biblio

顺便说一下,请注意,没有简单的方法来表达以类似命令开头的數據段落。例如

=begin stuff

=shazbot

=end stuff

在那里,"=shazbot" 将被解析为 Pod 命令 "shazbot",而不是数据段落 "=shazbot\n"。但是,您可以使用以下代码来表达包含 "=shazbot\n" 的数据段落

=for stuff =shazbot

这种情况可能很少见。

请注意,=end 命令必须与当前打开的 =begin 命令匹配。也就是说,它们必须正确嵌套。例如,这是有效的

=begin outer

X

=begin inner

Y

=end inner

Z

=end outer

而这是无效的

=begin outer

X

=begin inner

Y

=end outer

Z

=end inner

后者是不正确的,因为当看到 "=end outer" 命令时,当前打开的区域的格式名称是 "inner",而不是 "outer"。(碰巧 "outer" 是更高层区域的格式名称。)这是一个错误。处理器默认情况下必须将此报告为错误,并且可能会停止处理包含该错误的文档。这的一个推论是,区域不能“重叠”。也就是说,上面的后一个块不代表一个名为 "outer" 的区域,它包含 X 和 Y,与一个名为 "inner" 的区域重叠,它包含 Y 和 Z。但由于它无效(因为所有明显重叠的区域都是无效的),它不代表那个,也不代表任何东西。

同样,这也是无效的

=begin thing

=end hting

这是一个错误,因为该区域由 "thing" 打开,而 "=end" 试图关闭 "hting" [sic]。

这也是无效的

=begin thing

=end

这是无效的,因为每个 "=end" 命令都必须有一个格式名称参数。

另请参阅

perlpod"PODs: 嵌入式文档" 在 perlsyn 中podchecker

作者

Sean M. Burke