#格式 #解析器 #标记语言 #序列化

bin+lib udl

UDL(通用数据语言)解析器

4 个版本 (2 个重大更新)

0.3.1 2023年5月21日
0.3.0 2023年5月18日
0.2.0 2023年5月2日
0.1.0 2023年4月10日

#1620 in 解析实现

每月47次下载

MIT/Apache

170KB
2K SLoC

udl-rs

UDL(通用数据语言)的 Rust 解析器。

通用数据语言(UDL)

基于 UDL 的预处理器:在此测试 UDL

UDL 是一种文本 元格式,主要用于定义用户读取和手工编写的数据格式。这些格式主要是配置和标记格式,或者两者混合。

UDL 本地支持其他编程语言和格式(如 XMLJSONLaTeX)中找到的通用数据结构。它可以表达结构化数据(字典、序列、层次结构、值)和非结构化数据(文本、标记),并且可以表达由这些数据的任意组合组成的复杂结构。

UDL 是一种注重人类可读性和可写的文本格式。格式良好的 UDL 文档易于阅读、理解和编辑。该格式简洁,语法噪声最小;构建文档所需的字符很少。它便于手工编码,因此作为源格式。因此,该格式适合作为配置和标记格式的基座。

UDL 简单:特殊情况很少,异常很少,保留字符很少。这使得它易于推理、生成和解析。虽然牺牲了可读性,但它可以被压缩。虽然不是为此目的而设计的,但它适合于序列化、数据存储和数据交换,尽管这里其他格式可能更优。

比较

XML 相比,UDL 本地支持序列和字典的通用数据结构。 UDL 的标签符号语法基于 XML 的标签符号语法,但进行了一些修改。在 UDL 中,还支持编码命令和操作。

JSON 相比,UDL 的语法噪声更少;它不需要将字符串放在引号中。UDL 本地支持标记,并且更重要的是,注释。UDL 的序列和字典语法受 JSON 启发。

与(常规)LaTeX 相比,UDL 支持结构化数据。它们在语法噪声和简洁性方面相似。可以认为 UDL 中的命令符号比 LaTeX 中的命令符号更易读,因为可以从语法中清楚地看到命令适用于哪些参数。此外,命令可以接受结构化数据作为参数,这对于某些应用来说很方便。

示例 & 展示

以下是基于 UDL 的格式和文档的示例。展示了结构化数据和非结构化数据如何共存并形成更复杂的结构,以及如何用于标记和配置。

维基百科文章示例

这是一个基于 UDL 的维基百科文章格式的示例。

此示例展示了由结构化数据(值、字典和序列)和非结构化数据(标记)组成的复杂层次结构。

此示例的目的是展示 UDL 在充分发挥其功能时的能力。特别是,维基百科文章通常包含结构化数据和非结构化数据。因此,这是一个很好的示例,说明 UDL 如何将这两种类型组合成更复杂的层次结构。

此外,此示例展示了 UDL 语法。与其他编码相同数据的格式相比,该格式的可读性、简洁性和简单性。

注意

  • 宏应用看起来是这样的: <macro>:arg1:arg2:...:argN。参数以冒号附加。
  • 宏也可以用标签表示。这是一个带有一个参数的宏: <+macro>arg<-macro>
  • @ 宏插入一个链接。它接受两个参数:第一个参数是要链接的文章,第二个参数是将在文章中出现的链接标签。
  • title 宏不接受任何参数,并用作文章标题。
title: Aluminium;
shortdesc: The <@>:element:{chemical element} aluminium.;
uuid: 0c5aacfe-d828-43c7-a530-12a802af1df4;
type: chemical-element;
tags: [metal; common];
key: aluminium;

chemical-symbol: Al;
atomic-number: 13;
stp-phase: solid;
melting-point: 933.47;
boiling-point: 2743;
density: 2.7;
electron-shells: [2; 8; 3];

# External references
ext-refs: {
  wikipedia: "https://en.wikipedia.org/wiki/Aluminium";
  snl: "https://snl.no/aluminium";
};

# Intra-wiki references
refs: {
  element: 740097ea-10fa-4203-b086-58632f099167;
  chemsym: 6e2f634c-f180-407a-b9ce-2138b412b248;
  atomnum: 1a5e1974-a78c-4820-afeb-79bef6974814;
  react: ab7d8a1f-c028-4466-9bb2-41a39d153241;
  aloxide: c1ff08e7-a88f-42d5-83c3-6adc4835a07b;
  stab: b3b13474-4fe3-4556-9568-925c066916a5;
  purity: 40786551-85c4-461c-ba6e-4d54d5863820;
  ion: effd5c7a-da31-4357-a94c-91343e9a05eb;
  metal: 84333088-cfcc-4e78-8d3f-7307dcab144b;
};

content: {

  <@>:self:<title> is a <@>:element:{chemical element} with
  <@>:chemsym:{chemical symbol} <chemsym> and <@>:atomnum:{atomic number}
  <atomnum>.

  <p>

  In <@>:purity:pure form, it is a highly <@>:react:reactive <@>:metal:{metal},
  but normally a thin coat of <@>:aloxide:{aluminium oxide} forms on its
  surface, keeping it highly <@>:stab:{stable}.

  <p>

  In nature, it occurs as the <@>:ion:ion <+$>Al^{3+}<-$>. It constitutes 8.2%
  of the earth's crust, making it the most common <@>:metal:metal found there.

  ...

};

HTML 预处理器示例

这是一个基于 UDLHTML 预处理器输入格式的文档示例。预处理器可以将此文档编译为 HTML

此示例的目的是展示基于 UDL 的标记和类似 XML 的结构的编码。

将此文档与相应的 HTML 文档进行比较。在术语冗长性和语法噪声方面,UDL 允许使用短和长闭合标签。在不同的情况下,两者都很有用。 UDL 不需要在属性值周围使用引号。

注意

  • 在此格式中,常规标记标签和特殊宏通过 @ 符号区分。宏以 @ 开头,而常规标签仅由字母组成。
  • @doctype 宏用 <!doctype html> 替换自己。
<@doctype>
<+html> # <+tag> is an opening tag and <-tag> or <-> is a closing tag.
  <+head>
    <+title><@title><->
    <+script src:script.js><->
  <-head>
  <+body>
    <+h1 id:main-heading><@title><->
    <+p>Hello world!<-> # These two paragraph notations are equivalent.
    <p>:{Hello world!}
    <img src:frontpage.jpg>
    <+div class:dark-background><+p>
      This is a paragraph<br>
      with a line break.
      <+em class:italic-text>This text is italic.<->
    <-><->
  <-body>
<-html>

TeX 预处理器示例

这是一个基于 UDLLaTeX 预处理器输入格式的文档示例。预处理器可以将此文档编译为 LaTeX

此示例的目的是展示基于 UDL 的类似 LaTeX 的标记的编码。

将此文档与相应的 LaTeX 文档进行比较。它们很相似,但 UDL 文档的一个优点是,命令应用的参数可以仅从语法中确定。

作为一个应用,这种编码可能适用于维基百科文章示例。文章可能包含数学符号,而此编码可用于编码 LaTeX-math,该数学符号随后由 MathJax 显示。

注意

  • 预处理器宏以 @ 开头,而常规命令仅由字母组成。
  • @tabulate-sq 自动制表一个方格,例如矩阵。它接受一个数字和一系列制表值。
<documentclass>:article

<usepackage>:amsmath

<begin>:document

<section>:Equations

  # Define a sum-range command.
  <newcommand>:<SumRn>:*:4:{
    <sum>_{#1}^{#2 <dots> #3} #4
  }

  <begin>:math
    <SumRn>:k:0:100:k
    = 0 + 1 + 2 + <dots> + 99 + 100
    = (0 + 100) + (1 + 99) + <dots> (49 + 51) + 50
    = 5050
  <end>:math

  <begin>:math
    <SumRn>:k:0:n:k
    = 0 + 1 + 2 + <dots> + (n - 1) + n
    = n <cfrac>:n:2 + <cfrac>:n:2
    = <cfrac>:n^2:2 + <cfrac>:n:2
    = n <cdot> <cfrac>:{n + 1}:2
  <end>:math

<section>:Matrices

  <begin>:math
    <mathbf>:X = <begin>:bmatrix <@tabulate-sq>:3:[
      1;0;0;
      0;1;0;
      0;0;1;
    ] <end>:bmatrix
  <end>:math

<end>:document

材料配置示例

这是一个基于 UDL 的配置示例。

本例的目的是展示一个基于 UDL 的配置文件,并将其与相应的 JSON 配置文件进行比较。

在语法噪声方面,相应的 JSON 文档要求所有键周围都要加引号,所有文本值周围也要加引号,不允许注释,并且要求根级别元素用括号括起来。显然,UDL 的语法噪声更少。这两种格式都具有最小的冗余性,并且都很简单。

oak-planks: {
  name: Oak planks;
  description: Planks made from oak wood.;
  tags: [wood];
  price: 200;
};
birch-planks: {
  name: Birch planks;
  description: Planks made from birch wood.;
  tags: [wood];
  price: 200;
};
stone: {
  name: Stone;
  description: A solid material, but does not insulate well.;
  price: 100;
  tags: [heavy; stone];
};
marble: {
  name: Marble;
  price: 450;
  beauty: 2;
  tags: [heavy; stone; wealth];
};
# This material is not available yet.
glass: {
  disabled;
  name: Glass;
  price: 400;
};

语法

UDL 文档由表达式组成,而表达式由参数组成。一些参数可能还包含嵌套的表达式。

表达式和参数

表达式 是参数的序列。

示例: arg1 arg2 arg3 ...

参数 是表达式的元素。有 6 种参数变体:文本序列字典指令复合

分组

括号 { } 用于分组和限定参数。

示例: {Text 1} {Text 2} 是一个包含 2 个文本参数的表达式。括号用于限定文本参数,防止它们合并为一个文本参数。

通过分组参数,可以将任意数量的参数作为一个单独的参数给出。一个空分组表示一个空参数。一个参数的分组简单地表示该参数本身。多个(2 个或更多)参数的分组表示一个复合参数。

示例: { arg } 是单个参数的分组。这可能有助于界定文本或界定指令参数。作为参数,arg 等于 { arg },这等于 { { arg } }。确实,将单个参数括在括号中没有结构上的效果,但有时它可以提高可读性。

示例: { arg1 arg2 arg3 } 是 3 个参数的分组,它产生一个包含 3 个参数的复合参数。

空参数

参数由括号内的空表达式表示: {}

文本参数

文本 参数简单地说是一系列单词或引号文本。

示例: This is a text argument

示例: "Text argument 1" Text argument 2 {Text argument 3} {Text argument 4} Text argument 5 是一个由 5 个文本参数组成的表达式。

示例: "引号允许插入任意空白和保留字符,例如 : 或 ]"

未引用的文本不能包含保留字符,除非它们被反斜杠 \ 转义。

示例: Some 保留字符\: \:, \;, \<, \},.

可以使用重复来在未引用的文本中插入冒号 :

示例: Some text:: More text 解析为文本 Some text: More text

此外,未引用的文本中的任何空白都将缩减为一个空格字符。 UDL 是一个空白等价格式,其中所有空白都等于一个空格字符,除非它被转义或位于引号内。

字典参数

一个 字典 参数是由分号 ; 分隔的键值对序列,用花括号 { } 括起来。条目中的键和值由冒号 : 分隔。键由一个单词或一个引号给出;它不能由多个单词给出。值是一个表达式。

示例: { k1: v1; "key 2": v2; k3: v3; ... }

空字典参数必须包含一个冒号,以区分空表达式。

示例: {:} 是一个空字典。

键后跟分号 ; 表示其值是一个空表达式。

示例: {k1; k2: v2; k3;} 包含键 k1k3,它们后面跟着分号 ;。这意味着它们的值是空表达式。

允许尾部分号。

示例: {k1: v1; k2: v2;}{k1: v1; k2: v2} 是相等的。

序列参数

一个 序列 参数是由分号 ; 分隔的表达式序列,用方括号 [ ] 括起来。

示例: [expr1; expr2; expr3; ...]

示例: [] 是一个空序列。

允许尾部分号。

示例: [expr1; expr2;][expr1; expr2] 是相等的。

指令参数

指令表达式 是应用于多个参数的指令。有两种记法可以生成指令表达式:命令记法和标签记法。

命令记法

在命令记法中,一个 指令表达式 被编码为一个在尖括号内的 指令,后跟应用于它的参数,这些参数以冒号 : 后跟,其中没有周围空白。

示例: <dir>:arg1:arg2:...:argN 是一个具有 N 个参数的指令表达式。

示例: <dir> 是一个没有参数的指令。

示例: <text-weight>:600:{This is bold text} 是应用于 2 个文本参数的指令 text-weight

指令,即尖括号内的部分,由一个标签后跟属性组成。标签由一个词或引号给出。在标签之后,可以插入属性。一个属性是一个键值对。键和值由冒号 : 分隔。

示例: <p id:opening class:fancy> 编码了具有属性 id:openingclass:fancy 的指令 p

允许不跟冒号的属性键。此类属性的值被视为一个空参数。

示例: <input type:checkbox checked> 有标签 input。它有两个属性:具有值 checkboxtype 和具有值 {}checked

可以将指令作为参数插入到指令表达式中。在那里,它们被解释为零参数的指令表达式。

示例:<cmd0>:arg1:arg2:<cmd3>:arg4:arg5 中,<cmd3> 是一个零参数的指令表达式。 <cmd0> 是一个具有 5 个参数的指令表达式。

优先运算符 <> 是一个特殊运算符,可以在指令表达式中使用。它将右侧的指令表达式作为参数应用于左侧的指令表达式。

示例: <bold>:<>:<italic>:text 等价于 <bold>:{ <italic>:text }

标签符号

在标签符号中,标签用于生成指令表达式。一个开标签以 + 开始,而一个闭标签以 - 开始。

示例: <+tag> content <-tag>

标签内的内容是一个表达式,作为最后一个参数附加到标签表示的指令表达式上。

示例: <+math>1 + 2 + 3 + <dots><-math> 等价于 <math>:{1 + 2 + 3 + <dots>}

可以使用冒号将参数附加到开标签上。

示例: <+Sum>:k:1:n 3k^2-2k <-Sum> 等价于 <Sum>:k:1:n:{3k^2 - 2k}

在闭标签中是否包含指令名是可选的。在某些情况下这很有用,但在其他情况下可能过于冗长。

示例: <+tag>arg<-tag> 等价于 <+tag>arg<->

在某些情况下,标签符号可以提高文档的可读性。

示例:在跨越多行的长范围内,可能难以判断哪些括号属于哪个指令,以及每个作用域在哪里结束。标签符号可以在关闭标签中显示作用域的名称,从而解决此问题。 <+html> 多行和多内容... <-html>

示例:有时括号放置得太紧凑,难以区分。标签符号通过引入冗余性使区分作用域更容易。 <bold>:{Bold <italic>:{italic <underline>:{underlined <strikethrough>:{strikethrough text}}}}<+bold>Bold <+italic>italic <+underline>underlined <+strikethrough>strikethrough text<-><-><-><->更容易阅读。

复合参数

一个复合参数简单来说就是包含多个(2个或更多)参数的表达式,这些参数用大括号{ }括起来。

示例: { {Text} Some more text [1; 2; 3] {k1: v1; k2: v2} {} }是一个包含2个文本参数、1个序列、1个字典以及最后1个空参数的复合参数。

根节点

UDL文档的根节点是一个表达式、一个序列或一个字典。根节点不是一个参数,因此不被括号包围。

保留字符

TODO:将关于保留字符序列的说明写入,而不是保留字符。

括号<>[]{}、引号"、冒号:和分号;保留字符。除非转义,否则它们不能用于文本中。

转义字符

反斜杠 \ 是转义字符。紧随其后的字符将作为文本插入,无论该字符是否为保留字符。

示例: \[ 解析为文本 [\

特殊转义序列

冒号 : 有时用于常规文本,因此如果它们是保留字符,可能会不方便。因此,允许一些特殊的转义序列::: 将冒号作为文本插入,而不是解析为保留字符。

示例: Price:: 300 解析为文本 Price: 300

空白等价性和重要性

任何空白序列都与单个空格字符等价,除非空白被转义或位于引号内。表达式中的参数之间的空白是重要的,但表达式开头或结尾的空白是不重要的。

示例: arg1{arg2} 不等于 arg1{arg2},因为存在重要的空白差异。

示例: arg1{ arg2 } 等于 arg1{arg2},因为没有重要的空白差异。

注释

单词开头的数字符号 # 可能会根据其后跟随的字符打开注释。如果其后跟随空白或另一个 #,则打开一个以下一行结束的注释。否则,如果其后跟随文本符号,该单词将按常规解析为文本。

示例: # This is a comment 是注释,因为 # 后跟空白。

示例: ##### Configuration ### 是注释,因为 # 后跟 #

示例: #2#0FA60F#elements 不是注释,因为 # 后跟文本符号。

示例:This is text# Is this a comment? 中不会打开注释,因为 # 不是单词的开头。

语义

UDL 规定了表达式和参数的语法,但并未规定它们的语义或数据结构的编码方式。语义,例如指令表达式的有效性、字典键和表达式组合,是在定义基于 UDL 的格式时确定的。这与 XMLJSON 作为元语言的方式相似。就它们本身而言,它们仅确定文档是否在语法上正确,但将有效性问题留给了格式实现者。

数据结构可以以许多任意的方式在 UDL 中进行编码。因此,实现者必须为每个数据结构定义特定的编码。实现者还必须定义文档根是表达式、序列还是字典。这可以通过编写文档、使用模式或最好通过在程序中实现反序列化过程来完成。一旦完成这些,就有一个具有良好定义的语法和语义的格式。

尽管关于数据结构应该如何编码没有明确的规则,但在表达和变体表示方面有一些最佳实践。在实现编码时遵循这些实践可以使基于 UDL 的格式更加统一,从而使它们更容易理解。以下描述了关于表达式和参数编码的最佳实践。

表达式和参数语义

表达式以规范或默认方式编码数据结构。表达式由任意数量的参数组成,这些参数提供了表达式所需的信息。从表达式的角度来看,参数可以被视为自身编码一个子结构,该子结构是外部表达式构建的,或者简单地作为提供信息给表达式的普通变体。

以下是参数的可能解释总结以及这样的参数编码的内容。

参数 用途
文本 编码原始值。
序列 编码多个数据结构。
字典 编码多个命名数据结构。
指令 以特定方式编码数据结构。
表达式/单例/复合/空 以规范或默认方式编码数据结构。

鉴于某些参数可能有多种解释,假设当基于 UDL 的格式定义时提供了参数的解释。

在定义复杂结构时,必须将数据结构拆分为多个参数,并使用最适合的变体对每个部分进行编码。

指令语义

作为一个数据结构的编码,指令表达式以特定方式编码结构。然而,指令有两个维度

  • 首先,指令描述了输入如何编码数据结构。
  • 其次,指令描述了输入如何编码一个动作(动作是环境的外部效应、有状态变化或查询)。如果指令不依赖于环境,则它是纯的,否则是不纯的。

据此,指令表达式表示编码的数据结构、编码的动作或两者的混合。指令可以被视为 XML 标签、LaTeX 命令/宏和文本占位符/标记的泛化。

示例:在类似 XML 的标记中,标签用于标记和添加文本的语义。标签不编码任何动作。在 UDL 中,标签可以编码为指令,当应用于文本时,编码语义文本。例如,HTML <span class="italic">text</span> 对应于 UDL <+span class:italic>text<->

示例:<sender> sent <amount> to <recipient>. 中,指令用于表示占位符。

示例:在类似于 LaTeX 的标记中,命令/宏用于执行替换、计算和状态动作(例如增加章节计数,或包含一个包)。在 UDL 中,命令可以简单地编码为指令。例如,LaTeX 的 \frac{2a}{b} 对应于 UDL 的 <frac>:2a:b

示例:<set>:x:100 编码了一个将变量 x 设置为 100 的动作。它编码了一个空的数据结构,因为这纯粹是一个命令。

原始编码

原始值简单地编码为文本。

  • 字符串编码为文本。
  • 数字(包括布尔值)编码为文本。有效的编码进一步由数字类型确定。

标记编码

标记编码为一个包含任意数量文本和指令表达式参数的表达式。产生语义文本且不表示任何动作的指令可以编码为标签,但这不是必需的。其他可能表示动作的宏类型编码为命令符号。

示例:上述预处理器示例演示了标记编码。

结构/产品类型编码

没有字段的结构编码为空表达式。具有字段的结构编码为字典或序列,具体取决于它们是否命名或位置。可选地,可以包含结构类型。

变体 示例
命名字段 { x: 10; y: 30; z: 5 }Coordinates { x: 10; y: 30; z: 5 }
位置字段 [10; 30; 5]Coordinates [10; 30; 5]
没有字段 {}EmptyEmpty {}

枚举/求和类型编码

枚举以1或2个参数进行编码。第一个参数是一个文本参数,用于指定枚举变体。如果枚举没有字段,则没有第二个参数。否则,第二个参数是一个序列或字典,这取决于枚举是否有命名或位置字段。可选地,可以包含枚举类型,以下展示了如何编码类型和变体。

变体 示例
命名字段 Binomial { n: 50; p: 10% }Distribution::Binomial { n: 50; p: 10% }
位置字段 Uniform [0; 10]Distribution::Uniform [0; 10]
没有字段 StandardNormalDistribution::StandardNormal

目的

目标是设计一种文本格式,以满足以下要求。同时,也考虑了现有格式如何满足这些要求。最重要的要求是 1267810,而 9 的要求较低。设计这个新格式的首要原因确实是缺乏满足要求 610 的格式。请注意,某些要求可能是主观的。

目标JSONXML&HTMLYAMLTOML
1 格式是可读的。假设遵循最佳格式化实践,该格式应易于阅读和理解。 ✔️ ✔️ ✔️ ✔️
2 格式是可写的。在这里,不考虑写作的便利性或方便性。 ✔️️ ✔️ ✔️ ✔️
3 格式简单。特殊案例很少。简单格式的优点是它更容易解析。 ✔️ ✔️ 有时会对是否将数据编码为标签或属性存在一些小的困惑。 ❌ YAML 复杂。有许多特殊案例,并且某些值可能会产生令人惊讶的结果。 ✔️
4 格式简洁,包含最少的语法噪音。 ➖ JSON 简洁,但并不最小化语法噪音。即使在没有歧义的情况下,它也要求在键周围使用引号。 ❌ XML 不最小化语法噪音。它非常冗长。 ✔️ ✔️
5 格式有注释。 ✔️ ✔️ ✔️
6 格式可以原生地表达结构和非结构化数据,例如
  • 数字、文本、结构体、枚举、序列和字典。
  • 由文本和具有属性和内容的标签组成的标记,如 HTML。
  • 由文本、分组和宏命令组成的标记,如 TeX。
❌ JSON 不支持标记,并且关于如何表示求和类型并不完全清楚。 ➖️ XML 可以通过其灵活性表示这些结构,但它没有原生支持序列和字典。然而,显然可以如何建模它们。 ❌ YAML 不支持标记,并且关于如何表示求和类型并不完全清楚。 ❌ TOML不支持标记,而且表示联合类型的方式并不完全明确。
7 该格式适合用于标记。 ✔️
8 该格式适合用于配置。 ➖️ JSON可以用于配置,但它缺少注释,这是一个很大的缺点。 ➖️ XML可以用于配置,但其冗长使其作为通用配置格式不太方便。 ✔️ ✔️
9 该格式适用于序列化、数据存储和数据交换。 ✔️ ✔️ ➖️ YAML可以用于序列化,但不是最佳选择。 ❌ TOML不适合用于序列化。
10 该格式适合手动编码。它非常适合作为源格式。它可以方便地编码结构化数据和标记。 ➖️️ JSON易于手动编码,但它的注释缺失使其作为源格式不太实用。 ❌️ XML由于其冗长不适合作为源格式。 ✔️ 在大多数情况下,YAML易于手动编码,但当YAML文档变得很大或复杂时,它们可能很难管理,尤其是在空格缩进的情况下。 ✔️

设计

以下是在设计过程中做出的部分决策。这些决策背后的原因可能具有主观性。

空格等价性

空格等价性使用户可以按照自己的喜好格式化文档。对于简单的表达式,这种灵活性是不必要的,但对于跨越多行的复杂表达式,这种灵活性是受欢迎的。

空格缩进

空格缩进简单且在表达式跨越单行时表现良好。在许多使用空格缩进的格式和语言中,这种情况占大多数。然而,当表达式需要跨越多行时,空格缩进需要复杂的规则,这些规则对用户来说感觉像是特殊情况。跟踪空格和缩进级别也增加了解析器的复杂性。因此,决定坚持使用括号分隔的作用域。

参数变体

观察现代编程语言和普遍使用的格式,如JSONXMLLaTeX,以下结构被普遍使用:数字、文本、结构/产品类型、枚举/联合类型、字典、序列以及由文本和命令/标签组成的标记。

实现的参数变体能够以简洁方便的语法原生地支持这些结构。

元格式

采用了XML方法,即文档的语义(如包含表达式的类型)必须在外部定义。用户必须通过使用模式、编写文档或在一个程序中实现序列化/反序列化过程来定义语义。

采取这种方法仅仅是因为它给格式实现者提供了很大的灵活性。此外,通常文档不是盲目地读取。用户或程序已经对编码表达式的类型有所预期。因此,没有必要添加语法类型表达式。

无运行时依赖

功能