MCDOC格式

|

Mcdoc 是一种用于描述 Minecraft 数据结构的模式格式,包括其 CODECJSONNBT

本文档定义了 mcdoc 格式的语法和语义。

Contents

项目根目录

通常,工作区(mcdoc 解释器操作的目录:对于命令行工具,这可能是工作目录;对于像 VS Code 这样的代码编辑器,这可能是侧边栏资源管理器中显示的根目录)被视为 mcdoc 项目的根目录。

但是,如果在工作区下存在一个名为 mcdoc 的文件夹,并且工作区内的所有 mcdoc 文件都存储在该目录下,则该目录将被视为根目录。

语法语法

以下是本文档用于描述 mcdoc 语法的语法——如果你愿意,可以称之为“语法语法”。

符号含义
str字面量 str
U+xxxx代码点为 xxxx 的 Unicode 字符
A*A 重复零次或多次
A+A 重复一次或多次
A?A 重复零次或一次
A | BAB
[A B C]字面量 ABC 之一
[A-Z]AZ 之间的任何字面量
(A)一般分组
notA任何不是 A 的内容
Aexcept: BA,除了 B
Alookahead: BA 后跟 B,但只消耗 A
Anochild: BA,但 B 不应是其子项
NAME引用的标记规则
Name引用的解析规则
表 1. 语法语法

标记规则语法不能包含任何空白(空格、制表符、CRLF)或标记之间的个别部分。

解析规则语法可以包含空白和注释。

所有语法规则应为贪婪的(即尽可能多地消耗字符)。

注释

语法

COMMENTS
COMMENT*

COMMENT
// lookahead: not/ (notEOL)* (EOL | EOF)

EOL
End of line: CR (Unicode U+000D) or LF (Unicode U+000A).

EOF
End of file.

在 mcdoc 中,可以使用注释来编写只有查看或编辑 mcdoc 文件的用户才能看到的信息。它们会被 mcdoc 解释器忽略。

要编写注释,只需写下两个正斜杠 (//)——之后的所有内容,直到行尾,都被视为注释的一部分。注释可以放在允许空白的任何地方。但是,注释不能以三个斜杠 (///) 开头,因为这保留用于文档注释。

// 这是一个注释。
struct Foo {
    Bar: boolean, // 这是另一个注释。
}

文档注释

语法

Prelim
DocComments Attributes

DocComments
DOC_COMMENT*

尽管这是一条语法规则,但在各个 DOC_COMMENT 之间不允许有常规注释。只应允许空白(包括换行)。

语法

DOC_COMMENT
/// (notEOL)* (EOL | EOF)

文档注释在语法上类似于注释——它们以三个斜杠 (///) 开头。文档注释块可以为紧随其后的组件提供人类可读的文档。与常规注释不同,文档注释只能放在枚举定义、枚举字段、结构体定义、结构体字段和类型别名前,作为 [prelim] 的一部分。

文档注释块的文本内容应被视为 MarkDown 内容,前导的三个斜杠(以及如果所有行都共享一个前导空格,则最多一个前导空格)应被剥离。

/// 这个文档注释描述了结构体 Foo。
/// 外部工具,如 VS Code,可能会在用户悬停在名称 "Foo" 上时显示这段文本。
struct Foo {
    /// 这是另一个描述字段 "Bar" 的文档注释。
    Bar: boolean, // 这只是一个常规注释,因为它只以两个斜杠开头。
}

由于文档注释块的内容被视为 MarkDown,因此某些字符可能具有特殊含义。例如,如果你在文档注释中写 <foo>,它可能在显示给用户时消失,因为它可能被 MarkDown 解析器解释为 XML 标签。用反斜杠 (\) 转义这些特殊字符(例如 \<foo>) 可以解决这个问题。

整数

语法

INTEGER
0 |
[- +]? [1-9] [0-9]*

整数表示一个完整的数字。

0
+123
-456

浮点数

语法

FLOAT
[- +]? [0-9]+ FLOAT_EXPONENT? |
[- +]? [0-9]* . [0-9]+ FLOAT_EXPONENT?

FLOAT_EXPONENT
[e E] [- +]? [0-9]+

浮点数表示一个小数。可以使用字母 e(不区分大小写)表示科学记数法。

1
+1.2
-1.2e3 // -1.2×10^3

类型化数字

语法

TYPED_NUMBER
INTEGER [b B s S l L]? |
FLOAT [d D f F]?

类型化数字在语法上类似于在 SNBT 中使用的数字。它是一个正常的数字,后跟一个表示其类型的后缀:

后缀 (不区分大小写)类型
bByte
sShort
lLong
fFloat
dDouble
(无后缀,整数)Integer
(无后缀,小数)Double
表 2. 后缀表
1b      // Byte 1
1       // Integer 1
1.2     // Double 1.2
1.2d    // Double 1.2
1.2e1f  // Float 12

数字范围

语法

FLOAT_RANGE
RANGE_DELIMITER? FLOAT |
FLOAT RANGE_DELIMITER FLOAT?

RANGE_DELIMITER
.. |
..< |
<.. |
<..<

INT_RANGE
RANGE_DELIMITER? INTEGER |
INTEGER RANGE_DELIMITER INTEGER?

数字范围表示一个数字范围。其语法源自 Minecraft 命令中的数字范围,并额外支持使用严格小于符号 (<) 表示排他性结束。mcdoc 中有两种类型的范围:浮点数范围,由浮点数组成;整数范围,由整数组成。

1       // 精确为 1
1..1    // 精确为 1
1..2    // 在 1 和 2 之间(包括两端)
1<..<2  // 在 1 和 2 之间(排除两端)
4.2..   // 大于或等于 4.2
4.2<..  // 大于 4.2
..9.1   // 小于或等于 9.1
..<9.1  // 小于 9.1

字符串

语法

STRING
" (not[" \ UNICODE_CC] | (\ [b f n r t \ "]))* "

UNICODE_CC
Unicode 控制字符。

字符串表示一系列字符。它必须用双引号 (") 包围。某些字符需要用反斜杠 (\) 转义。

转义序列含义
\"双引号(", Unicode U+0022)
\\反斜杠(`\, Unicode U+005C)
\b退格(Unicode U+0008)
\f换页(Unicode U+000C)
\n换行(Unicode U+000A)
\r回车(Unicode U+000D)
\t制表符(Unicode U+0009)
表 3. 转义字符
"foo"            // 表示 foo 的字符串
"bar\"qux\\baz"  // 表示 bar"qux\baz 的字符串

命名空间ID

语法

RES_LOC
RES_LOC_CHAR* : RES_LOC_CHAR* (/ RES_LOC_CHAR*)*

RES_LOC_CHAR
[a-z 0-9 - _ .]

命名空间ID在语法上类似于 Minecraft 的命名空间ID,不同之处在于必须存在一个冒号 (:) 以消除与标识符的歧义。

minecraft:foo
:foo  // 这也表示 minecraft:foo,并且在 Minecraft 中是合法的。
spyglassmc:bar

标识符

语法

IDENTIFIER
((IDENT_START) (IDENT_CONTINUE)*)except: RESERVED_WORDS

IDENT_START
Any character in the Unicode general categories “Letter (L)” or “Letter Number (Nl)”

IDENT_CONTINUE
IDENT_START | U+200C | U+200D | (any character in the Unicode general categories “Non-Spacing Mark (Mn)”, “Spacing Combining Mark (Mc)”, “Decimal Digit Number (Nd)”, or “Connector Punctuation (Pc)”)

RESERVED_WORDS
any | boolean | byte | double | enum | false | float | int | long | short | string | struct | super | true

标识符是 mcdoc 中赋予类型定义的区分大小写的名称。它可以包含任何 Unicode 字母、数字和下划线 (_),但不能以数字开头。并且不能使用保留字。

struct Foo { // Foo 是一个标识符。
    B_1: boolean, // B_1 是一个标识符。
}

路径

语法

PATH
(::)? PATH_SEGMENT (:: PATH_SEGMENT)*

PATH_SEGMENT
IDENTIFIER | super

路径用于在 mcdoc 项目中定位类型定义。双冒号 (::) 用作路径分隔符。

如果路径以双冒号开头,则为绝对路径,从项目根目录解析。否则为相对路径,从当前文件的绝对路径解析。

文件的绝对路径是通过连接所有父文件夹的名称直到根目录,以及文件自身的名称(不包括 .mcdoc 文件扩展名)与路径分隔符连接起来,并在前面加上路径分隔符。名为 mod.mcdoc 的文件是特例,它们不会成为路径的一部分。

类型定义的绝对路径是其所在文件的绝对路径与类型定义的标识符通过路径分隔符连接起来的结果。

如果多个文件/类型定义具有相同的路径,则仅最早加载的生效;所有后续的应被 mcdoc 解释器警告并忽略。

对于相对路径,可以使用关键字 super 向上移动一级。

/
    foo.mcdoc 
    foo/
        bar.mcdoc 
        mod.mcdoc 
    qux.mcdoc 
  • foo.mcdoc 的绝对路径是 ::foo
  • bar.mcdoc 的绝对路径是 ::foo::bar
  • mod.mcdoc 的绝对路径是 ::foo(因为 mod.mcdoc 是特例)。
  • qux.mcdoc 的绝对路径是 ::qux

如果 /foo/bar.mcdoc 的内容是:

struct Foo {} 

type Bar = super::super::qux::Something 
  • struct Foo 的绝对路径是 ::foo::bar::Foo
  • 类型别名 Bar 的绝对路径是 ::foo::bar::Bar

相对路径的解释如下:

  • /foo/bar.mcdoc 的绝对路径是 ::foo::bar。给定的相对路径是 super::super::qux::Something
  • 遇到关键字 super,向上移动一级到 ::foo。剩余相对路径是 super::qux::Something
  • 再次遇到 super,向上移动一级到 ::。剩余相对路径是 qux::Something
  • 遇到标识符 qux,向下移动到 ::qux。剩余相对路径是 Something
  • 遇到标识符 Something,向下移动到 ::qux::Something。相对路径已解析。

因此,类型别名 Bar 指向文件 /qux.mcdoc 中名为 Something 的类型定义。

类型

语法

Type
Attributes UnattributedType (IndexBody | TypeArgBlock)*

UnattributedType
KeywordType |
StringType |
LiteralType |
NumericType |
PrimitiveArrayType |
ListType |
TupleType |
Enum |
Struct |
ReferenceType |
DispatcherType |
UnionType

TypeArgBlock
< > |
< Type (, Type)* ,? >

类型是 mcdoc 格式的基本组成部分。它定义了实际数据值必须符合的模式,以确保数据有效。

mcdoc 可用于描述各种数据格式。本节将仅提供一些 JSON 数据作为每种类型的示例。

any 类型

语法

KeywordType
any |
boolean

any 类型是 mcdoc 类型系统的顶层类型。任何其他类型(包括 any 本身)都可以赋值给 anyany 不能赋值给除 any 之外的任何其他类型。

null
true
[0, 1, 2, 3]
{ "foo": "bar" }

boolean 类型

boolean 类型表示期望一个布尔值(falsetrue)。

false
true

string 类型

语法

StringType
string (@ INT_RANGE)?

string 类型表示期望一个字符串值。可选的范围定义了字符串长度的范围。

"foo"
"bar"

字面量布尔类型

语法

LiteralType
false | true | STRING | TYPED_NUMBER

字面量布尔类型是两个布尔值(falsetrue)之一,数据必须与其完全匹配才有效。

false
true

字面量字符串类型

字面量字符串类型是一个字符串值,数据必须与其完全匹配才有效。

""
"foo"

字面量数字类型

字面量数字类型包括一个数值和一个类型,数据必须与其完全匹配才有效。

-1
1.2f
42L

数字类型

语法

NumericType
byte (@ INT_RANGE)? |
short (@ INT_RANGE)? |
int (@ INT_RANGE)? |
long (@ INT_RANGE)? |
float (@ FLOAT_RANGE)? |
double (@ FLOAT_RANGE)?

数字类型表示数据必须是该类型。如果提供了可选范围,则数据还必须在该范围内。

byte
short@1..
float @ 4.2..9.1

原始数组类型

语法

PrimitiveArrayType
byte (@ INT_RANGE)? [] (@ INT_RANGE)? |
int (@ INT_RANGE)? [] (@ INT_RANGE)? |
long (@ INT_RANGE)? [] (@ INT_RANGE)?

原始数组类型表示数据必须是某些数值的集合。第一个可选范围定义了值必须在的范围,而第二个可选范围定义了集合大小的范围。

byte[]              // 字节集合。
byte#0..1[]         // 0 或 1 的字节集合。
int[] # 4           // 4 个整数的集合。
long#0..[] # 3..    // 3 个或更多非负长整数的集合。

列表类型

语法

ListType
[ Type ] (@ INT_RANGE)?

列表类型表示数据必须是某种其他类型的集合。可选范围定义了集合大小的范围。

[byte]          // 字节集合。
[[string]]      // 字符串集合的集合。
[struct Foo {}] // 结构体集合。

与 NBT 不同,JSON 不区分原始数组和列表——它只有一种数组类型。因此,对于 JSON 验证,byte[][byte] 基本上是相同的。

元组类型

语法

TupleType
[ Type , ]
[ Type (, Type)+ ,? ]

元组类型表示数据必须是按指定顺序排列的某些其他类型的集合。

为了区分只包含一个元素的元组类型与列表类型,需要在类型后面添加一个尾随逗号(,)。或者,可以使用大小为 1 的列表类型来表示包含一个元素的元组(例如 [byte] @ 1)。

[byte,]             // 一个字节的元组。
[string, boolean]   // 一个字符串后跟一个布尔值的元组。

元组类型通常对 NBT 结构没有用,因为 NBT 没有混合类型的集合。

枚举

语法

Enum
Prelim enum ( ENUM_TYPE ) IDENTIFIER? EnumBlock

EnumBlock
{ } |
{ EnumField (, EnumField)* ,? }

EnumField
Prelim IDENTIFIER = ENUM_VALUE

SYNTAX (TOKEN)

ENUM_TYPE
byte | short | int | long | string | float | double

ENUM_VALUE
TYPED_NUMBER | STRING

虽然预期枚举的值是 TYPED_NUMBER,但用户可以不带适当后缀地编写数字,因为 mcdoc 解释器能够从枚举定义中推断出正确的类型。

结构体

语法

Struct
Prelim struct IDENTIFIER? StructBlock

StructBlock
{ } |
{ StructField (, StructField)* ,? }

StructField
Prelim StructKey ?? : Type |
Attributes ... Type

StructKey
STRING |
IDENTIFIER |
[ Type ]

结构体定义了由键值对组成的类似字典的结构的模式,如 JSON 对象或 NBT 复合标签。如果键重复,后一个的类型将覆盖前一个的类型。可以在键和冒号(:)之间添加一个问号(?)以指示可选字段。

struct Tag {
    replace?: boolean,
    values: [string],
}

扩展操作符(三个点,…)后跟结构体类型,可以用于重用另一个结构体的字段。

struct Player {
    ...Mob, // 重用 Mob 结构体中的字段。
    abilities: Abilities,
    CustomName: (), // 将 CustomName 从 Mob 结构体中重写为空联合。
}

虽然结构体定义中不直接允许类型参数,但可以在类型别名定义的右侧内联一个结构体。

type Tag<V> = struct {
    replace?: boolean,
    values: [V],
}

type BlockTag = Tag<#[id=block] string>
type EntityTypeTag = Tag<#[id=entity_type] string>
type FunctionTag = Tag<#[id=function] string>
type ItemTag = Tag<#[id=item] string>

引用类型

语法

ReferenceType
PATH

分派器类型

语法

DispatcherType
RES_LOC IndexBody

联合类型

语法

UnionType
( ) |
( Type (| Type)* |? )

一对空括号表示从结构体定义中删除该字段。

类型索引

语法

IndexBody
[ Index (, Index)* ,? ]

可以在方括号内放置多个索引,以从目标中访问多个类型。

示例. 从分派器访问多个类型

minecraft:entity[ender_dragon, wither] → 生成一个联合类型,包括末影龙和凋零的类型。

minecraft:entity[[id], allay] → 生成一个联合类型,包括动态获取的 id 实体类型和小精灵的类型。

索引

STATIC_INDEX_KEY | DynamicIndex

DynamicIndex
[ ACCESSOR ]

语法

STATIC_INDEX_KEY
%fallback | %none | %unknown | IDENTIFIER | STRING | RES_LOC

ACCESSOR
ACCESSOR_KEY (. ACCESSOR_KEY)*

ACCESSOR_KEY
%key | %parent | IDENTIFIER | STRING

索引可以从分派器访问类型或从现有结构体获取字段类型,既可以是静态的(即用户在 mcdoc 文件中直接提供键),也可以是动态的(即用户指定一种方法在运行时从给定数据结构获取键)。

示例. 静态和动态索引

struct Foo {
    id: string,
    cow_data: minecraft:entity[cow], 
    dynamic_entity_data: minecraft:entity[[id]], 
    command: minecraft:block[command_block][Command], 
    dynamic_memories: minecraft:entity[[id]][Brain][memories], 
}
  • 静态索引在分派器上。
  • 动态索引在分派器上。
  • 静态索引在分派器上,接着是结构体上的静态索引。
  • 动态索引在分派器上,接着是两个结构体上的静态索引。

默认情况下,所有情况(包括两个可变的特殊键 %none 和 %unknown)使用回退案例。

示例. 特殊静态键:%fallback

type AnyEntity = minecraft:entity[%fallback]

%fallback 键用于访问分派器的回退案例。不能在分派语句的左侧使用,因为回退案例是自动生成的,不能手动声明。

示例. 特殊静态键:%none

struct RandomIntGenerator {
    type?: ("uniform" | "binomial" | "constant"), 
    ...minecraft:random_int_generator[[type]], 
}

dispatch minecraft:random_int_generator[uniform, %none] to struct { min?: int, max?: int }
  • type 在这里定义为可选。
  • 运行时使用 type 的值作为动态索引。
  • %none 对应的案例被分派到结构体,因此当运行时没有提供 type 的值时,随机整数生成器仍然可以正确验证为 uniform 生成器。

示例. 特殊静态键:%unknown

dispatch minecraft:block[%unknown] to ()

%unknown 对应的案例用于在访问分派器时使用未知键。

示例. 特殊访问器键:%key

struct DebugStick {
    DebugProperty: struct {
        [#[id=block] string]: mcdoc:block_state_name[[%key]], // 获取存储在键中的块状态名称的类型。
    },
}

该结构体可以用于验证以下数据:

{
    "DebugProperty": {
        "minecraft:anvil": "facing",
        "minecraft:oak_fence": "east"
    }
}

示例. 特殊访问器键:%parent

struct Item {
    id: #[id=item] string,
    tag: struct ItemTag {
        BlockStateTag: mcdoc:block_item_states[[%parent.id]]
    },
}

TODO

文件结构

语法

File
(Struct | Enum | TypeAlias | UseStatement | Injection | DispatchStatement)*

mcdoc 文件由结构体、枚举、类型别名语句、使用语句、注入语句和分派语句组成。

类型别名语句

语法

TypeAlias
Prelim type IDENTIFIER TypeParamBlock? = Type

TypeParamBlock
< > |
< TypeParam (, TypeParam)* ,? >

TypeParam
IDENTIFIER

类型别名可以创建以引用另一个复杂类型,从而提高代码的可读性和可重用性。

示例. 类型别名

type Integer = (byte | short | int | long)
type Float = (float | double)
type Number = (Integer | Float)

有时我们可能希望创建具有大致相同结构但在某些小方面有所不同的不同类型定义。与其重复代码,不如创建一个带有类型参数的“模板”类型别名。类型别名语句的右侧可以引用这些类型参数,当类型别名在其他地方实例化时,这些参数将被实际类型替换。

示例. 带有类型参数的类型别名

type NumericRange<T> = ( 
    T | 
    [T, T] | 
    struct { min: T, max: T } 
)

type FloatRange = NumericRange<float> 
type IntegerRange = NumericRange<int> 
type NaturalRange = NumericRange<int @ 0..> 
  • 类型参数 T 在尖括号中声明。
  • 类型参数 T 现在可以在右侧引用。
  • 当在其他地方引用 NumericRange 类型别名时,必须为类型参数提供实际类型。

绑定类型参数

所有路径引用通过路径中描述的规则解析,类型参数引用也不例外。当在类型别名语句中声明类型参数时,它会被暂时绑定到当前模块,直到语句结束。因此,就像其他类型定义一样,类型参数在模块范围内应该是唯一的。

示例. 重复的类型参数标识符

// 文件 '/example.mcdoc'

struct T {}

type List<T> = [T] //注1
//        ^
//        WARNING: Duplicated declaration for "::example::T"
  • 注1: T 的声明被警告并忽略,右侧的 T 实际上引用的是上面定义的结构体 T。
type List<T> = [T] // 注1

type Struct<T> = struct { value: T }
  • 注1: 这是可以的,因为尽管 T 也在 List 类型别名语句中声明,但该声明的效果仅在该语句结束前有效。

使用语句

语法

UseStatement
use PATH (as IDENTIFIER)?

TODO

注入

语法

Injection
inject (EnumInjection | StructInjection)

EnumInjection
enum (ENUM_TYPE) PATH EnumBlock

StructInjection
struct PATH StructBlock

TODO

分派语句

语法

DispatchStatement
Prelim dispatch RES_LOC IndexBody nochild: DynamicIndex TypeParamBlock? to Type

分派器用于从给定索引分派到特定类型。每个分派器的案例可以通过分派语句声明,并通过分派器类型访问。

分派器以资源位置命名,因此与其他需要导入才能在外部文件中使用的 mcdoc 值不同,分派器本质上是全局的,可以在 mcdoc 项目中的任何地方访问。

回退分支

当使用未知索引访问分派器时,会在运行时生成一个包含分派器下注册的所有类型的联合作为回退分支。该联合标记为“nonexhaustive”元数据。

TODO

属性

语法

Attributes
Attribute*

Attribute
#[ IDENTIFIER ] |
#[ IDENTIFIER = Value ] |
#[ IDENTIFIER TreeValue ]

Value
Type | TreeValue

TreeValue
( TreeBody? ) |
[ TreeBody? ] |
{ TreeBody? }

TreeBody
PositionalValues ,? |
NamedValues ,? |
PositionalValues , NamedValues ,?

PositionalValues
Value (, Value)*

NamedValues
NamedValue (, NamedValue)*

NamedValue
(IDENTIFIER | STRING) = Value |
(IDENTIFIER | STRING) TreeValue

示例. 属性示例(非最终版)

以下所有示例在当前属性提案下在语法上是合法的。然而,哪些应该在语义上合法仍在讨论中。

struct Foo {
    #[id=item]
    id1: string,
    id2: #[id=item] string,
    // id1 和 id2 可能都会被支持并具有等效效果。

    blockStateValue1: (
        #[serializable] string |
        byte | short | int | long | float | double
    ),
    #[serialize_to=string]
    blockStateValue2: (string | byte | short | int | long | float | double),

    evilUUID1: (
        #[until("1.16", uuid_string_to_compound)] #[parser=uuid] string |
        #[until("1.17", uuid_compound_to_array)] MostLeastCompound |
        int[] @ 4
    ),
    #[history{
        (#[parser=uuid] string, until="1.16", updater=uuid_string_to_compound),
        (MostLeastCompound, until="1.17", updater=uuid_compound_to_array),
    }]
    evilUUID2: int[] @ 4
}

类型实例化

类型实例化是将用户定义的类型转换为数据验证器易于使用的类型的过程。用户定义的类型可以根据实例化的目的分类如下:

索引类型

  • 具有索引的类型。

自包含类型

  • 包含所有验证所需信息的类型。包括任意类型、布尔类型、字符串类型、字面量布尔类型、字面量字符串类型、字面量数字类型、数值类型、原始数组类型和枚举。

容器类型

  • 提供部分信息但需要其子类型信息以完成验证的类型。包括列表类型、元组类型和结构体。

引用类型

  • 引用类型。

分派器类型

  • 分派器类型。

联合类型

  • 联合类型。

不同类别的用户定义类型使用不同的程序进行实例化。

实例化索引类型

首先实例化不带索引的部分,然后在实例化类型上解析索引。重复此过程,直到所有索引都解析完毕。

实例化自包含类型

自包含类型无需实例化。

实例化容器类型

容器类型无需实例化。其子类型在需要时懒加载实例化。

实例化引用类型

取消引用路径。如果有类型参数,使用提供的实际类型替换模板类型中的所有出现。然后根据实例化规则再次实例化结果类型。

实例化分派器类型

分派类型。然后根据实例化规则再次实例化结果类型。

联合类型

联合的每个成员类型单独实例化。

实例化后处理

类型按照上述规则实例化后,应在返回前进行简化。

类型简化

TODO

简化联合类型时,任何可以分配给另一个成员的成员将从联合中移除。

遮蔽类型

TODO

虽然将 (string | "foo" | "bar") 简化为 string 是合理的,但我们会失去一些原始类型的更具体的信息,这些信息可以被自动补全器等处理器使用。因此,对于某些特殊情况,在简化过程中修剪的类型可能会在简化类型的 shadowedTypes 属性下访问。

类型可赋值性

mcdoc 中的类型可以看作集合。类型 A 可赋值给类型 B 当且仅当 A 是 B 的子集。any 是包含所有其他类型的通用集合,而空联合 () 是空集合。unsafe(实际上,any 是 TypeScript 的 unknownunsafe 是 TypeScript 的 any。一个配置规则还将被添加,使 any 等同于 unsafe,默认启用,因此大多数用户不必处理繁琐的验证机制,因为 vanilla-mcdoc 可能会使用 any 而不是 unsafe 作为标记的数据,这会使其在健全的类型系统下非法分配到其他地方。我稍后会更新文档和代码以添加 unsafe 类型)。

TODO

TODO: 数据验证器钩子可以贡献额外的类型可赋值性规则。例如:

  • 对于 JSON:byte = short = int = long = float = double
  • 对于 NBT:boolean = (byte @ 0..1) ⊂ byte

标识

“Mcdoc” 是一个普通名词,只有在语法上需要时才应将其首字母大写(例如在句子的开头)。

制作

mcdoc 格式受到 Yurihaia 创建的 nbtdoc 格式的启发,并根据 MIT 许可证授权。Misode、MulverineX、NeunEinser 和 vdvman1 也为 mcdoc 格式提供了宝贵的反馈。

本文档由Alumopper翻译。

类似文章

发表回复