扩展:Scribunto/Lua参考手册

本頁使用了標題或全文手工轉換
快捷方式: Lua manual  LUAREF
From mediawiki.org
This page is a translated version of the page Extension:Scribunto/Lua reference manual and the translation is 100% complete.

此手册记载Lua 的文档,用于MediaWiki的Scribunto 扩展。 部分内容(英文原文)取自Lua 5.1参考手册(翻译时参考了Lua参考手册的中文翻译),其许可协议为MIT许可证

介绍

入门

在已启用Scribunto的MediaWiki wiki上,以“Module:”前缀为标题创建一个页面,例如“Module:Bananas”。 进入该新页面,复制下列文本:

local p = {} --p代表包(package)

function p.hello( frame )
    return "Hello, world!"
end

return p

保存之后,另一个非模块的页面将会写入:

{{#invoke:Bananas|hello}}

你应该将“Bananas”替换成你需要调用的模块。这将会使模块调用“hello”函数。{{#invoke:Bananas|hello}}将会被函数输出的结果替换,在这个例子中他会被替换成,“Hello, world!”

从模板的环境中调用Lua代码确实是一个好办法。 这意味着从调用页面的角度来说,语法不依赖于模板逻辑是否被Lua或者维基文本执行。 这也避免了把复杂语法加到wiki内容命名空间中。

模块结构

模块本身必须返回一个包含能被{{#invoke:}}调用的函数的表(table)。 一般来说,如上所示,先声明一个局部变量表,函数被写在这个表中,并模块代码的结尾返回(return)这个表。

任何没有加到这个表中的函数,无论是局部还是全局,都不能被{{#invoke:}}访问,但是局部变量可能会被其他的用require()加载的模块访问。 对于模块来说,最好将所有的函数和变量声明为局部变量。

访问维基文本中的参数

{{#invoke:}}调用函数时,会将框架对象(frame object)作为单一的参数。要访问传递到{{#invoke:}}的参数,代码通常会使用框架对象的args表,也可以访问传递到了包含{{#invoke:}}的模板的参数,方法是使用frame:getParent(),并访问该框架的args

框架对象也用来访问对应环境的维基文本解析器,例如调用解析器函数扩展模板以及展开任意的维基文本字符串

返回文本

模块函数通常返回单个的字符串;无论返回什么值都会通过tostring()转换,然后连接在一起。这个字符串就是转化成维基文本代码{{#invoke:}}的结果。

在解析页面的这一点上,模板都是已经被展开的,解析器函数和扩展标签都已经被处理,而且预存的转换(例如签名的扩展以及pipe trick)都已经完成。因此,模块不能在输出文本中使用这些特性。例如,如果一个模块返回"Hello, [[world]]! {{welcome}}",页面就会显示“Hello, world! {{welcome}}”。

另一方面,替换引用是在加工的早期阶段处理的,所以只有当其他尝试的替换引用会被处理时才使用{{subst:#invoke:}}。失败的替换引用因为会保持为维基文本,所以都会在下一次编辑时被处理。这通常需要避免。

模块文档

Scruibunto允许模块可以被自动关联的模块维基文本页面关联文档;默认情况下,模块的“/doc”子页面是用来作为文档,并且会在模块页面的代码顶部显示其内容。例如,“模块:Bananas”的文档页面就是“模块:Bananas/doc”。

这也可以使用以下MediaWiki命名空间下的消息配置:

  • scribunto-doc-page-name —设置用来作文档页面的名称。 模块(除了模块:prefix)的名称会通过$1。 如果在模块命名空间,这里的页面通常会视为维基文本而不是Lua代码,也不会被{{#invoke:}}使用。 模块的页面通常是“Module:$1/doc”这样的模块/doc子页面。注意解析器函数等其他可扩展代码在这个消息中不被使用。 请注意,此消息中解析器函数和其他大括号扩展可能无法使用。
  • scribunto-doc-page-does-not-exist — 文档页面不存在时显示的消息。 页面名称传递为$1。 默认为空。
  • scribunto-doc-page-show — 文档页面存在时显示的消息。 页面名称传递为$1。 默认是嵌入包含文档页面。
  • scribunto-doc-page-header — 查看文档页面本身时显示的标题。 模块的名称(有Module:前缀)传递为$1。 默认以斜体显示简要说明。

注意模块不可以被直接分类,也不可以直接添加跨维基链接。 这些应该放在文档页面里面的‎<includeonly>...‎</includeonly>标签中,当文档页面被引用到模块页面时,这些分类和跨维基链接就会应用于模块页面。

Lua语言

变量名称

Lua中的名称(names)(也叫标识符(identifiers))通常可以是任何字母、数字和下划线的字符串,但不可以以数字开头。名称区分大小写;“foo”“Foo”和“FOO”都是不同的名称。

下列关键字是保留的,不能用作名称:

  • and
  • break
  • do
  • else
  • elseif
  • end
  • false
  • for
  • function
  • if
  • in
  • local
  • nil
  • not
  • or
  • repeat
  • return
  • then
  • true
  • until
  • while

以下划线开头后面接大写字母的名称是留给内部的Lua全局变量的。

下面这些文本也属于保留字:

  • #
  • %
  • (
  • )
  • *
  • +
  • ,
  • -
  • --
  • .
  • ..
  • ...
  • /
  • :
  • ;
  • <
  • <=
  • =
  • ==
  • >
  • >=
  • [
  • ]
  • ^
  • {
  • }
  • ~=

注释

注释在任何地方(除了字符串内)由--开始。如果--紧接着开放的中括号,注释就会一直延伸到下一个中括号;否则注释会延伸到这一行的结尾。

-- Lua的注释是由两个连字符(<code>-</code>)开始,延续到一行末尾。
--[[ 多行的字符串和注释
     可以被两层中括号括起来。]]
--[=[ 像这样的注释可以紧随其他--[[注释]]。]=]
--[==[ 像这样的注释可以有其他的
      --[===[ 修长的 --[=[注释]=] --跟随着
        ]===] 多次,即使它们所有
      --[[ 都没有用匹配的长括号分隔! ]===]
  ]==]

数据类型

Lua是动态类型语言,意味着变量和函数值都不会有种类,只有值会有。所有的值都有类型。

Lua有8个基本类型,然而只有6个是与Scribunto扩展相关。type()函数会返回值的类型。

tostring()能够将值转化为字符串(string)。tonumber()能够将值转化为数字(number)(如果可能的话),而其他情况则会返回空值(nil)。没有用于将一个值转化为其他的数据类型的明确的函数。

凡是将与字符串(string)连接在一起的地方,数字(number)会自动转化为字符串。使用计算符号时,字符串会由tonumber()自动转化为数字辨识。当运算中需要将一个值作为布尔值(boolean)时,除了nil和false之外,所有的值都会视为true。

空值(nil)

“空值”是nil的数据类型,用来表示这个值不存在。

空值不能用作表(table)中的键(key),且表中某个字段“未指定”与“值为nil”没有区别。

当空值转化为字符串时,其结果会是“nil”;转化为布尔值(boolean)时,空值会被视为false。

布尔值(boolean)

布尔值就是true(真)和false(假)。

当布尔值转化为字符串时,结果就是“true”或者“false”。

不像很多其他语言,布尔值不会直接转化为数字。而且只有false和nil作为布尔值时也会视为false;数字0和空字符串都是视为true。

字符串(string)

Lua字符串会视为一系列的8比特字节;这取决于应用程序以哪种特定的编码来解析。

字符串会记录在任何一组单双引号('")中;就像JavaScript而不像PHP,这两者(指的单引号和双引号)无区别。以下转义序列能被识别:

  • \a(响铃,字节7)
  • \b(退格,字节8)
  • \t(水平制表符,字节9)
  • \n(换行,字节10)
  • \v(纵向制表符,字节11)
  • \f(换页,字节12)
  • \r(回车,字节13)
  • \"(双引号,字节34)
  • \'(单引号,字节39)
  • \\(反斜线,字节92)

在字符串代码中直接换行,应该在前面加一个反斜线(\)。字节也可以通过转义序列'\ddd',其中ddd是0~255中的字节值。使用转义序列来代替Unicode字符,则为UTF-8的单个编码字节必须要指定;总的来说,直接输入Unicode字符会更加简单。

字符串也可以用长括号定义。 长括号之间可以夹杂着0个或更多个等号(两边的等号要等量),例如[[[=[[=====[。 开放的长括号必须被相应的闭合的长括号(或者说是结束标记)链接,例如]]]=]或者]=====].。 特殊情况下,开放的长括号紧跟着连续换行但未被包括的字符串,新一行只会持续到闭合长括号前。 由长括号定义的字符串不会处理转义序列。

-- 长字符串
foo = [[
bar\tbaz
]]

-- 等效于此引号分隔的字符串
foo = 'bar\\tbaz\n'

注意,在转化为布尔值时,所有的字符串都会视为true(真)。这不像其他的语言将空字符串视为false(假)。

数字(number)

Lua只有一种数字类型,就是典型的双精度浮点数。这种格式下,-9007199254740992到9007199254740992之间的整数都会准确表达,更大的数和带有小数部分的数将会受到舍入的误差。

数字可以用点(.)来表示小数,例如123456.78。数字也可以用不带空格的科学计数法,例如1.23e-10123.45e20或者1.23E5。也可以用16进制表示整数,方法就是以0x开头,例如0x3A

虽然NaN和正负无穷大都可以正确地储存、处理,但是Lua不提供相应的直接文字表示方法。math.huge是正无穷大,相当于1/0,此外,像0/0这样的除法就可以生成NaN。

重申一遍,在转化为布尔值时,任何数字都会被视为true。这不像其他语言,数字0通常视为false。数字在转化为字符串时,数字都会被视为有限小数和科学计数;NaN是"nan"或者"-nan";无穷大就是"inf""-inf"

表(table)

Lua的表是关联数组(associative arrays),很像PHP的数组和JavaScript的对象(object)。

表要用一对花括号创建。空表是{}。创建有多个字段的表时,逗号和分号可以分隔表中的各个字段。表的内容可以用以下形式表示:

  • [表达式1] = 表达式2,意思是键为表达式1,它与值表达式2匹配。即表达式1的值是表达式2
  • 名称 = 表达式,它等价于["名称"] = 表达式
  • 表达式大致相当于[i] = 表达式,在这里i是在这个表中从1开始排序的正整数。如果最后一个字段和表达式有多个值,所有的值都会使用;否则只有第一个会保留。

表中的字段用中括号访问,例如table[key]。字符串键(key)同样也可以用作点来表示的名字,例如table.key就与table['key']等价。调用表中的一个函数也可以用冒号,例如table:func( ... ),相当于table['func']( table, ... )或者table.func( table, ... )

序列(sequence)是从1到N的所有正整数且没有比N更大的正整数、且非空值的表。很多Lua函数仅会操作序列,忽略所有的非正整数的键。

不像很多其他的语言(例如PHP或JavaScript),在Lua任何值(除了nil和NaN)都可以作为键,而且不会执行类型转换。参看下面的示例:

-- 创建表
t = {}
t["foo"] = "foo"
t.bar = "bar"
t[1] = "一"
t[2] = "二"
t[3] = "三"
t[12] = "数字十二"
t["12"] = "字符串十二"
t[true] = "true"
t[tonumber] = "是的,即使函数也可以用作表格键"
t[t] = "是的,表格也可以用作表格的键,即使是它本身。"

-- 这将创建一个大致相当于上面的表
t2 = {
    foo = "foo",
    bar = "bar",
    "一",
    "二",
    [12] = "数字十二",
    ["12"] = "字符串十二",
    "三",
    [true] = "true",
    [tonumber] = "是的,即使函数也可以用作表格键",
}
t2[t2] = "是的,表格也可以用作表格的键,即使是它本身。"

类似地,任何非nil的值都可以在表中储存为值。将表中存储nil相当于删除表中的对应的键,并且调用表中任何一个不存在的键都会返回nil。

注意,在Lua中,表从来不会复制出一个独立的新表;如果表作为一个参数传递到函数,那么函数能修改表中的键或值,这些变化在调用者(caller)的作用域中都会可见。

当转化为字符串时,结果通常是"table",但使用__tostring元方法可以重写这个转化方法。作为布尔值时,即使是空的表也会视为真(true)。

函数(function)

Lua中的函数是一等的(first-class)的值:可以匿名创建,或作为参数传递,或给变量赋值,等等。

函数通过function(“函数”的英文)关键字,并使用圆括号调用。有一些语法糖可以用来命名函数,局部函数,并且可以作为表中的一个域值。参看函数声明函数调用

Lua函数是闭包,这意味着它们维护对它们声明的作用域的引用,并可以访问和操作该作用域中的变量。

类似于表,如果函数一个函数被分配给另一个变量,或者作为参数传递给另一个函数,它仍然是相同的被调用的底层“函数对象”。

函数转化为字符串时,结果是"function"。

不支持的类型

用户数据(userdata)用来储存其他语言中的值;例如,一个用户数据可以用来储存C的指针或结构。使用Scribunto运行环境不允许使用用户数据。

线程数据类型代表协同处理,在Scribunto的沙盒中不可用。

元表

每个表都可以关联另一个表,成为元表(metatable)。元表的字段将由特定的操作符或函数使用,从而为表指定不同的或者回落的行为。表的元表通过getmetatable()函数获取,通过setmetatable()函数设置。

访问元函数时,会像rawget()这样访问元表字段。

以下元表字段可以影响表本身:

__index
如果表访问t[key]返回nil就使用这个。 如果这个字段的值是表,访问就会在这个表中重复进行,例如__index[](会调用这个表的元表的__index)。 如果这个字段的值是函数,函数就会像__index( t, )这样调用。 rawget()函数会绕过这个元方法。
__newindex
这个元方法用于将键赋值到表中,即t[]=,但是rawget( t, key )会返回空值(nil)。 如果这个字段的值是表,赋值就会在这个表中重复进行,例如__newindex[] = (会调用这个表的元表的__newindex)。 如果这个字段的值是函数,那么函数就会像这样调用:__newindex( t, , )rawset()函数会绕过这个元方法。
__call
对表应用函数调用语法t( ... )时,就会使用这个元方法。 这个值必须是函数,会像__call( t, ··· )这样调用。
__mode
这用于使表保持弱引用(weak references)。 这个值一定是一个字符串。 默认情况下,任何一个值被作为表格中的键或值时是不会被垃圾回收的。 但如果元表字段包含字母k,且没有非弱引用,键可能会被作为垃圾收集。而且如果包含v则值有可能也会作为垃圾收集;其他的情况,对应的键和值都会从表中移除。 注意,如果在表用作元表之后字段被改变,则行为未定义。

其他元表字段包括:

对于二元运算符,Lua首先检查左边的变量的元表(若有),如果左边变量的元方法不存在则寻找右边的变量的元方法。
对于关系运算符,只有当两个参数的元方法都指定了同一个函数时,元表才会被使用。不同的匿名函数,即使具有相同的主体和闭包,也不可能被认为是相同的。
* __metatable会同时影响getmetatable()setmetatable()

注意:在Lua中,所有的字符串都会共用一个单一的元表,其__index就是指的string表。在Scribunto中,这个元表不允许访问,也不能被string表引用;对模块可用的string表是一个副本。

变量

变量是储存值的地方。Lua有三种变量:全局(global)变量、局部(local)变量和表(table)中的字段(field,又称“域”)。

名称分为全局和局部变量(或者是函数变量,是局部变量的一种)。定义一个局部变量,可以使用关键词local,否则默认视为全局变量。任何没有赋值的变量都会视为有nil值。

全局变量储存在叫做环境的Lua表中;这个表通常是作为全局变量_G的值。这个全局变量表也可以设置元表;__index和__newindex元方法都可以用于全局变量表,就像其他的表一样。

函数的环境可以使用getfenv()函数获取,使用setfenv()函数修改;在Scribunto中,这些函数如果全部可用,就会受到严重限制。

局部变量是有词法作用域的;参见局部变量定义了解详细信息。

表达式

表达式是有值的内容:直接量(数字、字符串、true、false、nil)、匿名函数声明、表构造函数、变量引用、函数调用、变量参数表达式、用括号括起来的表达式、组合有一元运算符的表达式、和与二元运算组合的表达式。

大多数表达式都有一个值;函数调用和变量参数表达式可以有任何数量个值。注意用括号括一个函数调用或变量参数表达式只能保留第一个值,其他的值会失去。

表达式列表是逗号分隔的表达式列表。除了最后一个表达式以外,所有的值都只能是一个值(丢弃附加值,如果表达式没有值则使用nil);最后一个表达式的所有值都包含在表达式列表的值中。

算数运算符

Lua支持以下常见的算数运算符:加减乘除、模运算、幂和取反。

当所有操作值为数字或字符串时,即使用tonumber()返回非nil时,这些操作符有他们通常的意义。

如果一个操作数是一个有合适的元方法的表,元方法就会被调用。

操作 功能 示例 元方法 注释
+ 加法 a + b __add
- 减法 a - b __sub
* 乘法 a * b __mul
/ 除法 a / b __div 除以零不会出错,会返回NaN或者无穷大
% 模运算 a % b __mod 定义为a % b == a - math.floor( a / b ) * b
^ 幂运算 a ^ b __pow 允许非整数指数
- 取相反数 -a __unm

关系运算符

Lua的关系运算符是==~=<><=>=。关系运算的结果一定是布尔值(boolean)。

等于号(==)首先比较两个值的种类;如果两个值是不同的种类,结果为假(false)。然后比较值:空值、布尔值、数字和字符串照常比较。对于函数,则是看两个函数是否引用同一个准确的函数对象;像这样检测两个不同的(但作用相同的)匿名函数function() end == function() end一定会返回假(false)。表也会默认像函数这样比较,但是可能会由于使用__eq元方法而改变结果。

不等号(~=)与等于号作用相反。

对于排序运算符,如果两者都是数字,或者两者都是字符串,则直接进行比较。其次检查元方法:

  • a < b使用__lt
  • a <= b使用__le如果可用,或者__lt可用,那么它等价于not ( b < a )
  • a > b等价于b < a
  • a >= b等价于b <= a

如果必需的元方法不可用,会产生错误。

逻辑运算符

逻辑运算是and(与)、or(或)和not(非)。在逻辑运算中,只有空值(nil)和false被视为false,其他的都被视为true。

对于and,如果左边的操作数被视为假,那么被返回这个操作数,将右边的操作数忽略;否则返回右边的操作数。

对于or,如果左边的操作数视为真,那么返回左边的操作数,忽略右边的操作数;否则返回右边的操作数。

对于not,其结果一定是布尔值(true或false)。

注意andor采用短路求值。例如,如果foo()返回的第一个值为false或nil,foo() or bar()只会调用bar()

连接运算符

连接运算符就是两个点(dot),比如a .. b。如果两个操作数都是数字或字符串,它们会被转化为字符串然后返回。但是如果__concat元方法可用,就会使用这样的元方法。如果它存在但无效,则会产生错误。

注意Lua的字符串是不可变的(immutable),而且Lua不提供任何类型的“字符串构造器(string builder)”,所以反复进行a = a .. b会必须为每次迭代创建一个新字符串,并最终将旧字符串作为垃圾收集。如果许多字符串都需要连接,则应使用string.format(),或将所有的字符串添加到一个序列然后最后使用table.concat()连接。

长度运算符

长度运算符是#,像#a这样使用。如果a是字符串,会返回字符串的字节长度。如果a序列表,会返回序列的长度。

如果a是一个不是序列的表,#a会返回0或者能够使“a[n]不是nil而a[N+1]是nil”成立的值N,即使有的更高的索引不是nil值。例如,

-- 这不是序列,因为a[3]是nil而a[4]不是
a = { 1, 2, nil, 4 }

-- 会输出2或4。
-- 即使这个表没有被修改,这个值也有可能改变。
mw.log( #a )

运算优先级

Lua的操作符优先级,从高到低为:

  1. ^
  2. not # - (负号)
  3. * / %
  4. + - (减号)
  5. ..
  6. < > <= >= ~= ==
  7. and
  8. or

在同一级中,二元运算符会从左到右运算,例如a / b / c相当于(a / b) / c。幂运算和连接会从右往左,例如a ^ b ^ c相当于a ^ (b ^ c)

函数调用

Lua的函数调用与其他的语言很类似:函数名称后面跟着被括号括起来的参数列表。

func( 表达式列表 )

和Lua表达式列表的通常情况一样,列表中的最后一个表达式可以提供多个参数值。

如果传递给函数的参数比函数定义中的参数少,则额外的参数将被赋值nil。如果表达式列表的值比参数多,多余的值会被舍弃。可以让函数接受可变个数的参数,参见函数声明的细节。

Lua允许直接调用由函数返回的值,例如func()()。如果需要比变量访问更复杂的表达式来确定要调用的函数,则可以使用括号表达式来代替变量访问。

Lua的语法有两个常见的语法糖例子。第一个是当一个表被当做对象使用,并且这个对象有一个函数被调用,那么语法

table:name( 表达式列表 )

完全等同于

table.name( table, 表达式列表 )

第二种常见的情况就是Lua通过含有名称至值的映射的表作为函数唯一参数实施命名参数的工具。这种情况下,包围着参数的括号可以省去。如果将一个(由引号或中括号分割的)字符串作为调用函数时的唯一参数,那么包围着这个参数的括号也可以省去。比如调用

func{ arg1 = exp, arg2 = exp }
func"string"

等同于

func( { arg1 = exp, arg2 = exp } )
func( "string" )

它们也可以被组合使用。以下使用是等价的:

table:name{ arg1 = exp, arg2 = exp }
table.name( table, { arg1 = exp, arg2 = exp } )

函数声明

函数的定义语法如下:

function nameoptional ( var-listoptional )
    语句块
end

var-list中的所有变量对函数都是局部的,这些变量被函数调用的表达式列表赋值。在这个语句块中,多余的局部变量会被忽略。

函数被调用时,block中的语句会在由var-list创建与变量表对应的局部变量和指定值之后执行。如果执行到返回语句,语句块就会退出,函数调用表达式的值就会是返回语句给予的值。如果执行到代码段末尾,还没有返回语句,则函数调用返回的结果是0个值。

Lua函数是词法闭包。一个常见的习惯用法是在函数声明的作用域内声明“私有静态(private static)”变量作为局部变量。比如,

-- 这会返回将它的参数增加一个数字的函数
function makeAdder( n )
    return function( x )
        -- 外面的这个n在这里是可以加到x中的
        return x + n
    end
end

local add5 = makeAdder( 5 )
mw.log( add5( 6 ) )
-- 输出11

函数可以声明接受可变数量的(即任何数量个)参数,通过将...作为变量列表的最后一项:

function nameoptional ( var-list, ... )
    语句块
end
-- or
function nameoptional ( ... )
    语句块
end

在这个语句块内,可以使用变量表达式...,结果会是函数调用中的所有额外的值。比如,

local join = function ( separator, ... )
    -- 把额外的变量收集到一个新表中
    local args = { ... }
    -- 获得当前额外变量的准确个数
    local n = select( '#', ... )
    return table.concat( args, separator, 1, n )
end

join( ', ', 'foo', 'bar', 'baz' )
-- 返回字符串"foo, bar, baz"

select()函数就是用来作用于这些参数表达式的;特别地,如要获取参数表达式的值的数量,应当使用select( '#', ... )而非#{ ... }来数参数表达式有多少个,因为{ ... }不一定是序列

Lua为将函数声明和赋值提供到变量中提供语法糖;参见函数声明语句的细节。

注意这个不会起效:

local factorial = function ( n )
    if n <= 2 then
        return n
    else
        return n * factorial( n - 1 )
    end
end

因为这个函数声明是在局部变量赋值声明完成之前就处理好的,函数主体中的“factorial”会指向函数外(通常是未定义的)变量。这个问题可以通过先声明一个局部变量然后再将它分配到后来的声明中,或者使用函数声明语句语法。

语句

语句是执行的基本单元:一个赋值、控制结构、函数调用、变量声明,等等。

代码块(chunk)是语句(statements)的序列,可以由分号分开。一个代码块基本上被考虑为匿名函数的主体,所以它可以声明局部变量,接受参数,并返回值。

语句块(block)也是语句序列,就像一个代码块(chunk)。语句块可以被分隔以创建单个语句(statement):do 语句块 end。这些通常用来限制局部变量的作用范围,或者在另一个块的中间加入returnbreak

赋值

变量列表 = 表达式列表

变量列表是由逗号分隔的变量;表达式列表是由逗号分隔的一组一个或多个表达式。所有的表达式都会在赋值之前被求值,所以a, b = b, a会交换ab的值。

局部变量定义

local 变量列表

local 变量列表 = 表达式列表

局部变量可以在一个语句块代码块内任意地方声明。第一种形式,不需要表达式列表,声明变量但不赋值,所以所有变量的值都是nil。第二种形式为局部变量赋值,参见上面的赋值

注意局部变量从局部变量声明后的语句中才可见。因此local x = x这样的声明声明了一个名为“x”的局部变量,并将外层范围中“x”的值赋予这个变量。局部变量只在其声明的语句块结尾前的地方生效。

控制结构

while 表达式 do 语句块 end

while语句会在表达式结果为true时,反复执行语句块。

repeat 语句块 until 表达式

repeat语句会反复执行语句块,直到表达式结果为true。语句块内声明的局部变量可以用于表达式中。

for 名称 = 表达式1, 表达式2, 表达式3 do 语句块 end
for 名称 = 表达式1, 表达式2 do 语句块 end

for循环的第一种形式会声明一个局部变量,然后从exp1exp3的步长到exp2重复语句块。注意exp3可以省略,相当于1,但是非数字值比如nilfalse是错误的。循环开始前,所有的表达式都会先被计算。

这种形式的for循环大致相当于

do
    local var, limit, step = tonumber( exp1 ), tonumber( exp2 ), tonumber( exp3 )
    if not ( var and limit and step ) then
        error()
    end
    while ( step > 0 and var <= limit ) or ( step <= 0 and var >= limit ) do
        local name = var
        block
        var = var + step
    end
end

不过变量var、limit和step在其他地方都不可访问。注意变量“name”是对语句块是局部变量;如果要在循环以外用它的值,它必须被复制到在循环外面定义的变量。

for 变量列表 in 表达式列表 do 语句块 end

第二种形式的for循环会与迭代函数一起作用。就像在第一种形式里,表达式列表会在开始循环之前就赋值了。

这种形式的for循环大致相当于

do
    local func, static, var = expression-list
    while true do
        local var-list = func( static, var )
        var = var1  -- ''var1''是''var-list''中的第一个变量
        if var == nil then
            break
        end
        block
    end
end

除了变量func、static和var在其他地方不能被获得。注意var-list中的变量在语句块中相当于局部变量;如要在循环外使用它们,需要复制到在循环之外声明的变量。

通常expression-list是返回3个值的一个函数调用。如果迭代器函数可以被写入,那么只取决于传入的参数,这会更加有效。否则,在Lua中编程建议返回闭包比返回表更好,因为静止的变量在每次迭代更新其成员。

if 表达式1 then 语句块1 elseif 表达式2 then 语句块2 else 语句块3 end

表达式1返回true时执行语句块1,否则,当表达式2返回true时执行语句块2,否则执行语句块3else 语句块3部分可以省去,elseif 表达式2 then 语句块2部分可以重复,也可以省去。

return 表达式列表

return语句用来从函数或者代码块(只是一个函数)返回值。表达式列表是由逗号分隔开的零个或更多个表达式。

Lua实现尾调用(tail calls)。如果表达式列表包含一个函数调用,对那个函数的调用会重新使用当前的堆栈项(stack frame)。这可用于处理调用堆栈的函数,比如getfenv()debug.traceback()

返回语句必须是语句块的最后一个语句。如果某些原因语句块的中间需要返回,那么可能需要明显的语句块do return end

break

break语句用来中止一个while、repeat或for循环的执行,并跳到循环后面的语句。

break声明必须是语句块(block)的最后一个声明。如果某些原因语句块的中间需要中断,那么可能需要明显的语句块do return end

函数调用作为语句

函数可以作为语句被调用,此时函数只会起到任何的“副作用(side effects)”(例如mw.log()向日志输出信息),返回的值会被忽略。

函数声明语句

Lua提供了语法糖来使得函数的定义更加自然。以下几种定义相互等价。

-- 基本声明
function func( var-list ) 语句块 end
func = function ( var-list ) 语句块 end
-- 局部函数
local function func( var-list ) 语句块 end
local func; func = function ( var-list ) 语句块 end
-- 作为表中的字段的函数
function table.func( var-list ) 语句块 end
table.func = function ( var-list ) 语句块 end
-- 作为表的方法的函数
function table:func( var-list ) 语句块 end
table.func = function ( self, var-list ) 语句块 end

注意,这里的冒号符号与函数调用的冒号符号相类似,在参数列表的开头添加了一个隐式参数,名为self

错误处理

错误可以通过error()assert()“抛出”,使用pcall() 或者xpcall()可以“捕获”错误。注意,某些Scribunto的内部错误是不能被Lua层面的代码捕获处理。

垃圾回收

Lua能自动管理内存。这意味着你不需要关心新建对象时内存空间的申请,或者对象不需使用时内存空间的释放。Lua的内存管理会自动执行“垃圾回收”,将不会再被访问的死对象或者被弱引用保持的对象的内存空间收回。几乎所有Lua类型元素都能被回收,包括表、方法、字符串等。

垃圾回收是自动运行的,不能被Scribunto配置。

标准库

Lua标准库为Lua提供基本的性能和关键功能。本文档只给出Lua标准库中在Scribunto启用的一部分内容。

基本函数

_G

这个变量持有对当前全局变量表的引用;全局变量foo也可以通过_G.foo访问。注意,然而,对_G本身并没有什么特别的,可能被以同样的方式作为任何其他变量:

foo = 1
mw.log( foo ) -- 输出1
_G.foo = 2
mw.log( foo ) -- 输出2
_G = {}       -- _G不再指向全局变量表
_G.foo = 3
mw.log( foo ) -- 还是输出2

全局变量表可以当作普通的表来使用。例如,

-- 调用一个名称存储在变量中的函数
_G[var]()

-- 输出所有全局变量的名称和转化为字符串后的值
for k, v in pairs( _G ) do
   mw.log( k, v )
end

-- 输出新全局变量的创建
setmetatable( _G, {
    __newindex = function ( t, k, v )
         mw.log( "Creation of new global variable '" .. k .. "'" )
         rawset( t, k, v )
    end
} )

_VERSION

包含正在运行的Lua语言版本的字符串,例如“Lua 5.1”。

assert

assert( v, message, ... )

如果v是nil或false,抛出错误,message就会用作错误的文本:如果错误文本为nil(或者未指定),则文本是"assertion failed!"(“断言失败!”);如果是字符串或者数字,文字就是那个值;否则assert本身就会抛出错误。

如果v是任何其他的值,assert就会返回包括vmessage在内的全部变量。

在Lua的一个比较常见的惯用用法是,一个函数在正常操作时返回true值,并在失败时返回nil或false作为为第一个值、错误信息作为第二值。容易的错误检查可以通过调用函数assert来实现:

-- 这个不会检查错误
local result1, result2, etc = func( ... )

-- 这个作用相同,但是会查错
local result1, result2, etc = assert( func( ... ) )

error

error( message, level )

用文本message来发出一个错误。

error一般会提供出错的位置的信息。如果level是1或者省略,那么信息就是调用error本身的位置,2使用被调用错误的函数调用的位置,等等。0会省略位置信息。

getfenv

getfenv( f )

注意,这个函数可能会无效,取决于配置中的allowEnvFuncs

返回运行环境(全局变量表),就像f指定的:

  • 如果1、nil或者未指定,返回调用getfenv函数的环境。通常这与_G相同。
  • 整数2~10返回更高一级的环境。例如,2返回被这一个函数调用的函数的环境,3返回调用这一个函数的函数的环境,以此类推。如果这个堆栈值越高,错误也会随之上升,否则这个堆栈级别会返回尾调用(tail call)。
  • 传递函数返回在调用该函数时将使用的环境。

通过所有的基本函数和Scribunto基本库函数使用的环境都被保护。尝试访问这些环境中使用getfenv将返回nil。

getmetatable

getmetatable( table )

返回这个元表。其他的类型都会返回nil。

如果元表拥有__metatable字段,这个值就会直接返回,而不是返回事实上的元表。

ipairs

ipairs( t )

返回3个值:迭代函数、表t和0。它是供for的迭代形式使用的:

for i, v in ipairs( t ) do
    -- process each index-value pair
end

它会迭代数对(1, t[1])、(2,t[2])等,直到t[i]是nil时。

如果__ipairs元方法存在,那么标准行为会被覆盖,调用会返回与__ipairs( t )的返回值一致的值。

next

next( table, key )

允许遍历表中的键。如果key是nil或者未指定,返回表中的第一个键和值,否则,返回下一个键和值。如果没有更多的键可以用,返回nil。可以通过使用next( t ) == nil来检查表是否是空的。

注意键被返回的顺序是没有指定的,即使表有数字的索引(indexes)。如要以数字顺序遍历表,使用数字的foripairs

如果遍历时使用next,任何不存在的键被赋给一个值,则行为未定义。可以给一个存在的字段赋一个新的值(包括nil)。

pairs

pairs( t )

返回三个值:迭代器函数(next或者类似的)、表t和nil。这是用于for的迭代器形式

for k, v in pairs( t ) do
    -- 处理每个键值对
end

这会遍历t中的键值对,就像next做的那样。参考next的文档以了解遍历期间修改表的限制。

如果__pairs元方法存在,那么标准行为会被覆盖,调用会返回与__pairs( t )的返回值一致的值。

pcall

pcall( f, ... )

用指定的参数在“保护模式”下调用函数f。这意味着如果在调用f时出错,pcall会返回false与错误消息。如果没有错误发生,pcall会返回true与调用返回的所有值。

伪代码表示,pcall的定义类似如下:

function pcall( f, ... )
    try
        return true, f( ... )
    catch ( message )
        return false, message
    end
end

rawequal

rawequal( a, b )

a == b相同,只是忽略了所有__eq元方法

rawget

rawget( table, k )

table[k]相同,只是忽略了所有__index元方法

rawset

rawset( table, k, v )

table[k] = v相同,只是忽略了所有__newindex元方法

select

select( index, ... )

如果index是数字,返回index之后的...中对应位置的数。如果index是字符串"#",返回...中参数的个数。

也就是说,select有点像以下代码,只是即使当...包含nil值时也可以正常工作(参考#unpack以了解关于nil的问题)。

function select( index, ... )
    local t = { ... }
    if index == '#' then
        return #t
    else
        return unpack( t, index )
    end
end

setmetatable

setmetatable( table, metatable )

设置元表metatable可能是nil,但是必须是清楚地提供的。

如果当前的元表有一个__metatable字段,setmetatable会报错。

tonumber

tonumber( value, base )

尝试将value转化为数字。如果已经是数字或者可以转化为数字的字符串,tonumber就会返回这个数字,否则会返回nil。

base是可选的,默认为10,指定解析数字的进位基数。这个基数可以是2到36之间的任何整数。对于大于10,字母A(大小写均可)代表10,B代表11,以此类推,Z代表35。

十进制下,值可以有小数部分,或者以科学计数法表示,而且甚至可以以“0x”开头以表示16进制。其他情况,只会接受不带符号的整数。

tostring

tostring( value )

value转化为字符串。参考上方的数据类型以了解各种类型被转换时的细节。

对于表,标准的转换行为可以被__tostring元方法覆盖。如果这个元方法存在,那么调用tostring会返回由__tostring( value )返回的单个值。

type

type( value )

以字符串形式返回value的类型"nil""number""string""boolean""table""function"

unpack

unpack( table, i, j )

从给定的表返回值,像table[i], table[i+1], ···, table[j]这样的如果被写出会直接返回。ij如果没有给出或者是nil,取其默认值1和#table

注意如果table不是一个序列j是nil或未指定,则结果是不确定的,参考长度操作符以了解详细。

xpcall

xpcall( f, errhandler )

这个很像pcall,只是错误消息在返回之前传递到函数errhandler中。

伪代码表示,xpcall的定义类似如下:

function xpcall( f, errhandler )
    try
        return true, f()
    catch ( message )
        message = errhandler( message )
        return false, message
    end
end

调试库

debug.traceback

debug.traceback( message, level )

以字符串的形式返回调用栈。可选的message参数会被连接到调用栈信息的前面。可选的level参数表示返回多少层调用栈。

数学(math)库

math.abs

math.abs( x )

返回x的绝对值。

math.acos

math.acos( x )

返回x的反余弦值(以弧度表示)。

math.asin

math.asin( x )

返回x的反正弦值(以弧度表示)。

math.atan

math.atan( x )

返回x的反正切值(以弧度表示)。

math.atan2

math.atan2( y, x )

使用两个参数的符号(signs)以找到结果的象限,返回y/x的反正切值(弧度制)。

math.ceil

math.ceil( x )

返回不小于x的最小整数。

math.cos

math.cos( x )

返回x(以弧度表示)的余弦值。

math.cosh

math.cosh( x )

返回x的双曲余弦值。

math.deg

math.deg( x )

将弧度角x转化为角度。

math.exp

math.exp( x )

返回

math.floor

math.floor( x )

返回小于或等于x的最大整数。

math.fmod

math.fmod( x, y )

返回x除以y的余数,并将商舍入到零。比如,math.fmod( 10, 3 )产生1

math.frexp

math.frexp( x )

返回像这样的两个值me

  • 如果x有限且非零,那么e是整数,m的绝对值在区间内
  • 如果x是零,那么me都是0
  • 如果x是NaN(非数字)或无穷大,那么mxe不指定

math.huge

代表正无穷大的值;比任何其他数值都要大或与之相等。

math.ldexp

math.ldexp( m, e )

返回e是整数)。

math.log

math.log( x )

返回x的自然对数。

math.log10

math.log10( x )

返回x的以10为底的对数。

math.max

math.max( x, ... )

返回其参数的最大值。

对于NaN的行为未指定。当前,如果x是NaN,那么会返回NaN,但是其他的NaN就会被忽略。

math.min

math.min( x, ... )

返回其参数的最小值。

对于NaN的行为未指定。当前,如果x是NaN,那么会返回NaN,但是其他的NaN就会被忽略。

math.modf

math.modf( x )

返回两个数字,x的整数部分和x的小数部分。比如,math.modf( 1.25 )产生1, 0.25

math.pi

的值。

math.pow

math.pow( x, y )

x^y相同。

math.rad

math.rad( x )

将角度x转化为弧度角。

math.random

math.random( m, n )

返回伪随机数。

参数mn可省略,但是如果指定了,则必须能够转化为整数。

  • 没有参数时返回区间内的实数。
  • 有一个参数时返回区间内的整数。
  • 有两个参数时返回区间内的整数。

注意,如果m或者n小于−2147483648或者大于2147483647,或者n - m大于2147483646,那么输出可能会不正确。


math.randomseed

math.randomseed( x )

x作为伪随机数生成器的种子

注意使用同一个种子会导致math.random输出相同的数列。

math.sin

math.sin( x )

返回 x(以弧度表示)的正弦值。

math.sinh

math.sinh( x )

返回x的双曲正弦值。

math.sqrt

math.sqrt( x )

返回x的平方根。与x^0.5相同。

math.tan

math.tan( x )

返回x(以弧度表示)的正切值。

math.tanh

math.tanh( x )

返回x的双曲正切值。

操作系统库

os.clock

os.clock()

返回程序大约使用的CPU时间,以秒为单位。

os.date

os.date( format, time )

语言库的formatDate可以被用于更加全面的日期格式。

返回包含日期和时间的字符串或表,以format为格式。如果格式被省略或者是nil,则使用“%c”。

如果给定了time,则就是被格式化的时间(参考os.time()。否则使用当前的时间。

如果format以!开头,则日期按照UTC格式而不是服务器的本地时间。在这个可选字符之后,如果格式是字符串"*t",那么日期返回带有以下字段的表。

  • year(完整的)
  • month(1~12)
  • day(1~31)
  • hour(0~23)
  • min(0~59)
  • sec(0~60,允许闰秒的情况)
  • wday(weekday,Sunday是1)
  • yday(一年的某一天)
  • isdst(夏令时,布尔值,信息不可用时可能不存在)

如果格式不是"*t",那么日期以字符串形式返回日期,按照和C函数strftime相同的规则格式化。

os.difftime

os.difftime( t2, t1 )

返回从t1t2两个时间的秒数。

os.time

os.time( table )

返回代表当前时间的数字。

调用且没有变量时,返回当前时间。若传入了表,则表中编码的时间就会被解析。表必须有years、month、days这几个字段,并且可能也包括hour(默认为12)、min(默认为0)、sec(默认为0)和isdst。

包库

require

require( modulename )

加载指定的模块。

首先会检查package.loaded[模块名]以检查模块是否已被加载,如果是,返回package.loaded[模块名]

否则,返回package.loaders序列中的每一个加载器以尝试寻找模块的加载器。若找到加载器,则调用这个加载器。加载器返回的值存储在package.loaded[modulename]中并将其返回。

参见package.loaders以了解可用的加载器的信息。

例如,如果你有一个模块“模块:Giving”其中有如下代码:

local p = {}

p.someDataValue = '您好!'

return p

你可以用以下代码在其它模块载入此模块:

local giving = require( "Module:Giving" )

local value = giving.someDataValue -- 值现在等于 '您好!'

package.loaded

这个表存储已加载的模块。键为模块名称,值为模块被加载时返回的值。

package.loaders

表存储了在加载模块时要用的搜索器函数(searcher function)的序列。每一个搜索器函数会被带有单个参数调用:要加载的模块名称。如果找到模块,搜索器必须返回会事实上加载这个模块的函数,并返回要被require返回的值。否则,返回nil。

Scribunto提供了两种搜索器:

  1. package.preoload[modulename]中寻找加载器函数
  2. Scribunto提供的模块中寻找模块名称,如果没找到,在“模块:”命名空间下寻找。“模块:”前缀必须提供。

注意没有包括标准的Lua加载器。

package.preload

这个表存储加载器函数,被Scribunto在package.loaders中包括的第一个搜索器。

package.seeall

package.seeall( table )

table的__index元方法设置为_G

strict库

strict库不是普通的库。其会在使用一个变量,而该变量在该作用域内不是局部变量时抛出错误。可以通过在模块顶部加入 require( "strict" ) 以启用。

在维基媒体的很多 wiki 中,这是以 Module:No globals 的形式实现的。

字符串库

在所有字符串函数中,第一个字符是第1位,而不是像C、PHP、JavaScript中的那样第0位。索引(indexes)可以是负的,表示从字符串最末开始倒数,比如-1表示最后一个字符,-2表示倒数第二个,等等。

警告:字符串库假定单字节字符串编码。不能处理Unicode字符。要对Unicode字符进行操作,使用Scribunto Ustring库中对应的方法。

string.byte

string.byte( s, i, j )

如果字符串被考虑为字节的数组,返回s[i]s[i+1]、···、s[j]的字节值。i的默认值为1;j的默认值为i。等价于mw.ustring.byte()

string.char

string.char( ... )

接受零个或更多个整数。返回和长度与参数个数相同的字符串,每个字符的字节值等于其对应的参数。

local value = string.char( 0x48, 0x65, 0x6c, 0x6c, 0x6f, 0x21 ) --值现在是'Hello!'

参看mw.ustring.char()以了解使用Unicode码位而非字节值的类似函数。

string.find

string.find( s, pattern, init, plain )

寻找字符串spattern的第一个匹配(match)。如果找到一个匹配,返回s中起止点的序列,否则返回nil。如果匹配模式有捕获物(capture),则在成功的匹配中被捕获的值也会被返回在两个索引之后。

第三个参数init是可选的,指定从哪里开始搜索,默认值是1,可以是负数。第四个参数plain也是可选的,如果是true,则会关闭模式匹配机制,则函数做的只是简单地“找子串”操作,pattern中的字符就不会被看作魔法字符。

注意给了plain就必须给init

参考mw.ustring.find()以了解用于Ustring pattern的类似函数,而且init是按照字符而非字节。

string.format

string.format( formatstring, ... )

返回变量数字的参数将第一个参数(必须是字符串)格式化后的版本。

格式字符串使用printf格式指定器但有所限制。

  • “-”“+”“ ”“#”和“0”是被认可的。
  • 支持最多99个整数字段。“*”不支持。
  • 支持最多99个整数精确度。“*”不支持。
  • 长度修改器不支持。
  • 认可c、d、i、o、u、x、X、e、E、f、g、G、s、%这些转换器,以及非标准的q。
  • 不支持像“%2$s”这样的位置指定器。

转换指定器q类似于s,但是会以特定的专用于表述字符串的格式来形成字符串;字符串会被写在双引号中,所有的双引号、新行、嵌入的零、反斜杠都会被转义。

数字和字符串之间的转换会像数据类型中的描述那样进行,其他类型不会自动转化为字符串。包含NUL字符的字符串(字节值0)不能被正常处理。

等同于mw.ustring.format()

string.gmatch

string.gmatch( s, pattern )

返回一个迭代器函数,每次被调用,都会从字符串s中的pattern返回下一个捕获。如果pattern不会指定捕获,则整个匹配都会在每次调用时产生。

对于这个函数,开始的^不是魔法字符,因为这个会避免迭代。它被看做是普通字符。

参看mw.ustring.gmath()以了解其类似函数,其匹配模式是被扩展的,参考Ustring匹配模式

string.gsub

string.gsub( s, pattern, repl, n )

将字符串s的所有(如果给定了n则前n个)出现的pattern替换为由repl(replacement)指定的替换物,这个替换物可以是字符串、表或者函数。gsub也会返回第二个值,是匹配到的总次数。

如果repl是一个字符串,则其值直接用于替换。字符%用作转义符(escape character):若d是1到9之间的整数,则repl中任何%d形式的序列代表第d个被捕获的子串。序列%0代表整个匹配,序列%%代表单个%

如果repl是表,则每次匹配查询这个表,其键(key)为第一个捕获物(capture);如果pattern(匹配模式)中不指定捕获物,则整个匹配用作键。

如果repl是函数,则每次匹配发生时调用这个函数,所有被捕获的子串都会依次被传入作为参数。如果匹配模式不指定捕获,则整个匹配会作为单个参数传入函数。

如果表格查询或者函数调用返回的值是字符串或者数字,则用作替换字符串,否则,如果是false或nil,则不会进行替换,也就是说原始匹配仍保留在字符串中。

参看mw.ustring.gsub()以了解其类似函数,其匹配模式是被扩展的,参考Ustring匹配模式

string.len

string.len( s )

返回字符串的字节长度。不会被ASCII NUL字符混淆。等同于#s

参看mw.ustring.len()以了解使用Unicode码位而非字节值的类似函数。

string.lower

string.lower( s )

将字符串的所有ASCII大写字符转为小写后返回。所有其他字符不改变。

参考mw.ustring.lower()以了解类似的函数,该函数可以将Unicode定义的所有的大写字母转化为小写字母。

string.match

string.match( s, pattern, init )

寻找字符串中pattern的第一个匹配,如果找到一个,则match返回pattern中的捕获物,否则返回nil。如果pattern不指定捕获物,则返回整个字符串。

第三个参数init是可选的,用来表示从哪里开始搜索,其默认值为1,可以是负数。

参看mw.ustring.match()以了解其类似函数,其匹配模式是被扩展的,参考Ustring匹配模式,而且其默认偏移(init)是按字符的,而非按字节的。

string.rep

string.rep( s, n )

返回字符串s重复n次并连接后的字符串。等价于mw.ustring.rep()

string.reverse

string.reverse( s )

返回字符串s被逆转后的字符串(按字节逆转)。

string.sub

string.sub( s, i, j )

返回s的从i开始持续到j的子串(substring);ij可以是负的。如果j是nil或未指定,则会一直到字符串末尾。

特别地,调用string.sub(s,1,j)会返回字符串s的长度j的前缀,而string.sub(s, -i)会返回字符串s的长度为i的后缀。

参看mw.ustring.sub()以了解使用字符而非字节值的类似函数。

string.upper

string.upper( s )

将字符串的所有ASCII小写字符转为大写后返回。所有其他字符不改变。

参考mw.ustring.upper()以了解类似的函数,该函数可以将Unicode定义的所有的小写字母转化为大写字母。

匹配模式(patterns)

注意Lua的匹配模式类似于正则表达式(regular expression),但是并不一样。特别地,注意正则表达式和PCRE之间的以下区别:

  • 用于引用的字符串是百分号(%)而非反斜杠(\)。
  • 点(.)总是匹配所有字符,包括新行。
  • 没有大小写不敏感模式。
  • 没有选择功能(|操作符)。
  • 量词(quantifier,*+?-)只能用于单独的字符或者字符类,不能捕获组。
  • 唯一非贪婪的量词是-,相当于PCRE的*?量词。
  • 没有广义的有限量词,比如PCRE中的{n,m}
  • 零宽的预查(assertion)有^$%f[set]“边界”模式,像PCRE中的\b(?=…)预查不可用。
  • 匹配模式本身不识别像“\ddd”这样的字符转义。然而,由于匹配模式是字符串,这类转义可以用于创建样式匹配字符串的字符串文字中。

注意匹配模式不能包含嵌入的零字节(ASCII NUL,"\0")。应该使用%z

参考Ustring匹配模式以了解使用Unicode字符的类似的样式匹配机制。

字符类

字符类是一组字符的集合。以下集合可以用于定义字符类。

x x不是魔法字符^$()%.[]*+-?之一)代表字符x自身。
. (一个点)代表所有字符。
%a 代表所有ASCII字母。
%c 代表所有ASCII控制字符。
%d 代表所有数字。
%l 代表所有ASCII小写字母。
%p 代表所有标点字符。
%s 代表所有ASCII空白字符。
%u 代表所有ASCII大写字母。
%w 代表所有ASCII字母和数字字符。
%x 代表所有十六进制数字。
%z 代表所有ASCII NUL,即零字节。
%A 所有不在%a中的字符。
%C 所有不在%c中的字符。
%D 所有不在%d中的字符、
%L 所有不在%l中的字符。
%P 所有不在%p中的字符。
%S 所有不在%s中的字符。
%U 所有不在%u中的字符。
%W 所有不在%w中的字符。
%X 所有不在%x中的字符。
%Z 所有不在%z中的字符。
%x x不是字母或数字字符)代表字符x。这是转义魔法字符的标准方式。任何标点字符(甚至是非魔法字符)都可以跟'%'用以代表样式匹配中的自身。
[set]

代表集合set中所有字符的组合。一系列字符可以通过用'-'分隔字符末尾来指定。上面讲到的所有的%x都可以是set中的一部分。set中所有其他字符代表其自身。 例如,[%w_](或[_%w])代表所有的字母数字字符加上下划线,[0-7]代表八进制数,[0-7%l%-]代表八进制数字加上小写字母加上字符'-'。

范围和类之间的交互是没被定义的,因此像[%a-z][a-%%]这样的匹配模式没有含义。

[^set] 代表set的补集。

模式条目(pattern item)

模式条目可以是

  • 单个字符类,匹配类中的单个字符。
  • '*'前的单个字符类,匹配这个类中重复0次或者更多次的字符。这个重复项总是匹配可能的最长序列。
  • '+'前的单个字符类,匹配这个类中重复一次或者更多次的字符。这个重复项总是匹配可能的最长序列。
  • '-'前的单个字符类,匹配这个类中重复0次或者更多次的字符。不像*,这个重复项总是匹配可能的最短序列。
  • '?'前的单个字符类,匹配这个类中0次或1次出现的字符。
  • %nn可以是1到9之间,匹配相当于第n个被捕获的子字符串(见下)。
  • %bxyxy是两个不同的字符,匹配从x开始、以y结尾的字符,且xy的数目相等。也就是说,如果从前向后读字符串,读到x时加一,读到y时减一,那么结尾的y是第一次计数到达0时的y。例如,%b()会匹配两边圆括号成对的字符串。
  • %f[set]边界模式,这样的模式条目匹配任何位置的空字符串,且此字符串的下一个字符属于集合set,且前一个字符不属于集合set。集合set在前面已有描述。目标字符串的开始和结尾位置按照字符'\0'处理。
    注意,边界模式存在于Lua 5.1,但是该版本的文档没有提及,Lua 5.2中才正式加入,不过Lua 5.2.1中的实现和5.1.0中的是一样的。

模式(pattern)

模式(pattern)是模式条目(pattern item)的序列。

模式开头的^会匹配字符串的开始处,模式末尾的$会匹配字符串的最末尾。其他位置的^$无特殊含义,代表自身。

捕获

模式可以包括用小括号括起来的子模式(sub-patterns),描述了“捕获”。匹配成功时,字符串的匹配捕获的子字符串会被存储(被“捕获”)以备使用。捕获是根据左边的括号被标号的。比如,在模式(a*(.)%w(%s*))中,匹配a*(.)%w(%s*)的字符串部分被存储在第一个捕获(所以是第一项),匹配.的字符被捕获,记为第2项,匹配%s*的则是第3项。

捕获可以在模式字符串中引用自身,并返回匹配中更早捕获到的文本。比如,([a-z])%1会匹配任何一对相同的小写字母,而([a-z])([a-z])([a-z])[a-z]%3%2%1会匹配任何7个字母的回文

作为特殊情况,空的捕获()会捕获当前的字符串位置(一个数字)。比如我们对字符串"flaaap"执行模式"()aa()",那么会有两个捕获:3和5。

表(table)库

这个库的大多数函数假设作为参数的表是序列

函数table.foreach()table.foreachi()table.getn()或许可用但是不推荐。应该使用pairs()的for循环、ipairs()的for循环和长度操作符。

table.concat

table.concat( table, sep, i, j )

给定一个所有元素都是字符串或数字的数组,返回table[i] .. sep .. table[i+1] …… sep .. table[j]

sep的默认值为空字符串,i的默认值为1,j的默认值为表的长度。如果i大于j,返回空字符串。

table.insert

table.insert( table, value )
table.insert( table, pos, value )

在表table的位置pos处插入元素value,会将其他元素的位置增加1以给这个元素提供插入空间。pos的默认值为表的长度加1,所以调用table.insert(t, x)会在表t的末尾插入x

会移动#table及之前的元素,参考长度操作符以了解当表不是序列时的注意事项。

table.maxn

table.maxn( table )

返回给定的表的最大正数索引,或者表没有正数索引时返回零。

会遍历整个表,大致相当于

function table.maxn( table )
    local maxn, k = 0, nil
    repeat
        k = next( table, k )
        if type( k ) == 'number' and k > maxn then
            maxn = k
        end
    until not k
    return maxn
end

table.remove

table.remove( table, pos )

从表格table中移走位于pos处的元素,将后面的元素前移。pos的默认值为表的长度,所以调用table.remove( t )会移除表t的最后一个元素。

会移动#table及之前的元素,参考长度操作符以了解当表不是序列时的注意事项。

table.sort

table.sort( table, comp )

table[1]table[#table],按照给定次序就地排序表的元素。如果给定了comp,则其必须是接收两个表元素的函数,并且当第一个小于第二个时返回true(所以not comp(a[i+1],a[i])会在排序后为true)。如果没有给定comp,则使用标准的Lua操作符<

排序算法并不稳定;也就是说,当两个元素按照给定的顺序被看作相等时,它们在排序后的相对位置可能会改变。

Scribunto库

所有Scribunto库位于表mw中。

基本函数

mw.addWarning

mw.addWarning( text )

在预览编辑时加一个警告信息。text解析为维基文本。

mw.allToString

mw.allToString( ... )

对所有的参数调用tostring(),然后用制表符作为分隔符连接起来。

mw.clone

mw.clone( value )

深拷贝一个值。所有的表和它们的元表被重构,但是新表和旧表共用函数。

mw.getCurrentFrame

mw.getCurrentFrame()

返回目前的框架对象,通常是最近的#invoke中的框架对象。

mw.incrementExpensiveFunctionCount

mw.incrementExpensiveFunctionCount()

使得“高开销解析器函数”计数器加一,并在其超过限制时抛出异常(参见$wgExpensiveParserFunctionLimit )。

mw.isSubsting

mw.isSubsting()

如果当前的#invoke替换引用时返回true,否则false。参见上方讨论的返回文本

mw.loadData

mw.loadData( module )

有时一个模块需要大量表格的数据,例如,用于转换计量单位的通用模块可能需要一张大表的已识别的单位及其转换系数。而且有时这些模块会在一个页面中使用多次。每次{{#invoke:}}都解析大表格的数据会用大量时间。mw.loadData()正是为了避免这个问题。

mw.loadData类似于require(),但是有以下区别:

  • 加载的模块每页只会解析一次,而不是每次调用{{#invoke:}}就解析一次。
  • 加载的模块不会记录在package.loaded中。
  • 所加载模块返回的值必须是表。不支持其他数据类型。
  • 返回的表及其所有子表只能包含布尔值、数字、字符串和其他表。不允许其他数据类型,尤其是函数。
  • 返回的表及其所有子表不能有元表
  • 所有的表键必须是布尔值、数字、或字符串。
  • mw.loadData()返回的表有元方法,这些元方法可以为模块返回的表提供只读访问。因为其并不直接包含数据,pairs()ipairs()会起作用,但是其他方法,包括#值next()表库的函数无法正常运作。

上述假定的单位转换模块可以将其代码存储在“Module:Convert”然后将其数据存储在“Module:Convert/data”,且“Module:Convert”会使用local data = mw.loadData( 'Module:Convert/data' )来有效地加载数据。

mw.loadJsonData

mw.loadJsonData( page )

这个和上面的mw.loadData()一样,但是会从JSON页面而非Lua表中加载数据。 JSON内容必须是数组或者对象。 参见mw.text.jsonDecode()

mw.dumpObject

mw.dumpObject( object )

object序列化为人类可读形式,返回字符串结果。

mw.log

mw.log( ... )

将参数传递到mw.allToString(),然后将结果字符串加载控制台后。

在调试控制台,print()等价于这个函数。

mw.logObject

mw.logObject( object )
mw.logObject( object, prefix )

调用mw.dumpObject()然后将结果加到控制台输出的后面。如果给定了prefix,其会被加到控制台输出,紧随一个等于号,位于序列化字符串之前,即输出的字符串会是“prefix = object字符串”。

框架对象(Frame object)

框架对象是接口,可用来访问传递至{{#invoke:}}和解析器的参数。

注意没有框架库,也没有叫做frame的全局变量。框架对象通常为被{{#invoke:}}调用的函数的参数,当然也可以从mw.getCurrentFrame()获得。

frame.args

用来访问传递到frame的参数的表。例如,一个模块从如下的维基文本调用

{{#invoke:module|function|arg1|arg2|name=arg3}}

那么frame.args[1]会返回"arg1"frame.args[2]会返回"arg2"frame.args['name']frame.args.name会返回"arg3"。在参数列表迭代可以使用pairs( frame.args )ipairs( frame.args )。 然而,由于Lua实现表迭代器的方式,迭代参数的返回顺序不确定,参数出现在维基文本中的顺序也无从得知。

注意这个表中的值总是为字符串,tonumber()可以用于在需要时转化为数字。但是,键是数字,即使在调用时清楚提供{{#invoke:module|function|1|2=2}},也只会给出被数字键12索引的字符串值"1""2"

就像在MediaWiki模板调用中那样,命名参数(named arguments)会在传递到Lua前移除名称和值的多余空格,而匿名参数(unnamed arguments)不会移除空格。

考虑到性能,frame.args使用元表,而非直接包含参数。参数值会从根据需求从MediaWiki请求得到。这意味着大多数其他表格方法不会正常生效,包括#frame.argsnext( frame.args )表库中的函数。

如果在#invoke的参数中包含了像模板调用和三层括号变量这样的预处理器(preprocessor)语法,那么这些在传递到Lua之后不会被展开,直到值被Lua请求时才会展开。如果在#invoke的参数中包含了用XML标记形式写的特殊标签,例如<pre><nowiki><gallery><ref>,这些标签会被转化为strip marker——一种特殊字符串,由删除符(ASCII 127)开始,在从#invoke返回后转化为HTML。

frame:callParserFunction

frame:callParserFunction( name, args )
frame:callParserFunction( name, ... )
frame:callParserFunction{ name = string, args = table }

注意使用了命名参数

调用解析器函数,返回适当的字符串。这相比于frame:preprocess更优,但如果有可能,最好优先使用原生的Lua函数或Scribunto库函数。

以下调用与註出的维基文本基本相同:

-- {{ns:0}}
frame:callParserFunction{ name = 'ns', args = 0 }

-- {{#tag:nowiki|some text}}
frame:callParserFunction{ name = '#tag', args = { 'nowiki', 'some text' } }
frame:callParserFunction( '#tag', { 'nowiki', 'some text' } )
frame:callParserFunction( '#tag', 'nowiki', 'some text' )
frame:callParserFunction( '#tag:nowiki', 'some text' )

-- {{#tag:ref|some text|name=foo|group=bar}}
frame:callParserFunction{ name = '#tag:ref', args = {
    'some text', name = 'foo', group = 'bar'
} }

注意,函数名称和参数不会在传递到解析器函数之前被预处理(preprocess),frame:expandTemplate()也是如此。

frame:expandTemplate

frame:expandTemplate{ title = title, args = table }

注意使用了命名参数

这是嵌入包含。函数调用

frame:expandTemplate{ title = 'template', args = { 'arg1', 'arg2', name = 'arg3' } }

在Lua脚本中的行为大致和{{template|arg1|arg2|name=arg3}}在维基文本中的行为一样。如果页面名没有命名空间前缀,那么函数会认为页面在模板命名空间内。

注意标题与参数在传递到模版之前并未预处理:

-- 这与维基文本{{template|{{!}}}}基本相同
frame:expandTemplate{ title = 'template', args = { '|' } }

-- 这与维基文本{{template|{{((}}!{{))}}}}基本相同
frame:expandTemplate{ title = 'template', args = { '{{!}}' } }

frame:extensionTag

frame:extensionTag( name, content, args )
frame:extensionTag{ name = string, content = string, args = table_or_string }

这大致相当于调用frame:callParserFunction(),其函数名称为'#tag:' .. namecontent加到参数args中。

-- 这些都相同
frame:extensionTag{ name = 'ref', content = 'some text', args = { name = 'foo', group = 'bar' } }
frame:extensionTag( 'ref', 'some text', { name = 'foo', group = 'bar' } )

frame:callParserFunction{ name = '#tag:ref', args = {
    'some text', name = 'foo', group = 'bar'
} }

-- 这些都相同
frame:extensionTag{ name = 'ref', content = 'some text', args = 'some other text' }
frame:callParserFunction{ name = '#tag:ref', args = {
    'some text', 'some other text'
} }

frame:getParent

frame:getParent()

若在由{{#invoke:}}创建的框架调用,返回调用了{{#invoke:}}的页面的框架。若在该框架调用,会返回nil。

比如,如果模板{{Example}}包含代码{{#invoke:ModuleName|FunctionName|A|B}},而另一页通过代码{{Example|C|D}}来嵌入包含该模板,那么在Module:ModuleName中,调用frame.args[1]frame.args[2]会返回"A""B",调用frame:getParent().args[1]frame:getParent().args[2]则会返回"C""D",其中frame是函数调用的第一个参数。

frame:getTitle

frame:getTitle()

以字符串形式返回与框架关联的标题。对于由{{#invoke:}}创建的框架,返回被调用的模块的标题。

frame:newChild

frame:newChild{ title = title, args = table }

注意使用了命名参数

创建新的框架对象,该对象是当前框架对象的子对象,可以有可选的参数和标题。

这个主要用于测试函数要被{{#invoke:}}调用时的调试。任何一次创建的框架的数量是受限制的。

frame:preprocess

frame:preprocess( string )
frame:preprocess{ text = string }

在当前框架环境下展开维基文本,比如,模板、解析器函数以及像{{{1}}}这样的参数都会展开。某些特殊的写成XML格式的标记,比如<pre><nowiki><gallery><ref>都会被替换为“strip marker”——一类特殊的字符串,由删除符(ASCII 127)开头,在从#invoke返回后被替换成HTML。

如果你使用单个模板,使用frame:expandTemplate而非尝试建造维基文本以使用这个方法。这样更快且更不容易出错,尤其是参数包含管道符或其他维基标记时。

同理,展开单个解析器函数时应使用frame:callParserFunction

frame:getArgument

frame:getArgument( arg )
frame:getArgument{ name = arg }

获得特定参数的对象,或者没有提供参数时返回nil。

返回的对象有一个方法,object:expand(),可以返回对应参数的展开后的维基文本。

frame:newParserValue

frame:newParserValue( text )
frame:newParserValue{ text = text }

返回有一个方法object:expand()的对象,该方法可以返回frame:preprocess( text )的结果。

frame:newTemplateParserValue

frame:newTemplateParserValue{ title = title, args = table }

注意使用了命名参数

返回有一个方法object:expand()的对象,该方法可以返回使用给定的参数调用frame:expandTemplate的结果。

frame:argumentPairs

frame:argumentPairs()

等同于pairs( frame.args )。考虑到向后兼容。

哈希库

mw.hash.hashValue

mw.hash.hashValue( algo, value )

按照指定的算法算出字符串的哈希值。可以通过mw.hash.listAlgorithms()获得有效的算法。

mw.hash.listAlgorithms

mw.hash.listAlgorithms()

返回支持的哈希算法的列表,以用于mw.hash.hashValue()

HTML库

mw.html是用于在Lua中构建复杂HTML树的友好接口。使用mw.html.create方法来创建mw.html对象。

被记为mw.html.name在全局mw.html表是可用的,被记为mw.html:namehtml:name是一个HTML对象的方法(参见mw.html.create)。

一个基础的例子如下所示:

local div = mw.html.create( 'div' )
div
     :attr( 'id', 'testdiv' )
     :css( 'width', '100%' )
     :wikitext( 'Some text' )
     :tag( 'hr' )
return tostring( div )
-- Output: <div id="testdiv" style="width:100%;">Some text<hr /></div>

mw.html.create

mw.html.create( tagName, args )

创建一个新的mw.html对象,其包含一个标签名称为tagName的HTML元素。你也可以传递一个空的字符串或nil作为tagName,以创建一个空的mw.html对象。

args可以是有以下键的表:

  • args.selfClosing:强制当前标签自我闭合,即使mw.html不认为其为自我闭合的
  • args.parent:当前mw.html实例的上级对象(用于内部使用)

mw.html:node

html:node( builder )

向当前的mw.html实例附加mw.html(builder)子节点。如果传递nil,那么函数不做任何事。(builder)节点是要创建的HTML元素的种类的字符串代表。

mw.html:wikitext

html:wikitext( ... )

给mw.html对象加上不定数量的维基文本字符串。

注意遇到第一个nil项就会停。

mw.html:newline

html:newline()

给mw.html对象加新行。

mw.html:tag

html:tag( tagName, args )

给mw.html对象添加一个新的节点,其标签名称为tagName,返回代表这个新的节点的mw.html实例。args参数和mw.html.create的参数是一样的。

注意,与其他方法如html:node()相比,这个方法不会返回当前的mw.html示例,而是新插入的标签的mw.html示例。请确保使用html:done()以获得上级的mw.html示例,或者html:allDone()以获得多层嵌套标签的最顶级实例。

mw.html:attr

html:attr( name, value )
html:attr( table )

将节点的HTML属性的name属性的值设为value。也可以选择使用表,其键值对即为属性名称-值对。第一种形式中,如果值为nil,会导致这个给定名称的已被设置属性设为未被设置。

mw.html:getAttr

html:getAttr( name )

获得已经通过html:attr()设置的属性name的值。

mw.html:addClass

html:addClass( class )

给节点的类(class)属性添加一个类名称(class name)。如果参数为nil,则无效。

mw.html:css

html:css( name, value )
html:css( table )

使用给定的namevalue给节点设置CSS属性。也可以选择使用表,其键值对即为CSS名称-值对。第一种形式中,如果值为nil,会导致这个给定名称的已被设置的CSS设为未被设置。

mw.html:cssText

html:cssText( css )

给节点的属性添加一些纯css文本。如果参数为nil,则无效。

mw.html:done

html:done()

返回当前节点被创建时所在的上级节点。类似于jQuery.end,这个方便的函数可以将多个子节点的创建串在一句语句中。

mw.html:allDone

html:allDone()

类似于html:done(),但是会遍历直到树节点的根部再返回。

语言库

语言代码在语言代码中有描述。很多MediaWiki的语言代码类似于IETF语言标签,但不是所有MediaWiki语言代码都是有效的IETF标签,反之亦然。

被记为mw.language.name在全局mw.language表是可用的,被记为mw.language:namelang:name是一个语言对象的方法(参见mw.language.newmw.language.getContentLanguage)。

mw.language.fetchLanguageName

mw.language.fetchLanguageName( code, inLanguage )

给定的语言代码的完整语言名称:默认为本地名称,如有inLanguage的值,则是被翻译为目标语言的语言名称。

mw.language.fetchLanguageNames

mw.language.fetchLanguageNames()
mw.language.fetchLanguageNames( inLanguage )
mw.language.fetchLanguageNames( inLanguage, include )

获取MediaWiki已知的语言列表,返回将语言代码映射到语言名称的表。

默认返回的名称是语言自称;传递inLanguage值会返回那个语言下的所有名称。

默认只会返回MediaWiki已知的语言名称,给include传递'all'会返回所有可用的语言(例如,从拓展:CLDR 中),传递'mwfile'会仅包括拥有在MediaWiki核心或启用的扩展包括的自定义消息的语言。如要清楚地选择默认,可以传递'mw'

mw.language.getContentLanguage

mw.language.getContentLanguage()
mw.getContentLanguage()

返回wiki当前的默认语言的语言对象。

mw.language.getFallbacksFor

备选链

mw.language.getFallbacksFor( code )

返回MediaWiki针对指定语言代码的备选语言代码列表。

mw.language.isKnownLanguageTag

mw.language.isKnownLanguageTag( code )

若语言代码为MediaWiki已知的语言,返回true。

语言代码“已知”意味着是个“有效的内置代码”(例如,对于mw.language.isValidBuiltInCode返回true),对于mw.language.fetchLanguageName返回一个非空字符串。

mw.language.isSupportedLanguage

mw.language.isSupportedLanguage( code )

检查MediaWiki的语言代码内是否有任何本地化可用。

语言“受支持”意味着是个“有效的”代码(对于mw.language.isValidCode返回true),不包含任何大写字母,并在MediaWiki的当前运行版本中有个消息文件。

一个语言代码可能既“受支持”又“未知”(例如,对mw.language.isKnownLanguageTag返回true)。还要注意,尽管mw.language.isValidBuiltInCode返回false,特定的代码仍是“受支持”的。

mw.language.isValidBuiltInCode

mw.language.isValidBuiltInCode( code )

如果语言代码是用于MediaWiki内部自定义的有效形式,返回true。

这个代码可能不对应任何已知语言。

语言代码是“有效的内置代码”意味着这是个“有效”代码(比如,对于mw.language.isValidCode返回true),只包括ASCII字母、数字和连字符,并至少两个字符长度。

注意有些代码“受支持”(即mw.language.isSupportedLanguage会返回true),即使这个函数返回false。

mw.language.isValidCode

mw.language.isValidCode( code )

若语言代码字符串是有效形式的,返回true,无论其是否存在。这包括了所有单独用于通过MediaWiki命名空间自定义使用的语言代码。

这个代码可能不对应任何已知语言。

一个语言代码有效意味着不包含任何不安全字符(逗号、单双引号、斜杠、反斜杠、尖括号、连字号或ASCII NUL)且允许出现在页面标题中。

mw.language.new

mw.language.new( code )
mw.getLanguage( code )

创建新的语言对象。语言对象没有任何可公开获取的属性,不过可以含有几个方法,下面列出。

不同语言代码在页面中的使用有次数限制。超过限制会报错。

mw.language:getCode

lang:getCode()

返回这个语言对象的语言代码。

mw.language:getFallbackLanguages

lang:getFallbackLanguages()

返回MediaWiki针对此语言对象的备选语言代码列表。等同mw.language.getFallbacksFor( lang:getCode() )

mw.language:isRTL

lang:isRTL()

若语言是从右至左写的,返回true,否则返回false。

mw.language:lc

lang:lc( s )

将字符串转化为小写,但遵从给定的语言的任何特殊规则。

当加载了ustring库mw.ustring.lower()函数就会通过调用mw.language.getContentLanguage():lc( s )来实现。

mw.language:lcfirst

lang:lcfirst( s )

将字符串的第一个字符转化为小写,就像lang:lc()那样。

mw.language:uc

lang:uc( s )

将字符串转化为大写,但遵从给定的语言的任何特殊规则。

当加载了ustring库mw.ustring.upper()函数就会通过调用mw.language.getContentLanguage():lc( s )来实现。

mw.language:ucfirst

lang:ucfirst( s )

将字符串的第一个字符转化为大写,就像lang:lc()那样。

mw.language:caseFold

lang:caseFold( s )

将字符串转换为不区分大小写的比较表示形式。注意,显示其结果可能无任何意义。

mw.language:formatNum

lang:formatNum( n )
lang:formatNum( n, options )

格式化数字,使用给定语言的合适的分组符合小数点。给予123456.78,可能会产生123,456.78、123.456,78或甚至١٢٣٬٤٥٦٫٧٨,取决于语言和wiki配置。

options是包含选项设置的表,可以是:

  • noCommafy:若为true,则忽略分组符并使用点(.)作为小数点。 数字转化仍会发生,可能包括转化数字分隔符。

mw.language:formatDate

lang:formatDate( format, timestamp, local )

根据给定的格式字符串格式化一个日期。如果省略了timestamp,默认为当前时间。local的值必须是boolean或nil,如果是true,时间会格式化为wiki的当地时间而非UTC。

格式字符串format和timestamp支持的值与扩展:解析器函数 #time解析器函数完全相同。 但是要注意,反斜杠可能需要在Lua字符串中写两次,因为Lua也使用反斜杠转义,而维基文本并不:

-- 这个字符串包含新行,不是两个字符"\n",所以不等同于{{#time:\n}}。
lang:formatDate( '\n' )

-- 等同于{{#time:\n}},不是{{#time:\\n}}。
lang:formatDate( '\\n' )

-- 等同于{{#time:\\n}},不是{{#time:\\\\n}}。
lang:formatDate( '\\\\n' )

mw.language:formatDuration

lang:formatDuration( seconds )
lang:formatDuration( seconds, chosenIntervals )

将秒数的时间间隔转化为更加可读的单位,例如12345转化为3小时25分45秒,结果作为字符串返回。

chosenIntervals是可以给定的表,其中指定了要在响应中使用的间隔单位名称。包括millenniacenturiesdecadesyearsweeksdayshoursminutesseconds

mw.language:parseFormattedNumber

lang:parseFormattedNumber( s )

这需要一个以lang:formatNum()格式化的数字,并返回实际数字。换句话说,这基本上是tonumber()能根据语言转换的版本。

mw.language:convertPlural

lang:convertPlural( n, ... )
lang:convertPlural( n, forms )
lang:plural( n, ... )
lang:plural( n, forms )

forms(必须是序列表)或...基于数字n选择合适的语法形式。例如,在英语你必须使用n .. ' ' .. lang:plural( n, 'sock', 'socks' )n .. ' ' .. lang:plural( n, { 'sock', 'socks' } )来生成语法正确的文本:只有1 sock或200 socks。

序列的必填值取决于语言,详见魔术字的本地化translatewiki针对PLURAL的常见问题

mw.language:convertGrammar

lang:convertGrammar( word, case )
lang:grammar( case, word )

注意两个别称之间的不同参数顺序。convertGrammar匹配MediaWiki的Language对象的相同名称的方法的顺序,而grammar匹配相同名称的解析器函数的顺序,可参考帮助:魔术字#本地化

这会选择给定的变形码case的合适的word变形形式。

wordcase的可能的值是依赖于语言的,参见Special:MyLanguage/Help:Magic words#Localisationtranslatewiki:语法以了解详情。

mw.language:gender

lang:gender( what, masculine, feminine, neutral )
lang:gender( what, { masculine, feminine, neutral } )

选择对应what的性别的字符串,可以是male、female或注册的用户名。

mw.language:getArrow

lang:getArrow( direction )

返回对应方向direction的Unicode箭头字符:

  • forwards:“→”或“←”,取决于语言的书写方向。
  • backwards:“←”或“→”,取决于语言的书写方向。
  • left:“←”
  • right:“→”
  • up:“↑”
  • down:“↓”

mw.language:getDir

lang:getDir()

根据语言的书写方向,返回"ltr"(从左至右)或"rtl"(从右至左)。

mw.language:getDirMark

lang:getDirMark( opposite )

返回包含U+200E(从左至右标记)或U+20F(从右至左标记)的字符串,取决于语言的书写方向以及opposite的值。

mw.language:getDirMarkEntity

lang:getDirMarkEntity( opposite )

返回“&lrm;”或“&rlm;”,取决于语言的书写方向和opposite的值。

mw.language:getDurationIntervals

lang:getDurationIntervals( seconds )
lang:getDurationIntervals( seconds, chosenIntervals )

将秒数的时间间隔转化为更加已读的单位,例如12345转化为3 hours, 25 minutes and 45 seconds(3时25分45秒),返回从单位名称映射到数字的表。

chosenIntervals,如提供,是一个指定了要在响应中使用的间隔单位名称的表(table)。包括millenniacenturiesdecadesyearsweeksdayshoursminutesseconds

这些单元关键字也是响应表中使用的键。响应中仅设置具有非零值的单位,除非响应为空,在这种情况下,返回值为0的最小单位。

系统消息库

这个库是本地化消息和MediaWiki:命名空间的接口。

写成mw.message.name的函数在全局mw.message表中可用,写成mw.message:namemsg:name的函数是一个消息对象的方法(参见mw.message.new)。

mw.message.new

mw.message.new( key, ... )

创建一个给定消息key的新消息对象。 剩下的参数传递到新的对象的params()的方法。

消息对象没有属性,但是有下面列出的几个方法。

mw.message.newFallbackSequence

mw.message.newFallbackSequence( ... )

从给定的一个或多个消息创建一个新的消息对象(会使用第一个存在的消息)。

消息对象没有属性,但有下面列出的几种方法。

mw.message.newRawMessage

mw.message.newRawMessage( msg, ... )

创建新的消息对象,直接使用给定的文本,而不是查找国际化的消息。剩下的参数会传递到新对象的params()方法中。

消息对象没有属性,但是有下面列出的几个方法。

mw.message.rawParam

mw.message.rawParam( value )

包装(wrap)该值使之不会被msg:parse()解析为维基文本。

mw.message.numParam

mw.message.numParam( value )

包装(wrap)该值使之自动被格式化为lang:formatNum()。注意这不依赖事实上有效的语言库

mw.message.getDefaultLanguage

mw.message.getDefaultLanguage()

返回默认语言的语言对象。

mw.message:params

msg:params( ... )
msg:params( params )

给消息添加参数,参数可以被传递为独立的参数,或者传递为序列表。参数必须是数字、字符串或由mw.message.numParam()mw.message.rawParam()返回的特殊的值。如果使用序列表,参数必须是直接出现在表中,使用__index元方法的引用不起作用。

返回msg对象,以允许链式调用。

mw.message:rawParams

msg:rawParams( ... )
msg:rawParams( params )

:params(),但是会先通过mw.message.rawParam()传递所有的参数。

返回msg对象,以允许链式调用。

mw.message:numParams

msg:numParams( ... )
msg:numParams( params )

:params(),但是会先通过mw.message.numParam()传递所有的参数。

返回msg对象,以允许链式调用。

mw.message:inLanguage

msg:inLanguage( lang )

指定一个语言,以在加工消息时使用。lang可以是字符串,或者带有getCode()方法的表(比如语言对象)。

默认的语言是由mw.message.getDefaultLanguage()返回的。

返回msg对象,以允许链式调用。

mw.message:useDatabase

msg:useDatabase( bool )

指定是否在MediaWiki:命名空间查找消息(比如在数据库中查找),或者只是使用MediaWiki分配的默认消息。

默认为true。

返回msg对象,以允许链式调用。

mw.message:plain

msg:plain()

替换参数并按原样返回消息维基文本。模板调用和解析器函数都是完整的。

mw.message:exists

msg:exists()

返回表示消息键是否存在的布尔值。

mw.message:isBlank

msg:isBlank()

返回表示消息键是否有内容的布尔值。当消息键不存在或者消息是空字符串时返回true。

mw.message:isDisabled

msg:isDisabled()

返回表示消息键是否被禁用的布尔值。如果消息键不存在,或者消息是空字符串,或者是字符串"-"则返回true。

站点信息库

mw.site.currentVersion

包含当前MediaWiki版本信息的字符串值。

mw.site.scriptPath

$wgScriptPath 的值。

mw.site.server

$wgServer 的值。

mw.site.siteName

$wgSitename 的值。

mw.site.stylePath

$wgStylePath 的值。

mw.site.namespaces

包含所有命名空间数据的表,由数字索引。

可用的数据为:

  • id:命名空间数字。
  • name:本地命名空间名称。
  • canonicalName:规范(canonical)命名空间名称。
  • displayName:为命名空间0设置,用于显示的名称(因为这个名称通常是空白字符串)。
  • hasSubpages:这个命名空间是否启用了子页面。
  • hasGenderDistinction:这个命名空间在不同的性别是否有不同的别称。
  • isCapitalized:这个命名空间的页面的第一个字母是否会转化为大写。
  • isContent:这个命名空间是否为内容命名空间。
  • isIncludable:这个命名空间的页面是否可以被嵌入包含。
  • isMovable:这个命名空间的页面是否可以移动。
  • isSubject:这个命名空间是否为一个主题命名空间(subject namespace)。
  • isTalk:这个命名空间是否为讨论页。
  • defaultContentModel:这个命名空间的默认内容模型(字符串)。
  • aliases:这个命名空间的别称的列表。
  • subject:对应主题命名空间的数据。
  • talk:对应讨论页命名空间的数据。
  • associated:与之有关联的命名空间的数据。

设置了元表以便于按照名称(本地化的或者规范的)查找命名空间。比如,mw.site.namespaces[4]mw.site.namespaces.Project都会返回关于Project命名空间的信息。

mw.site.contentNamespaces

只包含内容命名空间的表,按数字索引。参考mw.site.namespaces以了解详细内容。

mw.site.subjectNamespaces

只包含主题命名空间的表,按数字索引。参考mw.site.namespaces以了解详细内容。

mw.site.talkNamespaces

只包含讨论命名空间的表,按数字索引。参考mw.site.namespaces以了解详细内容。

mw.site.stats

包含站点统计的表。可用的统计有:

  • pages:wiki的页面数量。
  • articles:wiki的文章数量。
  • files:wiki的文件数量。
  • edits:wiki的编辑次数。
  • users:wiki的用户数量。
  • activeUsers:wiki的活跃用户数量。
  • admins:wiki中sysop用户组的用户数量。

mw.site.stats.pagesInCategory

mw.site.stats.pagesInCategory( category, which )

这个函数是高开销函数

获得关于分类的统计数据。如果which拥有指定值"*",则结果为有如下内容的表:

  • all:总共的页面、文件和子分类。
  • subcats:子分类的数量。
  • files:文件的数量。
  • pages:页面的数量。

如果which是以上键(all、subcats、files、pages)中的一个,则结果为对应值的数字。

每次新的分类查询都会增大高开销函数次数。

mw.site.stats.pagesInNamespace

mw.site.stats.pagesInNamespace( ns )

返回给定的命名空间(由数字指定)的页面数量。

mw.site.stats.usersInGroup

mw.site.stats.usersInGroup( group )

返回给定的用户组的用户数量。

mw.site.interwikiMap

mw.site.interwikiMap( filter )

返回包含可用的跨wiki前缀的数据的表。如果filter是字符串"local",则只会返回关于本地跨wiki前缀的数据。如果filter是字符串"!local",则只会返回非本地跨wiki前缀的数据。如果没有指定filter,则所有的前缀的数据都会返回。这个"local"前缀是同一个项目的。比如在英文维基百科,其他语言的维基百科都会考虑为本地的(local),而维基词典不会。

这个函数返回的表的键是跨wiki前缀,值是带有以下属性的子表:

  • prefix——跨wiki前缀。
  • url——跨wiki指向的URL。页面名称由参数$1体现。
  • isProtocalRelative——布尔值,显示URL是否为URL
  • isLocal——这个URL是否是当前wiki的站点的。
  • isCurrentWiki——这个URL是否是为当前wiki的。
  • isTranscludable——使用这个跨wiki前缀的页面能否嵌入包含。这个要求scary transclusion,而这在维基媒体的wiki是禁用的。
  • isExtraLanguageLink——跨wiki是否列举在了$wgExtraInterlanguageLinkPrefixes
  • displayText——对于列举在$wgExtraInterlanguageLinkPrefixes中的链接,跨语言链接显示的文本。未指定时为nil。
  • tooltip——对于列举在$wgExtraInterlanguageLinkPrefixes的链接,当用户悬浮在跨语言链接上时显示的浮窗提示文本。未指定时为nil。

文本库

文本库提供了一些常见的文本处理函数,这些函数是字符串库Ustring库中缺少的,并且可用于UTF-8字符串。

mw.text.decode

mw.text.decode( s )
mw.text.decode( s, decodeNamedEntities )

将字符串中的HTML元素替换为对应的字符。

如果布尔值decodeNameEntities被省略或者为false,则只有被命名的实体“&lt;”“&gt;”“&amp;”“&quot;”和“&nbsp;”会被认可。否则,认可的HTML5命名实体的列表会从PHP的get_html_translation_table函数中加载。

mw.text.encode

mw.text.encode( s )
mw.text.encode( s, charset )

使用HTML实体替换字符串中的字符。字符“<”“>”“&”“"”和非换行空格会被适当的命名实体替换,所有其他的都会被替换为数字实体。

如果提供了charset,则它应该是合适的字符串,能直接由Ustring模式的括号括住,比如[set]中的“set”。默认的charset是'<>&"\' '(末尾的空格为非换行空格,U+00A0)。

mw.text.jsonDecode

mw.text.jsonDecode( s )
mw.text.jsonDecode( s, flags )

解析JSON字符串。flags是0或flagsmw.text.JSON_PRESERVE_KEYSmw.text.JSON_TRY_FIXING的组合(使用+)。

通常JSON的数组会被重组到Lua的有键的序列表,如果要避免这个,可以传入mw.text.JSON_PRESERVE_KEYS

如果要降低JSON的特定需求,比如数组或对象没有结尾的逗号,可传入mw.text.JSON_TRY_FIXING。不推荐这样做。

限制:

  • 如果数组含有null值,解析的JSON数组可能不是Lua序列。
  • JSON对象会丢弃含有null值的键。
  • 不能直接分辨带有序列整数键的是JSON数组还是JSON对象。
  • 带有序列整数键从1开始的的JSON对象会被解析到作为有相同值的JSON数组的相同的表结构,尽管这些不一定总是相等,除非使用了mw.text.JSON_PRESERVE_KEYS

mw.text.jsonEncode

mw.text.jsonEncode( value )
mw.text.jsonEncode( value, flags )

编码JSON字符串。如果传入的值不能被JSON编码则会报错。flags是0或flagsmw.text.JSON_PRESERVE_KEYSmw.text.JSON_PRETTY的组合(使用+)。

通常的Lua中从1开始的序列表会编码成JSON中从0开始的数组;如果flags中设置了mw.text.JSON_PRESERVE_KEYS,从0开始的序列表会编码成JSON数组。

限制:

  • 空的表总是会被编码为空的数组([]),而不是空的对象({})。
  • 如果没有加入无效(dummy)元素,序列表不能被编码为JSON对象。
  • 如果要产生带有nil值的对象,需要设置__pairs元方法。
  • 带有从0开始的序列整数键的Lua表会被编码为JSON数组,带有从1开始的整数键的Lua表也是如此,除非使用了mw.text.JSON_PRESERVE_KEYS
  • 如果同一个表中,既使用了数字,又使用了代表那个数字的字符串作为键,则行为未指定。

mw.text.killMarkers

mw.text.killMarkers( s )

除去字符串中所有的strip marker

mw.text.listToText

mw.text.listToText( list )
mw.text.listToText( list, separator, conjunction )

使用自然语言组合一个列表,类似于table.concat(),但是最后一项前面的分隔符不同。

默认的分隔符取自wiki内容语言的MediaWiki:comma-separator,默认的连词取自由MediaWiki:word-separator连接的MediaWiki:and

比如,对消息使用默认值:

 -- 返回空字符串
 mw.text.listToText( {} )
 
 -- 返回"1"
 mw.text.listToText( { 1 } )
 
 -- 返回"1和2"(如果wiki的语言设置为“中文”,下同)
 mw.text.listToText( { 1, 2 } )
 
 -- 返回"1、2、3、4和5"
 mw.text.listToText( { 1, 2, 3, 4, 5 } )
 
 -- 返回"1; 2; 3; 4 or 5"
 mw.text.listToText( { 1, 2, 3, 4, 5 }, '; ', ' or ' )

mw.text.nowiki

mw.text.nowiki( s )

使用HTML实体替换字符串中的多种字符,以避免被解析为维基文本。包括:

  • 以下字符:"&'<=>[]{|}
  • 字符串开头或新行后面的的以下字符:#*:;、空格、制表符('\t')
  • 空行会有关联的新行或返回转义的字符(Blank lines will have one of the associated newline or carriage return characters escaped)
  • 字符串开始处或新行后的的----会使得第一个-被转义
  • __会使一个下划线被转义
  • ://会使冒号被转义
  • ISBNRFCPMID后的空白字符会被转义

mw.text.split

mw.text.split( s, pattern, plain )

在匹配了Ustring模式pattern的地方分割字符串。如果指定了plain且为true,则pattern会被解释为纯字符串而不是Lua模式(就像mw.ustring.find()的这个参数一样)。返回包含子字符串的表。

比如,mw.text.split( 'a b\tc\nd', '%s' )会返回表{ 'a', 'b', 'c', 'd' }

如果pattern匹配空字符串,s会被分割成单个字符。

mw.text.gsplit

mw.text.gsplit( s, pattern, plain )

返回会迭代子字符串的迭代器函数,返回的相当于调用mw.text.split()

mw.text.tag

mw.text.tag( name, attrs, content )
mw.text.tag{ name = string, attrs = table, content = string|false }

注意使用了命名参数

生成name的HTML样式的标签。

如果指定了attrs,则必须是带有字符串键的表。字符串和数字值都会被使用为属性的值,布尔值的true会被输出为HTML5的无值参数,布尔值的false会跳过这个键,其他的都会错误。

如果没有给出content(或者是nil),只会返回开始标签。如果content是布尔值false,则会返回自我闭合标签。否则它必须是字符串或数字,这样内容就会被开始标签和结束标签包围。注意内容不会自动被编码为HTML,如有需要,使用mw.text.encode()

对于返回扩展标签的属性,比如‎<ref>,应该使用frame:extensionTag()

mw.text.trim

mw.text.trim( s )
mw.text.trim( s, charset )

从字符串的开始和结尾移除空白字符或其他字符。

如果提供了charset,则它应该是合适的字符串,能直接由Ustring模式的括号括住,比如[set]中的“set”。默认的charset是ASCII空白字符,"\t\r\n\f "

mw.text.truncate

mw.text.truncate( text, length )
mw.text.truncate( text, length, ellipsis )
mw.text.truncate( text, length, ellipsis, adjustLength )

将文本text裁剪到指定的长度,如果执行了裁剪,加入ellipsis(省略号)。如果长度为正,则字符串末尾被裁剪,如果为负,则字符串的开头会被裁剪。如果给定了adjustLength且为true,则包含省略号的字符串不会长于指定的长度。

ellipsis的默认值取自wiki的内容语言的MediaWiki:ellipsis

例如,使用默认的“...”省略号:

-- 返回"foobarbaz"
mw.text.truncate( "foobarbaz", 9 )

-- 返回"fooba..."
mw.text.truncate( "foobarbaz", 5 )

-- 返回"...arbaz"
mw.text.truncate( "foobarbaz", -5 )

-- 返回"foo..."
mw.text.truncate( "foobarbaz", 6, nil, true )

-- 返回"foobarbaz",因为比"foobarba..."更短
mw.text.truncate( "foobarbaz", 8 )

mw.text.unstripNoWiki

mw.text.unstripNoWiki( s )

将MediaWiki的<nowiki> strip marker替换为对应的文本。其他类型的strip marker不会改变。

mw.text.unstrip

mw.text.unstrip( s )

等同于mw.text.killMarkers( mw.text.unstripNoWiki( s ) )

这个不再显示特殊页面嵌入包含、<ref>后的HTML,就像Scribunto早期版本的那样。

标题库

mw.title.equals

mw.title.equals( a, b )

检测两个标题是否相等。注意比较时忽略片段(fragments)。

mw.title.compare

mw.title.compare( a, b )

返回-1、0、1,表示标题a是否小于、等于或大于标题b

这会按照作为字符串的跨wiki前缀(如果有)比较,然后按命名空间数字,然后按照作为字符串的未加前缀的标题文本比较。字符串比较使用Lua标准的<操作符。

mw.title.getCurrentTitle

mw.title.getCurrentTitle()

返回当前页面的标题对象。

mw.title.new

mw.title.new( text, namespace )
mw.title.new( id )

用ID调用时该函数是高开销函数

创建新的标题对象。

如果给了id,则那个带有那个page_id的页面的对象会被创建。引用的标题会被认为链接到了当前页面。如果page_id不存在,返回nil。如果创建的标题对象不是对于已经加载的标题,则高开销函数数量会被增加。

然而,如果给了字符串text,那么就会创建那个标题的对象(即使那个页面不存在)。如果文本字符串text不指定命名空间,则使用namespace(可以是在mw.site.namespaces中找到的任何键)。如果文本不是有效的标题,则会返回nil。

mw.title.makeTitle

mw.title.makeTitle( namespace, title, fragment, interwiki )

创建命名空间namespace内标题title的标题对象,可以有指定的片段fragment和跨wiki前缀interwikinamespace可以是在mw.site.namespaces中找到的任何键。如果结果的标题无效,返回nil。

注意,不像mw.title.new(),这个方法总会应用指定的命名空间。比如,mw.title.makeTitle( 'Template', 'Module:Foo' ) 会为页面Template:Module:Foo创建对象,而mw.title.new( 'Module:Foo', 'Template' )会为页面Module:Foo创建对象。

标题对象

标题对象有许多属性和方法。大多数属性是只读的。

注意以text结尾的字段返回作为字符串值的标题,而以title结尾的字段返回标题对象。

  • id:页面ID。页面不存在时为0注意可能有高开销.
  • interwiki:跨wiki前缀,如果无,则为空白字符串。
  • namespace:命名空间数字。
  • fragment:片段(也叫段落/锚点连接),或者空字符串。可以赋值。
  • nsText:页面的命名空间的文本。
  • subjectNsText:页面的主题命名空间的文本。
  • text:页面标题,不含命名空间和跨wiki前缀。
  • prefixedText:页面标题,带有命名空间和跨wiki前缀。
  • fullText:页面标题,包括命名空间、跨wiki前缀和片段。如果跨wiki等于当前wiki则不返回跨wiki。
  • rootText:如果这是子页面,根页面的标题,不带前缀。否则,等同于title.text
  • baseText:如果这是子页面,则这个子页面的上级页面的标题,不带前缀。否则,等同于title.text
  • subpageText:如果这是个子页面,子页面名称。否则,和title.text相同。
  • canTalk:这个标题的页面能否拥有讨论页。
  • exists:该页面是否存在。Media命名空间页面的file.exists别名。对于File命名空间的页面,这将检查文件描述页面的存在,而不是文件本身。此函数可能为高开销
  • filefileExists:参见下面的#文件元数据
  • isContentPage:这个页面是否在内容命名空间内。
  • isExternal:此页面是否具有跨wiki的前缀。
  • isLocal:此页面是否在此项目中。例如,在英语维基百科上,任何其他语言维基百科都被视为“本地”(Local),而维基字典等则被视为非“本地”。
  • isRedirect:是否是重定向页面的标题。此函数可能为高开销
  • isSpecialPage:该页面是否可能是特殊页面(即“Special”命名空间中的页面)。
  • isSubpage:该页面是否为其他页面的子页面。
  • isTalkPage:该页面是否为讨论页。
  • isSubpageOf( title2 ):该页面是否为给定页面的子页面。
  • inNamespace( ns ):该页面是否在给定的命名空间中。命名空间可以由在mw.site.namespaces中定义的任何键指定。
  • inNamespaces( ... ):标题是否在给定的命名空间中的任何一个中间。命名空间可以由在mw.site.namespaces中定义的任何键指定。
  • hasSubjectNamespace( ns ):标题的主题命名空间是否在指定的命名空间内。命名空间必可以由在mw.site.namespaces中定义的任何键指定。
  • contentModel:此标题的内容模型,字符串,可能会造成高开销
  • basePageTitle:等同于mw.title.makeTitle( title.namespace, title.baseText )
  • rootPageTitle:等同于mw.title.makeTitle( title.namespace, title.rootText )
  • talkPageTitle:等同于mw.title.makeTitle( mw.site.namespaces[title.namespace].talk.id, title.text ),如果该标题没有讨论页则为nil
  • subjectPageTitle:等同于mw.title.makeTitle( mw.site.namespaces[title.namespace].subject.id, title.text )
  • redirectTarget:如果页面是重定向且页面存在,返回重定向页面目标的标题对象,否则返回false
  • protectionLevels:页面的保护等级。其值为一个表,键对应每个操作(如"edit""move"),值为数组,第一项是包含对应保护等级的字符串,如果页面未被保护,则表的值或者数组的项就会为nil此函数高开销
  • cascadingProtection:该页面应用的级联保护,为一个表,键为"restrictions"(自身为一个表,键类似于protectionLevels拥有的那些)和"sources"(列举保护级联来源的标题的数组)。如果没有保护级联到此页面,则"restrictions""sources"可能是空的。此函数高开销
  • subPageTitle( text ):等同于mw.title.makeTitle( title.namespace, title.text .. '/' .. text )
  • partialUrl():返回编码的title.text,就像在URL中会有的那样。
  • fullUrl( query, proto )::返回此标题的完整URL(带有可选的查询表或字符串query)。可以指定proto以控制返回URL的协议:"http""https""relative"(默认值)或"canonical"
  • localUrl( query ):返回此标题的本地的URL(带有可选的查询表或字符串query)。
  • canonicalUrl( query ):返回此标题的规范URL(带有可选的查询表或字符串query)。
  • getContent():返回页面的(未解析的)内容,如果页面不存在则返回nil。页面会被记录为嵌入包含。

标题对象可以使用关系运算符比较。tostring( title )会返回title.prefixedText

由于这可能难以置信,所以请注意获取标题对象的任何高开销的字段会记录一次对页面的“链入”(就像Special:链入页面中展示的那样)。使用标题对象的getContent()方法或访问redirectTarget字段会记录为“嵌入”,访问标题对象的filefileExists字段会记录为“文件链接”。

文件元数据

代表File或Media命名空间下的页面的标题对象会拥有称为file属性。这是高开销的。这是一个表,其结构如下:

  • exists:文件是否存在。会记录一次图像使用。标题对象的fileExists属性的存在是为了考虑向后兼容,可以看做该属性的别称。如果这是false,所有其他属性都会是nil
  • width:文件宽度。若文件有多个页面,第一页的宽度。
  • height:文件的高度。若文件有多个页面,第一页的高度。
  • pages:如果文件格式支持多页,这是包含文件每个页面的表,否则为nil“#”操作符可以用于获取文件页数。每个单独的页面表都包含一个width和height属性。
  • size:文件的字节长度。
  • mimeType:文件的MIME类型
  • length:媒体文件的长度,单位为秒。不支持长度的媒体则为0。

高开销属性

属性idisRedirectexistscontentModel需要从数据库获取标题数据。因此,第一次获取除了当前页面之外的任何页面的这些属性中的一个时,高开销函数数量会增加。之后获取那个页面的这些属性中的任何一个都不会再次增加高开销函数数量。

其他标记为高开销的属性总是会在第一次获取除当前页面之外的页面时增加高开销函数数量。

URI库

mw.uri.encode

mw.uri.encode( s, enctype )

百分号编码字符串。默认类型,"QUERY",使用“+”编码空格以用于查询字符;"PATH"将空格编码为%20,"WIKI"将空格编码为“_”。

注意"WIKI"格式不是完全可以逆转的,因为空格和下划线都会编码为“_”。

mw.uri.decode

mw.uri.decode( s, enctype )

百分号解码字符串。默认类型,"QUERY",将“+”解码为空格;"PATH"不执行额外的解码,"WIKI"将“_”解码为空格。

mw.uri.anchorEncode

mw.uri.anchorEncode( s )

编码字符串以用于MediaWiki URI片段。

mw.uri.buildQueryString

mw.uri.buildQueryString( table )

将表编码为URI查询字符串。键必须是字符串,值可以是字符串、数字、序列表或布尔值false。

mw.uri.parseQueryString

mw.uri.parseQueryString( s, i, j )

将查询字符串s解码为表。字符串中没有值的键会拥有false值,重复多次的键会有序列表作为其值,其他的都会有字符串作为值。

可选的数字参数ij可以用于指定要解析的s的子串,而非整个字符串。i是子串的第一个字符位置,默认为1。j是子串的最后一个字符位置,默认为字符串长度。ij都可以是负数,就像string.sub那样。

mw.uri.canonicalUrl

mw.uri.canonicalUrl( page, query )

返回页面的规范URLURI对象,附带可选的查询字符串或表。

mw.uri.fullUrl

mw.uri.fullUrl( page, query )

返回页面的完整URLURI对象,可选查询字符串或表。

mw.uri.localUrl

mw.uri.localUrl( page, query )

返回页面的本地URLURI对象,可选查询字符串或表。

mw.uri.new

mw.uri.new( s )

利用传入的字符串或表构造一个新的URI对象。参见URI对象的描述以了解表可能的字段。

mw.uri.validate

mw.uri.validate( table )

验证传入的表(或URI对象)是否有效。返回布尔值,以表示表是否有效,以及如果无效,一个描述问题发生原因的字符串。

URI对象

URI对象有以下字段,其中的部分或全部可能是nil:

  • protocol:字符串 协议
  • user:字符串 用户
  • password:字符串 密码
  • host:字符串 主机名
  • port:整数 端口
  • path:字符串 路径
  • query:表,就像从mw.uri.parseQueryString中的那样
  • fragment:字符串 片段

以下属性也是可用的:

  • userInfo:字符串 用户和密码
  • hostPort:字符串 主机和端口
  • authority:字符串 用户、密码、主机、端口
  • queryString::字符串 查询表的字符串版本
  • relativePath:字符串 路径、查询字符串、分段

tostring()会产生URI字符串。

URI对象的方法为:

mw.uri:parse

uri:parse( s )

将字符串解析进当前的URI对象。字符串中指定的任何字段都会替换进当前对象;未指定的字段会保留旧值。

mw.uri:clone

uri:clone()

制作URI对象的拷贝。

mw.uri:extend

uri:extend( parameters )

将参数表合并到对象的查询表。

ustring库

ustring库相当于标准字符串库的重新实现,不过方法会操作于UTF-8编码的字符串的字符,而非字节。

大多数函数会在字符串不是有效的UTF-8时报错,但有例外。

mw.ustring.maxPatternLength

匹配模式(pattern)允许的最大长度,以字节为单位。

mw.ustring.maxStringLength

字符串允许的最大长度,以字节为单位。

mw.ustring.byte

mw.ustring.byte( s, i, j )

返回单独的字节,等同于string.byte()

mw.ustring.byteoffset

mw.ustring.byteoffset( s, l, i )

返回字符在字符串内的字节偏移值。li的默认值都是1.i可以是负数,即倒过来计数。

位于l == 1的字符是从第i字节或该字节之后开始的第一个字符,位于l == 0的字符是从第i字节或该字节之前开始的第一个字符。注意这可能是同一个字符。更大或者更小的l值都是相对这些进行计算的。

mw.ustring.char

mw.ustring.char( ... )

非常像string.char(),但是整数为Unicode码位而非字节值。

local value = mw.ustring.char( 0x41f, 0x440, 0x438, 0x432, 0x435, 0x442, 0x21 ) -- 值现在为“Привет!“

mw.ustring.codepoint

mw.ustring.codepoint( s, i, j )

很像string.byte(),但是返回的值是码位,且偏移值为字符而非字节。

mw.ustring.find

mw.ustring.find( s, pattern, init, plain )

非常像string.find(),但是匹配模式是扩展了的,在Ustring patterns中有描述,且init是按照字符而非字节。

mw.ustring.format

mw.ustring.format( format, ... )

等同于string.format()。字符串的宽度和精度是以字节表达,而非码位。

mw.ustring.gcodepoint

mw.ustring.gcodepoint( s, i, j )

返回用于在字符串内迭代代码点的三个值。i默认为1,j默认为-1。这是为了用于for形式的迭代

for codepoint in mw.ustring.gcodepoint( s ) do
     -- 语句块
end

mw.ustring.gmatch

mw.ustring.gmatch( s, pattern )

非常像string.gmatch(),但是匹配模式是扩展了的,在ustring匹配模式中有描述。

mw.ustring.gsub

mw.ustring.gsub( s, pattern, repl, n )

非常像string.gsub(),但是匹配模式是扩展了的,在ustring匹配模式中有描述。

mw.ustring.isutf8

mw.ustring.isutf8( s )

若字符串是有效的UTF-8,返回true,否则返回false。

mw.ustring.len

mw.ustring.len( s )

返回字符串的码位长度,或者不是有效的UTF-8时返回nil。

参见string.len()以了解使用字节长度而非码位的类似函数。

mw.ustring.lower

mw.ustring.lower( s )

非常像string.lower(),但是所有在Unicode中定义了小写到大写转换的字符都会被转换。

如果加载了语言库,则这会调用默认语言对象的lc()

mw.ustring.match

mw.ustring.match( s, pattern, init )

非常像string.match(),但是匹配模式是扩展了的,在ustring匹配模式中有描述,且init偏移是按照字符而非字节。

mw.ustring.rep

mw.ustring.rep( s, n )

等同于string.rep()

mw.ustring.sub

mw.ustring.sub( s, i, j )

非常像string.sub(),但是偏移值为字符而非字节。

mw.ustring.toNFC

mw.ustring.toNFC( s )

将字符串转化为正规形式C。如果字符串不是有效的UTF-8则返回nil。

mw.ustring.toNFD

mw.ustring.toNFD( s )

将字符串转化为正规形式D。如果字符串不是有效的UTF-8则返回nil。

mw.ustring.upper

mw.ustring.upper( s )

非常像string.upper(),但是所有在Unicode中定义了大写到小写转换的字符都会被转换。

如果加载了语言库,则这会调用默认语言对象的uc()

ustring匹配模式

ustring函数中的匹配模式使用和字符串库匹配模式相同的语法。主要区别是,字符串类会根据Unicode字符属性重新定义。

  • %a:代表一般类别“字母”中的所有字符。
  • %c:代表一般类别“控制”中的所有字符。
  • %d:代表一般类别“数字、十进制数字”中的所有字符。
  • %l:代表一般类别“小写字母”中的所有字符。
  • %p:代表一般类别“标点”中的所有字符。
  • %s:代表一般类别“分隔符”以及制表符、换行、回车、垂直制表符和换页符中的所有字符。
  • %u:代表一般类别“大写字母”中的所有字符。
  • %w:代表一般类别“字母”或“十进制数字”中的所有字符。
  • %x:加上十六进制数字的全宽字符。

就像在字符串库匹配模式中的那样,%A, %C, %D, %L, %P, %S, %U%W代表所有补集(不在给定的一般类别内的所有字符)。

在所有情况下,字符串解析为Unicode字符而非字节,所以像[0-9]这样的范围、%b«»这样的模式匹配和量词应用在多字节字符时可以正确起作用。空的捕获会捕获码位位置而非字节。

可加载的库

这些库默认不包括,但是如有需要可通过require()加载。

bit32

可以通过以下方式模拟加载Lua 5.2 bit32

bit32 = require( 'bit32' )

bit32库提供了无符号的32位整数的位运算。输入的整数会被截成整数(方法未指定)并用232模除,这样值就是在0到232-1之间的,返回的值也会是这个范围。

当位有编号时(就像bit32.extract()中那样),0是最低位(带有值20的),31是最高位(带有值231的)。

bit32.band

bit32.band( ... )

返回参数的“按位与”运算:对于结果中的每一位,只有当所有参数中都设置了该位时,结果中才会有这一位。

如果没有给参数,则结果会设置所有位。

bit32.bnot

bit32.bnot( x )

返回x的“取反”。

bit32.bor

bit32.bor( ... )

返回参数的“按位或”运算:对于结果中的每一位,只要所有参数中有任何一个设置了该位,结果中就会有这一位。

如果没有给参数,则结果会清除所有位。

bit32.btest

bit32.btest( ... )

等同于bit32.band( ... ) ~= 0

bit32.bxor

bit32.bxor( ... )

返回参数的“按位异或”运算:对于结果中的每一位,只有所有参数中设置了该位的参数个数为奇数,结果中才会有这一位。

如果没有给参数,则结果会清除所有位。

bit32.extract

bit32.extract( n, field, width )

n导出width位,以field为开始。访问0到31范围之外的位都是错误的。

如未指定,width默认为1。

bit32.replace

bit32.replace( n, v, field, width )

从第field位元开始,将n中的width个位元替换为v最低的width个位元。访问0到31范围之外的位元有错误。

如未指定,width默认为1。

bit32.lshift

bit32.lshift( n, disp )

返回数n向左移动disp位。这是一个逻辑移位:插入的位为0。这通常相当于乘以2的disp次方。

请注意,超过31的位移将导致0。

bit32.rshift

bit32.rshift( n, disp )

返回数n向右移动disp位。这是一个逻辑移位:插入的位为0。这通常相当于除以2的disp次方。

请注意,超过31的位移将导致0。

bit32.arshift

bit32.arshift( n, disp )

返回数n向右移动disp位。这是一个算数移位:如果disp是正的,那么插入的位将会与原始数字的31位相同。

注意超过31的移位将会导致0或4294967295。

bit32.lrotate

bit32.lrotate( n, disp )

返回数n向左旋转disp位。

注意旋转位数相当于模32:旋转32位相当于旋转0位,旋转33位相当于旋转1位,以此类推。

bit32.rrotate

bit32.rrotate( n, disp )

返回数n向右旋转disp位。

注意旋转位数相当于模32:旋转32位相当于旋转0位,旋转33位相当于旋转1位,以此类推。

libraryUtil

本库包含在实现Scribunto库时的一些有用方法。可以通过以下方式加载:

libraryUtil = require( 'libraryUtil' )

libraryUtil.checkType

libraryUtil.checkType( name, argIdx, arg, expectType, nilOk )

type( arg )不符合预期类型expectType时报错。此外,如果arg是nil且nilOk为true,则不会报错。

name是调用函数的名称,argIdx是参数列表中参数的位置。这些用于格式化错误消息。

libraryUtil.checkTypeMulti

libraryUtil.checkTypeMulti( name, argIdx, arg, expectTypes )

type( arg )不符合预期类型数组expectTypes中的任何字符串时报错。

用于可以有多个有效类型的参数。

libraryUtil.checkTypeForIndex

libraryUtil.checkTypeForIndex( index, value, expectType )

type( value )不符合预期类型expectType时报错。

用于实现__newindex元方法

libraryUtil.checkTypeForNamedArg

libraryUtil.checkTypeForNamedArg( name, argName, arg, expectType, nilOk )

type( arg )不符合预期类型expectType时报错。此外,如果arg是nil且nilOk为true,则不会报错。

在使用Lua的命名参数语法(func{ name = value })中,此函数用作libraryUtil.checkType()的等效。

libraryUtil.makeCheckSelfFunction

libraryUtil.makeCheckSelfFunction( libraryName, varName, selfObj, selfObjDesc )

此函数用于在用于被obj:method()语法调用的对象表中实现“方法”,会返回一个应该在这些方法顶部调用的函数,并使用self参数和方法名称,如果self对象不是selfObject则会抛出错误。

此函数通常用于库的构造函数中,类似于这样:

 function myLibrary.new()
     local obj = {}
     local checkSelf = libraryUtil.makeCheckSelfFunction( 'myLibrary', 'obj', obj, 'myLibrary object' )
 
     function obj:method()
         checkSelf( self, 'method' )
     end
 
     function obj:method2()
         checkSelf( self, 'method2' )
     end
 
     return obj
 end

luabit

可使用如下代码来加载luabit库模块的“bit”和“hex”:

bit = require( 'luabit.bit' )
hex = require( 'luabit.hex' )

注意bit32库包含了luabit.bit中的相同操作,luabit.hex也可以使用string.format()tonumber()来运行。

luabit模块noki不可用,因为在Scribunto中完全无用。luabit模块utf8也不可用,因为对Ustring库来说是多余的。

ustring

纯Lua中对Ustring库的后备支持可以使用如下代码加载

ustring = require( 'ustring' )

任何情况下,都应该使用Ustring库(mw.ustring),因为这将取代很多带有PHP代码回调、更慢且消耗内存的操作。

扩展库

一些MediaWiki扩展提供额外的Scribunto库。这些都位于表mw中,通常是在表mw.text中,然而,这些只有在安装了特定扩展时才存在(加上Scribunto扩展本身)。

这些扩展使用Scribunto提供的钩子:

Writing Scribunto libraries提供了如何开发这些库以为MediaWiki扩展提供Lua接口的信息。

以下库是计划中的,在Gerrit中等待复核。

  • (目前还没有)

mw.wikibase

擴充功能:Wikibase客戶端 提供了对可本地化的结构数据的访问,尤其是维基数据。 参见docs_topics_lua.htmlExtension:Wikibase Client/Lua

mw.wikibase.lexeme

WikibaseLexeme提供了对Wikibase Lexeme实体的访问,由Wikidata:Lexicographical data支持。

mw.wikibase.mediainfo

WikibaseMediaInfo提供了对Wikibase MediaInfo实体的访问。 参见Extension:WikibaseMediaInfo/Lua 。 这是由Structured Data on Commons支持的,参见Commons:Structured data/Lua

mw.bcmath

BCmath提供了Lua模块中的任意精度运算,可通过Extension:BCmath#Usage中的LDoc链接参见BCmath文档。

mw.smw

Semantic Scribunto提供了Scribunto扩展对 Semantic MediaWiki扩展的原生支持。

mw.ext.data

JsonConfig 提供了对可本地化的表格和地图数据的访问。 参见Extension:JsonConfig/Tabular Tabular Data和GeoJSON Map Data是在Commons的“Data:”命名空间中支持的。

mw.ext.cargo

Cargo提供了从Lua中查询数据的方法,参见Extension:Cargo/Other features#Lua support

mw.ext.cattools

CategoryToolbox提供了在Lua中查询某个页面是否属于特定分类的方法。 该扩展是实验性的,在公开的WikiMedia wiki中没有启用。

mw.ext.FlaggedRevs

FlaggedRevs提供了从Lua中访问页面的稳定性数据的方法。

mw.ext.TitleBlacklist

TitleBlacklist提供了从Lua中检测黑名单中的页面名称项以及获取有关信息的方法。

mw.ext.ParserFunctions

ParserFunctions提供了从Lua中评估解析器函数表达式的方法。

mw.ext.articlePlaceholder

ArticlePlaceholder提供了从Lua中覆盖默认的Wikibase渲染的方法,参见Extension:ArticlePlaceholder/Module:AboutTopic

mw.ext.externalData

ExternalData提供了从Lua中从互联网中获取结构化数据的方法,参见Extension:External Data/Lua

mw.ext.seo

WikiSEO提供了为当前页面获取SEO数据的方法,参见Extension:WikiSEO#Usage in lua modules

与标准Lua的不同之处

改变的函数

以下函数被修改了:

setfenv()
getfenv()
可能不可用,取决于配置。如果可用,尝试获取上级环境会失败。
getmetatable()
仅应用于表以避免未授权而获取上级环境。
tostring()
不提供表和函数的指针地址。这是为了使得内存侵蚀更难被利用。
pairs()
ipairs()
支持__pairs和__ipairs元方法(在Lua 5.2中增加的)。
pcall()
xpcall()
不能拦截某些内部错误。
require()
可以获取Scribunto分发的内置模块,以及在wiki的Module命名空间下存在的模块。如需获取wiki模块,使用包括命名空间的完整页面名称。不能获取本地文件系统。

移除的函数和包

以下包被几乎移除。只有列出来的函数可用。

package.*
文件系统和对C库的取得被移除了。 可用的函数和表为:
package.loaded
package.preload
package.loaders
可获取本地文件系统或加载C库的加载器不存在。 添加了Module命名空间页面的加载器。
package.seeall()
os.*
有些不安全的函数,例如os.execute(),因而不被允许。 可用的函数为:
os.clock()
os.date()
os.difftime()
os.time()
debug.*
大多数函数都是不安全的。 可用的函数为:
debug.traceback()

以下函数和包可用。

collectgarbage()
module()
coroutine.*
没有已知的应用,故没有检查安全性。
dofile()
loadfile()
io.*, file.*
允许获取本地文件系统,不安全。
load()
loadstring()
这些被省略了以允许Lua源代码的静态解析。 此外,允许这些会使得Lua代码被直接添加在文章和模板页面,而这不会因为可用性原因被需要。
print()
这在wikitech-l中有讨论,决定省略以支持返回值,以提高代码质量。 如有需要,mw.log()可以用于在调试控制台输出信息。
string.dump()
可能会从上级环境中暴露私有信息。

额外注意事项

参考数据结构
循环数据结构和同一节点可能通过多条路径到达的数据结构无法正确发送到PHP。 尝试这样做会导致不确定的行为。 这包括(但不限于)从{{#invoke:}}调用的模块返回此类数据结构,并将此类数据结构作为参数传递给在PHP中作为回调实现的库函数。
这样的数据结构可以在Lua中自由使用,包括用作通过mw.loadData()加载的模块的返回值

编写Scribunto库

此信息对于编写其他Scribunto库的开发者很有用,无论是包含在Scribunto本身还是为他们自己的扩展提供接口。

Scribunto库通常由五个部分组成:

  • 库的PHP部分。
  • 库的Lua部分。
  • 测试用例的PHP部分。
  • 测试用例的Lua部分。
  • 文档。

现有的库就是一个很好的例子。

库的PHP部分必须是继承了Scribunto_LuaLibraryBase的类。有关实现细节,请参阅该类的文档。在Scribunto扩展中,这个文件应该放在engines/LuaCommon/NameLibrary.php中,并在Scribunto_LuaEngine::$libraryClasses中添加一个映射。其他扩展应该使用ScribuntoExternalLibraries钩子。在上述两种中的任何一种情况下,键都应该与Lua模块名称匹配(“mw.name”用于Scribunto中的库,“mw.ext.name”用于扩展库)。

库的Lua部分设置了包含可以从Lua模块调用的函数的表。在Scribunto扩展中,该文件应该放在engines/LuaCommon/lualib/mw.名称.lua中,通常应包含如下样板文件:

local object = {}
local php

function object.setupInterface( options )
    -- 移除setup函数
    object.setupInterface = nil

    -- 将PHP回调复制到局部变量,并删除全局变量
    php = mw_interface
    mw_interface = nil

    -- 在此处进行任何其他设置

    -- 安装到mw全局变量
    mw = mw or {}
    mw.ext = mw.ext or {}
    mw.ext.NAME = object

    -- 表示我们已经加载了
    package.loaded['mw.ext.NAME'] = object
end

return object

engines/LuaCommon/lualib/libraryUtil.lua中的模块(使用local util = require 'libraryUtil'加载此模块)包含一些可能有用的函数。

确保在加载库的情况下运行Scribunto测试用例,即使您的库本身不提供任何测试用例。标准测试用例包括像添加意外全局变量的库之类的测试。此外,如果库是用PHP加载的,其Lua函数所具有的任何上值都不会在#invoke之间重置,必须注意确保模块不能滥用在#invoke之间传输信息。

测试样例

Scribunto扩展包括一个用于测试用例的基类Scribunto_LuaEngineTestBase,该类将针对LuaSandbox和LuaStandalone引擎运行测试。 库的测试用例应该扩展这个类,并且不应该覆盖static function suite()。 在Scribunto扩展中,测试用例应该在tests/engines/LuaCommon/NameLibraryTest.php并添加到ScribuntoHooks::unitTestsList()中的数组中(在common/Hooks.php中),扩展应该在自己的UnitTestsList 钩子函数中添加测试用例,可能取决于是否设置了$wgAutoloadClasses['Scribunto_LuaEngineTestBase']

大多数时候,制作测试用例需要做的包括:

class ClassNameTest extends Scribunto_LuaEngineTestBase {
    protected static $moduleName = 'ClassNameTest';

    function getTestModules() {
         return parent::getTestModules() + array(
             'ClassNameTest' => __DIR__ . '/ClassNameTests.lua';
         );
    }
}

这将加载文件ClassNameTests.lua,就好像它是页面“Module:ClassNameTests”一样,并期望返回具有以下属性的对象:

  • count: 整数,测试次数
  • provide(n):函数,返回三个值:n,测试n的名称,以及作为测试预期输出的字符串n
  • run(n):函数,运行测试n并返回一个字符串。

如果getTestModules()像上面一样被声明,那么“Module:TestFramework”是可以用来提供许多有用的辅助方法,如果使用了,那么ClassNameTests.lua看起来像这样:

local testframework = require 'Module:TestFramework'

return testframework.getTestProvider( {
    -- 此处放测试
} )

每个测试本身就是一个表,具有以下属性:

  • name:测试的名称。
  • func:要执行的函数。
  • args:传递给函数的可选参数表。
  • expect:预期结果。
  • type:测试的可选类型,默认为“Normal”。

type控制expect的格式以及func的调用方式。包括以下类型:

  • Normal: expect是返回值的表,但如果测试应该引发错误,则为字符串。func被简单地调用。
  • Iterator: expect是返回值的表。func迭代for循环一样被调用,并且每次迭代的返回值都会累加。
  • ToString:和“Normal”一样,但每个返回值都通过tostring()传递。

在其他扩展中的测试样例

有(至少)两种方式来运行PHPUnit测试:

  1. 针对核心运行phpunit,允许tests/phpunit/suites/ExtensionsTestSuite.php使用UnitTestsList 钩子找到扩展的测试。 如果您的扩展的测试类名称都包含一个唯一的组件(例如扩展的名称),则可以使用--filter选项以仅运行您的扩展的测试。
  2. 对扩展目录运行phpunit,这将获取以“Test.php”结尾的任何文件。

如果在LocalSettings.php中加载了Scribunto,则其中任何一个都可以正常运作。如果未加载Scribunto,那么方法#1很容易生效,因为可以轻松编写UnitTestsList钩子以避免在未设置$wgAutoloadClasses[ 'Scribunto_LuaEngineTestBase' ]时返回Scribunto测试。

但是Jenkins使用方法#2。为了让Jenkins正确运行测试,您需要将Scribunto添加到扩展的依赖项。有关如何完成此操作的示例,请参见Gerrit change 56570

如果由于某种原因,您需要能够在不加载Scribunto的情况下使用方法#2运行测试,一种解决方法是将此检查添加到单元测试文件的顶部:

 if ( !isset( $GLOBALS['wgAutoloadClasses']['Scribunto_LuaEngineTestBase'] ) ) {
     return;
 }

文档

Scribunto中包含的模块应该在上面的Scribunto库部分中包含文档。扩展库应该在其自己的扩展页面的子页面中包含文档,并从上方的扩展库章节中链接到该文档。

参见

许可协议

以下内容为译文,请以英文原文为准。

本手册(的英文原文)取自Lua 5.1 参考手册,在MIT许可证下可用。

本衍生手册也可以根据同一许可证的条款进行复制。