InkMark:一种轻量级的标记语言(撰写中……)

2021年5月31日 // InkMark

1 概述

InkMark 是一种用于编写各种文档的轻量级标记语言。它的语法规定了如何利用带简单标记的纯文本,为文档添加结构、语义和格式。通过特定的 InkMark 编译器,可以将纯文本形式的 InkMark 文档转换为规范的 HTML 或其他格式的文档。InkMark 尤其适用于学术文档的网络书写与展示。

1.1 为什么要设计 InkMark?

Web 技术在当今被广泛应用,其中许多信息主要以 HTML 格式在浏览器或手机应用中展示。然而,对于大多数人而言,要编写格式和结构良好的网页内容相比于使用 MicroSoft Office Word 编写一个文档要困难得多。许多网站要求用户从浏览器中在线输入内容,并提供了简单的内容编辑器。对于多数网站(如各类社交网站),这样的编辑器已基本够用了。但有时候我们会有更高的要求,如在维基百科上编辑“调和矩阵”这样一个包含大量公式且结构较为复杂的数学条目,或是直接在浏览器中书写并发表自己的学术论文,甚至直接在浏览器中和他人协作编写一本专著。通常这些学术文档篇幅巨大、结构复杂,尤其是其中包含大量的图像、表格、公式和参考文献,需要对这些内容分别标注自动编号的标题,然后在文档其他位置进行交叉引用和索引。不得不说,要在浏览器文本输入框中编写这类内容几乎是一场梦魇,即便对当今如 Google Docs 这样强大的在线文档编辑器来说也是如此。

为了能在浏览器中轻松编写并正确地展示格式复杂的学术内容,我们需要做一些折中。即不完全依赖所见即所得的 HTML 编辑器,而是让用户以纯文本的形式输入内容,纯文本中包含一些简单的标记,用于标示文档的各种元素及其属性,之后通过编译过程将这些被标记的文本转化为网页内容(HTML 格式的文本)。当然,这些标记规则要容易记忆、便于输入、可读性好。基于最新的 Web 技术,这些标记文本被编译后生成的网页看起来完全可以像正规的出版物一样,甚至在交互性操作、多种尺寸屏幕上的响应式显示还优于以往常用的便携式文档格式(PDF 格式)或 MicroSoft Office Word 文档。

这种标记方法以及如何将其转换为 HTML 格式文本的约定构成了 InkMark 这种轻量级标记语言的规范。实际上,目前已有许多轻量级标记语言被设计出来了,如非常流行的 Markdown维基百科网站使用的 Mediawiki 标记语法。但这些语言在设计时并没有太多考虑对学术内容的表达能力,因此 InkMark 的设计者就此找到了重新设计一种新的标记语言的借口。或者,不必避讳,此语言的诞生或许只是为了满足设计者对轻量级标记语言的某些特殊爱好而已。

1.2 特性

相对于其他各种轻量级标记语言,InkMark 具有以下特性:

  • 具有几乎与 HTML 相匹配的文档内容描述能力。理论上来说,InkMark 可以表示 HTML 中所有类型的元素,以及为元素添加任意属性。
  • 很好地在简单和功能强大之间取得平衡。InkMark 的元素标记法包括完整模式和简写模式,其完整模式像 HTML 一样功能强大,而简写模式则像 Markdown 一样简单,且可以按统一的规则由完整模式过渡到简写模式。
  • 简写模式使用独特的“标记符号加方括号对接龙”形式的语法。例如,在其他轻量级标记语言中,常用 **着重强调** 表示要 着重强调 的文本,Inkmark 中却使用 *[着重强调] 达到同样的效果。其他轻量级标记语言的用户在刚开始可能会不太习惯,但一旦适应,他们多数会喜欢上这种标记方式。
  • 能够用于科学技术报告、学术论文、书籍的书写。通常这些学术文档篇幅巨大,结构复杂,含有大量的图像、表格、公式和参考文献,需要对这些内容标注题注、自动编号,并在文档其他位置进行交叉引用和索引。
  • 具有灵活的文档组织能力。各个文档可独立编写和呈现,之后不必改变就可以合并在一起形成更长的主文档。无论是论坛帖子、博客文章、期刊论文、报告或书籍,其书写体验基本一致。
  • 注重语义的表述,不直接指定显示格式。如该语言关心你文本中的一行文字是普通段落、标题还是引文,但却不关心你的段落对齐方式,以及首行是否缩进等。
  • 尽可能保持语法的一致性和对称性。使用户能根据一定的规则来理解和学习其语法,降低语法学习和编译器实现的难度。
  • 良好的中文支持。其语法在元素标签和属性名称,换行、空格及标点处理,东西方文字混排等方面都更符合中文用户的使用习惯。
  • 当使用相对高级的功能时,所生成 HTML 文本的正确显示需要依赖特定的层叠样式表(CSS)甚至 JavaScript 代码。如通常要借助 MathJax 这类 JavaScript 数学显示引擎才能正确地显示文档中的公式,带编号公式的合理显示布局(通常公式和编号显示在同一行内,公式居中对齐,编号右对齐)也需要借助额外的 CSS 实现,而一些自定义类独有的显示方式也需要借助 CSS 实现。
  • 更加复杂。功能强大必然带来复杂性的提升,因此针对其全部功能的学习及编译器的实现也更加困难。

相对于 LaTeX 这种专业的学术论文排版系统,InkMark 主要专注于为文本添加常用的结构和语义,而其他更多的功能,如文本和页面样式,则大多交给其他程序(如和网页关联的层叠样式表,CSS)来完成。因此,其功能要远远少于 LaTeX,但也简单得多。

1.3 名称的由来

在给此语言命名的时候,首先考虑给它一个雅致的中文名称。 既然和文字有关,那就与墨(Ink)有关。 而此语言又是一种标记(Mark)语言,那就叫 “InkMark” 吧。 “Ink Mark” 一词在中文中可翻译为“墨迹”或“墨痕”,他们表示笔墨在纸上留下的痕迹,或前人的诗文书画,甚至也可理解为用墨斗标记的直线痕迹。 因此用 “InkMark” 为此标记语言命名是再贴切不过了。

另外,“墨斗”翻译为英文就是 “Ink Marker”,这是旧时木匠在待创作的木料上标记直线常用的工具。 我们不妨将 InkMark 编译器看做是一个墨斗,并希望每一个使用此工具进行书写的人,都别具匠心,创作出美的作品。

1.4 关于此文档

此文档并不是语言规范,而是本语言的设计和论证说明。 即其内容不光是说明该语言的基本语法,同时还讨论了为什么要这样设计。 希望以此建立一个和协作者讨论改进此语言的基点,感兴趣的读者可基于此文档理解本语言中各项设计的出发点,并提出改进意见。

2 元素标记法概述

2.1 元素

元素是组成 InkMark 文档的基本单元,InkMark 文档采用树状结构组织元素,这些元素赋予文档结构、语义和格式。元素具有类型、内容和属性,InkMark 的语法主要规定了如何标记不同类型的元素内容及其属性。

InkMark 元素主要用于表征文档的内容,这些元素的类型基本与 HTML 中的流式内容元素相对应。常见的元素类型名称有段落、标题、列表、表格、单元格、图像、图像标题、超链接、强调的文字,等等。在 HTML 中,这些不同的元素类型用不同的标签(tag)名称表示,如段落的标签名称为 p,表格的标签名称为 table,着重强调的文字的标签名称为 strong,InkMark 同样如此。

与 HTML 一样,InkMark 中的元素主要可分为两大类:

  • 块级元素:内部可以包含行内元素或其他块级元素的元素。它是比行内元素更“大型”的结构。通常大多数块级元素开始时会新起一行。常见的块级元素有段落、标题、水平线、表格、表格行、单元格、列表、列表项等。
  • 行内元素:又称为内联元素,只能包含内容文本或其他行内元素的元素。通常不会以新行开始。常见的行内元素有着重强调(通常显示为粗体)、强调(通常显示为斜体)、删除(通常显示为带删除线文字)、插入(通常显示为带下划线文字)、超链接、上标、下标、变量、行内公式、代码等。

块级元素和行内元素的这种区别并不是绝对的,个别时候,一个行内元素内部可能包含其他块级元素。如表示超链接的元素 a,一般将其看作是行内元素,但为了使用方便,有时其内部也可以包含块级元素。

元素内容是指被标签标记的文本,或元素中包括的子元素。如对于指向维基百科网站的超链接,其中 维基百科 四个字组成的文本就是元素内容。再如,对于列表元素,其中的各个列表项是其子元素,他们就是列表元素的内容。并非所有元素都有内容,如用于表示在文本中生成一个换行符号的元素(对应于 HTML 中的 <br> 元素)就不包含内容。有内容的元素被称为容器元素,无内容的元素被称为空元素

元素属性给出了元素的行为或额外的性质。元素属性通常以 名称=值 的形式给出,其中名称为属性名称标识,值为该属性对应的值。如对于超链接维基百科,就有一个名称为 href 的属性,其值为 https://wikipedia.org/,用于给出超链接指向的 URL 或 URL 片段。

为了在简单和功能强大之间取得平衡,InkMark 元素的标记语法包括完整模式和简写模式。完整模式能全面地表示元素的类型、内容和属性,其功能与 HTML 相匹配;简写模式则是按一定规则规定了常见元素及其常见属性的简化表示法。完整模式和简写模式并不是相互隔离的,他们在内容及属性表示方面符合大致相同的规则。多数情况下只需要使用简写模式,并且也应该尽量使用简写模式。

2.2 完整模式

完整模式实际上是对 HTML 元素标记方法的改造,其所表示的内容本质上与 HTML 元素是相同的。如对于如下 HTML 超链接元素:

<a href="https://wikipedia.org/" target="_blank">维基百科</a>

图 1 是对其元素结构的分析。

HTML 元素结构
图 1 HTML 元素结构

若用 InkMark 完整模式的元素表示法来表述,其形式为:

[=a][维基百科][href="https://wikipedia.org/" target="_blank"]

图 2 是对该 InkMark 元素结构的分析。

HTML 元素结构
图 2 InkMark 元素结构

可以看出,InkMark 完整模式的元素结构形式为:

[=标签名称][元素内容][属性1="值 1" 属性2="值 2"][属性3="值 3"]

这是一种可称之为方括号对接龙的标记法。其中第一对方括号称为标签项,它通过标签名称标明了元素的类型,标签名称前方的等号 = 用于和余下的各个方括号对项目区分。第二对方括号通常为内容项(有时不存在该项),它包含元素的内容。余下的零个或多个方括号都称为属性项,他们内部都包含元素的属性。

标签项中的标签名称与 HTML 元素的标签名称大致相同,不过 InkMark 还定义了一些额外的标签名称。这里列出少数几个元素标签名称:p(段落)、h1(一级标题)、h2(二级标题)、h3(三级标题)、uli(无序列表项,InkMark 独有)、oli(编号列表项,InkMark 独有)、table(表格)、td(单元格)、img(图像)、figcaption(图像标题)、a(超链接)、strong(着重强调),等等。

内容项可以写在一行内,也可以跨行书写。其两侧的方括号对可以是单层的,也可以通过重复书写变成双层或更多层。一般当元素包含多行内容,或属于块级元素时,建议将其外围的方括号对写成两层或两层以上,且左侧的左方括号个数必须等于右侧的右方括号个数,这样可使被标记内容的边界更加醒目,避免与元素内部其他元素混淆。例如,通常用预格式化文本标签 pre 标记代码块,并用等宽字体显示,以下是一段 JavaScript 代码的标记方法,该段代码用双层的方括号对 [[ ]] 界定:

[=pre][[
class User {
	constructor(name) {
		this.name = name;
	}

	sayHi() {
		alert(this.name);
	}
}
]]

对于空元素,由于不具备任何内容,在表示时也不应包含内容项。如对于换行符元素,可以将其表示为 [=br]

属性项中对属性名称和值的要求与 HTML 语法完全相同。属性值通常用一对单引号 ' 或双引号 " 包括起来。当属性值中不包括空格时,可以省略这些引号。另外,InkMark 规定,当元素有多个属性时,各个属性可以放在一个方括号对中,中间用一个或多个空格分割;或者将不同的属性分别放在不同的方括号对中。例如,上述超链接元素也可以表示为:

[=a][维基百科][href=https://wikipedia.org/][target=_blank]

InkMark 这种完整模式的元素标记法和 HTML 相比只是形式不同,他们的表达能力基本相同。由于不像 HTML 那样,其元素结束标签也包含标签名称,导致 InkMark 元素内容的结束标签不够醒目,在表示多层嵌套的元素结构时,还不如 HTML 更加方便。之所以设计这种完整模式,是因为它可以轻易地和简写模式相互转换,从而满足不同程度的文档表达需求。

在设计完整模式时,之所以采用方括号 [ ] 界定标签项而不是其他括号类型,是因为方括号输入时不需要同时按键盘上的 Shift 键,更方便手工输入。另外,之所以要求标签项的标签名称前面有一个等号 =,是因为各个方括号项很容易和文本中非标记作用的正常方括号发生混淆,加上 = 就能更加明确地表示这是一个元素的开头。另外,= 在键盘上距离 [ ] 较近,输入时同样不需要按 Shift 键,方便手工输入。

2.3 简写模式

可通过以下规则,将完整模式逐渐转换为简写模式。

简写规则 1:对于一些常见的元素类型,可以用键盘上出现的各种标点符号代替完整模式中的标签项(第 1 项)。

这种用标点符号代替 [=标签名称] 形式的标签项的标记法称为符号标记法

如用句点 . 代替段落标签 [=p],则段落元素可分别按如下方式书写(后面还有更简化的书写方式):

[=p][[这是一个段落。]]

.[[这也是一个段落。]] 

由于键盘上的标点符号数目毕竟有限,而要用符号标记的元素类型却很多。为了充分利用这些标点符号,强制规定当采用符号标记法时,对于行内元素,其内容项外围的方括号对必须是单层的;而对于块级元素,其内容项外围的方括号对必须是双层或更多层。这样,就可以用同一个标点符号分别标记不同的行内元素和块级元素。例如,对于着重强调 [=strong] 这一行内元素标签,规定可以用星号 * 代替;同样,对于无序列表项 [=li] 这一块级元素标签,也规定可以使用星号 * 代替。这样当这两种标签都出现在行首时,可通过区分后面所跟的内容项外围方括号对的层数来判断他们的元素类型了。根据这种规则,*[标记内容] 表示一个行内的着重强调的元素,*[[标记内容]] 表示一个块级的无序列表项。

在使用符号标记法时,若标记符号作用于行内元素,则要求标记符号只能书写一次,且后面紧跟内容或属性项的左方括号;而当标记符号作用于块级元素,则标记符号可以重写多次,且符号与后面的左方括号之间可以有水平空白,以此使标记更加醒目。如规定可以使用双引号 " 代替块级引用标签 [=blockquote],则以下是一个合法的块级引用元素:

""""""""""  [[
	学而时习之,不亦说乎?有朋自远方来,不亦乐乎?人不知而不愠,不亦君子乎?
]]

对于部分块级元素,需要通过行首标记符号重复的次数来判断其类型或等级。如对于各级标题,其行首标记元素重复的次数就是该标题的等级:

=[[ 一级标题 ]]
==[[ 二级标题 ]]
===[[ 三级标题 ]]

简写规则 2:对于单独占一行的块级元素,其内容项两侧的方括号可以省略,但这时标签项和内容之间必须至少有一个水平空白。

这种仅在行首放标签项,不用方括号界定标记内容,且无属性项的标记方法称为行首标记法。同时使用行首标记法和符号标记法时,又称为行首符号标记法

如,对于上述引文,可以表示为:

[=blockquote] 学而时习之,不亦说乎?有朋自远方来,不亦乐乎?人不知而不愠,不亦君子乎?

或使用行首符号标记法表示:

" 学而时习之,不亦说乎?有朋自远方来,不亦乐乎?人不知而不愠,不亦君子乎?

又如,对上述的三个标题,可表示为:

= 一级标题
== 二级标题
=== 三级标题

简写规则 3:对于部分元素的重要属性,当其出现在各方括号项的特定次序位置时,可以在单独的一个方括号对中只写其属性值,而省略其前方的属性名称以及等号 =

例如,对于超链接元素,href 就是一个非常重要的属性,如果属性列表中没有显式根据属性名称给定该属性,则第一个出现的无属性名称的属性就是 href 属性的值。由于超链接元素的标签 [=a] 可用 @ 代替,则如下两个超链接元素是等同的:

[=a][维基百科][href=https://wikipedia.org/]
@[维基百科][https://wikipedia.org/]

再如,图像元素 [=img](可用 & 符号代替)是一个空元素,它必须包含一个 src 属性,另外一个重要但不是必须的属性是 alt。则规定标签后方第一个出现的无属性名称属性就是其 src 属性的值,第二个无属性名称的属性就是其 alt 属性的值。如下是两个图像元素是等同的:

[=img][src="http://inkmark.org/images/image.png" alt="替换文字"]
&[http://inkmark.org/images/image.png][替换文字]

简写规则 4:对于一些由多个元素构成的较大型的复合类型元素,可以将其简化为单个元素或多个同类元素。

例如,常用 [=figure] 标签表示带标题的内容元素,其内容通常是图片。以下是一个完整模式的 figure 元素示例:

[=figure][[
	[=img][src="http://inkmark.org/images/image.png" alt="替换文字"]
	[=figcaption][[图片标题]]
]]

以上代码可转化为如下简写模式:

&[[http://inkmark.org/images/image.png]][图片标题][alt="替换文字"]

注该简写模式与前面的 img 示例元素相比,其标记符号后的方括号项由单层方括号变为双层了,仅仅这些变化,就使 & 符号所标记的元素由 img 变为外层为 figure 的复合块级元素。注意,以上简写的标记方法只针对 figure 元素内包含一个图片和一个标题的情况下适用。由于该标记形式并不与具体的 HTML 标签对应,我们需要在 InkMark 中新定义一个标签 figureimg,专门应对图像况中包含一个图像以及一个标题的情况。

又如,对于无序列表元素 ul,其内部会包含多个列表项 li,其完整模式下的标记方法如下所示:

[=ul][[
	[=li][[列表项 1]]
	[=li][[列表项 2]]
	[=li][[列表项 3]]
]]

在转化为简写模式时,可以不再标示其外围的 [=ul] 标记,而只标示列表项标签。如下所示:

* 列表项 1
* 列表项 2
* 列表项 3

同样的,以上 * 代表的无序列表项标记对应于 InkMark 独有的标签 uli

简写规则 5:当确定一项内容属于块级元素,但没有给出任何标签以标示其元素类型时,该元素默认就是段落。

段落在文档中是如此常见,以致我们都懒得对它进行标记了。由于规定凡是没有标示元素类型的块级元素都是段落,因此就不用往每个段落的开头放一个 .[=p] 标记了。这使我们的标记工作量大大减少,且文档更加美观。以下两行分别表示两个段落:

这是一个段落。

这也是一个段落。

2.4 拥有两种模式的好处

InkMark 在完全使用完整模式时并不轻量,只有使用简写模式时才算作一种轻量级的标记语言。其他的轻量级标记语言都不像 InkMark 这样拥有两种书写模式。这导致这些轻量级标记语言通常只能书写简单的文档内容,当书写复杂结构的文档时,大多需要混合使用 HTML,或依赖预先写好的内容模板,导致这时的写作难度加大,且文档不美观。InkMark 则允许我们在简写模式和完整模式间无缝地切换,不仅做到了使文档便于输入、可读性好,并且对各种标记及属性的支持使文档的表述能力具有无限的扩展空间。

为方便输入和阅读,应该尽可能地使用简写模式。只能在简写模式无法满足要求时,才使用完整模式。

3 元素标记的通用规则

本节将详细介绍各种元素标记的通用规则,这些规则同时适用于所有元素。

3.1 空白的处理

在 InkMark 中,空白字符特指空格 U+0020、水平制表符 U+0009、回车符 U+000D 和换行符 U+000A,其中空格和水平制表符又称为水平空白。空白虽然不可见,但常常被用来分割和美化文本。InkMark 对空白的处理具有以下规则:

  • 所有处在行首或行尾的水平空白都被忽略;
  • 连续的多个空格或水平制表符的任意组合都被看作是一个空格;
  • 行尾单个的换行符、回车符以及两者的任意组合都被当作是一个回车符;
  • 如果一行中仅包括空白字符,则该行称为空行
  • 连续的多个空行都被看作是一个空行。

以上对空白的处理有一个例外,就是预格式化元素 pre,该元素中的文本通常会保留空格和换行符。

因此,在本文档的余下部分,当提到空格时,可能对应 InkMark 源码中的多个空格或水平制表符,或者他们的任意组合;同样,当提到换行符时,在 InkMark 源码中可能是换行符、回车符及其组合。

InkMark 对行首、行尾空格的处理与 Markdown 有很大的不同。Markdown 依靠行首和行尾的空格进行标记,而 InkMark 则完全忽略行首和行尾的空格。他们各有优缺点,但 InkMark 的这种处理方法使我们可以自由地用空格缩进以美化排版,且不必纠结每级缩进用 2 个空格、4 个空格或者是水平制表符的问题。

3.2 适用于所有元素的规则

  • InkMark 的元素可以包括标签项、内容项和属性项,三者依次出现。
  • 当内容项以方括号包围时,其内容可以包含一行或多行,但标签项和内容项的左方括号必须出现在同一行中;内容项的右方括号和各个属性项也必须出现在同一行中。
  • 除了段落元素的标签项可以省略外,其他元素的标签项都不可以省略。
  • 标签项可以用 [=标签名称] 的形式表示,也可以用一个或多个重复书写的标点符号表示。
  • 标签项以 [=标签名称] 的形式表示时,其左右方括号必须处在同一行上,[=、标签名称和 ] 四者两两之间可以有任意个数的空格。
  • 标签名称可以使用 HTML 中的所有流式内容元素的标签名称。
  • 所有容器元素必须包含内容项,所有空元素都不包含内容项。
  • 当空元素的标签项用标记符号代替,且不保括属性项时,则必须重复书写其标记符号 2 次及以上,且块级元素必须单独占一行。如换行符 [=br] 可以标记符号 ;; 表示;主题转换元素(水平线) [=hr] 可用在一行内单独出现的 -- 表示。
  • 除了仅包含一行的块级元素可以选择性地不用方括号包括之外,其余内容项应使用方括号对包括起来,方括号对可以是单层、双层或多层。
  • 当内容项的定界方括号有多层时,左、右两侧重复书写的各个方括号之间不能有空白,且左右两侧的方括号数目应相等。
  • 内容项的左方括号、内容和右方括号两两之间可以有任意数目的空白(包括换行符和回车符),但这些空白将被忽略。
  • 元素可以包含多个属性,各个属性以 名称=值 的形式出现。
  • 多个属性可以出现在一个方括号对中,也可以出现在多个方括号对中。
  • 左方括号、各个属性、右方括号两两之间可以有任意个数的空白。
  • 属性名称通常与 HTML 的属性名称相同,或使用任意自定义的属性名称。
  • 属性值一般同时用单引号 ' 或双引号 " 包括起来,属性值也可能是一个列表,中间用空格分开,如 class="title paragraph"。当属性值中间不包含空格时,可以省略外围的引号。
  • 一些布尔属性,其名称形如 requiredreadonlydisabled,他们的值只有 truefalse 两种。这种属性可以只给出属性名称,如果存在该名称,则表示其值为 true,否则为 false

3.3 行内元素标记规则

  • 行内元素的标签项、内容项、各个属性项之间必须紧密相连,不能有任何空白。
  • 当采用符号标记法时,行内元素的标记符号只有一个,不能使用多个符号组合书写的方式。
  • 除了超链接元素 a,其他行内元素的内容项内只可以包括其他行内元素,如 [*[/[粗斜体]]] 将显示为 粗斜体

3.4 块级元素标记规则

  • 块级元素的标签项、内容项、各个属性之间不必像行内元素那样必须紧密相连,可以有任意数目的水平空白分割。
  • 当采用符号标记法时,块级元素的标记符号可以有一个或多个重复书写,但多个相同的标记必须紧密相连,之间不能有空白。
  • 除了表格单元格外,其他块级元素都不能和其兄弟块级元素共占一行。即元素的必须新起一行书写,其标记项前方不能有任何非空白字符;元素末尾处同一行内不能有任意其他内容,如果有,这些内容将被忽略。
  • 当采用符号标记法且用方括号界定标记内容时,该方括号必须是双层或更多层的方括号对。
  • 当采用行首标记法时,标记项和内容之间必须有一个或多个水平空白,且只有本行的内容才是被标记内容,即便下一行和该行紧连,也应被看作是下一个同级块级元素。
  • 块级元素的内容项可以包含其他块级元素或行内元素。

3.5 转义

InkMark 特殊的标记法使其较难发生常规的文本符号与标记符号混淆的情形,但有时也会出问题。如参考文献常用形如 [3] 的上标形式出现,其标注形式为 ^[ [3] ],这时标注内容中的右方括号可能会被判定为与用作内容定界的左方括号配对,从而产生歧义。为了尽可能地少进行字符转义,以方便输入,InkMark 规定:若标记内容中的方括号成对出现,则应被看作是内容而不是内容定界符。因此,以上给出的 ^[ [3] ] 标记是完全合法的。但这时标记内容和用作定界符的方括号之间必须用水平空白分开,如果写成 ^[[3]] 的形式,由于不存在以 ^ 作为符号标记的元素,该元素将被认为等同于 ^[3],否则将被看作是块级元素。

如果标记内容是 3] 而不是 [3],这时将元素写成 ^[ 3] ] 将是错误的。这是因为内容项的右方括号将会与内容定界符中的左方括号配对,从而产生混淆。对这种内容中左右方括号不是成对出现的情况,需要对额外多出的左侧或右侧方括号进行转义表示。即用特定的字符实体代替各个单一字符。

InkMark 字符实体的表示与 HTML 完全相同。它包括三部分:第一部分是一个 & 符号;第二部分是实体名字或者是 # 加上实体编号;第三部分是一个分号。这里列出了所有定义的字符实体表 1 列出了 InkMark 中常用的字符实体。

表 1 常用字符实体
字符 描述 实体名称 实体编号
空格 &nbsp; &#160;
[ 左方括号 &lsqb; 或 &lbrack; &#91;
] 右方括号 &rsqb; 或 &rbrack; &#93;
& 与和符 &amp; &#38;
" 双引号 &quot; &#34;
' 单引号 &apos; &#39;

因此,以上所述的只有半边方括号的上标元素,应表示为 ^[ 3&rsqb; ]^[ 3&rbrack; ]

除了防止混淆,字符实体还可以用于输入一些较难输入,或者在编辑器中无法正确显示的字符。

5 元素列表

流式内容元素(Flow content) c
标签名称 元素名称 元素类型 标记符号 说明
a 超链接,锚 行内或块级 @
abbr 缩写 行内
address 地址 行内
article 文章,文章内容 块级
aside 伴随,伴随内容 块级
audio 音频 行内
b 提醒注意 行内 Bring Attention To
bdi 双向隔离元素 行内 告诉浏览器将其包含的文本与周围的文本隔离,当网站动态插入一些文本且不知道所插入文本的方向性时,此功能特别有用。
bdo 双向文本替代元素 行内 改写了文本的方向性,使文本以不同的方向渲染呈现出来。
blockquote 块级引用 块级 "
br 换行符 行内
button 按钮 行内
canvas 画布 行内
cite 引用 行内 "
code 代码 行内 `
data
datalist 详细信息
del 从文档中删除的文本 行内 -
details
dfn 术语定义 行内
dialog
div 通用容器 块级 通用的流内容容器。
dl 块级
em /
embed 块级
fieldset 块级
figure 块级
figureimg 块级 & InkMark 自定义标记。对应块级符号标记 &
footer 块级
form 块级
h1 块级 =
h2 块级 =
h3 块级 =
h4 块级 =
h5 块级 =
h6 块级 =
header 块级
hgroup 块级
hr 块级 -
i 行内
iframe 块级
img 行内 &
input 行内
ins 插入 行内 + 被插入到文档的文本。
kbd 行内
label 行内
link
main 块级
map 块级
mark 行内
math 行内或块级 $
menu 块级
meta
meter
nav 块级
nomark 无标记文本 行内 行内或块级 InkMark 的自定义标记。不将标记内容看作 InkMark 文本,即不进行 InkMark 解析。同时可作为行内元素和块级元素。
noscript
object
ol 编号列表 块级
oli 编号列表项 块级 # InkMark 自定义的标签,表示编号列表项,是 olli 的组合。
output
p 段落 块级 .
picture
pre 预格式化文本 块级
precode 预格式化代码块 块级 `
progress 块级
q
ruby
s
samp
script
section 块级
select
slot
small
span 通用容器 行内 通用的流内容容器。
strong 着重强调 行内 *
sub 下标 行内 _
sup 上标 行内 ^
svg 可伸缩矢量图形
table 块级
template
text
area 块级
time 时间 行内 表示 24 小时制时间或者公历日期,若表示日期则也可包含时间和时区。
u 表意不清 行内 一个需要标注为非文本化(non-textual)的内联文本域。
ul 无序列表 块级 表示一个内部可包含多个元素的无序列表或项目符号列表。
uli 无序列表项 块级 * InkMark 自定义的标签,表示无序列表项,是 ulli 的组合。
var 变量 行内 ' 数学表达式或编程上下文中的变量名称。
video 视频 将视频内容嵌入到文档中。
wbr 单词换行时机 行内 规定在文本中的何处适合添加换行符。与 [=br] 标签不同,如果浏览器窗口的宽度足够,则不换行;反之,则在添加了 [=wbr] 标签的位置进行换行。
autonomous 行内
custom 行内
elements 行内
text 行内

3.2 标记符号

InkMark 几乎使用了 QWERTY 键盘上所有的标点符号来标记元素。同样一个标记符号,用在不同的上下文中,可能表示不同的的含义。如由于行内元素和块级元素的标记方法很容易区分,因此一个符号在被用来标记一种行内元素后,还可以同时被用来标记另外一种块级元素。

InkMark 中使用的标记符号及其标记内容主要包括:

表 2 键盘上的标点符号及对应的标记
符号 名称 行内元素 块级元素
~ 波浪字符,代字号 非标记文本 非标记文本
` 反引号 代码文本 预格式化的代码文本
! 惊叹号 表格中的表头单元格
@ 爱特,小老鼠 超链接 超链接;插入编号索引
# 井号 自动编号文本;元素 id 属性 编号列表项;元素 id 属性
$ 美元符 行内公式 块级公式
% 百分号
^ 脱字符 上标文本
& 与和符 图像 带标题的独立展示的图像
* 星号 着重强调的文本 无编号列表项
- 连字符 删除的文本 主题转换(水平线)元素;表格环境中表示表格行
_ 下划线 下标文本
+ 加号 下划线文本 表格
= 等号 行内元素标签名称前导符号 块级元素标签名称前导符号;正文中各级标题
() 小括号,圆括号
[] 中括号,方括号 界定被标记内容 界定被标记内容
{} 大括号,花括号
<> 尖括号 右尖括号标记引用的外部文档
: 冒号 术语定义 定义列表中的术语定义
; 分号 两个连续分号代表一个换行符元素 定义列表中的术语描述
" 双引号 引用的文本 块级引用
' 单引号 变量
, 逗号
. 句号,点 元素的 class 属性;自动编号标签 元素的 class 属性;段落(可省略)
? 问号 Inkmark 行内注释 Inkmark 块级注释
/ 斜线 强调文本
\ 反斜线
| 竖线 表格中的标准单元格

5 段落和换行

5.1 段落

段落是最常见的元素。其标签名称为 p,标记符号为句点 .。由于太过常见,我们索性给其一定的“特权”,规定当使用行首标记符号标记法时,行首的标签项可以省略。或者说,当一个块级元素的类型不确定时,它默认就是段落。但如果前后两个段落都采用这种纯内容的标记法,则必须在两者之间添加一个空行以进行分割。而当段落和其他块级元素相邻时,由于这些块级元素必须显式地标注,因此他们之间可以不添加空行。但通常在各个相邻块级元素间添加空行以美化排版。以下给出了一些段落示例:

## 二级标题 1
这是一个段落。

这是另外一个段落。
## 二级标题 2

这还是一个段落,
该行和上一行同属一个段落。

当需要为段落添加属性时,其标记项不可以省略,内容两侧的方括号也不可以省略。如下所示:

.[[一段包含重要内容的段落。]][class=important]

由于段落标记常被嵌入到其他块级元素内,因此在使用行首标记法标记这些元素时,可以将段落标记符 . 紧跟行首标记放置,表示该单行块级标记的内容包含一个段落。如下所示:

". 学而时习之,不亦说乎?有朋自远方来,不亦乐乎?人不知而不愠,不亦君子乎?

以上标记对应的 HTML 文本为:

<blockquote><p>学而时习之,不亦说乎?有朋自远方来,不亦乐乎?人不知而不愠,不亦君子乎?</p></blockquote>

5.2 换行

有些时候,我们需要将同一段落或其他块级元素内的文字,分割成多行显示,其方法有多种。最直观的方法就是在输入文字时,直接在需要换行的地方按回车键,然后紧接着书写下一行。如下所示:

远远的街灯明了,
好像闪着无数的明星。
天上的明星现了,
好像点着无数的街灯。

这将生成如下 HTML 代码:

<p>远远的街灯明了,<br>好像闪着无数的明星。<br>天上的明星现了,<br>好像点着无数的街灯。</p>

即在文本中的每一个断行处自动插入 HTML 换行符元素 <br>。这是一种硬换行(hard wrap)的换行方式,该方式对中文书写更为适用。因为在中文书写时,一般不将一个段落分成多行书写,也不像英文那样可以将断行的地方变成一个空格。

除了以上直接以多行形式输入多行文本外,也可以将多行文本书写为一行,在中间显式地插入换行符元素 [=br]。其形式如下:

远远的街灯明了,[=br]好像闪着无数的明星。[=br]天上的明星现了,[=br]好像点着无数的街灯。

这种显式输入换行符的方法可以使我们利用行首标记法输入多行文本,在对表格单元格内容进行排版时尤其有用。上述换行符元素 [=br] 对应的标记符号是 ;,由于是空元素,需要将该符号重复书写两次,因此以上的诗句也可以写成:

远远的街灯明了,;;好像闪着无数的明星。;;天上的明星现了,;;好像点着无数的街灯。

还有一些情况,我们在书写 InkMark 源文件时,为了美化排版,需要将其分行书写,但实际显示时,则需要将他们显示为一行。这时可以使用 InkMark 独有的 [=nobr] 标记,或其对应的符号标记 <<,该标记仅在行尾时有效。如下示例代码:

&[./path/to/image1.png][图像 1]
&[./path/to/image2.png][图像 2]

将会生成以下 HTML 代码:

<p><img src="./path/to/image1.png" alt="图像 1"><br>
<img src="./path/to/image2.png" alt="图像 2"></p>

注意上述代码会自动在两个图片中插入 <br>,从而使两个图片分别显示在两行。但我们的本意可能是想要两个尺寸较小的图片显示在一行,这需要消除其中的 <br>。当然,将两个图像元素放在一行可以解决这种需求:

&[./path/to/image1.png][图像 1]&[./path/to/image2.png][图像 2]

然而有时我们就是要把他们放在两行,又希望两个图片显示在一行,这就需要在第一个图像元素行的最后方插入 [=nobr]<<

&[./path/to/image1.png][图像 1]<<
&[./path/to/image2.png][图像 2]

[=nobr]<< 会删除行尾的回车符,但并不会像软换行(soft wrap)那样再加入一个额外的空格,这样显然更适合中文排版。要加入额外的空格,请在这两个元素前方输入空格。后方不行,因为行尾的所有水平空白将被删除。

6 标题

InkMark 的标题元素包括 h1h2h3h4h5h6。主要依靠各级标题表示文档的结构,依靠这些标题也可生产文档的大纲。所有这 6 级标题对应的标记符号都是等号 =,不过在书写时,要通过符号的个数来判断标题的级别。其形式如下:

= 一级标题
== 二级标题
=== 三级标题
==== 四级标题
===== 五级标题
====== 六级标题

有关标题还有更复杂的用法,在后续的自动编号节会介绍如何为标题设置编号形式、引用标题、生成目录等;在文档组织节中会介绍在将多个文档合并成一个主文档时,如何根据上下文动态调整标题等级。

7 简单行内元素

在日常使用时,许多行内元素都不需要额外添加属性,其结构比较简单。对于最常见的行内元素,InkMark 同时定义了他们简写模式下的标记符号,如表 4 所示。

表 4 常见的简单行内元素
元素类型 示例 生成的 HTML 显示效果 备注
着重强调 *[示例文本] <strong>示例文本</strong> 示例文本 表示重要的文字,一般用粗体显示。
强调 /[示例文本] <em>示例文本</em> 示例文本 表示较重要的文字,一般用斜体显示。
插入 +[示例文本] <ins>示例文本</ins> 示例文本 表示已经被插入文档中的文本,一般显示为带下划线文本。
删除 -[示例文本] <del>示例文本</del> 示例文本 表示被从文档中删除的文本,一般显示为带删除线的文本。
上标 a^[2] a<sup>2</sup> a2 上标文本。
下标 a_[2] a<sub>2</sub> a2 下标文本。
代码 `[func] <code>func</code> func 行内代码。
行内引用 "[示例文本] <cite>示例文本</cite> 示例文本 表示直接引用他人的话语。
变量 '[f]_[1] <var>f</var><sub>1</sub> f1 表示变量,变量通常用拉丁字母表示,以斜体显示。
行内公式 $['[f]_[1]] <span class="math"><var>f</var><sub>1</sub></span> f1 标记常规书写的公式、LaTeX 公式或 MathML 公式。
行内非标记文本 ~[*[strong]] *[strong] *[strong] 不将被标记的内容看作 InkMark 文本。
行内注释 ?[示例文本]     对文档的注释,在解析时将被忽略。

可以看出,其中许多标点符号与元素的语义具有一定的关联性。如最常使用的行内元素着重强调、强调、插入和删除分别与 */+-,他们也是乘号、除号、加号和减号。着重强调对应乘号,大多轻量级标记语言都这样用;强调常显示为斜体,因此对应斜线;插入文字就是向文本中添加文字,对应的符号就是加号;相反,删除就对应减号。这种理解虽然有点牵强,但确实能帮助记忆。

上标和下标分别用 ^_ 符号,这和 LaTeX 中对上下标的表示是一致的。代码对应 `,这和 Markdown 是一样的,并且代码块也是用此标记符号。行内引用使用双引号 ",这与我们经常用引号来引述别人的话是一样的,块级引用也使用该符号。行内公式使用 $,这与在 HTML 或各种轻量级标记语言中插入 LaTeX 公式时使用的公式分界符是一样的。不过请注意,使用 formula 标签或 $ 符号插入的行内或块级公式并不一定就是 LaTeX 公式,也可能是其他格式的公式。

8 简单块级元素

9 超链接

9.1 普通超链接

9.2 为所有元素添加链接

&[[./path/to/image-thumb.png]][图像标题][href="./path/to/image.png"]
<figure>
    <a href="./path/to/image.png">
        <img src="./path/to/image-thumb.png" alt="图像标题">
        <figcaption>图像标题</figcaption>
    </a>
</figure>

TODO: “Block-level” links in HTML5

== [[这是一个带链接的标题]] [href="#这是一个带链接的标题"]
<h2 id="这是一个带链接的标题"><a href="#这是一个带链接的标题">这是一个带链接的标题</a></h2>

10 代码

所有以下各项都各等价的,他们都将被转换为 <code>package main</code>

  • `[package main]
  • `[    package main]
  • `[package     main]
`[[
package main

import "fmt"

func main() {
	fmt.Printf("Hello, 世界!\n")
}
]] [Go]
<pre><code>package main

import "fmt"

func main() {
	fmt.Printf("Hello, 世界!\n")
}
</code></pre>

11 列表

11.1 无序列表

* 列表项1
* 列表项2
	** 列表项2-1
	** 列表项2-2
	** 列表项2-3
		*** 列表项2-3-1
		*** 列表项2-3-2
	** 列表项2-4
* 列表项3

11.2 有序列表

# 列表项1
# 列表项2
	## 列表项2-1
	## 列表项2-2
	## 列表项2-3
		### 列表项2-3-1
		### 列表项2-3-2
# 列表项3
	** 列表项2-1
	** 列表项2-2
	** 列表项2-3

11.3 定义列表

: HTML
; HyperText Markup Language, extended from SGML.
: XHTML
; HTML 4.0 rewritten to be compliant with XML rules.
: HTML5
; The latest revision of the HTML standard. Still under development.
<dl>
    <dt>HTML</dt>
    <dd>HyperText Markup Language, extended from SGML.</dd>
    <dt>XHTML</dt>
    <dd>HTML 4.0 rewritten to be compliant with XML rules.</dd>
    <dt>HTML5</dt>
    <dd>The latest revision of the HTML standard. Still under development.</dd>
</dl>

12 自动编号

学术文档中通常包含大量的图像、表格、公式和参考文献。在文档中插入图像时,通常会在其下方单独一行给出一个题注(又称图像的标题),题注的最前方是该题注在文档中的编号。

#[.编号类型][编号内容]
<p id="编号类型">编号类型 1 编号内容</p>
#[.编号类型#编号ID][编号内容]
如文献 #[.文献][张三, 李四, 王老五, 等. 文献标题 [J]. 期刊名称, 2021, 06(7): 55-66.][ [##1] ] 所示。

如文献 [1] 所示。

12.1 自动编号文本

12.2 引用编号项

@[[.编号类型#编号ID]][图 ##]

12.3 生成编号索引

显示编号内容列表。

@[[.编号类型]]

13 图像

14 表格

15 公式

$x=\frac{-b \pm \sqrt{b^2 - 4ac}}{2a}$

x = b ± b2 4ac 2 a x=\frac{-b\pm\sqrt{b^2-4ac}}{2a} x={-b plusminus sqrt {b^2 - 4 ac}} over {2 a} a x2 +bx +c

16 参考文献

17 classid 属性

18 文档组织

19 中文支持