Lua Lua 5.4 参考手册

作者:Roberto Ierusalimschy、Luiz Henrique de Figueiredo、Waldemar Celes

版权所有 © 2020–2023 Lua.org、PUC-Rio。根据 Lua 许可证 的条款免费提供。

1 – 简介

Lua 是一种功能强大、高效、轻量级的可嵌入脚本语言。它支持过程式编程、面向对象编程、函数式编程、数据驱动编程和数据描述。

Lua 将简单的过程语法与基于关联数组和可扩展语义的强大数据描述结构相结合。Lua 是动态类型的,通过使用基于寄存器的虚拟机解释字节码来运行,并具有带代际垃圾回收的自动内存管理,使其非常适合配置、脚本编写和快速原型制作。

Lua 以库的形式实现,用标准 C 和 C++ 的通用子集“干净的 C”编写。Lua 发行版包括一个名为 `lua` 的宿主程序,它使用 Lua 库提供一个完整的独立 Lua 解释器,用于交互式或批处理使用。Lua 旨在既可以用作任何需要它的程序的强大、轻量级的可嵌入脚本语言,又可以用作强大但轻量级且高效的独立语言。

作为扩展语言,Lua 没有“主”程序的概念:它以“嵌入”方式在称为“嵌入程序”或简称“宿主”的宿主客户端中工作。(通常,此宿主是独立的 `lua` 程序。)宿主程序可以调用函数来执行一段 Lua 代码,可以写入和读取 Lua 变量,并且可以注册 C 函数以供 Lua 代码调用。通过使用 C 函数,可以扩展 Lua 以应对各种不同的领域,从而创建共享语法框架的定制编程语言。

Lua 是免费软件,并且按照其许可证中的规定,照常不提供任何保证。本手册中描述的实现可在 Lua 的官方网站 `www.lua.org` 上获得。

与任何其他参考手册一样,本文档在某些地方很枯燥。有关 Lua 设计背后的决策的讨论,请参阅 Lua 网站上提供的技术论文。有关 Lua 编程的详细介绍,请参阅 Roberto 的书《Lua 编程》。

2 – 基本概念

本节介绍了该语言的基本概念。

2.1 – 值和类型

Lua 是一种动态类型语言。这意味着变量没有类型;只有值有类型。该语言中没有类型定义。所有值都带有自己的类型。

Lua 中的所有值都是一等值。这意味着所有值都可以存储在变量中,作为参数传递给其他函数,并作为结果返回。

Lua 中有八种基本类型:nilbooleannumberstringfunctionuserdatathreadtable。类型 nil 只有一个值 nil,其主要属性是与任何其他值不同;它通常表示没有有用的值。类型 boolean 有两个值,falsetruenilfalse 都使条件为 false;它们统称为false 值。任何其他值都会使条件为 true。尽管其名称为 false,但它经常用作 nil 的替代,其关键区别在于 false 在表中表现得像一个常规值,而表中的 nil 表示一个不存在的键。

类型 number 使用两个子类型integerfloat 表示整数和实数(浮点数)。标准 Lua 使用 64 位整数和双精度(64 位)浮点数,但你也可以编译 Lua,使其使用 32 位整数和/或单精度(32 位)浮点数。对于小型机器和嵌入式系统,同时使用 32 位整数和浮点数的选项特别有吸引力。(请参阅文件 luaconf.h 中的宏 LUA_32BITS。)

除非另有说明,否则在操作整数值时发生的任何溢出都会环绕,遵循二进制补码算术的通常规则。(换句话说,实际结果是唯一可表示的整数,它在模 2n 时等于数学结果,其中 n 是整数类型的位数。)

Lua 有明确的规则规定何时使用每个子类型,但它也会根据需要自动在它们之间进行转换(请参阅 §3.4.3)。因此,程序员可以选择在很大程度上忽略整数和浮点数之间的差异,或者完全控制每个数字的表示。

类型 string 表示不可变的字节序列。Lua 是 8 位干净的:字符串可以包含任何 8 位值,包括嵌入式零('\0')。Lua 也是与编码无关的;它不假设字符串的内容。Lua 中任何字符串的长度都必须适合 Lua 整数。

Lua 可以调用(和操作)用 Lua 编写的函数和用 C 编写的函数(请参阅 §3.4.10)。两者都由类型 function 表示。

userdata 类型允许将任意 C 数据存储在 Lua 变量中。userdata 值表示一个原始内存块。userdata 有两种类型:完整 userdata,它是一个由 Lua 管理的内存块对象,以及轻量级 userdata,它只是一个 C 指针值。除了赋值和同一性测试之外,userdata 在 Lua 中没有预定义的操作。通过使用元表,程序员可以为完整 userdata 值定义操作(请参阅 §2.4)。userdata 值不能在 Lua 中创建或修改,只能通过 C API。这保证了宿主程序和 C 库所拥有数据的完整性。

thread 类型表示独立的执行线程,它用于实现协程(请参阅 §2.6)。Lua 线程与操作系统线程无关。Lua 在所有系统上都支持协程,即使那些本机不支持线程的系统也是如此。

table 类型实现了关联数组,即除了数字之外,还可以将任何 Lua 值(除了 nil 和 NaN)作为索引的数组。(非数字是 IEEE 754 标准用来表示未定义数值结果(例如 0/0)的特殊浮点值。)表可以是异构的;也就是说,它们可以包含所有类型的值(除了 nil)。与值 nil 关联的任何键都不被视为表的一部分。相反,任何不属于表一部分的键都有一个关联的值 nil

表是 Lua 中唯一的 data-structuring 机制;它们可用于表示普通数组、列表、符号表、集合、记录、图形、树等。为了表示记录,Lua 使用字段名称作为索引。该语言通过提供 a.name 作为 a["name"] 的语法糖来支持这种表示。在 Lua 中创建表有几种方便的方法(请参阅 §3.4.9)。

与索引一样,表字段的值可以是任何类型。特别是,由于函数是一等值,所以表字段可以包含函数。因此,表也可以携带方法(请参阅 §3.4.11)。

表的索引遵循语言中原始相等性的定义。当且仅当 ij 原始相等(即在没有元方法的情况下相等)时,表达式 a[i]a[j] 表示相同的表元素。特别是,具有整数值的浮点数等于它们各自的整数(例如,1.0 == 1)。为了避免歧义,任何用作键的浮点数(等于整数)都将转换为该整数。例如,如果你编写 a[2.0] = true,则实际插入到表中的键将是整数 2

表、函数、线程和(完整)userdata 值是对象:变量实际上并不包含这些值,而只是包含对它们的引用。赋值、参数传递和函数返回始终操作对这些值的引用;这些操作不暗示任何类型的复制。

库函数 type 返回一个描述给定值类型的字符串(请参阅 type)。

2.2 – 环境和全局环境

正如我们在 §3.2§3.3.3 中进一步讨论的那样,对自由名称(即未绑定到任何声明)var 的任何引用在语法上都转换为 _ENV.var。此外,每个块都在名为 _ENV 的外部局部变量的作用域内编译(请参阅 §3.3.2),因此 _ENV 本身永远不是块中的自由名称。

尽管存在此外部 _ENV 变量和自由名称的转换,但 _ENV 是一个完全规则的名称。特别是,您可以使用该名称定义新变量和参数。对自由名称的每个引用都使用在程序中该点可见的 _ENV,遵循 Lua 的通常可见性规则(请参阅 §3.5)。

用作 _ENV 值的任何表都称为环境

Lua 保留一个称为全局环境的特殊环境。此值保存在 C 注册表的特殊索引中(请参阅 §4.3)。在 Lua 中,全局变量 _G 使用此相同的值初始化。(_G 在内部从未使用,因此更改其值只会影响您自己的代码。)

当 Lua 加载块时,其 _ENV 变量的默认值是全局环境(请参阅 load)。因此,默认情况下,Lua 代码中的自由名称引用全局环境中的条目,因此它们也称为全局变量。此外,所有标准库都在全局环境中加载,并且那里的一些函数在该环境中运行。您可以使用 load(或 loadfile)使用不同的环境加载块。(在 C 中,您必须加载块,然后更改其第一个 upvalue 的值;请参阅 lua_setupvalue。)

2.3 – 错误处理

Lua 中的几个操作可以引发错误。错误会中断程序的正常流程,可以通过捕获错误来继续执行程序。

Lua 代码可以通过调用 error 函数来显式引发错误。(此函数永远不会返回。)

要在 Lua 中捕获错误,您可以使用 pcall(或 xpcall)进行受保护的调用。函数 pcall受保护模式调用给定函数。在运行函数时发生的任何错误都会停止其执行,并且控制权会立即返回到 pcall,后者会返回一个状态代码。

由于 Lua 是一种嵌入式扩展语言,Lua 代码的运行是从主机程序中的 C 代码发起的。(当您独立使用 Lua 时,lua 应用程序就是主机程序。)通常,此调用是受保护的;因此,当在 Lua 块的编译或执行过程中发生其他不受保护的错误时,控制权将返回到主机,主机可以采取适当的措施,例如打印错误消息。

每当出现错误时,都会传播一个错误对象,其中包含有关错误的信息。Lua 本身仅生成错误对象为字符串的错误,但程序可能会生成错误对象为任何值的错误。处理此类错误对象取决于 Lua 程序或其主机。出于历史原因,错误对象通常称为错误消息,即使它不必是字符串。

当您使用 xpcall(或在 C 中使用 lua_pcall)时,您可以提供一个消息处理程序,以便在出现错误时调用。此函数使用原始错误对象调用并返回一个新的错误对象。它在错误展开堆栈之前调用,以便它可以收集有关错误的更多信息,例如通过检查堆栈并创建堆栈回溯。此消息处理程序仍然受保护调用的保护;因此,消息处理程序中的错误将再次调用消息处理程序。如果此循环持续时间过长,Lua 会中断它并返回一条适当的消息。消息处理程序仅针对常规运行时错误调用。对于内存分配错误以及在运行终结器或其他消息处理程序时发生的错误,不会调用它。

Lua 还提供了一个警告系统(请参阅 warn)。与错误不同,警告不会以任何方式干扰程序执行。它们通常只向用户生成一条消息,尽管可以从 C 调整此行为(请参阅 lua_setwarnf)。

2.4 – 元表和元方法

Lua 中的每个值都可以有一个元表。此元表是一个普通的 Lua 表,它定义了原始值在特定事件下的行为。您可以通过设置其元表中的特定字段来更改值的多个行为方面。例如,当非数字值是加法的操作数时,Lua 会在该值的元表的 __add 字段中查找一个函数。如果找到一个,Lua 会调用此函数来执行加法。

元表中每个事件的键都是一个字符串,其事件名称前缀为两个下划线;相应的值称为元值。对于大多数事件,元值必须是一个函数,然后称为元方法。在前面的示例中,键是字符串“__add”,元方法是执行加法的函数。除非另有说明,否则元方法实际上可以是任何可调用值,它可以是函数或具有 __call 元方法的值。

您可以使用 getmetatable 函数查询任何值的元表。Lua 使用原始访问(请参阅 rawget)查询元表中的元方法。

您可以使用 setmetatable 函数替换表的元表。除了使用调试库(§6.10)之外,您无法从 Lua 代码中更改其他类型的元表。

表和完整用户数据有单独的元表,尽管多个表和用户数据可以共享它们的元表。所有其他类型的元表都共享一个单一的元表;也就是说,所有数字有一个单一的元表,所有字符串有一个单一的元表,等等。默认情况下,一个值没有元表,但字符串库为字符串类型设置了一个元表(参见 §6.4)。

接下来给出了由元表控制的操作的详细列表。每个事件由其对应的键标识。按照惯例,Lua 使用的所有元表键都由两个下划线和拉丁小写字母组成。

除了上述列表之外,解释器还遵循元表中的以下键:__gc(参见 §2.5.3)、__close(参见 §3.3.8)、__mode(参见 §2.5.4)和 __name。(当 __name 条目包含字符串时,可由 tostring 和错误消息使用。)

对于一元运算符(否定、长度和按位非),使用虚拟第二个操作数(等于第一个操作数)计算和调用元方法。此额外操作数仅用于简化 Lua 内部(通过使这些运算符表现得像二元运算符),并且可能会在未来版本中删除。对于大多数用途,此额外操作数无关紧要。

因为元表是常规表,所以它们可以包含任意字段,而不仅仅是上面定义的事件名称。标准库中的一些函数(例如 tostring)将元表中的其他字段用于自己的目的。

在将表设置为某个对象的元表之前,最好向表中添加所有需要的元方法。特别是,只有在遵循此顺序时,__gc 元方法才有效(参见 §2.5.3)。在创建对象后立即设置其元表也是一种好习惯。

2.5 – 垃圾回收

Lua 执行自动内存管理。这意味着您不必担心为新对象分配内存或在不再需要对象时释放内存。Lua 通过运行垃圾回收器自动管理内存,以收集所有死亡对象。Lua 使用的所有内存都受自动管理:字符串、表、用户数据、函数、线程、内部结构等。

只要收集器能够确定在程序的正常执行过程中不会再次访问对象,该对象就会被视为死亡。(这里的“正常执行”不包括可以复活死亡对象的终结器(参见§2.5.3),也不包括使用调试库的操作。)请注意,收集器能够确定对象死亡的时间可能与程序员的预期不一致。唯一的保证是 Lua 不会收集在程序正常执行过程中仍可能访问的对象,并且最终会收集从 Lua 无法访问的对象。(此处,“从 Lua 无法访问”表示既没有变量也没有其他活动对象引用该对象。)由于 Lua 不了解 C 代码,因此它永远不会收集通过注册表(参见§4.3)访问的对象,其中包括全局环境(参见§2.2)。

Lua 中的垃圾收集器 (GC) 可以使用两种模式:增量模式和代际模式。

默认 GC 模式和默认参数足以满足大多数用途。但是,将大量时间浪费在分配和释放内存上的程序可以从其他设置中受益。请记住,GC 行为在不同平台和不同 Lua 版本之间不可移植;因此,最佳设置也不可移植。

您可以通过在 C 中调用lua_gc或在 Lua 中调用collectgarbage来更改 GC 模式和参数。您还可以使用这些函数直接控制收集器(例如,停止和重新启动它)。

2.5.1 – 增量垃圾收集

在增量模式下,每个 GC 周期都会执行标记和扫描收集,这些收集是与程序执行交错进行的小步骤。在此模式下,收集器使用三个数字来控制其垃圾收集周期:垃圾收集器暂停垃圾收集器步长乘数垃圾收集器步长大小

垃圾回收器暂停时间控制回收器在开始新周期前等待的时间。当内存使用量达到前一次回收后的使用量的n%时,回收器会开始新周期。较大的值会使回收器不那么激进。小于或等于 100 的值意味着回收器不会等待开始新周期。200 的值意味着回收器会等待正在使用的总内存加倍后才开始新周期。默认值为 200;最大值为 1000。

垃圾回收器步长乘数控制回收器相对于内存分配的速度,即每分配 1 KB 内存它标记或清理多少个元素。较大的值会使回收器更激进,但也会增加每个增量步长的规模。你不应使用小于 100 的值,因为它们会使回收器太慢,并可能导致回收器永远无法完成一个周期。默认值为 100;最大值为 1000。

垃圾回收器步长控制每个增量步长的规模,具体来说是解释器在执行一个步长前分配多少字节。此参数是按对数计算的:n 的值意味着解释器将在步长之间分配 2n 字节,并在步长期间执行等效的工作。较大的值(例如 60)会使回收器成为停止世界(非增量)回收器。默认值为 13,这意味着大约 8 KB 的步长。

2.5.2 – 代际垃圾回收

在代际模式中,回收器会频繁执行次要回收,它只遍历最近创建的对象。如果在次要回收后内存使用量仍然高于某个限制,回收器会执行停止世界的主要回收,它会遍历所有对象。代际模式使用两个参数:次要乘数主要乘数

次要乘数控制次要回收的频率。对于次要乘数x,当内存增长到比前一次主要回收后的内存使用量大x%时,将执行新的次要回收。例如,对于 20 的乘数,当内存使用量比前一次主要回收后的使用量大 20% 时,回收器将执行次要回收。默认值为 20;最大值为 200。

主要乘数控制主要回收的频率。对于主要乘数x,当内存增长到比前一次主要回收后的内存使用量大x%时,将执行新的主要回收。例如,对于 100 的乘数,当内存使用量比前一次回收后的使用量大两倍时,回收器将执行主要回收。默认值为 100;最大值为 1000。

2.5.3 – 垃圾回收元方法

您可以为表设置垃圾回收器元方法,并使用 C API 为完整用户数据设置(请参阅 §2.4)。这些元方法称为终结器,当垃圾回收器检测到相应的表或用户数据已失效时,将调用这些元方法。终结器允许您协调 Lua 的垃圾回收与外部资源管理,例如关闭文件、网络或数据库连接,或释放您自己的内存。

要使对象(表或用户数据)在回收时被终结,您必须标记它以进行终结。当您设置其元表且元表具有 __gc 元方法时,您会将对象标记为终结。请注意,如果您设置了一个没有 __gc 字段的元表,然后在元表中创建该字段,则不会将对象标记为终结。

当标记的对象失效时,它不会立即被垃圾回收器回收。相反,Lua 会将其放入列表中。在回收后,Lua 会遍历该列表。对于列表中的每个对象,它会检查该对象的 __gc 元方法:如果存在,Lua 会将其作为其唯一参数调用它。

在每个垃圾回收周期结束时,终结器将按相反的顺序调用,即在该周期中回收的对象的终结顺序;也就是说,第一个被调用的终结器是与程序中最后标记的对象关联的终结器。每个终结器的执行可能发生在常规代码执行期间的任何时间点。

由于被回收的对象仍必须由终结器使用,因此该对象(以及仅可通过它访问的其他对象)必须由 Lua复活。通常,这种复活是暂时的,并且对象内存将在下一个垃圾回收周期中释放。但是,如果终结器将对象存储在某个全局位置(例如全局变量),则复活是永久性的。此外,如果终结器再次标记终结对象进行终结,则其终结器将在对象失效的下一个周期中再次被调用。在任何情况下,对象内存仅在对象失效且未标记为终结的 GC 周期中释放。

当您关闭状态(请参阅 lua_close)时,Lua 会按照它们被标记的相反顺序调用所有标记为终结的对象的终结器。如果任何终结器在该阶段标记对象进行回收,则这些标记无效。

终结器不能产生或运行垃圾回收器。因为它们可以在不可预测的时间运行,所以最好将每个终结器限制为正确释放其关联资源所需的最小值。

在运行终结器时发生的任何错误都会生成一个警告;该错误不会传播。

2.5.4 – 弱表

弱表是一个元素为弱引用的表。垃圾回收器会忽略弱引用。换句话说,如果对对象的唯一引用是弱引用,那么垃圾回收器将收集该对象。

弱表可以有弱键、弱值或两者兼有。具有弱值的表允许收集其值,但阻止收集其键。具有弱键和弱值的表允许收集键和值。在任何情况下,如果键或值被收集,整个对将从表中删除。表的弱性由其元表的__mode字段控制。如果存在此元值,则它必须是以下字符串之一:“k”,表示具有弱键的表;“v”,表示具有弱值的表;或“kv”,表示具有弱键和弱值的表。

具有弱键和强值的表也称为瞬态表。在瞬态表中,仅当键可访问时,值才被认为可访问。特别是,如果对键的唯一引用通过其值而来,则该对将被删除。

对表的弱性的任何更改可能仅在下一个收集周期生效。特别是,如果你将弱性更改为更强的模式,Lua 仍可能在更改生效之前从该表中收集一些项。

只有具有显式构造的对象才会从弱表中删除。值(例如数字和轻量级 C 函数)不受垃圾回收的影响,因此不会从弱表中删除(除非收集了其关联值)。虽然字符串受垃圾回收的影响,但它们没有显式构造,并且它们的相等性是按值计算的;它们的行为更像是值而不是对象。因此,它们不会从弱表中删除。

复活的对象(即正在完成的对象和仅可通过正在完成的对象访问的对象)在弱表中具有特殊行为。它们在运行完成处理程序之前从弱值中移除,但仅在运行完成处理程序后的下一次收集中从弱键中移除,此时这些对象实际上已释放。此行为允许完成处理程序通过弱表访问与对象关联的属性。

如果弱表在收集周期中属于复活的对象之一,则可能直到下一次周期才正确清除它。

2.6 – 协程

Lua 支持协程,也称为协作式多线程。Lua 中的协程表示一个独立的执行线程。然而,与多线程系统中的线程不同,协程仅通过显式调用 yield 函数来挂起其执行。

通过调用 coroutine.create 创建协程。其唯一参数是协程的主函数。create 函数仅创建一个新协程并返回对其的句柄(类型为thread 的对象);它不会启动协程。

通过调用 coroutine.resume 执行协程。首次调用 coroutine.resume 时,将 coroutine.create 返回的线程作为其第一个参数传递,协程通过调用其主函数来启动其执行。传递给 coroutine.resume 的额外参数作为参数传递给该函数。协程开始运行后,它将一直运行,直到终止或yield

协程可以通过两种方式终止其执行:正常终止,即当其主函数返回时(显式或隐式,在最后一条指令之后);以及异常终止,即如果存在未受保护的错误。在正常终止的情况下,coroutine.resume 返回 true,以及协程主函数返回的任何值。在发生错误的情况下,coroutine.resume 返回 false 以及错误对象。在这种情况下,协程不会展开其堆栈,因此可以在错误发生后使用调试 API 检查它。

协程通过调用 coroutine.yield 来 yield。当协程 yield 时,相应的 coroutine.resume 立即返回,即使 yield 发生在嵌套函数调用中(即,不在主函数中,而是在主函数直接或间接调用的函数中)。在 yield 的情况下,coroutine.resume 还返回 true,以及传递给 coroutine.yield 的任何值。下次恢复同一协程时,它将从 yield 的位置继续执行,coroutine.yield 调用返回传递给 coroutine.resume 的任何额外参数。

coroutine.create 类似,coroutine.wrap 函数也创建一个协程,但它不返回协程本身,而是返回一个函数,当调用时,恢复协程。传递给此函数的任何参数作为 coroutine.resume 的额外参数。 coroutine.wrap 返回 coroutine.resume 返回的所有值,除了第一个值(布尔错误代码)。与 coroutine.resume 不同,coroutine.wrap 创建的函数将任何错误传播给调用者。在这种情况下,该函数还关闭协程(参见 coroutine.close)。

作为协程工作原理的一个示例,考虑以下代码

     function foo (a)
       print("foo", a)
       return coroutine.yield(2*a)
     end
     
     co = coroutine.create(function (a,b)
           print("co-body", a, b)
           local r = foo(a+1)
           print("co-body", r)
           local r, s = coroutine.yield(a+b, a-b)
           print("co-body", r, s)
           return b, "end"
     end)
     
     print("main", coroutine.resume(co, 1, 10))
     print("main", coroutine.resume(co, "r"))
     print("main", coroutine.resume(co, "x", "y"))
     print("main", coroutine.resume(co, "x", "y"))

当您运行它时,它会生成以下输出

     co-body 1       10
     foo     2
     main    true    4
     co-body r
     main    true    11      -9
     co-body x       y
     main    true    10      end
     main    false   cannot resume dead coroutine

您还可以通过 C API 创建和操作协程:请参见函数 lua_newthreadlua_resumelua_yield

3 – 语言

本节介绍 Lua 的词法、语法和语义。换句话说,本节介绍哪些标记有效,如何组合它们,以及它们的组合意味着什么。

将使用通常的扩展 BNF 符号来解释语言结构,其中 {a} 表示 0 个或更多个 a,[a] 表示一个可选的 a。非终结符显示为非终结符,关键字显示为 kword,其他终结符显示为 ‘=’。Lua 的完整语法可以在本手册末尾的 §9 中找到。

3.1 – 词法约定

Lua 是一种自由格式语言。它忽略词法元素(标记)之间的空格和注释,但作为两个标记之间的分隔符除外。在源代码中,Lua 将标准 ASCII 空白字符空格、换页符、换行符、回车符、水平制表符和垂直制表符识别为空格。

Lua 中的名称(也称为标识符)可以是拉丁字母、阿拉伯-印度数字和下划线的任何字符串,不能以数字开头,也不能是保留字。标识符用于命名变量、表字段和标签。

以下关键字是保留字,不能用作名称

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

Lua 是一种大小写敏感的语言:and 是一个保留字,但 AndAND 是两个不同的有效名称。根据惯例,程序应避免创建以下划线后跟一个或多个大写字母开头的名称(例如 _VERSION)。

以下字符串表示其他标记

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

一个短字面字符串可以用匹配的单引号或双引号分隔,并且可以包含以下 C 类似的转义序列:'\a'(响铃)、'\b'(退格)、'\f'(换页)、'\n'(换行)、'\r'(回车)、'\t'(水平制表符)、'\v'(垂直制表符)、'\\'(反斜杠)、'\"'(引号 [双引号])和 '\''(撇号 [单引号])。反斜杠后跟换行符会导致字符串中换行。转义序列 '\z' 跳过随后的空白字符跨度,包括换行符;它特别适用于将长字面字符串拆分和缩进到多行,而无需将换行符和空格添加到字符串内容中。短字面字符串不能包含未转义的换行符或未形成有效转义序列的转义符。

我们可以通过其数值指定短字面字符串中的任何字节,包括嵌入的零。这可以通过转义序列 \xXX 来完成,其中 XX 是恰好两个十六进制数字的序列,或通过转义序列 \ddd 来完成,其中 ddd 是最多三个十进制数字的序列。(请注意,如果十进制转义序列后跟数字,则必须使用恰好三个数字来表示。)

Unicode 字符的 UTF-8 编码可以使用转义序列 \u{XXX}(带强制括号)插入到字面字符串中,其中 XXX 是表示字符代码点的序列,包含一个或多个十六进制数字。此代码点可以小于 231 的任何值。(Lua 在此使用原始 UTF-8 规范,该规范不限于有效的 Unicode 代码点。)

字面字符串也可以使用长括号括起来的长格式定义。我们将n 级打开长括号定义为一个方括号,后跟 n 个等号,再后跟另一个方括号。因此,0 级打开长括号写为 [[,1 级打开长括号写为 [=[,依此类推。关闭长括号的定义类似;例如,4 级关闭长括号写为 ]====]长字面以任何级别的打开长括号开头,并在同一级别的第一个关闭长括号处结束。它可以包含除同一级别的关闭括号之外的任何文本。这种括号形式的字面值可以跨多行,不解释任何转义序列,并忽略任何其他级别的长括号。任何类型的行尾序列(回车、换行、回车后跟换行或换行后跟回车)都转换为简单的换行符。当打开长括号紧跟换行符时,换行符不包含在字符串中。

例如,在使用 ASCII(其中 'a' 编码为 97,换行符编码为 10,'1' 编码为 49)的系统中,以下五个字面字符串表示相同的字符串

     a = 'alo\n123"'
     a = "alo\n123\""
     a = '\97lo\10\04923"'
     a = [[alo
     123"]]
     a = [==[
     alo
     123"]==]

字面字符串中未明确受前述规则影响的任何字节都表示它自身。但是,Lua 以文本模式打开文件进行解析,并且系统的文件函数可能会出现一些控制字符问题。因此,将二进制数据表示为带有非文本字符显式转义序列的带引号字面量会更安全。

数字常量(或数字)可以用一个可选的小数部分和一个可选的十进制指数来编写,用字母“e”或“E”标记。Lua 还接受十六进制常量,这些常量以 0x0X 开头。十六进制常量还接受一个可选的小数部分加上一个可选的二进制指数,用字母“p”或“P”标记并以十进制形式编写。(例如,0x1.fp10 表示 1984,即 0x1f / 16 乘以 210。)

带有小数点或指数的数字常量表示浮点数;否则,如果其值适合整数或它是十六进制常量,则表示整数;否则(即,溢出的十进制整数数字),则表示浮点数。既没有小数点也没有指数的十六进制数字始终表示一个整数值;如果值溢出,它会环绕以适合一个有效的整数。

有效整数常量的示例:

     3   345   0xff   0xBEBADA

有效浮点数常量的示例:

     3.0     3.1416     314.16e-2     0.31416E1     34e1
     0x0.1E  0xA23p-4   0X1.921FB54442D18P+1

注释以双连字符 (--) 开头,位于字符串外部的任何位置。如果 -- 之后的文本不是一个左长括号,则注释是一个短注释,它一直持续到该行的末尾。否则,它是一个长注释,它一直持续到相应的右长括号。

3.2 – 变量

变量是存储值的场所。Lua 中有三种类型的变量:全局变量、局部变量和表字段。

一个名称可以表示一个全局变量或一个局部变量(或一个函数的形式参数,它是一种特殊的局部变量)

	var ::= Name

名称表示标识符(参见 §3.1)。

任何变量名称都假定为全局变量,除非明确声明为局部变量(参见 §3.3.7)。局部变量是词法作用域:局部变量可以被其作用域内定义的函数自由访问(参见 §3.5)。

在对变量进行首次赋值之前,其值为 nil

方括号用于索引表

	var ::= prefixexp ‘[’ exp ‘]

可以通过元表来更改对表字段的访问的含义(参见 §2.4)。

语法 var.Name 只是 var["Name"] 的语法糖

	var ::= prefixexp ‘.’ Name

对全局变量 x 的访问等同于 _ENV.x。由于块的编译方式,变量 _ENV 本身永远不是全局的(请参阅 §2.2)。

3.3 – 语句

Lua 支持一组几乎约定俗成的语句,类似于其他约定俗成语言中的语句。这组语句包括块、赋值、控制结构、函数调用和变量声明。

3.3.1 –

块是按顺序执行的一系列语句

	block ::= {stat}

Lua 有空语句,允许你用分号分隔语句,用分号开始块,或连续写两个分号

	stat ::= ‘;

函数调用和赋值都可以以左括号开头。这种可能性导致了 Lua 语法中的歧义。考虑以下片段

     a = b + c
     (print or io.write)('done')

语法可以以两种方式查看此片段

     a = b + c(print or io.write)('done')
     
     a = b + c; (print or io.write)('done')

当前解析器总是以第一种方式查看此类结构,将左括号解释为调用参数的开始。为了避免这种歧义,一个好的做法是始终在以括号开头的语句前加上分号

     ;(print or io.write)('done')

可以明确地界定一个块以生成一个语句

	stat ::= do block end

明确的块对于控制变量声明的范围很有用。明确的块有时也用于在另一个块的中间添加一个 return 语句(请参阅 §3.3.4)。

3.3.2 –

Lua 的编译单元称为。在语法上,块只是一个块

	chunk ::= block

Lua 将块作为具有可变数量参数的匿名函数的主体(请参阅 §3.4.11)。因此,块可以定义局部变量、接收参数和返回值。此外,此类匿名函数是在名为 _ENV 的外部局部变量的范围内编译的(请参阅 §2.2)。即使结果函数不使用该变量,它也始终将 _ENV 作为其唯一的外部变量。

块可以存储在文件中或宿主程序中的字符串中。要执行块,Lua 首先加载它,将块的代码预编译为虚拟机的指令,然后 Lua 使用虚拟机的解释器执行编译后的代码。

块也可以预编译为二进制形式;有关详细信息,请参阅程序 luac 和函数 string.dump。源代码和编译后的形式的程序是可互换的;Lua 会自动检测文件类型并采取相应的操作(请参阅 load)。

3.3.3 – 赋值

Lua 允许多重赋值。因此,赋值的语法在左侧定义了一个变量列表,在右侧定义了一个表达式列表。两个列表中的元素都用逗号分隔

	stat ::= varlist ‘=’ explist
	varlist ::= var {‘,’ var}
	explist ::= exp {‘,’ exp}

表达式在 §3.4 中讨论。

在赋值之前,值列表会调整为变量列表的长度(参见§3.4.12)。

如果一个变量在多重赋值中既被赋值又读取,Lua 确保所有读取都获取赋值前的变量值。因此,代码

     i = 3
     i, a[i] = i+1, 20

a[3] 设为 20,而不影响 a[4],因为 a[i] 中的 i 在赋值为 4 之前已求值(为 3)。类似地,行

     x, y = y, x

交换了 xy 的值,并且

     x, y, z = y, z, x

循环排列了 xyz 的值。

请注意,此保证仅涵盖赋值语句中语法上的访问。如果在赋值期间调用的函数或元方法更改了变量的值,Lua 不会保证该访问的顺序。

对全局名称 x = val 的赋值等效于赋值 _ENV.x = val(参见§2.2)。

可以通过元表更改对表字段和全局变量(实际上也是表字段)的赋值的含义(参见§2.4)。

3.3.4 – 控制结构

控制结构 ifwhilerepeat 具有通常的含义和熟悉的语法

	stat ::= while exp do block end
	stat ::= repeat block until exp
	stat ::= if exp then block {elseif exp then block} [else block] end

Lua 也有一个 for 语句,有两种形式(参见§3.3.5)。

控制结构的条件表达式可以返回任何值。falsenil 都测试为 false。与 nilfalse 不同的所有值都测试为 true。特别是,数字 0 和空字符串也测试为 true。

repeatuntil 循环中,内部块不会在 until 关键字处结束,而只会在条件之后结束。因此,条件可以引用在循环块中声明的局部变量。

goto 语句将程序控制权转移到一个标签。出于语法原因,Lua 中的标签也被视为语句

	stat ::= goto Name
	stat ::= label
	label ::= ‘::’ Name ‘::

标签在其定义的整个块中可见,嵌套函数内部除外。goto 可以跳转到任何可见标签,只要它不进入局部变量的范围。不应在可见同名标签的情况下声明标签,即使此其他标签已在封闭块中声明。

break 语句终止 whilerepeatfor 循环的执行,跳到循环后的下一条语句

	stat ::= break

break 结束最内层的封闭循环。

return 语句用于从函数或块(作为匿名函数处理)返回值。函数可以返回多个值,因此 return 语句的语法为

	stat ::= return [explist] [‘;’]

return 语句只能作为块的最后一条语句编写。如果需要在块中间return,则可以使用显式内部块,如惯用语 do return end,因为现在 return 是其(内部)块中的最后一条语句。

3.3.5 – For 语句

for 语句有两种形式:一种是数字形式,另一种是泛型形式。

数字for 循环

数字for 循环在控制变量经历算术级数时重复代码块。它具有以下语法

	stat ::= for Name ‘=’ exp ‘,’ exp [‘,’ exp] do block end

给定的标识符(名称)定义控制变量,它是循环体()的局部新变量。

循环从一次评估三个控制表达式开始。它们的值分别称为初始值限制步长。如果步长不存在,则默认为 1。

如果初始值和步长都是整数,则循环使用整数完成;请注意,限制可能不是整数。否则,三个值将转换为浮点数,并且循环使用浮点数完成。在这种情况下,请注意浮点精度。

在该初始化之后,循环体重复执行,控制变量的值经历算术级数,从初始值开始,公差由步长给出。负步长产生递减序列;等于零的步长会引发错误。循环继续进行,同时值小于或等于限制(对于负步长,则大于或等于)。如果初始值已经大于限制(或小于,如果步长为负),则不执行主体。

对于整数循环,控制变量永远不会环绕;相反,在溢出的情况下,循环结束。

您不应该在循环期间更改控制变量的值。如果您在循环后需要它的值,请在退出循环之前将其赋值给另一个变量。

泛型for 循环

泛型for 语句适用于称为迭代器的函数。在每次迭代中,调用迭代器函数以生成新值,当此新值为nil 时停止。泛型for 循环具有以下语法

	stat ::= for namelist in explist do block end
	namelist ::= Name {‘,’ Name}

类似于

     for var_1, ···, var_n in explist do body end

for 语句按如下方式工作。

名称var_i声明循环体中的局部循环变量。第一个变量是控制变量

循环从评估explist 开始,以生成四个值:迭代器函数状态、控制变量的初始值和结束值

然后,在每次迭代中,Lua 使用两个参数调用迭代器函数:状态和控制变量。然后,根据多重赋值规则(请参阅 §3.3.3),将此调用的结果分配给循环变量。如果控制变量变为nil,则循环终止。否则,执行主体,并且循环进入下一个迭代。

结束值的行为类似于待关闭变量(请参阅 §3.3.8),当循环结束时,可以使用它来释放资源。否则,它不会干扰循环。

您不应该在循环期间更改控制变量的值。

3.3.6 – 函数调用作为语句

为了允许可能的副作用,函数调用可以作为语句执行

	stat ::= functioncall

在这种情况下,所有返回的值都会被丢弃。函数调用将在 §3.4.10 中进行说明。

3.3.7 – 局部声明

局部变量可以在块内的任何位置声明。声明可以包含初始化

	stat ::= local attnamelist [‘=’ explist]
	attnamelist ::=  Name attrib {‘,’ Name attrib}

如果存在,初始赋值具有与多重赋值相同的语义(参见 §3.3.3)。否则,所有变量都将初始化为 nil

每个变量名都可以通过一个属性(尖括号中的名称)作为后缀

	attrib ::= [‘<’ Name ‘>’]

有两个可能的属性:const,它声明一个常量变量,即一个在其初始化后不能被赋值的变量;和 close,它声明一个待关闭的变量(参见 §3.3.8)。变量列表最多可以包含一个待关闭的变量。

块也是一个块(参见 §3.3.2),因此可以在任何显式块外部的块中声明局部变量。

局部变量的可见性规则将在 §3.5 中进行说明。

3.3.8 – 待关闭变量

待关闭变量的行为类似于常量局部变量,不同之处在于,无论正常块终止、通过 break/goto/return 退出块,还是通过错误退出,只要变量超出范围,其值就会被关闭

此处,关闭一个值是指调用其 __close 元方法。在调用元方法时,值本身作为第一个参数传递,而导致退出的错误对象(如果有)作为第二个参数传递;如果没有错误,第二个参数为 nil

分配给待关闭变量的值必须具有 __close 元方法或为假值。(nilfalse 被忽略为待关闭的值。)

如果多个待关闭变量在同一事件中超出范围,它们将按照声明的相反顺序关闭。

如果在运行关闭方法时出现任何错误,则该错误将像在定义变量的常规代码中的错误一样被处理。在发生错误后,仍将调用其他待处理的关闭方法。

如果协程让步且不再恢复,某些变量可能永远不会超出范围,因此它们将永远不会被关闭。(这些变量是在协程内部创建的,并且在协程让步时处于范围内。)类似地,如果协程以错误结束,它不会展开其堆栈,因此不会关闭任何变量。在这两种情况下,您可以使用终结器或调用 coroutine.close 来关闭变量。但是,如果协程是通过 coroutine.wrap 创建的,则其对应的函数将在发生错误时关闭协程。

3.4 – 表达式

Lua 中的基本表达式如下

	exp ::= prefixexp
	exp ::= nil | false | true
	exp ::= Numeral
	exp ::= LiteralString
	exp ::= functiondef
	exp ::= tableconstructor
	exp ::= ‘...’
	exp ::= exp binop exp
	exp ::= unop exp
	prefixexp ::= var | functioncall | ‘(’ exp ‘)

数字和字符串常量在 §3.1 中进行了解释;变量在 §3.2 中进行了解释;函数定义在 §3.4.11 中进行了解释;函数调用在 §3.4.10 中进行了解释;表构造函数在 §3.4.9 中进行了解释。可变参数表达式由三个点(...)表示,只能在可变参数函数内部直接使用;它们在 §3.4.11 中进行了解释。

二元运算符包括算术运算符(参见 §3.4.1)、按位运算符(参见 §3.4.2)、关系运算符(参见 §3.4.4)、逻辑运算符(参见 §3.4.5)和连接运算符(参见 §3.4.6)。一元运算符包括一元减号(参见 §3.4.1)、一元按位非(参见 §3.4.2)、一元逻辑非(参见 §3.4.5)和一元长度运算符(参见 §3.4.7)。

3.4.1 – 算术运算符

Lua 支持以下算术运算符

除了求幂和浮点除法之外,算术运算符的工作方式如下:如果两个操作数都是整数,则对整数执行运算,结果为整数。否则,如果两个操作数都是数字,则将它们转换为浮点数,按照机器的浮点运算规则执行运算(通常是 IEEE 754 标准),结果为浮点数。(字符串库在算术运算中将字符串强制转换为数字;有关详细信息,请参见 §3.4.3。)

求幂和浮点除法 (/) 始终将操作数转换为浮点数,结果始终为浮点数。求幂使用 ISO C 函数 pow,因此它也适用于非整数指数。

取整除法 (//) 是一种将商朝负无穷方向舍入的除法,其结果为操作数除法的向下取整。

取模定义为将商朝负无穷方向舍入的除法的余数(取整除法)。

在整数运算中发生溢出时,所有运算都会环绕

3.4.2 – 按位运算符

Lua 支持以下位运算符

所有按位运算都会将其操作数转换为整数(参见 §3.4.3),对这些整数的所有位进行操作,并生成一个整数。

右移和左移都会用零填充空位。负位移向另一个方向移动;绝对值等于或大于整数中位数的位移将导致零(因为所有位都移出)。

3.4.3 – 强制转换和转换

Lua 在运行时提供了一些类型和表示形式之间的自动转换。按位运算符始终将浮点数操作数转换为整数。指数和浮点数除法始终将整数操作数转换为浮点数。应用于混合数字(整数和浮点数)的所有其他算术运算都会将整数操作数转换为浮点数。C API 也根据需要将整数转换为浮点数,并将浮点数转换为整数。此外,除了字符串之外,字符串连接还接受数字作为参数。

在从整数转换为浮点数时,如果整数值具有作为浮点数的确切表示形式,则该表示形式即为结果。否则,转换将获得最近的高值或最近的低值可表示值。这种转换永远不会失败。

从浮点数转换为整数时,将检查浮点数是否具有作为整数的确切表示形式(即,浮点数具有整数值,并且在整数表示形式的范围内)。如果是,则该表示形式即为结果。否则,转换将失败。

Lua 中的几个地方在必要时将字符串强制转换为数字。特别是,字符串库设置元方法,尝试在所有算术运算中将字符串强制转换为数字。如果转换失败,则库将调用另一个操作数的元方法(如果存在)或引发错误。请注意,按位运算符不会执行此强制转换。

始终建议不要依赖于从字符串到数字的隐式强制转换,因为它们并不总是适用;特别是,"1"==1 为假,而 "1"<1 会引发错误(参见 §3.4.4)。这些强制转换主要出于兼容性考虑,并且可能会在未来版本的语言中删除。

字符串会根据其语法和 Lua 词法分析器的规则转换为整数或浮点数。字符串还可以有前导和尾随空格和符号。从字符串到数字的所有转换都接受点和当前区域标记作为基数字符。(但是,Lua 词法分析器只接受点。)如果字符串不是有效的数字,转换会失败。如果需要,此第一步的结果会根据之前浮点数和整数转换的规则转换为特定的数字子类型。

从数字到字符串的转换使用未指定的人类可读格式。若要以任何特定方式将数字转换为字符串,请使用函数 string.format

3.4.4 – 关系运算符

Lua 支持以下关系运算符

这些运算符始终返回 falsetrue

相等 (==) 首先比较其操作数的类型。如果类型不同,则结果为 false。否则,比较操作数的值。如果字符串具有相同的字节内容,则它们相等。如果数字表示相同数学值,则它们相等。

表格、用户数据和线程通过引用进行比较:只有当两个对象是同一对象时,才认为它们相等。每次创建新对象(表格、用户数据或线程)时,此新对象与任何先前存在对象都不同。函数始终等于自身。具有任何可检测差异(不同行为、不同定义)的函数始终不同。在不同时间创建但没有可检测差异的函数可以分类为相等或不相等(取决于内部缓存详细信息)。

您可以使用 __eq 元方法(请参阅 §2.4)更改 Lua 比较表格和用户数据的方式。

相等比较不会将字符串转换为数字,反之亦然。因此,"0"==0 求值为 false,而 t[0]t["0"] 表示表格中的不同条目。

运算符 ~= 正好是相等 (==) 的否定。

顺序运算符的工作方式如下。如果两个参数都是数字,则根据其数学值比较它们,而不管其子类型如何。否则,如果两个参数都是字符串,则根据当前区域设置比较其值。否则,Lua 尝试调用 __lt__le 元方法(请参阅 §2.4)。比较 a > b 转换为 b < a,比较 a >= b 转换为 b <= a

根据 IEEE 754 标准,特殊值 NaN 被认为既小于、不等于,也不大于任何值,包括它自身。

3.4.5 – 逻辑运算符

Lua 中的逻辑运算符是 andornot。与控制结构类似(参见 §3.3.4),所有逻辑运算符都将 falsenil 视为 false,将其他任何内容视为 true。

取反运算符 not 始终返回 falsetrue。连接运算符 and 在其第一个参数为 falsenil 时返回其第一个参数;否则,and 返回其第二个参数。析取运算符 or 在其第一个参数不等于 nilfalse 时返回其第一个参数;否则,or 返回其第二个参数。andor 都使用短路求值;也就是说,只有在必要时才计算第二个操作数。以下是一些示例

     10 or 20            --> 10
     10 or error()       --> 10
     nil or "a"          --> "a"
     nil and 10          --> nil
     false and error()   --> false
     false and nil       --> false
     false or nil        --> nil
     10 and 20           --> 20

3.4.6 – 连接

Lua 中的字符串连接运算符表示为两个点 ('..')。如果两个操作数都是字符串或数字,则数字将以未指定格式转换为字符串(参见 §3.4.3)。否则,将调用 __concat 元方法(参见 §2.4)。

3.4.7 – 长度运算符

长度运算符表示为一元前缀运算符 #

字符串的长度是其字节数。(当每个字符为一个字节时,这是字符串长度的通常含义。)

应用于表的长度运算符返回该表中的边框。表 t 中的边框是满足以下条件的任何非负整数

     (border == 0 or t[border] ~= nil) and
     (t[border + 1] == nil or border == math.maxinteger)

换句话说,边框是表中存在的任何正整数索引,其后跟一个不存在的索引,加上两个极限情况:0(当索引 1 不存在时);以及整数的最大值(当该索引存在时)。请注意,不是正整数的键不会干扰边框。

仅有一个边框的表称为序列。例如,表 {10, 20, 30, 40, 50} 是一个序列,因为它只有一个边框 (5)。表 {10, 20, 30, nil, 50} 有两个边框 (3 和 5),因此它不是一个序列。(索引 4 处的 nil 称为空洞。)表 {nil, 20, 30, nil, nil, 60, nil} 有三个边框 (0、3 和 6),因此它也不是一个序列。表 {} 是边框为 0 的序列。

t 是一个序列时,#t 返回其唯一的边框,这对应于序列长度的直观概念。当 t 不是一个序列时,#t 可以返回其任何边框。(确切的边框取决于表的内部表示的详细信息,而这些详细信息又取决于表的填充方式及其非数字键的内存地址。)

计算表的长度的最坏时间保证为 O(log n),其中 n 是表中最大的整数键。

程序可以通过 __len 元方法修改任何值(但字符串除外)的长度运算符的行为(请参阅 §2.4)。

3.4.8 – 优先级

Lua 中的运算符优先级遵循下表,从低到高优先级

     or
     and
     <     >     <=    >=    ~=    ==
     |
     ~
     &
     <<    >>
     ..
     +     -
     *     /     //    %
     unary operators (not   #     -     ~)
     ^

和往常一样,你可以使用括号来更改表达式的优先级。连接('..')和指数('^')运算符是右结合的。所有其他二元运算符都是左结合的。

3.4.9 – 表构造函数

表构造函数是创建表的表达式。每次计算一个构造函数时,都会创建一个新表。构造函数可用于创建空表或创建表并初始化其某些字段。构造函数的一般语法为

	tableconstructor ::= ‘{’ [fieldlist] ‘}’
	fieldlist ::= field {fieldsep field} [fieldsep]
	field ::= ‘[’ exp ‘]’ ‘=’ exp | Name ‘=’ exp | exp
	fieldsep ::= ‘,’ | ‘;

每个形式为 [exp1] = exp2 的字段都会向新表中添加一个键为 exp1、值为 exp2 的条目。形式为 name = exp 的字段等效于 ["name"] = exp。形式为 exp 的字段等效于 [i] = exp,其中 i 是从 1 开始的连续整数;其他格式的字段不会影响此计数。例如,

     a = { [f(1)] = g; "x", "y"; x = 1, f(x), [30] = 23; 45 }

等效于

     do
       local t = {}
       t[f(1)] = g
       t[1] = "x"         -- 1st exp
       t[2] = "y"         -- 2nd exp
       t.x = 1            -- t["x"] = 1
       t[3] = f(x)        -- 3rd exp
       t[30] = 23
       t[4] = 45          -- 4th exp
       a = t
     end

构造函数中赋值的顺序是未定义的。(只有在存在重复键时,此顺序才相关。)

如果列表中的最后一个字段形式为 exp 并且表达式是多结果表达式,则此表达式返回的所有值都会连续进入列表(请参阅 §3.4.12)。

字段列表可以有一个可选的尾部分隔符,以方便机器生成的代码。

3.4.10 – 函数调用

Lua 中的函数调用具有以下语法

	functioncall ::= prefixexp args

在函数调用中,首先计算 prefixexp 和 args。如果 prefixexp 的值类型为 function,则使用给定的参数调用此函数。否则,如果存在,则调用 prefixexp __call 元方法:它的第一个参数是 prefixexp 的值,后跟原始调用参数(请参阅 §2.4)。

形式

	functioncall ::= prefixexp ‘:’ Name args

可用于模拟方法。调用 v:name(args)v.name(v,args) 的语法糖,只不过 v 只计算一次。

参数具有以下语法

	args ::= ‘(’ [explist] ‘)’
	args ::= tableconstructor
	args ::= LiteralString

所有参数表达式都在调用之前计算。形式为 f{fields} 的调用是 f({fields}) 的语法糖;也就是说,参数列表是一个新的表。形式为 f'string'(或 f"string"f[[string]])的调用是 f('string') 的语法糖;也就是说,参数列表是一个单一的文字字符串。

形式为 return functioncall 的调用不在要关闭的变量的范围内,称为尾调用。Lua 实现正确的尾调用(或正确的尾递归):在尾调用中,被调用函数重用调用函数的堆栈条目。因此,程序可以执行的嵌套尾调用的数量没有限制。但是,尾调用会擦除有关调用函数的任何调试信息。请注意,尾调用仅发生在特定语法中,其中 return 有一个单一的函数调用作为参数,并且它不在任何要关闭的变量的范围内。此语法使调用函数返回被调用函数的返回,没有任何中间操作。因此,以下示例都不是尾调用

     return (f(x))        -- results adjusted to 1
     return 2 * f(x)      -- result multiplied by 2
     return x, f(x)       -- additional results
     f(x); return         -- results discarded
     return x or f(x)     -- results adjusted to 1

3.4.11 – 函数定义

函数定义的语法为

	functiondef ::= function funcbody
	funcbody ::= ‘(’ [parlist] ‘)’ block end

以下语法糖简化了函数定义

	stat ::= function funcname funcbody
	stat ::= local function Name funcbody
	funcname ::= Name {‘.’ Name} [‘:’ Name]

语句

     function f () body end

转换为

     f = function () body end

语句

     function t.a.b.c.f () body end

转换为

     t.a.b.c.f = function () body end

语句

     local function f () body end

转换为

     local f; f = function () body end

而不是

     local f = function () body end

(这仅在函数体包含对 f 的引用时有区别。)

函数定义是一个可执行表达式,其值具有类型函数。当 Lua 预编译一个块时,其所有函数体也会被预编译,但它们尚未创建。然后,每当 Lua 执行函数定义时,该函数就会实例化(或闭合)。此函数实例或闭包是表达式的最终值。

参数充当使用参数值初始化的局部变量

	parlist ::= namelist [‘,’ ‘...’] | ‘...

当调用 Lua 函数时,它会将参数列表调整为其参数列表的长度(参见 §3.4.12),除非该函数是变参函数,其参数列表末尾用三个点(“...”)表示。变参函数不会调整其参数列表;相反,它收集所有额外参数并通过变参表达式(也写为三个点)将它们提供给函数。此表达式的值是所有实际额外参数的列表,类似于具有多个结果的函数(参见 §3.4.12)。

例如,考虑以下定义

     function f(a, b) end
     function g(a, b, ...) end
     function r() return 1,2,3 end

然后,我们有以下从参数到参数和变参表达式的映射

     CALL             PARAMETERS
     
     f(3)             a=3, b=nil
     f(3, 4)          a=3, b=4
     f(3, 4, 5)       a=3, b=4
     f(r(), 10)       a=1, b=10
     f(r())           a=1, b=2
     
     g(3)             a=3, b=nil, ... -->  (nothing)
     g(3, 4)          a=3, b=4,   ... -->  (nothing)
     g(3, 4, 5, 8)    a=3, b=4,   ... -->  5  8
     g(5, r())        a=5, b=1,   ... -->  2  3

使用 return 语句返回结果(参见 §3.3.4)。如果控制到达函数末尾而没有遇到 return 语句,则函数不带任何结果返回。

函数可以返回的值的数量有一个与系统相关的限制。此限制保证大于 1000。

冒号语法用于模拟方法,为函数添加一个隐式额外参数 self。因此,语句

     function t.a.b.c:f (params) body end

是以下内容的语法糖

     t.a.b.c.f = function (self, params) body end

3.4.12 – 表达式列表、多个结果和调整

函数调用和变参表达式都可以产生多个值。这些表达式称为多重表达式

当一个多值表达式用作表达式列表的最后一个元素时,该表达式中的所有结果都会被添加到由表达式列表产生的值列表中。请注意,在需要表达式列表的地方出现的单个表达式是该(单例)列表中的最后一个表达式。

Lua 需要表达式列表的地方如下

在后四种情况下,来自表达式列表的值列表必须调整为特定长度:非变参函数调用中的参数数量(参见 §3.4.11),多重赋值或局部声明中的变量数量,以及通用 for 循环中的四个值。调整遵循以下规则:如果值多于需要,则丢弃多余的值;如果值少于需要,则用 nil 扩展列表。当表达式列表以多值表达式结尾时,该表达式中的所有结果都会在调整之前进入值列表。

当多值表达式用在表达式列表中且不是最后一个元素,或用在语法需要单个表达式的的地方时,Lua 会将该表达式的结果列表调整为一个元素。作为一个特例,语法需要在括号表达式中使用单个表达式;因此,在多值表达式周围添加括号会强制它产生一个结果。

我们很少需要在语法期望单一表达式的某个位置使用变参表达式。(通常在变参部分之前添加一个常规参数并使用该参数会更简单。)当有这种需要时,我们建议将变参表达式分配给一个单一变量,并在其位置使用该变量。

以下是一些使用多结果表达式的示例。在所有情况下,当构造需要“第 n 个结果”时,如果没有这样的结果,它将使用 nil

     print(x, f())      -- prints x and all results from f().
     print(x, (f()))    -- prints x and the first result from f().
     print(f(), x)      -- prints the first result from f() and x.
     print(1 + f())     -- prints 1 added to the first result from f().
     local x = ...      -- x gets the first vararg argument.
     x,y = ...          -- x gets the first vararg argument,
                        -- y gets the second vararg argument.
     x,y,z = w, f()     -- x gets w, y gets the first result from f(),
                        -- z gets the second result from f().
     x,y,z = f()        -- x gets the first result from f(),
                        -- y gets the second result from f(),
                        -- z gets the third result from f().
     x,y,z = f(), g()   -- x gets the first result from f(),
                        -- y gets the first result from g(),
                        -- z gets the second result from g().
     x,y,z = (f())      -- x gets the first result from f(), y and z get nil.
     return f()         -- returns all results from f().
     return x, ...      -- returns x and all received vararg arguments.
     return x,y,f()     -- returns x, y, and all results from f().
     {f()}              -- creates a list with all results from f().
     {...}              -- creates a list with all vararg arguments.
     {f(), 5}           -- creates a list with the first result from f() and 5.

3.5 – 可见性规则

Lua 是一种词法作用域语言。局部变量的作用域从其声明后的第一个语句开始,并持续到包含声明的最内层块的最后一个非空语句。(空语句是标签和空语句。)考虑以下示例

     x = 10                -- global variable
     do                    -- new block
       local x = x         -- new 'x', with value 10
       print(x)            --> 10
       x = x+1
       do                  -- another block
         local x = x+1     -- another 'x'
         print(x)          --> 12
       end
       print(x)            --> 11
     end
     print(x)              --> 10  (the global one)

请注意,在类似于 local x = x 的声明中,正在声明的新 x 尚未在作用域中,因此第二个 x 引用外部变量。

由于词法作用域规则,内部函数可以自由访问其作用域内定义的局部变量。内部函数中由内部函数使用的局部变量称为上值(或外部局部变量,或简称外部变量)。

请注意,每次执行 local 语句都会定义新的局部变量。考虑以下示例

     a = {}
     local x = 20
     for i = 1, 10 do
       local y = 0
       a[i] = function () y = y + 1; return x + y end
     end

循环创建十个闭包(即匿名函数的十个实例)。这些闭包中的每一个都使用不同的 y 变量,而它们都共享相同的 x

4 – 应用程序接口

本节介绍 Lua 的 C API,即主机程序可用于与 Lua 通信的一组 C 函数。所有 API 函数以及相关的类型和常量都在头文件 lua.h 中声明。

即使我们使用术语“函数”,API 中的任何功能都可以作为宏来提供。除非另有说明,所有此类宏都使用它们的每个参数恰好一次(第一个参数除外,它始终是 Lua 状态),因此不会产生任何隐藏的副作用。

与大多数 C 库一样,Lua API 函数不会检查其参数的有效性或一致性。但是,您可以通过使用已定义的宏 LUA_USE_APICHECK 编译 Lua 来更改此行为。

Lua 库是完全可重入的:它没有全局变量。它将所需的所有信息保存在一个动态结构中,称为Lua 状态

每个 Lua 状态都具有一个或多个线程,它们对应于独立的协作执行行。类型 lua_State(尽管其名称如此)引用线程。(间接地,通过线程,它还引用与线程关联的 Lua 状态。)

必须将指向线程的指针作为库中每个函数的第一个参数传递,除了 lua_newstate,它从头创建一个 Lua 状态并返回一个指向新状态中主线程的指针。

4.1 – 堆栈

Lua 使用虚拟堆栈向 C 传递值并从 C 接收值。此堆栈中的每个元素都表示一个 Lua 值(nil、数字、字符串等)。API 中的函数可以通过它们接收的 Lua 状态参数访问此堆栈。

每当 Lua 调用 C 时,被调用的函数都会获得一个新堆栈,该堆栈独立于以前的堆栈和仍然处于活动状态的 C 函数的堆栈。此堆栈最初包含 C 函数的任何参数,并且 C 函数可以在其中存储临时的 Lua 值,并且必须将其结果压入以返回给调用者(请参阅 lua_CFunction)。

为方便起见,API 中的大多数查询操作都不遵循严格的堆栈规则。相反,它们可以通过使用索引来引用堆栈中的任何元素:正索引表示绝对堆栈位置,从堆栈底部的 1 开始;负索引表示相对于堆栈顶部的偏移量。更具体地说,如果堆栈有 n 个元素,则索引 1 表示第一个元素(即第一个压入堆栈的元素),索引 n 表示最后一个元素;索引 -1 也表示最后一个元素(即顶部的元素),索引 -n 表示第一个元素。

4.1.1 – 堆栈大小

当您与 Lua API 交互时,您有责任确保一致性。特别是,您有责任控制堆栈溢出。当您调用任何 API 函数时,您必须确保堆栈有足够的空间来容纳结果。

上述规则有一个例外:当您调用没有固定数量结果的 Lua 函数时(请参阅 lua_call),Lua 会确保堆栈有足够的空间容纳所有结果。但是,它不会确保任何额外的空间。因此,在这样的调用之后将任何内容压入堆栈之前,您应该使用 lua_checkstack

每当 Lua 调用 C 时,它都会确保堆栈至少有 LUA_MINSTACK 个额外的元素空间;也就是说,您可以安全地将最多 LUA_MINSTACK 个值压入其中。LUA_MINSTACK 被定义为 20,因此通常不必担心堆栈空间,除非您的代码有将元素压入堆栈的循环。在必要时,您可以使用函数 lua_checkstack 来确保堆栈有足够的空间来压入新元素。

4.1.2 – 有效和可接受的索引

API 中接收堆栈索引的任何函数仅适用于有效索引可接受索引

有效索引是指引用存储可修改 Lua 值的位置的索引。它包括 1 到堆栈顶部的堆栈索引(1 ≤ abs(index) ≤ top)以及伪索引,伪索引表示 C 代码可以访问但不在堆栈中的某些位置。伪索引用于访问注册表(请参阅 §4.3)和 C 函数的上值(请参阅 §4.2)。

不需要特定可变位置,但只需要一个值(例如,查询函数)的函数可以用可接受的索引调用。可接受的索引可以是任何有效索引,但它也可以是栈顶之后栈分配空间内的任何正索引,即,索引最多可达栈大小。(注意 0 永远不是可接受的索引。)大于当前 C 函数中实际 upvalue 数的 upvalue(参见 §4.2)的索引也是可接受的(但无效)。除非另有说明,API 中的函数使用可接受的索引。

可接受的索引有助于在查询栈时避免对栈顶进行额外的测试。例如,C 函数可以查询其第三个参数,而无需检查是否存在第三个参数,即,无需检查 3 是否为有效索引。

对于可以用可接受的索引调用的函数,任何非有效索引都将被视为包含虚拟类型 LUA_TNONE 的值,其行为类似于 nil 值。

4.1.3 – 指向字符串的指针

API 中的几个函数返回栈中 Lua 字符串的指针(const char*)。(参见 lua_pushfstringlua_pushlstringlua_pushstringlua_tolstring。还参见辅助库中的 luaL_checklstringluaL_checkstringluaL_tolstring。)

通常,Lua 的垃圾回收可以释放或移动内部内存,然后使指向内部字符串的指针失效。为了允许安全使用这些指针,API 保证在该索引处的字符串值未从栈中移除时,指向栈索引中字符串的任何指针都是有效的。(不过,它可以移动到另一个索引。)当索引是伪索引(指代 upvalue)时,指针在相应的调用处于活动状态且相应的 upvalue 未被修改时有效。

调试接口中的一些函数也会返回指向字符串的指针,即 lua_getlocallua_getupvaluelua_setlocallua_setupvalue。对于这些函数,在调用函数处于活动状态且给定的闭包(如果已提供)位于堆栈中时,该指针保证有效。

除了这些保证之外,垃圾回收器可以使任何指向内部字符串的指针无效。

4.2 – C 闭包

在创建 C 函数时,可以将一些值与其关联,从而创建一个C 闭包(参见 lua_pushcclosure);这些值称为上值,并且在每次调用函数时都可以访问这些值。

每当调用 C 函数时,其上值都位于特定的伪索引处。这些伪索引由宏 lua_upvalueindex 生成。与函数关联的第一个上值位于索引 lua_upvalueindex(1),依此类推。对 lua_upvalueindex(n) 的任何访问,其中 n 大于当前函数的上值数(但不大于 256,该值是闭包中最大上值数加一),都会产生一个可接受但无效的索引。

C 闭包还可以更改其相应上值的值。

4.3 – 注册表

Lua 提供了一个注册表,这是一个预定义的表,任何 C 代码都可以使用它来存储其需要存储的任何 Lua 值。注册表始终可在伪索引 LUA_REGISTRYINDEX 处访问。任何 C 库都可以将数据存储到此表中,但它必须注意选择与其他库使用的键不同的键,以避免冲突。通常,您应该使用包含库名称的字符串、带有代码中 C 对象地址的轻量级用户数据或由代码创建的任何 Lua 对象作为键。与变量名称一样,以下划线开头并后跟大写字母的字符串键是为 Lua 保留的。

注册表中的整数键由引用机制(参见 luaL_ref)和一些预定义值使用。因此,注册表中的整数键不得用于其他目的。

当您创建新的 Lua 状态时,其注册表会附带一些预定义值。这些预定义值使用在 lua.h 中定义为常量的整数键进行索引。定义了以下常量

4.4 – C 中的错误处理

在内部,Lua 使用 C longjmp 功能来处理错误。(如果您将其编译为 C++,Lua 将使用异常;在源代码中搜索 LUAI_THROW 以获取详细信息。)当 Lua 遇到任何错误(如内存分配错误或类型错误)时,它会引发一个错误;也就是说,它会执行一个 long jump。一个受保护的环境使用 setjmp 设置一个恢复点;任何错误都会跳转到最近的活动恢复点。

在 C 函数内部,您可以通过调用 lua_error 显式引发错误。

API 中的大多数函数都可以引发错误,例如由于内存分配错误。每个函数的文档都会指出它是否可以引发错误。

如果在任何受保护的环境之外发生错误,Lua 将调用一个恐慌函数(参见 lua_atpanic),然后调用 abort,从而退出宿主应用程序。您的恐慌函数可以通过永不返回(例如,执行 long jump 到 Lua 外部的您自己的恢复点)来避免此退出。

顾名思义,恐慌函数是一种最后的手段。程序应该避免它。作为一般规则,当 Lua 使用 Lua 状态调用 C 函数时,它可以在该 Lua 状态上执行任何它想要的操作,因为它应该已经受到保护。但是,当 C 代码在其他 Lua 状态上操作时(例如,函数的 Lua 状态参数、存储在注册表中的 Lua 状态或 lua_newthread 的结果),它应该仅在不会引发错误的 API 调用中使用它们。

恐慌函数的运行方式就像一个消息处理程序(参见 §2.3);特别是,错误对象位于堆栈的顶部。但是,无法保证堆栈空间。要将任何内容推送到堆栈,恐慌函数必须首先检查可用空间(参见 §4.1.1)。

4.4.1 – 状态码

API 中报告错误的几个函数使用以下状态码来指示不同类型的错误或其他条件

这些常量在头文件 lua.h 中定义。

4.5 – 在 C 中处理让步

在内部,Lua 使用 C longjmp 机制让步协程。因此,如果 C 函数 foo 调用 API 函数,并且此 API 函数让步(直接或间接地通过调用让步的另一个函数),则 Lua 无法再返回到 foo,因为 longjmp 从 C 堆栈中删除了其帧。

为了避免此类问题,Lua 在尝试跨 API 调用让步时引发错误,但三个函数除外:lua_yieldklua_callklua_pcallk。所有这些函数都接收一个延续函数(作为名为 k 的参数)以在让步后继续执行。

我们需要设置一些术语来解释延续。我们有一个从 Lua 调用的 C 函数,我们将称之为原始函数。然后,此原始函数调用 C API 中的这三个函数之一,我们将称之为被调用函数,然后让步当前线程。当被调用函数是 lua_yieldk 时,或者当被调用函数是 lua_callklua_pcallk 并且它们调用的函数让步时,可能会发生这种情况。

假设运行线程在执行被调用函数时让步。线程恢复后,它最终将完成被调用函数的运行。但是,被调用函数无法返回到原始函数,因为其在 C 堆栈中的帧已被让步销毁。相反,Lua 调用延续函数,该函数作为参数传递给被调用函数。顾名思义,延续函数应继续原始函数的任务。

作为一个示例,考虑以下函数

     int original_function (lua_State *L) {
       ...     /* code 1 */
       status = lua_pcall(L, n, m, h);  /* calls Lua */
       ...     /* code 2 */
     }

现在,我们希望允许 lua_pcall 运行的 Lua 代码让步。首先,我们可以像这里这样重写我们的函数

     int k (lua_State *L, int status, lua_KContext ctx) {
       ...  /* code 2 */
     }
     
     int original_function (lua_State *L) {
       ...     /* code 1 */
       return k(L, lua_pcall(L, n, m, h), ctx);
     }

在上面的代码中,新函数 k 是一个延续函数(类型为 lua_KFunction),它应该完成原始函数在调用 lua_pcall 后所做的所有工作。现在,我们必须通知 Lua,如果 lua_pcall 执行的 Lua 代码以某种方式(错误或让步)被中断,它必须调用 k,因此我们重写代码如下,用 lua_pcallk 替换 lua_pcall

     int original_function (lua_State *L) {
       ...     /* code 1 */
       return k(L, lua_pcallk(L, n, m, h, ctx2, k), ctx1);
     }

请注意对延续的外部显式调用:Lua 仅在需要时才调用延续,即在出现错误或在让步后恢复时。如果被调用的函数正常返回且从未让步,lua_pcallk(和 lua_callk)也将正常返回。(当然,在这种情况下,您可以直接在原始函数内部执行等效工作,而不是调用延续。)

除了 Lua 状态,延续函数还有两个其他参数:调用的最终状态和最初传递给 lua_pcallk 的上下文值 (ctx)。Lua 不使用此上下文值;它仅将此值从原始函数传递到延续函数。对于 lua_pcallk,状态与 lua_pcallk 将返回的值相同,但它在让步后执行时为 LUA_YIELD(而不是 LUA_OK)。对于 lua_yieldklua_callk,当 Lua 调用延续时,状态始终为 LUA_YIELD。(对于这两个函数,Lua 在出现错误的情况下不会调用延续,因为它们不处理错误。)类似地,在使用 lua_callk 时,您应该使用 LUA_OK 作为状态调用延续函数。(对于 lua_yieldk,直接调用延续函数意义不大,因为 lua_yieldk 通常不会返回。)

Lua 将延续函数视为原始函数。延续函数从原始函数接收相同的 Lua 堆栈,状态与被调用函数返回时的状态相同。(例如,在 lua_callk 之后,函数及其参数将从堆栈中删除,并替换为调用结果。)它还具有相同的向上值。它返回的任何内容都将由 Lua 处理,就好像它是原始函数的返回一样。

4.6 – 函数和类型

此处我们按字母顺序列出 C API 中的所有函数和类型。每个函数都有一个指示符,如下所示:[-o, +p, x]

第一个字段 o 表示函数从栈中弹出的元素数量。第二个字段 p 表示函数推入栈中的元素数量。(任何函数在弹出其参数后总是会推送其结果。)形式为 x|y 的字段表示函数可以推送(或弹出)xy 个元素,具体取决于情况;问号“?”表示我们无法仅通过查看函数的参数来得知函数弹出/推送的元素数量。(例如,它们可能取决于栈中的内容。)第三个字段 x 表示函数是否可能引发错误:“-”表示函数从不引发任何错误;“m”表示函数可能仅引发内存不足错误;“v”表示函数可能引发文本中说明的错误;“e”表示函数可以直接或通过元方法运行任意 Lua 代码,因此可能引发任何错误。


lua_absindex

[-0, +0, –]

int lua_absindex (lua_State *L, int idx);

将可接受的索引 idx 转换为等效的绝对索引(即不依赖于栈大小的索引)。


lua_Alloc

typedef void * (*lua_Alloc) (void *ud,
                             void *ptr,
                             size_t osize,
                             size_t nsize);

Lua 状态使用的内存分配函数的类型。分配器函数必须提供类似于 realloc 的功能,但又不完全相同。其参数为 ud,传递给 lua_newstate 的不透明指针;ptr,指向正在分配/重新分配/释放的块的指针;osize,块的原始大小或有关正在分配的内容的一些代码;以及 nsize,块的新大小。

ptr 不为 NULL 时,osizeptr 指向的块的大小,即分配或重新分配时给定的大小。

ptrNULL 时,osize 编码 Lua 正在分配的对象的类型。osizeLUA_TSTRINGLUA_TTABLELUA_TFUNCTIONLUA_TUSERDATALUA_TTHREAD 中的任何一个(仅当)Lua 创建该类型的新对象时。当 osize 为其他值时,Lua 正在为其他内容分配内存。

Lua 假设分配器函数具有以下行为

nsize 为零时,分配器必须表现得像 free,然后返回 NULL

nsize 不为零时,分配器必须表现得像 realloc。特别是,当且仅当分配器无法满足请求时,分配器才会返回 NULL

以下是分配器函数的简单实现。它在辅助库中由 luaL_newstate 使用。

     static void *l_alloc (void *ud, void *ptr, size_t osize,
                                                size_t nsize) {
       (void)ud;  (void)osize;  /* not used */
       if (nsize == 0) {
         free(ptr);
         return NULL;
       }
       else
         return realloc(ptr, nsize);
     }

请注意,ISO C 确保 free(NULL) 无效,并且 realloc(NULL,size) 等效于 malloc(size)


lua_arith

[-(2|1), +1, e]

void lua_arith (lua_State *L, int op);

对位于栈顶的两个值(在取反的情况下为一个值)执行算术或位运算,其中位于栈顶的值为第二个操作数,弹出这些值,并压入运算结果。该函数遵循相应的 Lua 运算符的语义(即,它可能会调用元方法)。

op 的值必须为以下常量之一


lua_atpanic

[-0, +0, –]

lua_CFunction lua_atpanic (lua_State *L, lua_CFunction panicf);

设置新的 panic 函数并返回旧的 panic 函数(参见 §4.4)。


lua_call

[-(nargs+1), +nresults, e]

void lua_call (lua_State *L, int nargs, int nresults);

调用函数。与常规 Lua 调用类似,lua_call 遵循 __call 元方法。因此,此处“函数”一词表示任何可调用值。

要进行调用,你必须使用以下协议:首先,将要调用的函数压入堆栈;然后,按顺序将调用参数压入堆栈;也就是说,先压入第一个参数。最后,调用 lua_callnargs 是你压入堆栈的参数数量。当函数返回时,所有参数和函数值都会弹出,并将调用结果压入堆栈。结果数量会调整为 nresults,除非 nresultsLUA_MULTRET。在这种情况下,将压入函数的所有结果;Lua 会确保返回的值适合堆栈空间,但不会确保堆栈中有任何额外空间。函数结果会按顺序压入堆栈(先压入第一个结果),以便在调用之后,最后一个结果位于堆栈顶部。

在调用和运行函数时发生的任何错误都会向上传播(使用 longjmp)。

以下示例展示了主机程序如何执行等效于此 Lua 代码的操作

     a = f("how", t.x, 14)

以下是 C 代码

     lua_getglobal(L, "f");                  /* function to be called */
     lua_pushliteral(L, "how");                       /* 1st argument */
     lua_getglobal(L, "t");                    /* table to be indexed */
     lua_getfield(L, -1, "x");        /* push result of t.x (2nd arg) */
     lua_remove(L, -2);                  /* remove 't' from the stack */
     lua_pushinteger(L, 14);                          /* 3rd argument */
     lua_call(L, 3, 1);     /* call 'f' with 3 arguments and 1 result */
     lua_setglobal(L, "a");                         /* set global 'a' */

请注意,以上代码是平衡的:在代码结束时,堆栈已恢复到其原始配置。这被认为是良好的编程实践。


lua_callk

[-(nargs + 1), +nresults, e]

void lua_callk (lua_State *L,
                int nargs,
                int nresults,
                lua_KContext ctx,
                lua_KFunction k);

此函数的行为与 lua_call 完全相同,但允许被调用函数让步(请参阅 §4.5)。


lua_CFunction

typedef int (*lua_CFunction) (lua_State *L);

C 函数的类型。

为了与 Lua 正确通信,C 函数必须使用以下协议,该协议定义了传递参数和结果的方式:C 函数从 Lua 中按顺序接收其堆栈中的参数(先压入第一个参数)。因此,当函数启动时,lua_gettop(L) 会返回函数接收到的参数数量。第一个参数(如果存在)位于索引 1,最后一个参数位于索引 lua_gettop(L)。要向 Lua 返回值,C 函数只需按顺序(先压入第一个结果)将它们压入堆栈,并在 C 中返回结果数量。结果下方堆栈中的任何其他值都会被 Lua 正确丢弃。与 Lua 函数一样,Lua 调用的 C 函数也可以返回多个结果。

例如,以下函数接收可变数量的数字参数,并返回它们的平均值和总和

     static int foo (lua_State *L) {
       int n = lua_gettop(L);    /* number of arguments */
       lua_Number sum = 0.0;
       int i;
       for (i = 1; i <= n; i++) {
         if (!lua_isnumber(L, i)) {
           lua_pushliteral(L, "incorrect argument");
           lua_error(L);
         }
         sum += lua_tonumber(L, i);
       }
       lua_pushnumber(L, sum/n);        /* first result */
       lua_pushnumber(L, sum);         /* second result */
       return 2;                   /* number of results */
     }

lua_checkstack

[-0, +0, –]

int lua_checkstack (lua_State *L, int n);

确保栈至少有 n 个额外元素的空间,也就是说,你可以安全地向其中压入最多 n 个值。如果无法满足请求,它将返回 false,原因可能是它会导致栈大于固定的最大大小(通常至少有几千个元素),或者它无法为额外空间分配内存。此函数绝不会缩小栈;如果栈已经为额外元素留有空间,则它将保持不变。


lua_close

[-0, +0, –]

void lua_close (lua_State *L);

关闭主线程中所有活动待关闭变量,释放给定 Lua 状态中的所有对象(如果存在,则调用相应的垃圾回收元方法),并释放此状态使用的所有动态内存。

在一些平台上,你可能不需要调用此函数,因为当宿主程序结束时,所有资源都会自然释放。另一方面,创建多个状态的长期运行程序(例如守护进程或 Web 服务器)可能需要在不再需要状态时立即关闭状态。


lua_closeslot

[-0, +0, e]

void lua_closeslot (lua_State *L, int index);

关闭给定索引处的待关闭槽,并将其值设为 nil。索引必须是先前标记为待关闭的最后一个索引(请参阅 lua_toclose),并且仍然处于活动状态(即尚未关闭)。

通过此函数调用时,__close 元方法不能产生。

(此函数在版本 5.4.3 中引入。)


lua_closethread

[-0, +?, –]

int lua_closethread (lua_State *L, lua_State *from);

重置线程,清理其调用栈并关闭所有待关闭的变量。返回状态代码:线程中没有错误(停止线程的原始错误或关闭方法中的错误)时为 LUA_OK,否则为错误状态。如果发生错误,则将错误对象保留在栈的顶部。

参数 from 表示正在重置 L 的协程。如果没有这样的协程,此参数可以是 NULL

(此函数在版本 5.4.6 中引入。)


lua_compare

[-0, +0, e]

int lua_compare (lua_State *L, int index1, int index2, int op);

比较两个 Lua 值。如果索引 index1 处的值与索引 index2 处的值比较时满足 op,则返回 1,遵循相应 Lua 运算符的语义(即,它可以调用元方法)。否则返回 0。如果任何索引无效,也返回 0。

op 的值必须为以下常量之一


lua_concat

[-n, +1, e]

void lua_concat (lua_State *L, int n);

连接栈顶的 n 个值,弹出它们,并将结果保留在栈顶。如果 n 为 1,结果是栈上的单个值(即,该函数不执行任何操作);如果 n 为 0,结果为空字符串。连接按照 Lua 的通常语义执行(参见 §3.4.6)。


lua_copy

[-0, +0, –]

void lua_copy (lua_State *L, int fromidx, int toidx);

将索引 fromidx 处的元素复制到有效索引 toidx 中,替换该位置的值。其他位置的值不受影响。


lua_createtable

[-0, +1, m]

void lua_createtable (lua_State *L, int narr, int nrec);

创建一个新的空表并将其压入栈中。参数 narr 是一个提示,表示该表将作为序列包含多少个元素;参数 nrec 是一个提示,表示该表将包含多少个其他元素。Lua 可能会使用这些提示为新表预分配内存。当您预先知道表将包含多少个元素时,这种预分配可能有助于提高性能。否则,您可以使用函数 lua_newtable


lua_dump

[-0, +0, –]

int lua_dump (lua_State *L,
                        lua_Writer writer,
                        void *data,
                        int strip);

将函数转储为二进制块。接收栈顶的 Lua 函数,并生成一个二进制块,如果再次加载,将生成一个等效于转储函数的函数。在生成块的部分时,lua_dump 使用给定的 data 调用函数 writer(参见 lua_Writer)来写入它们。

如果 strip 为真,则二进制表示可能不包含函数的所有调试信息,以节省空间。

返回的值是 writer 最后一次调用返回的错误代码;0 表示没有错误。

此函数不会从栈中弹出 Lua 函数。


lua_error

[-1, +0, v]

int lua_error (lua_State *L);

引发 Lua 错误,使用栈顶的值作为错误对象。此函数执行长跳转,因此永远不会返回(参见 luaL_error)。


lua_gc

[-0, +0, –]

int lua_gc (lua_State *L, int what, ...);

控制垃圾回收器。

此函数根据参数 what 的值执行多项任务。对于需要额外参数的选项,它们在选项后列出。

有关这些选项的更多详细信息,请参阅 collectgarbage

此函数不应由终结器调用。


lua_getallocf

[-0, +0, –]

lua_Alloc lua_getallocf (lua_State *L, void **ud);

返回给定状态的内存分配函数。如果 ud 不为 NULL,则 Lua 会将设置内存分配器函数时给出的不透明指针存储在 *ud 中。


lua_getfield

[-0, +1, e]

int lua_getfield (lua_State *L, int index, const char *k);

将值 t[k] 推送到堆栈上,其中 t 是给定索引处的值。与 Lua 中一样,此函数可能会触发“index”事件的元方法(请参阅 §2.4)。

返回推送值的类型。


lua_getextraspace

[-0, +0, –]

void *lua_getextraspace (lua_State *L);

返回与给定 Lua 状态关联的原始内存区域的指针。应用程序可以将此区域用于任何目的;Lua 不会将其用于任何目的。

每个新线程都使用主线程的区域副本初始化此区域。

默认情况下,此区域的大小为指向 void 的指针的大小,但您可以使用不同的区域大小重新编译 Lua。(请参阅 luaconf.h 中的 LUA_EXTRASPACE。)


lua_getglobal

[-0, +1, e]

int lua_getglobal (lua_State *L, const char *name);

将全局name的值推送到堆栈。返回该值类型。


lua_geti

[-0, +1, e]

int lua_geti (lua_State *L, int index, lua_Integer i);

将值t[i]推送到堆栈,其中t是给定索引处的值。与 Lua 中一样,此函数可能会触发“index”事件的元方法(参见 §2.4)。

返回推送值的类型。


lua_getmetatable

[-0, +(0|1), –]

int lua_getmetatable (lua_State *L, int index);

如果给定索引处的具有元表,则该函数将该元表推送到堆栈并返回 1。否则,该函数返回 0 且不将任何内容推送到堆栈。


lua_gettable

[-1, +1, e]

int lua_gettable (lua_State *L, int index);

将值t[k]推送到堆栈,其中t是给定索引处的值,k是堆栈顶部的值。

此函数从堆栈弹出键,将其位置处的结果值推入。与 Lua 中一样,此函数可能会触发“index”事件的元方法(参见 §2.4)。

返回推送值的类型。


lua_gettop

[-0, +0, –]

int lua_gettop (lua_State *L);

返回堆栈中顶部元素的索引。由于索引从 1 开始,因此此结果等于堆栈中的元素数;特别是,0 表示空堆栈。


lua_getiuservalue

[-0, +1, –]

int lua_getiuservalue (lua_State *L, int index, int n);

将与给定索引处的完整用户数据关联的第n个用户值推送到堆栈,并返回所推值类型。

如果用户数据没有该值,则推入nil 并返回 LUA_TNONE


lua_insert

[-1, +1, –]

void lua_insert (lua_State *L, int index);

将顶部元素移动到给定的有效索引中,向上移动此索引以上的元素以腾出空间。此函数不能使用伪索引调用,因为伪索引不是实际的堆栈位置。


lua_Integer

typedef ... lua_Integer;

Lua 中整数的类型。

默认情况下,此类型为long long(通常为 64 位二进制补码整数),但可以将其更改为longint(通常为 32 位二进制补码整数)。(参见 luaconf.h 中的 LUA_INT_TYPE。)

Lua 还定义了常量 LUA_MININTEGERLUA_MAXINTEGER,其中包含适合此类型的最小值和最大值。


lua_isboolean

[-0, +0, –]

int lua_isboolean (lua_State *L, int index);

如果给定索引处的值为布尔值,则返回 1,否则返回 0 。


lua_iscfunction

[-0, +0, –]

int lua_iscfunction (lua_State *L, int index);

如果给定索引处的值为 C 函数,则返回 1,否则返回 0 。


lua_isfunction

[-0, +0, –]

int lua_isfunction (lua_State *L, int index);

如果给定索引处的值为函数(C 或 Lua),则返回 1,否则返回 0 。


lua_isinteger

[-0, +0, –]

int lua_isinteger (lua_State *L, int index);

如果给定索引处的值为整数(即该值为数字且表示为整数),则返回 1,否则返回 0。


lua_islightuserdata

[-0, +0, –]

int lua_islightuserdata (lua_State *L, int index);

如果给定索引处的值为轻量级用户数据,则返回 1,否则返回 0。


lua_isnil

[-0, +0, –]

int lua_isnil (lua_State *L, int index);

如果给定索引处的值为 nil,则返回 1,否则返回 0。


lua_isnone

[-0, +0, –]

int lua_isnone (lua_State *L, int index);

如果给定索引无效,则返回 1,否则返回 0。


lua_isnoneornil

[-0, +0, –]

int lua_isnoneornil (lua_State *L, int index);

如果给定索引无效或该索引处的值为 nil,则返回 1,否则返回 0。


lua_isnumber

[-0, +0, –]

int lua_isnumber (lua_State *L, int index);

如果给定索引处的值为数字或可转换为数字的字符串,则返回 1,否则返回 0。


lua_isstring

[-0, +0, –]

int lua_isstring (lua_State *L, int index);

如果给定索引处的值为字符串或数字(始终可转换为字符串),则返回 1,否则返回 0。


lua_istable

[-0, +0, –]

int lua_istable (lua_State *L, int index);

如果给定索引处的值为表,则返回 1,否则返回 0。


lua_isthread

[-0, +0, –]

int lua_isthread (lua_State *L, int index);

如果给定索引处的值为线程,则返回 1,否则返回 0。


lua_isuserdata

[-0, +0, –]

int lua_isuserdata (lua_State *L, int index);

如果给定索引处的值为用户数据(完整或轻量级),则返回 1,否则返回 0。


lua_isyieldable

[-0, +0, –]

int lua_isyieldable (lua_State *L);

如果给定的协程可以生成,则返回 1,否则返回 0。


lua_KContext

typedef ... lua_KContext;

用于延续函数上下文的类型。它必须为数字类型。当 intptr_t 可用时,此类型定义为 intptr_t,以便它也可以存储指针。否则,它定义为 ptrdiff_t


lua_KFunction

typedef int (*lua_KFunction) (lua_State *L, int status, lua_KContext ctx);

用于延续函数的类型(请参阅 §4.5)。


lua_len

[-0, +1, e]

void lua_len (lua_State *L, int index);

返回给定索引处的值的长度。它等效于 Lua 中的“#”运算符(请参阅 §3.4.7),并且可能会触发“length”事件的元方法(请参阅 §2.4)。结果将推入堆栈。


lua_load

[-0, +1, –]

int lua_load (lua_State *L,
              lua_Reader reader,
              void *data,
              const char *chunkname,
              const char *mode);

加载 Lua 块而不运行它。如果没有错误,lua_load 会将已编译的块作为 Lua 函数推入堆栈顶部。否则,它会推送一条错误消息。

lua_load 函数使用用户提供的 reader 函数来读取块(请参阅 lua_Reader)。data 参数是不透明的值,传递给 reader 函数。

chunkname 参数为代码块指定一个名称,用于错误消息和调试信息(参见 §4.7)。

lua_load 会自动检测代码块是文本还是二进制文件,并相应地加载(参见程序 luac)。字符串 mode 的作用与函数 load 中相同,此外,NULL 值等效于字符串“bt”。

lua_load 在内部使用堆栈,因此读取器函数在返回时必须始终保持堆栈不变。

lua_load 可以返回 LUA_OKLUA_ERRSYNTAXLUA_ERRMEM。该函数还可以返回与读取器函数引发的错误相对应的其他值(参见 §4.4.1)。

如果结果函数具有上值,则其第一个上值将设置为存储在注册表中索引为 LUA_RIDX_GLOBALS 的全局环境的值(参见 §4.3)。在加载主代码块时,此上值将是 _ENV 变量(参见 §2.2)。其他上值将使用 nil 初始化。


lua_newstate

[-0, +0, –]

lua_State *lua_newstate (lua_Alloc f, void *ud);

创建一个新的独立状态并返回其主线程。如果无法创建状态(由于内存不足),则返回 NULL。参数 f 是分配器函数;Lua 将通过此函数为该状态执行所有内存分配(参见 lua_Alloc)。第二个参数 ud 是一个不透明指针,Lua 在每次调用中都会将其传递给分配器。


lua_newtable

[-0, +1, m]

void lua_newtable (lua_State *L);

创建一个新的空表并将其压入堆栈。它等效于 lua_createtable(L, 0, 0)


lua_newthread

[-0, +1, m]

lua_State *lua_newthread (lua_State *L);

创建一个新线程,将其压入堆栈,并返回一个指向表示此新线程的 lua_State 的指针。此函数返回的新线程与其原始线程共享其全局环境,但具有独立的执行堆栈。

线程与任何 Lua 对象一样,都受垃圾回收的约束。


lua_newuserdatauv

[-0, +1, m]

void *lua_newuserdatauv (lua_State *L, size_t size, int nuvalue);

此函数创建并压入堆栈一个新的完整用户数据,其中包含 nuvalue 个关联的 Lua 值(称为“用户值”),以及一个大小为 size 字节的关联原始内存块。(用户值可以使用函数 lua_setiuservaluelua_getiuservalue 进行设置和读取。)

该函数返回内存块的地址。Lua 确保只要相应用户数据存在,该地址就是有效的(参见 §2.5)。此外,如果用户数据标记为最终化(参见 §2.5.3),则其地址至少在调用其最终化之前有效。


lua_next

[-1, +(2|0), v]

int lua_next (lua_State *L, int index);

从栈中弹出键,并从给定索引处的表中推送一个键值对,即给定键之后的“下一个”对。如果表中没有更多元素,则 lua_next 返回 0 且不推送任何内容。

典型的表遍历如下所示

     /* table is in the stack at index 't' */
     lua_pushnil(L);  /* first key */
     while (lua_next(L, t) != 0) {
       /* uses 'key' (at index -2) and 'value' (at index -1) */
       printf("%s - %s\n",
              lua_typename(L, lua_type(L, -2)),
              lua_typename(L, lua_type(L, -1)));
       /* removes 'value'; keeps 'key' for next iteration */
       lua_pop(L, 1);
     }

在遍历表时,请避免直接对键调用 lua_tolstring,除非您知道该键实际上是字符串。请记住,lua_tolstring 可能会更改给定索引处的值;这会混淆对 lua_next 的下一次调用。

如果给定键既不是 nil 也不存在于表中,则此函数可能会引发错误。有关在遍历表期间修改表时的注意事项,请参见函数 next


lua_Number

typedef ... lua_Number;

Lua 中浮点数的类型。

默认情况下,此类型为双精度浮点数,但可以将其更改为单精度浮点数或长双精度浮点数。(请参见 luaconf.h 中的 LUA_FLOAT_TYPE。)


lua_numbertointeger

int lua_numbertointeger (lua_Number n, lua_Integer *p);

尝试将 Lua 浮点数转换为 Lua 整数;浮点数 n 必须具有整数值。如果该值在 Lua 整数的范围内,则将其转换为整数并赋值给 *p。宏的结果是一个布尔值,表示转换是否成功。(请注意,由于舍入,如果没有此宏,则此范围测试可能难以正确执行。)

此宏可能会多次计算其参数。


lua_pcall

[-(nargs + 1), +(nresults|1), –]

int lua_pcall (lua_State *L, int nargs, int nresults, int msgh);

以受保护模式调用函数(或可调用对象)。

nargsnresults 的含义与 lua_call 中的相同。如果没有在调用期间发生错误,则 lua_pcall 的行为与 lua_call 完全相同。但是,如果发生任何错误,lua_pcall 会捕获它,在栈上推送一个值(错误对象),并返回一个错误代码。与 lua_call 一样,lua_pcall 始终从栈中移除函数及其参数。

如果 msgh 为 0,则栈上返回的错误对象就是原始错误对象。否则,msgh消息处理程序的栈索引。(此索引不能是伪索引。)在运行时错误的情况下,此处理程序将使用错误对象调用,其返回值将是 lua_pcall 在栈上返回的对象。

通常,消息处理程序用于向错误对象添加更多调试信息,例如堆栈回溯。此类信息无法在 lua_pcall 返回后收集,因为那时堆栈已展开。

lua_pcall 函数返回以下状态代码之一: LUA_OKLUA_ERRRUNLUA_ERRMEMLUA_ERRERR


lua_pcallk

[-(nargs + 1), +(nresults|1), –]

int lua_pcallk (lua_State *L,
                int nargs,
                int nresults,
                int msgh,
                lua_KContext ctx,
                lua_KFunction k);

此函数的行为与 lua_pcall 完全相同,但它允许被调用函数让步(请参阅 §4.5)。


lua_pop

[-n, +0, e]

void lua_pop (lua_State *L, int n);

从堆栈中弹出 n 个元素。它作为 lua_settop 上的宏实现。


lua_pushboolean

[-0, +1, –]

void lua_pushboolean (lua_State *L, int b);

将布尔值(值为 b)推送到堆栈上。


lua_pushcclosure

[-n, +1, m]

void lua_pushcclosure (lua_State *L, lua_CFunction fn, int n);

将新的 C 闭包推送到堆栈上。此函数接收一个指向 C 函数的指针,并将类型为 function 的 Lua 值推送到堆栈上,当调用此值时,它将调用相应的 C 函数。参数 n 指示此函数将具有多少个上值(请参阅 §4.2)。

任何可由 Lua 调用的函数都必须遵循正确的协议来接收其参数并返回其结果(请参阅 lua_CFunction)。

创建 C 函数时,可以将一些值与其关联,即所谓的上值;然后,无论何时调用此函数,都可以访问这些上值。此关联称为 C 闭包(请参阅 §4.2)。要创建 C 闭包,首先必须将上值的初始值推送到堆栈上。(当有多个上值时,首先推送第一个值。)然后调用 lua_pushcclosure 以创建 C 函数并将其推送到堆栈上,参数 n 指示将与该函数关联多少个值。 lua_pushcclosure 还会从堆栈中弹出这些值。

n 的最大值为 255。

n 为零时,此函数将创建一个轻量级 C 函数,它只是一个指向 C 函数的指针。在这种情况下,它永远不会引发内存错误。


lua_pushcfunction

[-0, +1, –]

void lua_pushcfunction (lua_State *L, lua_CFunction f);

将 C 函数推送到堆栈上。此函数等效于没有上值的 lua_pushcclosure


lua_pushfstring

[-0, +1, v]

const char *lua_pushfstring (lua_State *L, const char *fmt, ...);

将格式化字符串推送到堆栈上,并返回指向此字符串的指针(请参阅 §4.1.3)。它类似于 ISO C 函数 sprintf,但有两个重要区别。首先,您不必为结果分配空间;结果是 Lua 字符串,Lua 负责内存分配(以及通过垃圾回收进行释放)。其次,转换说明符受到很大限制。没有标志、宽度或精度。转换说明符只能是 '%%'(插入字符 '%')、'%s'(插入一个以零结尾的字符串,没有大小限制)、'%f'(插入一个 lua_Number)、'%I'(插入一个 lua_Integer)、'%p'(插入一个指针)、'%d'(插入一个 int)、'%c'(插入一个 int 作为单字节字符)和 '%U'(插入一个 long int 作为 UTF-8 字节序列)。

此函数可能会因内存溢出或无效的转换说明符而引发错误。


lua_pushglobaltable

[-0, +1, –]

void lua_pushglobaltable (lua_State *L);

将全局环境推送到堆栈。


lua_pushinteger

[-0, +1, –]

void lua_pushinteger (lua_State *L, lua_Integer n);

将值 n 的整数推送到堆栈。


lua_pushlightuserdata

[-0, +1, –]

void lua_pushlightuserdata (lua_State *L, void *p);

将轻用户数据推送到堆栈。

用户数据在 Lua 中表示 C 值。轻用户数据表示指针,即 void*。它是一个值(比如数字):您不会创建它,它没有单独的元表,并且不会被收集(因为它从未被创建)。轻用户数据等于具有相同 C 地址的“任何”轻用户数据。


lua_pushliteral

[-0, +1, m]

const char *lua_pushliteral (lua_State *L, const char *s);

此宏等效于 lua_pushstring,但仅当 s 是一个文字字符串时才应使用它。(Lua 可能会优化这种情况。)


lua_pushlstring

[-0, +1, m]

const char *lua_pushlstring (lua_State *L, const char *s, size_t len);

s 指向的长度为 len 的字符串推送到堆栈。Lua 将制作或重用给定字符串的内部副本,因此在函数返回后可以立即释放或重用 s 处的内存。该字符串可以包含任何二进制数据,包括嵌入的零。

返回字符串的内部副本的指针(请参阅 §4.1.3)。


lua_pushnil

[-0, +1, –]

void lua_pushnil (lua_State *L);

将 nil 值推送到堆栈。


lua_pushnumber

[-0, +1, –]

void lua_pushnumber (lua_State *L, lua_Number n);

将值 n 的浮点数推送到堆栈。


lua_pushstring

[-0, +1, m]

const char *lua_pushstring (lua_State *L, const char *s);

s 指向的以零结尾的字符串推送到堆栈。Lua 将制作或重用给定字符串的内部副本,因此在函数返回后可以立即释放或重用 s 处的内存。

返回字符串的内部副本的指针(请参阅 §4.1.3)。

如果 sNULL,则推送 nil 并返回 NULL


lua_pushthread

[-0, +1, –]

int lua_pushthread (lua_State *L);

L 表示的线程推送到堆栈。如果此线程是其状态的主线程,则返回 1。


lua_pushvalue

[-0, +1, –]

void lua_pushvalue (lua_State *L, int index);

将给定索引处的元素的副本推送到堆栈。


lua_pushvfstring

[-0, +1, v]

const char *lua_pushvfstring (lua_State *L,
                              const char *fmt,
                              va_list argp);

等效于 lua_pushfstring,但它接收的是 va_list,而不是可变数量的参数。


lua_rawequal

[-0, +0, –]

int lua_rawequal (lua_State *L, int index1, int index2);

如果索引 index1index2 中的两个值在原始上相等(即在不调用 __eq 元方法的情况下相等),则返回 1。否则返回 0。如果任何索引无效,也返回 0。


lua_rawget

[-1, +1, –]

int lua_rawget (lua_State *L, int index);

类似于 lua_gettable,但执行原始访问(即不使用元方法)。index 处的值必须是表。


lua_rawgeti

[-0, +1, –]

int lua_rawgeti (lua_State *L, int index, lua_Integer n);

将值 t[n] 压入堆栈,其中 t 是给定索引处的表。访问是原始的,也就是说,它不使用 __index 元值。

返回推送值的类型。


lua_rawgetp

[-0, +1, –]

int lua_rawgetp (lua_State *L, int index, const void *p);

将值 t[k] 压入堆栈,其中 t 是给定索引处的表,而 k 是表示为轻量级用户数据的指针 p。访问是原始的;也就是说,它不使用 __index 元值。

返回推送值的类型。


lua_rawlen

[-0, +0, –]

lua_Unsigned lua_rawlen (lua_State *L, int index);

返回给定索引处的值的原始“长度”:对于字符串,这是字符串长度;对于表,这是长度运算符 ('#') 的结果,没有元方法;对于用户数据,这是为用户数据分配的内存块的大小。对于其他值,此调用返回 0。


lua_rawset

[-2, +0, m]

void lua_rawset (lua_State *L, int index);

类似于 lua_settable,但执行原始赋值(即,没有元方法)。index 处的 value 必须是表。


lua_rawseti

[-1, +0, m]

void lua_rawseti (lua_State *L, int index, lua_Integer i);

执行等效于 t[i] = v 的操作,其中 t 是给定索引处的表,而 v 是堆栈顶部的值。

此函数从堆栈中弹出值。赋值是原始的,也就是说,它不使用 __newindex 元值。


lua_rawsetp

[-1, +0, m]

void lua_rawsetp (lua_State *L, int index, const void *p);

执行等效于 t[p] = v 的操作,其中 t 是给定索引处的表,p 编码为轻量级用户数据,而 v 是堆栈顶部的值。

此函数从堆栈中弹出值。赋值是原始的,也就是说,它不使用 __newindex 元值。


lua_Reader

typedef const char * (*lua_Reader) (lua_State *L,
                                    void *data,
                                    size_t *size);

lua_load 使用的读取器函数。每当 lua_load 需要块的另一部分时,它都会调用读取器,同时传递其 data 参数。读取器必须返回一个指向包含块新部分的内存块的指针,并将 size 设置为块大小。该块必须存在,直到再次调用读取器函数。为了发出块结束信号,读取器必须返回 NULL 或将 size 设置为零。读取器函数可以返回任何大于零大小的部分。


lua_register

[-0, +0, e]

void lua_register (lua_State *L, const char *name, lua_CFunction f);

将 C 函数 f 设置为全局 name 的新值。它被定义为一个宏

     #define lua_register(L,n,f) \
            (lua_pushcfunction(L, f), lua_setglobal(L, n))

lua_remove

[-1, +0, –]

void lua_remove (lua_State *L, int index);

移除给定有效索引处的元素,向下移动此索引上方的元素以填补空白。此函数不能使用伪索引调用,因为伪索引不是实际的堆栈位置。


lua_replace

[-1, +0, –]

void lua_replace (lua_State *L, int index);

将栈顶元素移动到给定的有效索引中,而不移动任何元素(因此替换该给定索引处的值),然后弹出栈顶元素。


lua_resetthread

[-0, +?, –]

int lua_resetthread (lua_State *L);

此函数已弃用;它等效于 fromNULLlua_closethread


lua_resume

[-?, +?, –]

int lua_resume (lua_State *L, lua_State *from, int nargs,
                          int *nresults);

在给定的线程 L 中启动和恢复协程。

要启动协程,您将主函数以及任何参数推送到线程的空堆栈上。然后,您使用 nargs 作为参数数调用 lua_resume。此调用在协程挂起或完成其执行时返回。当它返回时,*nresults 被更新,并且堆栈顶部包含传递给 lua_yield 或由主体函数返回的 *nresults 值。lua_resume 在协程让步时返回 LUA_YIELD,在协程在没有错误的情况下完成其执行时返回 LUA_OK,或在发生错误时返回错误代码(请参见 §4.4.1)。如果发生错误,错误对象位于堆栈顶部。

要恢复协程,您从其堆栈中移除 *nresults 让步值,将值作为从 yield 传递的结果进行推送,然后调用 lua_resume

参数 from 表示正在恢复 L 的协程。如果没有这样的协程,此参数可以为 NULL


lua_rotate

[-0, +0, –]

void lua_rotate (lua_State *L, int idx, int n);

旋转有效索引 idx 和堆栈顶部之间的堆栈元素。对于正 n,元素在朝向顶部的方向上旋转 n 个位置,或者对于负 n,元素在朝向底部的方向上旋转 -n 个位置。n 的绝对值不得大于正在旋转的切片的尺寸。此函数不能使用伪索引调用,因为伪索引不是实际的堆栈位置。


lua_setallocf

[-0, +0, –]

void lua_setallocf (lua_State *L, lua_Alloc f, void *ud);

将给定状态的分配器函数更改为 f,并使用用户数据 ud


lua_setfield

[-1, +0, e]

void lua_setfield (lua_State *L, int index, const char *k);

执行等效于 t[k] = v 的操作,其中 t 是给定索引处的值,v 是堆栈顶部的值。

此函数从堆栈中弹出值。与 Lua 中一样,此函数可能会触发“newindex”事件的元方法(请参阅 §2.4)。


lua_setglobal

[-1, +0, e]

void lua_setglobal (lua_State *L, const char *name);

从堆栈中弹出值,并将其设置为全局name的新值。


lua_seti

[-1, +0, e]

void lua_seti (lua_State *L, int index, lua_Integer n);

执行等同于 t[n] = v 的操作,其中 t 是给定索引处的值,而 v 是堆栈顶部的值。

此函数从堆栈中弹出值。与 Lua 中一样,此函数可能会触发“newindex”事件的元方法(请参阅 §2.4)。


lua_setiuservalue

[-1, +0, –]

int lua_setiuservalue (lua_State *L, int index, int n);

从堆栈中弹出值,并将其设置为与给定索引处的完整用户数据关联的新第 n 个用户值。如果用户数据没有该值,则返回 0。


lua_setmetatable

[-1, +0, –]

int lua_setmetatable (lua_State *L, int index);

从堆栈中弹出表或nil,并将其设置为给定索引处的值的新元表。(nil 表示没有元表。)

(出于历史原因,此函数返回一个 int,现在始终为 1。)


lua_settable

[-2, +0, e]

void lua_settable (lua_State *L, int index);

执行等同于 t[k] = v 的操作,其中 t 是给定索引处的值,v 是堆栈顶部的值,而 k 是紧挨在顶部下面的值。

此函数从堆栈中弹出键和值。与 Lua 中一样,此函数可能会触发“newindex”事件的元方法(请参阅 §2.4)。


lua_settop

[-?, +?, e]

void lua_settop (lua_State *L, int index);

接受任何索引或 0,并将堆栈顶部设置为此索引。如果新顶部大于旧顶部,则新元素将填充为 nil。如果 index 为 0,则将删除所有堆栈元素。

此函数在从堆栈中删除标记为待关闭的索引时可以运行任意代码。


lua_setwarnf

[-0, +0, –]

void lua_setwarnf (lua_State *L, lua_WarnFunction f, void *ud);

设置警告函数,供 Lua 用来发出警告(请参阅 lua_WarnFunction)。ud 参数设置传递给警告函数的值 ud


lua_State

typedef struct lua_State lua_State;

指向线程的不透明结构,并间接地(通过线程)指向 Lua 解释器的整个状态。Lua 库完全可重入:它没有全局变量。有关状态的所有信息都可以通过此结构访问。

必须将指向此结构的指针作为库中每个函数的第一个参数传递,除了 lua_newstate,它从头创建一个 Lua 状态。


lua_status

[-0, +0, –]

int lua_status (lua_State *L);

返回线程 L 的状态。

状态可以是 LUA_OK(对于正常线程)、错误代码(如果线程使用错误完成了 lua_resume 的执行)或 LUA_YIELD(如果线程已暂停)。

只能在状态为 LUA_OK 的线程中调用函数。可以恢复状态为 LUA_OK(启动新协程)或 LUA_YIELD(恢复协程)的线程。


lua_stringtonumber

[-0, +1, –]

size_t lua_stringtonumber (lua_State *L, const char *s);

将以零结尾的字符串 s 转换为数字,将该数字压入堆栈,并返回字符串的总大小,即其长度加一。根据 Lua 的词法惯例(请参阅 §3.1),转换结果可以是整数或浮点数。字符串可以有前导和尾随空格以及一个符号。如果字符串不是有效的数字,则返回 0 且不压入任何内容。(请注意,结果可以用作布尔值,如果转换成功则为 true。)


lua_toboolean

[-0, +0, –]

int lua_toboolean (lua_State *L, int index);

将给定索引处的 Lua 值转换为 C 布尔值(0 或 1)。与 Lua 中的所有测试一样,lua_toboolean 对任何不同于 falsenil 的 Lua 值返回 true;否则返回 false。(如果只想接受实际布尔值,请使用 lua_isboolean 测试该值的类型。)


lua_tocfunction

[-0, +0, –]

lua_CFunction lua_tocfunction (lua_State *L, int index);

将给定索引处的值转换为 C 函数。该值必须是 C 函数;否则,返回 NULL


lua_toclose

[-0, +0, m]

void lua_toclose (lua_State *L, int index);

将堆栈中给定的索引标记为待关闭槽(请参阅 §3.3.8)。与 Lua 中的待关闭变量类似,当堆栈中该槽超出范围时,其值将被关闭。在此,在 C 函数的上下文中,超出范围意味着正在运行的函数返回到 Lua,或者发生错误,或者通过 lua_settoplua_pop 从堆栈中删除槽,或者调用 lua_closeslot。标记为待关闭的槽不应通过 API 中的任何其他函数从堆栈中删除,除非 lua_settoplua_pop,除非先前已通过 lua_closeslot 停用。

不应为等于或低于活动待关闭槽的索引调用此函数。

请注意,在发生错误和正常返回的情况下,__close 元方法运行时,C 堆栈已展开,因此在调用函数中声明的任何自动 C 变量(例如,缓冲区)都将超出范围。


lua_tointeger

[-0, +0, –]

lua_Integer lua_tointeger (lua_State *L, int index);

等效于 isnum 等于 NULLlua_tointegerx


lua_tointegerx

[-0, +0, –]

lua_Integer lua_tointegerx (lua_State *L, int index, int *isnum);

将给定索引处的 Lua 值转换为带符号整数类型 lua_Integer。Lua 值必须是整数或可转换为整数的数字或字符串(请参阅 §3.4.3);否则,lua_tointegerx 返回 0。

如果 isnum 不为 NULL,则为其引用分配一个布尔值,该值指示操作是否成功。


lua_tolstring

[-0, +0, m]

const char *lua_tolstring (lua_State *L, int index, size_t *len);

将给定索引处的 Lua 值转换为 C 字符串。如果 len 不为 NULL,则使用字符串长度设置 *len。Lua 值必须是字符串或数字;否则,该函数返回 NULL。如果该值是数字,则 lua_tolstring 还会将堆栈中的实际值更改为字符串。(当在表遍历期间将 lua_tolstring 应用于键时,此更改会使 lua_next 感到困惑。)

lua_tolstring 返回 Lua 状态中字符串的指针(请参阅 §4.1.3)。此字符串在其最后一个字符后始终带有零('\0')(如 C 中),但其主体中可能包含其他零。


lua_tonumber

[-0, +0, –]

lua_Number lua_tonumber (lua_State *L, int index);

等效于 isnum 等于 NULLlua_tonumberx


lua_tonumberx

[-0, +0, –]

lua_Number lua_tonumberx (lua_State *L, int index, int *isnum);

将给定索引处的 Lua 值转换为 C 类型 lua_Number(请参见 lua_Number)。Lua 值必须是数字或可转换为数字的字符串(请参见 §3.4.3);否则,lua_tonumberx 返回 0。

如果 isnum 不为 NULL,则为其引用分配一个布尔值,该值指示操作是否成功。


lua_topointer

[-0, +0, –]

const void *lua_topointer (lua_State *L, int index);

将给定索引处的值转换为通用 C 指针 (void*)。该值可以是用户数据、表、线程、字符串或函数;否则,lua_topointer 返回 NULL。不同的对象将提供不同的指针。无法将指针转换回其原始值。

通常,此函数仅用于散列和调试信息。


lua_tostring

[-0, +0, m]

const char *lua_tostring (lua_State *L, int index);

等效于 lua_tolstring,其中 len 等于 NULL


lua_tothread

[-0, +0, –]

lua_State *lua_tothread (lua_State *L, int index);

将给定索引处的值转换为 Lua 线程(表示为 lua_State*)。此值必须是线程;否则,函数返回 NULL


lua_touserdata

[-0, +0, –]

void *lua_touserdata (lua_State *L, int index);

如果给定索引处的值为完整用户数据,则返回其内存块地址。如果该值为轻量级用户数据,则返回其值(指针)。否则,返回 NULL


lua_type

[-0, +0, –]

int lua_type (lua_State *L, int index);

返回给定有效索引中值的类型,或对于非有效但可接受的索引返回 LUA_TNONE。由 lua_type 返回的类型由 lua.h 中定义的以下常量编码:LUA_TNILLUA_TNUMBERLUA_TBOOLEANLUA_TSTRINGLUA_TTABLELUA_TFUNCTIONLUA_TUSERDATALUA_TTHREADLUA_TLIGHTUSERDATA


lua_typename

[-0, +0, –]

const char *lua_typename (lua_State *L, int tp);

返回由值 tp 编码的类型的名称,该值必须是 lua_type 返回的值之一。


lua_Unsigned

typedef ... lua_Unsigned;

lua_Integer 的无符号版本。


lua_upvalueindex

[-0, +0, –]

int lua_upvalueindex (int i);

返回表示正在运行函数的第 i 个上值的伪索引(请参见 §4.2)。i 必须在范围 [1,256] 内。


lua_version

[-0, +0, –]

lua_Number lua_version (lua_State *L);

返回此核心的版本号。


lua_WarnFunction

typedef void (*lua_WarnFunction) (void *ud, const char *msg, int tocont);

警告函数的类型,由 Lua 调用以发出警告。第一个参数是由 lua_setwarnf 设置的不透明指针。第二个参数是警告消息。第三个参数是一个布尔值,表示消息是否由下一次调用的消息继续。

有关警告的更多详细信息,请参见 warn


lua_warning

[-0, +0, –]

void lua_warning (lua_State *L, const char *msg, int tocont);

发出带有给定消息的警告。在 tocont 为 true 的调用中,消息应在此函数的另一个调用中继续。

有关警告的更多详细信息,请参见 warn


lua_Writer

typedef int (*lua_Writer) (lua_State *L,
                           const void* p,
                           size_t sz,
                           void* ud);

lua_dump 使用的编写器函数的类型。每次 lua_dump 生成另一部分块时,它都会调用编写器,传递要写入的缓冲区 (p)、其大小 (sz) 以及提供给 lua_dumpud 参数。

编写器返回一个错误代码:0 表示没有错误;任何其他值表示错误,并阻止 lua_dump 再次调用编写器。


lua_xmove

[-?, +?, –]

void lua_xmove (lua_State *from, lua_State *to, int n);

在同一状态的不同线程之间交换值。

此函数从堆栈 from 弹出 n 个值,并将它们推送到堆栈 to


lua_yield

[-?, +?, v]

int lua_yield (lua_State *L, int nresults);

此函数等效于 lua_yieldk,但它没有延续(请参见 §4.5)。因此,当线程恢复时,它将继续调用调用 lua_yield 的函数的函数。为避免意外,此函数应仅在尾调用中调用。


lua_yieldk

[-?, +?, v]

int lua_yieldk (lua_State *L,
                int nresults,
                lua_KContext ctx,
                lua_KFunction k);

让出协程(线程)。

当 C 函数调用 lua_yieldk 时,正在运行的协程将暂停其执行,并且启动此协程的 lua_resume 调用将返回。参数 nresults 是将作为结果传递给 lua_resume 的堆栈中的值的数量。

当协程再次恢复时,Lua 调用给定的延续函数 k 以继续执行让步的 C 函数(参见 §4.5)。此延续函数从前一个函数接收相同的堆栈,其中删除了 n 个结果并替换为传递给 lua_resume 的参数。此外,延续函数接收传递给 lua_yieldk 的值 ctx

通常,此函数不会返回;当协程最终恢复时,它继续执行延续函数。但是,有一个特殊情况,即当此函数从行或计数钩子内部调用时(参见 §4.7)。在这种情况下,应在没有延续(可能采用 lua_yield 的形式)和没有结果的情况下调用 lua_yieldk,并且钩子应在调用后立即返回。Lua 将让步,当协程再次恢复时,它将继续触发钩子的(Lua)函数的正常执行。

如果从具有挂起的 C 调用且没有延续函数(称为C 调用边界)的线程调用此函数,或者从不在恢复中运行的线程(通常为主线程)调用此函数,则此函数可能会引发错误。

4.7 – 调试接口

Lua 没有内置的调试工具。相反,它通过函数和钩子提供了一个特殊接口。此接口允许构建不同类型的调试器、分析器和其他需要解释器“内部信息”的工具。


lua_Debug

typedef struct lua_Debug {
  int event;
  const char *name;           /* (n) */
  const char *namewhat;       /* (n) */
  const char *what;           /* (S) */
  const char *source;         /* (S) */
  size_t srclen;              /* (S) */
  int currentline;            /* (l) */
  int linedefined;            /* (S) */
  int lastlinedefined;        /* (S) */
  unsigned char nups;         /* (u) number of upvalues */
  unsigned char nparams;      /* (u) number of parameters */
  char isvararg;              /* (u) */
  char istailcall;            /* (t) */
  unsigned short ftransfer;   /* (r) index of first value transferred */
  unsigned short ntransfer;   /* (r) number of transferred values */
  char short_src[LUA_IDSIZE]; /* (S) */
  /* private part */
  other fields
} lua_Debug;

用于携带有关函数或激活记录的不同信息片段的结构。 lua_getstack 仅填充此结构的私有部分,以供以后使用。要使用有用信息填充 lua_Debug 的其他字段,必须使用适当的参数调用 lua_getinfo。(具体来说,要获取字段,必须将字段注释中括号内的字母添加到 lua_getinfowhat 参数。)

lua_Debug 的字段具有以下含义


lua_gethook

[-0, +0, –]

lua_Hook lua_gethook (lua_State *L);

返回当前钩子函数。


lua_gethookcount

[-0, +0, –]

int lua_gethookcount (lua_State *L);

返回当前钩子计数。


lua_gethookmask

[-0, +0, –]

int lua_gethookmask (lua_State *L);

返回当前钩子掩码。


lua_getinfo

[-(0|1), +(0|1|2), m]

int lua_getinfo (lua_State *L, const char *what, lua_Debug *ar);

获取有关特定函数或函数调用的信息。

要获取有关函数调用的信息,参数 ar 必须是一个有效的激活记录,该记录由对 lua_getstack 的先前调用填充或作为参数提供给钩子(请参阅 lua_Hook)。

要获取有关函数的信息,请将其推入堆栈并以字符“>”开头 what 字符串。(在这种情况下,lua_getinfo 从堆栈顶部弹出函数。)例如,要了解函数 f 在哪一行定义,可以编写以下代码

     lua_Debug ar;
     lua_getglobal(L, "f");  /* get global 'f' */
     lua_getinfo(L, ">S", &ar);
     printf("%d\n", ar.linedefined);

字符串 what 中的每个字符都选择结构 ar 的某些字段以填充或将值推入堆栈。(这些字符也在结构 lua_Debug 的声明中记录,在每个字段后面的注释中的括号中。)

此函数返回 0 以表示 what 中的无效选项;即使这样,也会正确处理有效的选项。


lua_getlocal

[-0, +(0|1), –]

const char *lua_getlocal (lua_State *L, const lua_Debug *ar, int n);

获取给定激活记录或给定函数的局部变量或临时值的信息。

在第一种情况下,参数 ar 必须是通过先前对 lua_getstack 的调用填充的有效激活记录,或作为挂钩的参数提供(参见 lua_Hook)。索引 n 选择要检查的局部变量;有关变量索引和名称的详细信息,请参见 debug.getlocal

lua_getlocal 将变量的值压入堆栈并返回其名称。

在第二种情况下,ar 必须为 NULL,并且要检查的函数必须位于堆栈顶部。在这种情况下,只有 Lua 函数的参数才可见(因为没有关于哪些变量处于活动状态的信息),并且没有值被压入堆栈。

当索引大于活动局部变量的数量时,返回 NULL(并且不压入任何内容)。


lua_getstack

[-0, +0, –]

int lua_getstack (lua_State *L, int level, lua_Debug *ar);

获取有关解释器运行时堆栈的信息。

此函数使用给定级别处正在执行的函数的激活记录的标识来填充 lua_Debug 结构的某些部分。级别 0 是当前正在运行的函数,而级别 n+1 是调用级别 n 的函数(尾调用除外,尾调用不在堆栈中计数)。当使用大于堆栈深度的级别调用时,lua_getstack 返回 0;否则返回 1。


lua_getupvalue

[-0, +(0|1), –]

const char *lua_getupvalue (lua_State *L, int funcindex, int n);

获取索引 funcindex 处闭包的第 n 个上值的信息。它将上值压入堆栈并返回其名称。当索引 n 大于上值的数量时,返回 NULL(并且不压入任何内容)。

有关上值的更多信息,请参见 debug.getupvalue


lua_Hook

typedef void (*lua_Hook) (lua_State *L, lua_Debug *ar);

调试挂钩函数的类型。

每当调用挂钩时,其 ar 参数的 event 字段都会设置为触发挂钩的特定事件。Lua 使用以下常量标识这些事件:LUA_HOOKCALLLUA_HOOKRETLUA_HOOKTAILCALLLUA_HOOKLINELUA_HOOKCOUNT。此外,对于行事件,还设置 currentline 字段。要获取 ar 中任何其他字段的值,挂钩必须调用 lua_getinfo

对于调用事件,event 可以是 LUA_HOOKCALL(正常值)或 LUA_HOOKTAILCALL(尾调用);在这种情况下,将没有相应的返回事件。

Lua 在运行钩子时,会禁用对其他钩子的调用。因此,如果钩子回调 Lua 来执行函数或块,则此执行将不会调用任何钩子。

钩子函数不能有延续,即它们不能使用非空 k 调用 lua_yieldklua_pcallklua_callk

钩子函数可以在以下条件下产生:只有计数和行事件可以产生;要产生,钩子函数必须完成其执行,并使用等于零的 nresults(即没有值)调用 lua_yield


lua_sethook

[-0, +0, –]

void lua_sethook (lua_State *L, lua_Hook f, int mask, int count);

设置调试钩子函数。

参数 f 是钩子函数。mask 指定钩子将在哪些事件上被调用:它由常量 LUA_MASKCALLLUA_MASKRETLUA_MASKLINELUA_MASKCOUNT 的按位 OR 运算形成。当掩码包含 LUA_MASKCOUNT 时,count 参数才有意义。对于每个事件,钩子将按如下所述被调用

通过将 mask 设置为零来禁用钩子。


lua_setlocal

[-(0|1), +0, –]

const char *lua_setlocal (lua_State *L, const lua_Debug *ar, int n);

设置给定激活记录的局部变量的值。它将栈顶的值分配给变量并返回其名称。它还从栈中弹出值。

当索引大于活动局部变量的数量时,返回 NULL(并且不弹出任何内容)。

参数 arn 与函数 lua_getlocal 中的参数相同。


lua_setupvalue

[-(0|1), +0, –]

const char *lua_setupvalue (lua_State *L, int funcindex, int n);

设置闭包的上值的。它将栈顶的值分配给上值并返回其名称。它还从栈中弹出值。

当索引 n 大于 upvalue 数量时,返回 NULL(且不弹出任何内容)。

参数 funcindexn 与函数 lua_getupvalue 中的参数相同。


lua_upvalueid

[-0, +0, –]

void *lua_upvalueid (lua_State *L, int funcindex, int n);

返回索引 funcindex 处闭包中编号为 n 的 upvalue 的唯一标识符。

这些唯一标识符允许程序检查不同的闭包是否共享 upvalue。共享 upvalue(即访问同一个外部局部变量)的 Lua 闭包将为这些 upvalue 索引返回相同的 id。

参数 funcindexn 与函数 lua_getupvalue 中的参数相同,但 n 不能大于 upvalue 的数量。


lua_upvaluejoin

[-0, +0, –]

void lua_upvaluejoin (lua_State *L, int funcindex1, int n1,
                                    int funcindex2, int n2);

使索引 funcindex1 处的 Lua 闭包的第 n1 个 upvalue 引用索引 funcindex2 处的 Lua 闭包的第 n2 个 upvalue。

5 – 辅助库

辅助库 提供了几个方便的函数,用于 C 与 Lua 的接口。虽然基本 API 提供了 C 与 Lua 之间所有交互的原始函数,但辅助库为一些常见任务提供了更高级别的函数。

辅助库中的所有函数和类型都在头文件 lauxlib.h 中定义,并带有前缀 luaL_

辅助库中的所有函数都建立在基本 API 之上,因此它们提供的功能都可以通过该 API 实现。尽管如此,使用辅助库可以确保代码更加一致。

辅助库中的几个函数在内部使用一些额外的栈槽。当辅助库中的函数使用少于五个槽时,它不会检查栈大小;它只是假设有足够的槽。

辅助库中的几个函数用于检查 C 函数参数。由于错误消息是针对参数格式化的(例如,“错误的参数 #1”),因此你不应将这些函数用于其他栈值。

名为 luaL_check* 的函数在检查不满足时始终引发错误。

5.1 – 函数和类型

我们在此按字母顺序列出辅助库中的所有函数和类型。


luaL_addchar

[-?, +?, m]

void luaL_addchar (luaL_Buffer *B, char c);

将字节 c 添加到缓冲区 B(参见 luaL_Buffer)。


luaL_addgsub

[-?, +?, m]

const void luaL_addgsub (luaL_Buffer *B, const char *s,
                         const char *p, const char *r);

将字符串 s 的副本添加到缓冲区 B(参见 luaL_Buffer),用字符串 r 替换字符串 p 的任何出现。


luaL_addlstring

[-?, +?, m]

void luaL_addlstring (luaL_Buffer *B, const char *s, size_t l);

将长度为l且由s指向的字符串添加到缓冲区B中(请参阅luaL_Buffer)。该字符串可以包含嵌入的零。


luaL_addsize

[-?, +?, –]

void luaL_addsize (luaL_Buffer *B, size_t n);

将长度为n的字符串添加到缓冲区B中,该字符串之前已复制到缓冲区区域中(请参阅luaL_prepbuffer)。


luaL_addstring

[-?, +?, m]

void luaL_addstring (luaL_Buffer *B, const char *s);

将由s指向的以零结尾的字符串添加到缓冲区B中(请参阅luaL_Buffer)。


luaL_addvalue

[-?, +?, m]

void luaL_addvalue (luaL_Buffer *B);

将位于栈顶的值添加到缓冲区B中(请参阅luaL_Buffer)。弹出该值。

这是字符串缓冲区中唯一可以(并且必须)使用栈上的额外元素调用的函数,该元素是要添加到缓冲区中的值。


luaL_argcheck

[-0, +0, v]

void luaL_argcheck (lua_State *L,
                    int cond,
                    int arg,
                    const char *extramsg);

检查cond是否为真。如果不是,则引发一个带有标准消息的错误(请参阅luaL_argerror)。


luaL_argerror

[-0, +0, v]

int luaL_argerror (lua_State *L, int arg, const char *extramsg);

引发一个错误,报告调用它的 C 函数的arg参数存在问题,并使用包含extramsg作为注释的标准消息

     bad argument #arg to 'funcname' (extramsg)

此函数永不返回。


luaL_argexpected

[-0, +0, v]

void luaL_argexpected (lua_State *L,
                       int cond,
                       int arg,
                       const char *tname);

检查cond是否为真。如果不是,则引发一个关于参数arg类型的错误,并带有标准消息(请参阅luaL_typeerror)。


luaL_Buffer

typedef struct luaL_Buffer luaL_Buffer;

字符串缓冲区的类型。

字符串缓冲区允许 C 代码分段构建 Lua 字符串。其使用模式如下

如果您事先知道结果字符串的最大大小,则可以使用如下方式使用缓冲区

在正常操作期间,字符串缓冲区会使用可变数量的堆栈槽。因此,在使用缓冲区时,您不能假设知道堆栈的顶部位置。只要这种使用是平衡的,您就可以在连续的缓冲区操作调用之间使用堆栈;也就是说,当您调用缓冲区操作时,堆栈处于前一个缓冲区操作后立即处于的同一级别。(此规则的唯一例外是 luaL_addvalue。)在调用 luaL_pushresult 后,堆栈会返回到缓冲区初始化时的级别,并在其顶部添加最终字符串。


luaL_buffaddr

[-0, +0, –]

char *luaL_buffaddr (luaL_Buffer *B);

返回缓冲区 B 的当前内容的地址(请参阅 luaL_Buffer)。请注意,对缓冲区的任何添加都可能使此地址失效。


luaL_buffinit

[-0, +?, –]

void luaL_buffinit (lua_State *L, luaL_Buffer *B);

初始化缓冲区 B(请参阅 luaL_Buffer)。此函数不会分配任何空间;缓冲区必须声明为变量。


luaL_bufflen

[-0, +0, –]

size_t luaL_bufflen (luaL_Buffer *B);

返回缓冲区 B 的当前内容的长度(请参阅 luaL_Buffer)。


luaL_buffinitsize

[-?, +?, m]

char *luaL_buffinitsize (lua_State *L, luaL_Buffer *B, size_t sz);

等同于序列 luaL_buffinitluaL_prepbuffsize


luaL_buffsub

[-?, +?, –]

void luaL_buffsub (luaL_Buffer *B, int n);

从缓冲区 B 中移除 n 个字节(请参阅 luaL_Buffer)。缓冲区必须至少有这么多字节。


luaL_callmeta

[-0, +(0|1), e]

int luaL_callmeta (lua_State *L, int obj, const char *e);

调用元方法。

如果索引 obj 处的对象有元表,并且此元表有字段 e,此函数将调用此字段,并传递对象作为其唯一参数。在这种情况下,此函数返回 true,并将调用返回的值推送到堆栈上。如果没有元表或元方法,此函数将返回 false,并且不会在堆栈上推送任何值。


luaL_checkany

[-0, +0, v]

void luaL_checkany (lua_State *L, int arg);

检查函数是否在位置 arg 处有任意类型的参数(包括 nil)。


luaL_checkinteger

[-0, +0, v]

lua_Integer luaL_checkinteger (lua_State *L, int arg);

检查函数参数 arg 是否为整数(或可转换为整数),并返回此整数。


luaL_checklstring

[-0, +0, v]

const char *luaL_checklstring (lua_State *L, int arg, size_t *l);

检查函数参数 arg 是否为字符串,并返回此字符串;如果 l 不为 NULL,则用字符串的长度填充其引用。

此函数使用 lua_tolstring 获取其结果,因此该函数的所有转换和注意事项都适用于此处。


luaL_checknumber

[-0, +0, v]

lua_Number luaL_checknumber (lua_State *L, int arg);

检查函数参数 arg 是否为数字,并返回转换为 lua_Number 的此数字。


luaL_checkoption

[-0, +0, v]

int luaL_checkoption (lua_State *L,
                      int arg,
                      const char *def,
                      const char *const lst[]);

检查函数参数 arg 是否为字符串,并在数组 lst(必须以 NULL 结尾)中搜索此字符串。返回在数组中找到字符串的索引。如果参数不是字符串,或者找不到字符串,则引发错误。

如果 def 不为 NULL,则在没有参数 arg 或此参数为 nil 时,函数将使用 def 作为默认值。

这是一个有用的函数,用于将字符串映射到 C 枚举。(Lua 库中的惯例是使用字符串而不是数字来选择选项。)


luaL_checkstack

[-0, +0, v]

void luaL_checkstack (lua_State *L, int sz, const char *msg);

将堆栈大小增加到top + sz个元素,如果堆栈无法增长到该大小,则引发错误。msg是添加到错误消息中的附加文本(或无附加文本的NULL)。


luaL_checkstring

[-0, +0, v]

const char *luaL_checkstring (lua_State *L, int arg);

检查函数参数arg是否为字符串并返回此字符串。

此函数使用 lua_tolstring 获取其结果,因此该函数的所有转换和注意事项都适用于此处。


luaL_checktype

[-0, +0, v]

void luaL_checktype (lua_State *L, int arg, int t);

检查函数参数arg的类型是否为t。有关t的类型编码,请参阅lua_type


luaL_checkudata

[-0, +0, v]

void *luaL_checkudata (lua_State *L, int arg, const char *tname);

检查函数参数arg是否为类型tname的用户数据(请参阅luaL_newmetatable),并返回用户数据的内存块地址(请参阅lua_touserdata)。


luaL_checkversion

[-0, +0, v]

void luaL_checkversion (lua_State *L);

检查进行调用的代码和被调用的 Lua 库是否使用相同的 Lua 版本和相同的数字类型。


luaL_dofile

[-0, +?, m]

int luaL_dofile (lua_State *L, const char *filename);

加载并运行给定的文件。它被定义为以下宏

     (luaL_loadfile(L, filename) || lua_pcall(L, 0, LUA_MULTRET, 0))

如果没有错误,则返回 0(LUA_OK);如果出错,则返回 1。


luaL_dostring

[-0, +?, –]

int luaL_dostring (lua_State *L, const char *str);

加载并运行给定的字符串。它被定义为以下宏

     (luaL_loadstring(L, str) || lua_pcall(L, 0, LUA_MULTRET, 0))

如果没有错误,则返回 0(LUA_OK);如果出错,则返回 1。


luaL_error

[-0, +0, v]

int luaL_error (lua_State *L, const char *fmt, ...);

引发错误。错误消息格式由fmt加上任何额外参数给出,遵循与lua_pushfstring相同的规则。它还在消息开头添加错误发生的文件名和行号(如果此信息可用)。

此函数永远不会返回,但将其用在 C 函数中作为return luaL_error(args)是一种习惯用法。


luaL_execresult

[-0, +3, m]

int luaL_execresult (lua_State *L, int stat);

此函数生成标准库中与进程相关的函数的返回值(os.executeio.close)。


luaL_fileresult

[-0, +(1|3), m]

int luaL_fileresult (lua_State *L, int stat, const char *fname);

此函数生成标准库中与文件相关的函数的返回值(io.openos.renamefile:seek等)。


luaL_getmetafield

[-0, +(0|1), m]

int luaL_getmetafield (lua_State *L, int obj, const char *e);

将索引为obj的对象的元表的字段e压入堆栈,并返回压入值的类型。如果对象没有元表,或者元表没有此字段,则不压入任何内容并返回LUA_TNIL


luaL_getmetatable

[-0, +1, m]

int luaL_getmetatable (lua_State *L, const char *tname);

将注册表中与名称tname关联的元表推入堆栈(参见luaL_newmetatable),如果该名称没有关联的元表,则推入nil。返回所推入值的类型。


luaL_getsubtable

[-0, +1, e]

int luaL_getsubtable (lua_State *L, int idx, const char *fname);

确保索引为idx的值t[fname](其中t是该索引的值)是一个表,并将该表推入堆栈。如果在该索引处找到先前的表,则返回 true;如果创建一个新表,则返回 false。


luaL_gsub

[-0, +1, m]

const char *luaL_gsub (lua_State *L,
                       const char *s,
                       const char *p,
                       const char *r);

创建字符串s的副本,将字符串p的任何出现替换为字符串r。将结果字符串推入堆栈并返回。


luaL_len

[-0, +0, e]

lua_Integer luaL_len (lua_State *L, int index);

将给定索引处值的“长度”作为数字返回;它等同于 Lua 中的“#”运算符(参见§3.4.7)。如果操作结果不是整数,则引发错误。(这种情况只能通过元方法发生。)


luaL_loadbuffer

[-0, +1, –]

int luaL_loadbuffer (lua_State *L,
                     const char *buff,
                     size_t sz,
                     const char *name);

等同于luaL_loadbufferx,其中mode等于NULL


luaL_loadbufferx

[-0, +1, –]

int luaL_loadbufferx (lua_State *L,
                      const char *buff,
                      size_t sz,
                      const char *name,
                      const char *mode);

将缓冲区加载为 Lua 块。此函数使用lua_load加载由buff指向且大小为sz的缓冲区中的块。

此函数返回与lua_load相同的结果。name是块名称,用于调试信息和错误消息。字符串mode的作用与函数lua_load中的相同。


luaL_loadfile

[-0, +1, m]

int luaL_loadfile (lua_State *L, const char *filename);

等同于luaL_loadfilex,其中mode等于NULL


luaL_loadfilex

[-0, +1, m]

int luaL_loadfilex (lua_State *L, const char *filename,
                                            const char *mode);

将文件加载为 Lua 块。此函数使用lua_load加载名为filename的文件中的块。如果filenameNULL,则从标准输入加载。如果文件的第一行以“#”开头,则忽略该行。

字符串mode的作用与函数lua_load中的相同。

此函数返回与lua_loadLUA_ERRFILE(对于文件相关错误)相同的结果。

lua_load一样,此函数仅加载块;它不运行该块。


luaL_loadstring

[-0, +1, –]

int luaL_loadstring (lua_State *L, const char *s);

将字符串作为 Lua 块加载。此函数使用 lua_load 在以零结尾的字符串 s 中加载块。

此函数返回与 lua_load 相同的结果。

此外,与 lua_load 一样,此函数只加载块,不运行它。


luaL_newlib

[-0, +1, m]

void luaL_newlib (lua_State *L, const luaL_Reg l[]);

创建一个新表,并在其中注册列表 l 中的函数。

它作为以下宏实现

     (luaL_newlibtable(L,l), luaL_setfuncs(L,l,0))

数组 l 必须是实际数组,而不是指向它的指针。


luaL_newlibtable

[-0, +1, m]

void luaL_newlibtable (lua_State *L, const luaL_Reg l[]);

创建一个新表,其大小经过优化,可存储数组 l 中的所有条目(但实际上并不存储它们)。它旨在与 luaL_setfuncs 结合使用(请参见 luaL_newlib)。

它作为宏实现。数组 l 必须是实际数组,而不是指向它的指针。


luaL_newmetatable

[-0, +1, m]

int luaL_newmetatable (lua_State *L, const char *tname);

如果注册表中已存在键 tname,则返回 0。否则,创建一个新表用作用户数据元表,将对 __name = tname 添加到此新表,将对 [tname] = 新表 添加到注册表,并返回 1。

在这两种情况下,该函数都会将与注册表中 tname 关联的最终值推送到堆栈上。


luaL_newstate

[-0, +0, –]

lua_State *luaL_newstate (void);

创建一个新的 Lua 状态。它使用基于 ISO C 分配函数的分配器调用 lua_newstate,然后设置一个警告函数和一个恐慌函数(请参见 §4.4),将消息打印到标准错误输出。

返回新状态,如果存在内存分配错误,则返回 NULL


luaL_openlibs

[-0, +0, e]

void luaL_openlibs (lua_State *L);

在给定状态中打开所有标准 Lua 库。


luaL_opt

[-0, +0, –]

T luaL_opt (L, func, arg, dflt);

此宏定义如下

     (lua_isnoneornil(L,(arg)) ? (dflt) : func(L,(arg)))

换句话说,如果参数 arg 为 nil 或不存在,则宏将产生默认值 dflt。否则,它将产生使用状态 L 和参数索引 arg 作为参数调用 func 的结果。请注意,它仅在需要时才计算表达式 dflt


luaL_optinteger

[-0, +0, v]

lua_Integer luaL_optinteger (lua_State *L,
                             int arg,
                             lua_Integer d);

如果函数参数 arg 为整数(或可转换为整数),则返回此整数。如果此参数不存在或为 nil,则返回 d。否则,引发错误。


luaL_optlstring

[-0, +0, v]

const char *luaL_optlstring (lua_State *L,
                             int arg,
                             const char *d,
                             size_t *l);

如果函数参数 arg 是一个字符串,则返回此字符串。如果此参数不存在或为 nil,则返回 d。否则,引发错误。

如果 l 不为 NULL,则用结果的长度填充其引用。如果结果为 NULL(仅在返回 dd == NULL 时可能),则其长度被视为零。

此函数使用 lua_tolstring 获取其结果,因此该函数的所有转换和注意事项都适用于此处。


luaL_optnumber

[-0, +0, v]

lua_Number luaL_optnumber (lua_State *L, int arg, lua_Number d);

如果函数参数 arg 是一个数字,则将其作为 lua_Number 返回。如果此参数不存在或为 nil,则返回 d。否则,引发错误。


luaL_optstring

[-0, +0, v]

const char *luaL_optstring (lua_State *L,
                            int arg,
                            const char *d);

如果函数参数 arg 是一个字符串,则返回此字符串。如果此参数不存在或为 nil,则返回 d。否则,引发错误。


luaL_prepbuffer

[-?, +?, m]

char *luaL_prepbuffer (luaL_Buffer *B);

等同于 luaL_prepbuffsize,预定义大小为 LUAL_BUFFERSIZE


luaL_prepbuffsize

[-?, +?, m]

char *luaL_prepbuffsize (luaL_Buffer *B, size_t sz);

返回一个大小为 sz 的空间的地址,您可以在其中复制一个字符串以将其添加到缓冲区 B(请参阅 luaL_Buffer)。将字符串复制到此空间后,您必须使用字符串的大小调用 luaL_addsize 以将其实际添加到缓冲区。


luaL_pushfail

[-0, +1, –]

void luaL_pushfail (lua_State *L);

fail 值推送到堆栈上(请参阅 §6)。


luaL_pushresult

[-?, +1, m]

void luaL_pushresult (luaL_Buffer *B);

完成使用缓冲区 B,将最终字符串留在堆栈顶部。


luaL_pushresultsize

[-?, +1, m]

void luaL_pushresultsize (luaL_Buffer *B, size_t sz);

等同于序列 luaL_addsizeluaL_pushresult


luaL_ref

[-1, +0, m]

int luaL_ref (lua_State *L, int t);

在索引为 t 的表中为堆栈顶部的对象创建一个 引用并返回该引用(并弹出对象)。

引用是一个唯一的整数键。只要您不手动将整数键添加到表 t 中,luaL_ref 就确保它返回的键的唯一性。您可以通过调用 lua_rawgeti(L, t, r) 检索引用 r 引用的对象。函数 luaL_unref 释放引用。

如果堆栈顶部的对象为 nil,则 luaL_ref 返回常量 LUA_REFNIL。常量 LUA_NOREF 保证与 luaL_ref 返回的任何引用不同。


luaL_Reg

typedef struct luaL_Reg {
  const char *name;
  lua_CFunction func;
} luaL_Reg;

要由 luaL_setfuncs 注册的函数数组的类型。name 是函数名,func 是指向函数的指针。任何 luaL_Reg 数组都必须以哨兵条目结尾,其中 namefunc 均为 NULL


luaL_requiref

[-0, +1, e]

void luaL_requiref (lua_State *L, const char *modname,
                    lua_CFunction openf, int glb);

如果 package.loaded[modname] 不为真,则使用字符串 modname 作为参数调用函数 openf,并将调用结果设置为 package.loaded[modname],就好像该函数已通过 require 调用一样。

如果 glb 为真,则还将模块存储到全局 modname 中。

在堆栈上保留模块的副本。


luaL_setfuncs

[-nup, +0, m]

void luaL_setfuncs (lua_State *L, const luaL_Reg *l, int nup);

将数组 l 中的所有函数(参见 luaL_Reg)注册到堆栈顶部的表中(在可选的上值下方,参见下一项)。

nup 不为零时,所有函数都使用 nup 个上值创建,并使用先前在库表上方堆栈上推送的 nup 个值的副本进行初始化。在注册后,这些值将从堆栈中弹出。

具有 NULL 值的函数表示占位符,其中填充了 false


luaL_setmetatable

[-0, +0, –]

void luaL_setmetatable (lua_State *L, const char *tname);

将堆栈顶部的对象的元表设置为注册表中与名称 tname 关联的元表(参见 luaL_newmetatable)。


luaL_Stream

typedef struct luaL_Stream {
  FILE *f;
  lua_CFunction closef;
} luaL_Stream;

标准 I/O 库使用的文件句柄的标准表示形式。

文件句柄实现为一个完整用户数据,其元表称为 LUA_FILEHANDLE(其中 LUA_FILEHANDLE 是一个宏,其名称为实际元表名称)。元表由 I/O 库创建(参见 luaL_newmetatable)。

此用户数据必须以结构 luaL_Stream 开头;它可以在此初始结构后包含其他数据。字段 f 指向对应的 C 流(或者它可以为 NULL 以指示一个不完整创建的句柄)。字段 closef 指向一个 Lua 函数,当句柄关闭或收集时,将调用该函数来关闭流;此函数接收文件句柄作为其唯一参数,并且必须返回一个真值(如果成功)或一个假值和一个错误消息(如果出错)。一旦 Lua 调用此字段,它将字段值更改为 NULL 以表示句柄已关闭。


luaL_testudata

[-0, +0, m]

void *luaL_testudata (lua_State *L, int arg, const char *tname);

此函数的工作方式与 luaL_checkudata 相同,不同之处在于,当测试失败时,它返回 NULL 而不是引发错误。


luaL_tolstring

[-0, +1, e]

const char *luaL_tolstring (lua_State *L, int idx, size_t *len);

将给定索引处的任何 Lua 值转换为合理格式的 C 字符串。结果字符串被推送到堆栈上,并且还由函数返回(参见 §4.1.3)。如果 len 不为 NULL,函数还使用字符串长度设置 *len

如果该值具有带有 __tostring 字段的元表,则 luaL_tolstring 使用该值作为参数调用相应的元方法,并使用调用结果作为其结果。


luaL_traceback

[-0, +1, m]

void luaL_traceback (lua_State *L, lua_State *L1, const char *msg,
                     int level);

创建并推送栈 L1 的回溯。如果 msg 不为 NULL,则将其追加到回溯的开头。level 参数指示从哪个级别开始回溯。


luaL_typeerror

[-0, +0, v]

int luaL_typeerror (lua_State *L, int arg, const char *tname);

针对调用它的 C 函数的参数 arg 引发类型错误,使用标准消息;tname 是预期类型的“名称”。此函数绝不会返回。


luaL_typename

[-0, +0, –]

const char *luaL_typename (lua_State *L, int index);

返回给定索引处的值的类型名称。


luaL_unref

[-0, +0, –]

void luaL_unref (lua_State *L, int t, int ref);

从索引 t 处的表中释放引用 ref(参见 luaL_ref)。该条目将从表中移除,以便可以收集引用的对象。引用 ref 也将被释放以供再次使用。

如果 refLUA_NOREFLUA_REFNIL,则 luaL_unref 不执行任何操作。


luaL_where

[-0, +1, m]

void luaL_where (lua_State *L, int lvl);

将一个字符串推送到栈上,该字符串标识调用堆栈中 lvl 级别的当前控制位置。通常,此字符串具有以下格式

     chunkname:currentline:

第 0 级是正在运行的函数,第 1 级是调用正在运行函数的函数,依此类推。

此函数用于为错误消息构建前缀。

6 – 标准库

Lua 标准库通过 C API 在 C 中实现,提供了有用的函数。其中一些函数为语言提供基本服务(例如,typegetmetatable);其他函数提供对外部服务的访问(例如,I/O);还有一些函数可以在 Lua 本身中实现,但出于不同的原因值得在 C 中实现(例如,table.sort)。

所有库均通过官方 C API 实现,并作为单独的 C 模块提供。除非另有说明,否则这些库函数不会调整其参数的预期参数数量。例如,记录为 foo(arg) 的函数不应在没有参数的情况下调用。

符号 fail 表示某种失败的错误值。(目前,fail 等于 nil,但未来版本中可能会更改。建议始终使用 (not status) 而不是 (status == nil) 测试这些函数的成功。)

目前,Lua 具有以下标准库

除了基本库和包库之外,每个库都将它的所有函数作为全局表的字段或作为其对象的方法提供。

要访问这些库,C 主机程序应调用 luaL_openlibs 函数,它将打开所有标准库。或者,主机程序可以使用 luaL_requiref 来调用 luaopen_base(用于基本库)、luaopen_package(用于包库)、luaopen_coroutine(用于协程库)、luaopen_string(用于字符串库)、luaopen_utf8(用于 UTF-8 库)、luaopen_table(用于表库)、luaopen_math(用于数学库)、luaopen_io(用于 I/O 库)、luaopen_os(用于操作系统库)和 luaopen_debug(用于调试库)来单独打开它们。这些函数在 lualib.h 中声明。

6.1 – 基本函数

基本库为 Lua 提供核心函数。如果您在应用程序中不包含此库,则应仔细检查是否需要为其某些设施提供实现。


assert (v [, message])

如果其参数 v 的值为 false(即 nilfalse),则引发错误;否则,返回其所有参数。如果发生错误,message 是错误对象;如果不存在,则默认为 "assertion failed!"


collectgarbage ([opt [, arg]])

此函数是垃圾回收器的通用接口。它根据其第一个参数 opt 执行不同的函数

有关垃圾回收和其中一些选项的更多详细信息,请参见 §2.5

此函数不应由终结器调用。


dofile ([filename])

打开指定的文件并将其内容作为 Lua 块执行。在不带参数调用时,dofile 执行标准输入 (stdin) 的内容。返回块返回的所有值。如果出错,dofile 将错误传播给其调用者。(即,dofile 不在受保护模式下运行。)


error (message [, level])

引发一个错误(参见 §2.3),其中 message 为错误对象。此函数绝不会返回。

通常,如果消息是字符串,error 会在消息开头添加一些有关错误位置的信息。level 参数指定如何获取错误位置。对于 level 1(默认值),错误位置是调用 error 函数的位置。Level 2 指向调用 error 的函数被调用的位置;以此类推。传递 level 0 可避免在消息中添加错误位置信息。


_G

一个全局变量(不是函数),它保存全局环境(参见 §2.2)。Lua 本身不使用此变量;更改其值不会影响任何环境,反之亦然。


getmetatable (object)

如果 object 没有元表,则返回 nil。否则,如果对象的元表有 __metatable 字段,则返回关联的值。否则,返回给定对象的元表。


ipairs (t)

返回三个值(一个迭代器函数、表 t 和 0),以便构造

     for i,v in ipairs(t) do body end

将遍历键值对 (1,t[1])、(2,t[2])、...,直至第一个不存在的索引。


load (chunk [, chunkname [, mode [, env]]])

加载一个块。

如果 chunk 是一个字符串,则该块就是这个字符串。如果 chunk 是一个函数,则 load 会反复调用它以获取块片段。每次调用 chunk 都必须返回一个字符串,该字符串与之前的结果连接。返回一个空字符串、nil 或无值表示块的结束。

如果没有语法错误,则 load 会将编译后的块作为函数返回;否则,它会返回 fail 和错误消息。

当加载一个主块时,结果函数将始终只有一个上值,即 _ENV 变量(请参阅 §2.2)。但是,当加载从函数创建的二进制块时(请参阅 string.dump),结果函数可以有任意数量的上值,并且无法保证其第一个上值是 _ENV 变量。(非主函数甚至可能没有 _ENV 上值。)

无论如何,如果结果函数有任何上值,则其第一个上值将设置为 env 的值(如果给出了该参数),或设置为全局环境的值。其他上值将初始化为 nil。所有上值都是新的,即它们与任何其他函数不共享。

chunkname 用作块的名称,用于错误消息和调试信息(请参阅 §4.7)。当不存在时,如果 chunk 是一个字符串,则默认为 chunk,否则默认为 "=(load)"。

字符串 mode 控制块是文本还是二进制(即预编译块)。它可以是字符串 "b"(仅二进制块)、"t"(仅文本块)或 "bt"(二进制和文本)。默认值为 "bt"。

加载格式错误的二进制块是安全的;load 会发出适当的错误。但是,Lua 不会检查二进制块中代码的一致性;运行恶意编写的字节码可能会使解释器崩溃。


loadfile ([filename [, mode [, env]]])

类似于 load,但从文件 filename 或标准输入(如果没有给出文件名)获取块。


next(table [, index])

允许程序遍历表中的所有字段。它的第一个参数是表,第二个参数是表中的索引。调用 next 会返回表的下一个索引及其关联的值。当使用 nil 作为第二个参数调用时,next 会返回一个初始索引及其关联的值。当使用最后一个索引或在空表中使用 nil 调用时,next 会返回 nil。如果第二个参数不存在,则将其解释为 nil。特别是,你可以使用 next(t) 检查表是否为空。

枚举索引的顺序未指定,即使对于数字索引也是如此。(要按数字顺序遍历表,请使用数字 for。)

在遍历表期间,你不应该为表中不存在的字段分配任何值。但是,你可以修改现有字段。特别是,你可以将现有字段设置为 nil。


pairs (t)

如果 t 有元方法 __pairs,则使用 t 作为参数调用它,并返回调用中的前三个结果。

否则,返回三个值:next 函数、表 tnil,以便构造

     for k,v in pairs(t) do body end

将遍历表 t 的所有键值对。

有关在遍历期间修改表时的注意事项,请参见函数 next


pcall (f [, arg1, ···])

保护模式下使用给定参数调用函数 f。这意味着 f 内部的任何错误都不会传播;相反,pcall 会捕获错误并返回状态码。它的第一个结果是状态码(一个布尔值),如果调用成功且没有错误,则为 true。在这种情况下,pcall 还会在第一个结果之后返回调用中的所有结果。如果发生任何错误,pcall 会返回 false 和错误对象。请注意,pcall 捕获的错误不会调用消息处理程序。


print (···)

接收任意数量的参数,并将它们的值打印到 stdout,按照 tostring 的相同规则将每个参数转换为字符串。

函数 print 不适用于格式化输出,而只是快速显示值的一种方法,例如用于调试。要完全控制输出,请使用 string.formatio.write


rawequal (v1, v2)

检查 v1 是否等于 v2,而不调用 __eq 元方法。返回一个布尔值。


rawget (table, index)

获取 table[index] 的真实值,而不使用 __index 元值。table 必须是一个表;index 可以是任何值。


rawlen (v)

返回对象 v 的长度,它必须是一个表或字符串,而无需调用 __len 元方法。返回一个整数。


rawset (table, index, value)

table[index] 的真实值设置为 value,而无需使用 __newindex 元值。table 必须是一个表,index 必须是除 nil 和 NaN 之外的任何值,value 必须是任何 Lua 值。

此函数返回 table


select (index, ···)

如果 index 是一个数字,则返回参数 index 之后的全部参数;负数从末尾开始索引(-1 是最后一个参数)。否则,index 必须是字符串 "#"select 返回它收到的全部额外参数的总数。


setmetatable (table, metatable)

为给定的表设置元表。如果 metatablenil,则删除给定表的元表。如果原始元表具有 __metatable 字段,则引发错误。

此函数返回 table

要从 Lua 代码更改其他类型的元表,您必须使用调试库(§6.10)。


tonumber (e [, base])

在不带 base 调用时,tonumber 尝试将其参数转换为数字。如果参数已经是数字或可转换为数字的字符串,则 tonumber 返回此数字;否则,它返回 fail

根据 Lua 的词法惯例(参见 §3.1),字符串的转换可得到整数或浮点数。字符串可能具有前导和尾随空格以及符号。

在带 base 调用时,e 必须是一个字符串,以该进制表示的整数数字。进制可以是 2 到 36(包括 2 和 36)之间的任何整数。在高于 10 的进制中,字母“A”(大写或小写)表示 10,“B”表示 11,依此类推,“Z”表示 35。如果字符串 e 不是给定进制中的有效数字,则函数返回 fail


tostring (v)

接收任何类型的值,并将其转换为人类可读格式的字符串。

如果v的元表有一个__tostring字段,那么tostring将调用对应的值,以v作为参数,并将调用的结果用作其结果。否则,如果v的元表有一个字符串值__name字段,tostring可能会在其最终结果中使用该字符串。

要完全控制数字的转换方式,请使用string.format


type (v)

返回其唯一参数的类型,编码为字符串。此函数可能的结果为“nil”(一个字符串,而不是值nil)、“number”、“string”、“boolean”、“table”、“function”、“thread”和“userdata”。


_VERSION

一个全局变量(不是函数),它包含一个字符串,其中包含正在运行的 Lua 版本。此变量的当前值为“Lua 5.4”。


warn (msg1, ···)

发出一个警告,其消息由其所有参数(应为字符串)的连接组成。

根据惯例,以“@”开头的单片消息旨在成为控制消息,即发送给警告系统本身的消息。特别是,Lua 中的标准警告函数识别控制消息“@off”,以停止发出警告,以及“@on”,以(重新)开始发出警告;它忽略未知的控制消息。


xpcall (f, msgh [, arg1, ···])

此函数类似于pcall,只是它设置了一个新的消息处理程序msgh

6.2 – 协程操作

此库包含用于操作协程的操作,这些操作位于表coroutine中。有关协程的常规说明,请参见§2.6


coroutine.close (co)

关闭协程co,即关闭其所有待关闭的变量,并将协程置于死状态。给定的协程必须处于死状态或暂停状态。如果出现错误(停止协程的原始错误或关闭方法中的错误),则返回false加上错误对象;否则返回true


coroutine.create (f)

创建一个新的协程,主体为 ff 必须是一个函数。返回此新协程,一个类型为 "thread" 的对象。


coroutine.isyieldable ([co])

当协程 co 可让出时返回 trueco 的默认值为正在运行的协程。

如果协程不是主线程,并且不在不可让出的 C 函数中,则该协程可让出。


coroutine.resume (co [, val1, ···])

启动或继续执行协程 co。第一次恢复协程时,它开始运行其主体。值 val1, ... 作为参数传递给主体函数。如果协程已经让出,resume 重新启动它;值 val1, ... 作为让出的结果传递。

如果协程在没有任何错误的情况下运行,resume 返回 true 加上传递给 yield 的任何值(当协程让出时)或主体函数返回的任何值(当协程终止时)。如果有任何错误,resume 返回 false 加上错误消息。


coroutine.running ()

返回正在运行的协程加一个布尔值,当正在运行的协程为主协程时为 true


coroutine.status (co)

返回协程 co 的状态,作为字符串:"running",如果协程正在运行(即,它调用了 status);"suspended",如果协程在调用 yield 时挂起,或者如果它尚未开始运行;"normal",如果协程处于活动状态但未运行(即,它已恢复另一个协程);"dead",如果协程已完成其主体函数,或如果它已因错误而停止。


coroutine.wrap (f)

创建一个新的协程,主体为 ff 必须是一个函数。返回一个函数,每次调用时都会恢复协程。传递给此函数的任何参数都作为 resume 的额外参数。该函数返回 resume 返回的相同值,除了第一个布尔值。如果发生错误,该函数将关闭协程并传播错误。


coroutine.yield (···)

挂起调用协程的执行。传递给 yield 的任何参数都作为额外结果传递给 resume

6.3 – 模块

package 库为在 Lua 中加载模块提供了基本工具。它直接在全局环境中导出一个函数: require。其他所有内容都导出到表 package 中。


require (modname)

加载给定的模块。该函数首先查看 package.loaded 表以确定 modname 是否已加载。如果是,则 require 返回存储在 package.loaded[modname] 中的值。(在这种情况下没有第二个结果表示此调用不必加载模块。)否则,它尝试为模块找到一个加载器

要查找加载器,require 由表 package.searchers 指导。此表中的每个项目都是一个搜索函数,它以特定方式搜索模块。通过更改此表,我们可以更改 require 查找模块的方式。以下说明基于 package.searchers 的默认配置。

首先require查询package.preload[modname]。如果它有值,则此值(必须是函数)是加载器。否则,require使用存储在package.path中的路径搜索 Lua 加载器。如果也失败,它将使用存储在package.cpath中的路径搜索 C 加载器。如果也失败,它将尝试一个一体化加载器(参见package.searchers)。

找到加载器后,require使用两个参数调用加载器:modname和一个额外值,即加载器数据,也由搜索器返回。加载器数据可以是模块有用的任何值;对于默认搜索器,它指示在何处找到加载器。(例如,如果加载器来自文件,则此额外值是文件路径。)如果加载器返回任何非 nil 值,则require将返回的值分配给package.loaded[modname]。如果加载器没有返回非 nil 值并且没有将任何值分配给package.loaded[modname],则requiretrue分配给此条目。在任何情况下,require都会返回package.loaded[modname]的最终值。除了该值外,require还将搜索器返回的加载器数据作为第二个结果返回,该数据指示require如何找到模块。

如果在加载或运行模块时出现任何错误,或者它找不到模块的任何加载器,则require会引发错误。


package.config

一个字符串,描述包的一些编译时配置。此字符串是一系列行


package.cpath

一个字符串,其中包含 require 用于搜索 C 加载器的路径。

Lua 初始化 C 路径 package.cpath 的方式与初始化 Lua 路径 package.path 的方式相同,使用环境变量 LUA_CPATH_5_4 或环境变量 LUA_CPATH,或在 luaconf.h 中定义的默认路径。


package.loaded

一个由 require 用于控制哪些模块已加载的表。当您需要一个模块 modnamepackage.loaded[modname] 不为 false 时,require 仅返回存储在那里的值。

此变量仅是对真实表的引用;对该变量的赋值不会更改 require 使用的表。真实表存储在 C 注册表中(请参阅 §4.3),其索引为字符串键 LUA_LOADED_TABLE


package.loadlib (libname, funcname)

将主机程序与 C 库 libname 动态链接。

如果 funcname 为 "*",则它仅与库链接,使库导出的符号可供其他动态链接库使用。否则,它将在库中查找一个函数 funcname 并将此函数作为 C 函数返回。因此,funcname 必须遵循 lua_CFunction 原型(请参阅 lua_CFunction)。

这是一个低级函数。它完全绕过了包和模块系统。与 require 不同,它不执行任何路径搜索,也不会自动添加扩展名。libname 必须是 C 库的完整文件名,包括必要的文件路径和扩展名。funcname 必须是 C 库导出的确切名称(可能取决于所使用的 C 编译器和链接器)。

此功能不受 ISO C 支持。因此,它仅在某些平台上可用(Windows、Linux、Mac OS X、Solaris、BSD 以及支持 dlfcn 标准的其他 Unix 系统)。

此函数本质上是不安全的,因为它允许 Lua 调用系统中任何可读动态库中的任何函数。(Lua 调用任何函数时都假设该函数具有正确的原型并遵守正确的协议(请参见 lua_CFunction)。因此,在任意动态库中调用任意函数通常会导致访问冲突。)


package.path

一个字符串,其中包含 require 用于搜索 Lua 加载器的路径。

在启动时,如果未定义这些环境变量,Lua 会使用环境变量 LUA_PATH_5_4 或环境变量 LUA_PATH 的值或 luaconf.h 中定义的默认路径来初始化此变量。环境变量的值中的“;;”将替换为默认路径。


package.preload

一个表,用于存储特定模块的加载器(请参见 require)。

此变量仅是对实际表的引用;对该变量的赋值不会更改 require 使用的表。实际表存储在 C 注册表中(请参见 §4.3),索引为字符串键 LUA_PRELOAD_TABLE


package.searchers

一个表,由 require 用于控制如何查找模块。

此表中的每个条目都是一个搜索器函数。在查找模块时,require 会按升序调用这些搜索器中的每一个,并以模块名称(传递给 require 的参数)作为其唯一参数。如果搜索器找到模块,它将返回另一个函数,即模块加载器,以及一个额外值,即加载器数据,该数据将传递给该加载器并作为第二个结果由 require 返回。如果它找不到模块,它将返回一个字符串,解释原因(或如果它无话可说,则返回 nil)。

Lua 使用四个搜索器函数初始化此表。

第一个搜索器只是在 package.preload 表中查找加载器。

第二个搜索器使用存储在 package.path 中的路径,将加载器作为 Lua 库进行查找。搜索按函数 package.searchpath 中所述进行。

第三个搜索器使用变量 package.cpath 给出的路径,将加载器作为 C 库进行查找。同样,搜索按函数 package.searchpath 中所述进行。例如,如果 C 路径为字符串

     "./?.so;./?.dll;/usr/local/?/init.so"

模块 foo 的搜索器将尝试按顺序打开文件 ./foo.so./foo.dll/usr/local/foo/init.so。一旦找到 C 库,此搜索器首先使用动态链接功能将应用程序与库链接。然后它尝试在库中查找一个 C 函数,用作加载器。此 C 函数的名称是字符串“luaopen_”与模块名称的副本连接而成,其中每个点都被下划线替换。此外,如果模块名称带有连字符,则会移除第一个连字符之后的(包括第一个连字符在内)后缀。例如,如果模块名称是 a.b.c-v2.1,则函数名称将为 luaopen_a_b_c

第四个搜索器尝试一个一体化加载器。它在 C 路径中搜索给定模块的根名称的库。例如,在需要 a.b.c 时,它将搜索 a 的 C 库。如果找到,它会在其中查找子模块的 open 函数;在我们的示例中,该函数将为 luaopen_a_b_c。借助此功能,一个包可以将多个 C 子模块打包到一个库中,每个子模块保留其原始 open 函数。

除了第一个搜索器(预加载)之外的所有搜索器都将模块被找到的文件路径作为额外值返回,如 package.searchpath 所返回。第一个搜索器始终返回字符串“:preload:”。

搜索器不应引发错误,并且在 Lua 中没有副作用。(它们可能在 C 中有副作用,例如通过将应用程序与库链接。)


package.searchpath (name, path [, sep [, rep]])

在给定的 path 中搜索给定的 name

路径是一个字符串,其中包含一系列用分号分隔的模板。对于每个模板,函数用 name 的副本替换模板中的每个问号(如果有),其中 sep(默认情况下为点)的所有出现都被 rep(默认情况下为系统的目录分隔符)替换,然后尝试打开生成的文件名。

例如,如果路径是字符串

     "./?.lua;./?.lc;/usr/local/?/init.lua"

则对名称 foo.a 的搜索将按顺序尝试打开文件 ./foo/a.lua./foo/a.lc/usr/local/foo/a/init.lua

返回第一个可以在读取模式下打开的文件(在关闭文件后)的结果名称,或者如果都不成功,则返回 fail 加上错误消息。(此错误消息列出它尝试打开的所有文件名。)

6.4 – 字符串操作

此库提供用于字符串操作的通用函数,例如查找和提取子字符串以及模式匹配。在 Lua 中对字符串进行索引时,第一个字符位于位置 1(而不是像在 C 中位于 0)。索引可以为负数,并且解释为从字符串末尾向后索引。因此,最后一个字符位于位置 -1,依此类推。

字符串库在其内部表 string 中提供所有函数。它还为字符串设置元表,其中 __index 字段指向 string 表。因此,您可以在面向对象风格中使用字符串函数。例如,string.byte(s,i) 可以写成 s:byte(i)

字符串库假定单字节字符编码。


string.byte (s [, i [, j]])

返回字符 s[i]s[i+1]、...、s[j] 的内部数字代码。i 的默认值为 1;j 的默认值为 i。这些索引按照与函数 string.sub 相同的规则进行更正。

数字代码不一定可以在不同平台之间移植。


string.char (···)

接收零个或多个整数。返回一个字符串,其长度等于参数数量,其中每个字符的内部数字代码等于其对应的参数。

数字代码不一定可以在不同平台之间移植。


string.dump (function [, strip])

返回一个包含给定函数的二进制表示(一个二进制块)的字符串,以便稍后对该字符串执行 load 将返回该函数的副本(但有新的 upvalue)。如果 strip 为真值,则二进制表示可能不包含有关该函数的所有调试信息,以节省空间。

带有 upvalue 的函数只保存它们的 upvalue 数量。当(重新)加载时,这些 upvalue 会接收新的实例。(有关如何初始化这些 upvalue 的详细信息,请参阅 load 函数。您可以使用调试库以适合您需求的方式序列化和重新加载函数的 upvalue。)


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

在字符串 s 中查找 pattern(请参阅 §6.4.1)的第一个匹配项。如果找到匹配项,则 find 返回 s 中此匹配项开始和结束的索引;否则,它返回 fail。第三个可选数字参数 init 指定从何处开始搜索;其默认值为 1,可以为负数。第四个可选参数 plaintrue 时,会关闭模式匹配功能,因此该函数执行一个简单的“查找子字符串”操作,pattern 中没有任何字符被视为魔术字符。

如果模式有捕获,则在成功匹配时,捕获的值也会在两个索引之后返回。


string.format (formatstring, ···)

返回格式化版本的可变数量参数,遵循第一个参数中给出的描述,该参数必须为字符串。格式字符串遵循与 ISO C 函数 sprintf 相同的规则。唯一的区别是转换说明符和修饰符 Fn*hLl 不受支持,并且有一个额外的说明符 q。宽度和精度(如果存在)都限制为两位数。

说明符 q 格式化布尔值、nil、数字和字符串,其结果是 Lua 源代码中的有效常量。布尔值和 nil 以明显的方式编写(truefalsenil)。浮点数以十六进制形式编写,以保持完全精度。字符串用双引号编写,必要时使用转义序列以确保 Lua 解释器可以安全地读回它。例如,调用

     string.format('%q', 'a string with "quotes" and \n new line')

可能会生成字符串

     "a string with \"quotes\" and \
      new line"

此说明符不支持修饰符(标志、宽度、精度)。

转换说明符 AaEefGg 都期望数字作为参数。说明符 cdiouXx 期望整数。当使用 C89 编译器编译 Lua 时,说明符 Aa(十六进制浮点数)不支持修饰符。

说明符 s 期望字符串;如果其参数不是字符串,则按照 tostring 的相同规则将其转换为字符串。如果说明符有任何修饰符,则相应的字符串参数不应包含嵌入式零。

说明符 p 格式化 lua_topointer 返回的指针。这为表、用户数据、线程、字符串和函数提供了唯一的字符串标识符。对于其他值(数字、nil、布尔值),此说明符会生成表示指针 NULL 的字符串。


string.gmatch (s, pattern [, init])

返回一个迭代器函数,每次调用时都会返回 pattern(参见 §6.4.1)在字符串 s 上的下一个捕获。如果 pattern 未指定任何捕获,则每次调用都会生成整个匹配项。第三个可选数字参数 init 指定从哪里开始搜索;其默认值为 1,可以为负数。

例如,以下循环将迭代字符串 s 中的所有单词,每行打印一个

     s = "hello world from Lua"
     for w in string.gmatch(s, "%a+") do
       print(w)
     end

下一个示例从给定的字符串中收集所有成对的 key=value 到一个表中

     t = {}
     s = "from=world, to=Lua"
     for k, v in string.gmatch(s, "(%w+)=(%w+)") do
       t[k] = v
     end

对于此函数,模式开头的脱字符 '^' 不可用作锚点,因为这会阻止迭代。


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

返回 s 的副本,其中 pattern(参见 §6.4.1)的所有(或给定的前 n 个)匹配项已被 repl 指定的替换字符串替换,该字符串可以是字符串、表或函数。 gsub 还返回匹配发生的总数作为其第二个值。 gsub 名称来自全局替换

如果 repl 是字符串,则其值用于替换。字符 % 用作转义字符:repl 中任何形式为 %d 的序列,其中 d 在 1 到 9 之间,表示第 d 个捕获子字符串的值;序列 %0 表示整个匹配项;序列 %% 表示单个 %

如果 repl 是表,则使用第一个捕获作为键,对每个匹配项查询表。

如果 repl 是函数,则每次发生匹配时都会调用此函数,并按顺序将所有捕获的子字符串作为参数传递。

在任何情况下,如果模式未指定任何捕获,则其行为就像整个模式位于捕获中一样。

如果表查询或函数调用返回的值是字符串或数字,则将其用作替换字符串;否则,如果它是 falsenil,则没有替换(即,原始匹配项保留在字符串中)。

以下是一些示例

     x = string.gsub("hello world", "(%w+)", "%1 %1")
     --> x="hello hello world world"
     
     x = string.gsub("hello world", "%w+", "%0 %0", 1)
     --> x="hello hello world"
     
     x = string.gsub("hello world from Lua", "(%w+)%s*(%w+)", "%2 %1")
     --> x="world hello Lua from"
     
     x = string.gsub("home = $HOME, user = $USER", "%$(%w+)", os.getenv)
     --> x="home = /home/roberto, user = roberto"
     
     x = string.gsub("4+5 = $return 4+5$", "%$(.-)%$", function (s)
           return load(s)()
         end)
     --> x="4+5 = 9"
     
     local t = {name="lua", version="5.4"}
     x = string.gsub("$name-$version.tar.gz", "%$(%w+)", t)
     --> x="lua-5.4.tar.gz"


string.len (s)

接收一个字符串并返回其长度。空字符串 "" 的长度为 0。嵌入的零会被计数,因此 "a\000bc\000" 的长度为 5。


string.lower (s)

接收一个字符串并返回此字符串的副本,其中所有大写字母都已更改为小写字母。所有其他字符保持不变。大写字母的定义取决于当前区域设置。


string.match (s, pattern [, init])

在字符串 s 中查找 pattern(参见 §6.4.1)的第一个匹配项。如果找到一个,则 match 返回模式的捕获;否则返回 fail。如果 pattern 未指定任何捕获,则返回整个匹配项。第三个可选数字参数 init 指定从哪里开始搜索;其默认值为 1,可以为负数。


string.pack (fmt, v1, v2, ···)

返回一个包含值 v1v2 等的二进制字符串,这些值按照格式字符串 fmt(请参阅 §6.4.2)以二进制形式(打包)序列化。


string.packsize (fmt)

返回使用给定格式执行 string.pack 后产生的字符串的长度。格式字符串不能包含可变长度选项 's' 或 'z'(请参阅 §6.4.2)。


string.rep (s, n [, sep])

返回一个字符串,该字符串是字符串 sn 个副本,由字符串 sep 分隔。sep 的默认值为一个空字符串(即没有分隔符)。如果 n 不是正数,则返回一个空字符串。

(请注意,只需调用此函数一次,就很容易耗尽计算机的内存。)


string.reverse (s)

返回一个字符串,该字符串是字符串 s 的反转。


string.sub (s, i [, j])

返回从 i 开始并持续到 js 子字符串;ij 可以为负数。如果 j 不存在,则假定它等于 -1(与字符串长度相同)。特别是,调用 string.sub(s,1,j) 返回长度为 js 前缀,而 string.sub(s, -i)(对于正数 i)返回长度为 is 后缀。

如果在转换负数索引后,i 小于 1,则将其更正为 1。如果 j 大于字符串长度,则将其更正为该长度。如果在这些更正后,i 大于 j,则该函数返回一个空字符串。


string.unpack (fmt, s [, pos])

根据格式字符串 fmt(请参阅 §6.4.2),返回字符串 s 中打包的值(请参阅 string.pack)。可选的 pos 标记在 s 中开始读取的位置(默认为 1)。在读取值后,此函数还返回 s 中第一个未读字节的索引。


string.upper (s)

接收一个字符串,并返回一个字符串副本,其中所有小写字母都已更改为大写字母。所有其他字符保持不变。小写字母的定义取决于当前语言环境。

6.4.1 – 模式

Lua 中的模式由正则字符串描述,模式匹配函数 string.findstring.gmatchstring.gsubstring.match 将其解释为模式。本节描述了这些字符串的语法和含义(即它们匹配的内容)。

字符类

字符类用于表示一组字符。在描述字符类时允许以下组合

对于由单个字母(%a%c等)表示的所有类,相应的大写字母表示该类的补集。例如,%S表示所有非空格字符。

字母、空格和其他字符组的定义取决于当前区域设置。特别是,类[a-z]可能不等于%l

模式项

模式项可以是

模式

模式是模式项的序列。模式开头的脱字符“^”将匹配锚定在主题字符串的开头。“$”在模式的末尾将匹配锚定在主题字符串的末尾。在其他位置,“^”和“$”没有特殊含义,表示它们自己。

捕获

模式可以包含括在圆括号中的子模式;它们描述“捕获”。当匹配成功时,与捕获匹配的主题字符串的子字符串将被存储(“捕获”)以备将来使用。捕获根据其左括号进行编号。例如,在模式 "(a*(.)%w(%s*))" 中,与 "a*(.)%w(%s*)" 匹配的字符串部分存储为第一个捕获,因此编号为 1;与“.”匹配的字符捕获编号为 2,与“%s*”匹配的部分编号为 3。

作为特殊情况,捕获 () 捕获当前字符串位置(一个数字)。例如,如果我们在字符串 "flaaap" 上应用模式 "()aa()",将有两个捕获:3 和 5。

多重匹配

函数 string.gsub 和迭代器 string.gmatch 匹配主题中给定模式的多个出现。对于这些函数,只有在结束至少比前一个匹配结束多一个字节时,才认为一个新匹配有效。换句话说,模式机器绝不会将空字符串作为紧接另一个匹配之后的匹配。例如,考虑以下代码的结果

     > string.gsub("abc", "()a*()", print);
     --> 1   2
     --> 3   3
     --> 4   4

第二个和第三个结果来自 Lua 在“b”之后匹配一个空字符串,以及在“c”之后匹配另一个空字符串。Lua 不会在“a”之后匹配一个空字符串,因为它会结束在与前一个匹配相同的位置。

6.4.2 – 打包和解包的格式字符串

string.packstring.packsizestring.unpack 的第一个参数是一个格式字符串,它描述了正在创建或读取的结构的布局。

格式字符串是一系列转换选项。转换选项如下

(“[n]”表示一个可选的整数。)除了填充、空格和配置(选项“xX <=>!”)之外,每个选项都对应于 string.pack 中的一个参数或 string.unpack 中的一个结果。

对于选项“!n”、“sn”、“in”和“In”,n 可以是 1 到 16 之间的任何整数。所有整数选项都会检查溢出;string.pack 检查给定值是否适合给定大小;string.unpack 检查读取的值是否适合 Lua 整数。对于无符号选项,Lua 整数也被视为无符号值。

任何格式字符串都以“!1=”为前缀,即最大对齐为 1(无对齐)和本机端序。

本机端序假定整个系统是大端或小端。打包函数不会正确模拟混合端格式的行为。

对齐的工作方式如下:对于每个选项,格式会获取额外的填充,直到数据从一个偏移量开始,该偏移量是选项大小和最大对齐之间的最小值的倍数;该最小值必须是 2 的幂。选项“c”和“z”不会对齐;选项“s”遵循其起始整数的对齐方式。

所有填充均由 string.pack 填充为零,并由 string.unpack 忽略。

6.5 – UTF-8 支持

此库为 UTF-8 编码提供基本支持。它在表 utf8 中提供其所有函数。此库除了处理编码外,不提供对 Unicode 的任何支持。任何需要字符含义的操作(如字符分类)都不在它的范围内。

除非另有说明,否则所有将字节位置作为参数的函数都假定给定位置是字节序列的开始或主题字符串的长度加一。与字符串库一样,负索引从字符串的末尾开始计数。

创建字节序列的函数接受原始 UTF-8 规范中定义的所有值,最高可达 0x7FFFFFFF;这意味着最多六个字节的字节序列。

解释字节序列的函数只接受有效序列(格式正确且不过长)。默认情况下,它们只接受生成有效 Unicode 代码点的字节序列,拒绝大于 10FFFF 和代理项的值。当可用时,布尔参数 lax 会取消这些检查,以便接受所有值,最高可达 0x7FFFFFFF。(格式不正确和过长的序列仍然会被拒绝。)


utf8.char (···)

接收零个或多个整数,将每个整数转换为其对应的 UTF-8 字节序列,并返回一个包含所有这些序列连接的字符串。


utf8.charpattern

模式(一个字符串,而不是一个函数)“[\0-\x7F\xC2-\xFD][\x80-\xBF]*”(参见 §6.4.1),它完全匹配一个 UTF-8 字节序列,假定主题是一个有效的 UTF-8 字符串。


utf8.codes (s [, lax])

返回值,以便构造

     for p, c in utf8.codes(s) do body end

将遍历字符串 s 中的所有 UTF-8 字符,其中 p 是每个字符的位置(以字节为单位),c 是每个字符的代码点。如果遇到任何无效字节序列,它会引发错误。


utf8.codepoint (s [, i [, j [, lax]]])

返回 s 中所有字符的代码点(作为整数),这些字符从字节位置 ij(均包含)之间开始。i 的默认值为 1,j 的默认值为 i。如果遇到任何无效字节序列,它会引发错误。


utf8.len (s [, i [, j [, lax]]])

返回字符串 s 中从位置 ij(均包含)之间开始的 UTF-8 字符数。i 的默认值为 1,j 的默认值为 -1。如果它找到任何无效字节序列,则返回 fail 加上第一个无效字节的位置。


utf8.offset (s, n [, i])

返回 s 的第 n 个字符(从位置 i 开始计数)的编码开始的位置(以字节为单位)。负 n 获取位置 i 之前的字符。当 n 为非负时,i 的默认值为 1,否则为 #s + 1,以便 utf8.offset(s, -n) 获取字符串末尾第 n 个字符的偏移量。如果指定字符既不在主题中,也不在主题的末尾之后,则该函数返回 fail

作为特殊情况,当n为 0 时,该函数返回包含s的第i个字节的字符编码的开始。

此函数假定s是一个有效的 UTF-8 字符串。

6.6 – 表格操作

此库提供用于表格操作的通用函数。它在表格 table 中提供所有函数。

请记住,每当操作需要表格长度时,所有关于长度运算符的警告都适用(请参阅 §3.4.7)。所有函数忽略作为参数提供的表格中的非数字键。


table.concat (list [, sep [, i [, j]]])

给定一个其中所有元素都是字符串或数字的列表,返回字符串list[i]..sep..list[i+1] ··· sep..list[j]sep的默认值为一个空字符串,i的默认值为 1,j的默认值为#list。如果i大于j,则返回一个空字符串。


table.insert (list, [pos,] value)

listpos位置插入元素value,将元素list[pos], list[pos+1], ···, list[#list]向上移动。pos的默认值为#list+1,因此调用table.insert(t,x)会在列表t的末尾插入x


table.move (a1, f, e, t [,a2])

将元素从表格a1移动到表格a2,执行等效于以下多重赋值:a2[t],··· = a1[f],···,a1[e]a2的默认值为a1。目标范围可以与源范围重叠。要移动的元素数量必须适合 Lua 整数。

返回目标表格a2


table.pack (···)

返回一个新表格,其中所有参数都存储在键 1、2 等中,并且有一个字段“n”,其中包含参数的总数。请注意,如果某些参数为nil,则结果表格可能不是一个序列。


table.remove (list [, pos])

list中移除位置pos处的元素,返回移除元素的值。当pos是 1 到#list之间的整数时,它会向下移动元素list[pos+1], list[pos+2], ···, list[#list]并擦除元素list[#list];当#list为 0 或#list + 1时,索引pos也可以为 0。

pos的默认值为#list,因此调用table.remove(l)会移除列表l的最后一个元素。


table.sort (list [, comp])

按给定顺序对列表元素进行排序,就地,从list[1]list[#list]。如果给定了comp,则它必须是一个函数,该函数接收两个列表元素,并在第一个元素必须在最终顺序中排在第二个元素之前时返回 true,以便在排序后,i <= j暗示not comp(list[j],list[i])。如果未给定comp,则使用标准 Lua 运算符<

comp 函数必须定义一个一致的顺序;更正式地说,该函数必须定义一个严格的弱序。(弱序类似于全序,但它可以将不同的元素等同起来进行比较。)

排序算法不稳定:给定顺序认为相等的不同的元素可能因排序而改变其相对位置。


table.unpack (list [, i [, j]])

返回给定列表中的元素。此函数等效于

     return list[i], list[i+1], ···, list[j]

默认情况下,i 是 1,j#list

6.7 – 数学函数

此库提供基本数学函数。它在表 math 中提供所有函数和常量。带注释“integer/float”的函数对整数参数给出整数结果,对非整数参数给出浮点数结果。舍入函数 math.ceilmath.floormath.modf 在结果适合整数范围时返回一个整数,否则返回一个浮点数。


math.abs (x)

返回 x-x 之间的最大值。(integer/float)


math.acos (x)

返回 x 的反余弦值(以弧度为单位)。


math.asin (x)

返回 x 的反正弦值(以弧度为单位)。


math.atan (y [, x])

返回 y/x 的反正切值(以弧度为单位),使用两个参数的符号来查找结果的象限。它还正确处理 x 为零的情况。

x 的默认值为 1,因此调用 math.atan(y) 返回 y 的反正切值。


math.ceil (x)

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


math.cos (x)

返回 x 的余弦值(假定以弧度为单位)。


math.deg (x)

将角度 x 从弧度转换为度。


math.exp (x)

返回 ex 的值(其中 e 是自然对数的底)。


math.floor (x)

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


math.fmod (x, y)

返回 x 除以 y 的余数,该余数将商四舍五入为零。(integer/float)


math.huge

浮点数 HUGE_VAL,大于任何其他数值的值。


math.log (x [, base])

返回 x 在给定底数下的对数。base 的默认值为 e(因此该函数返回 x 的自然对数)。


math.max (x, ···)

根据 Lua 运算符 < 返回具有最大值的参数。


math.maxinteger

具有整数最大值的整数。


math.min (x, ···)

根据 Lua 运算符 < 返回具有最小值的参数。


math.mininteger

具有整数最小值的整数。


math.modf (x)

返回 x 的整数部分和 x 的小数部分。其第二个结果始终是浮点数。


math.pi

π 的值。


math.rad (x)

将角度 x 从度转换为弧度。


math.random ([m [, n]])

在不带参数的情况下调用时,返回一个均匀分布在 [0,1) 范围内的伪随机浮点数。当使用两个整数 mn 调用时,math.random 返回一个均匀分布在 [m, n] 范围内的伪随机整数。对于一个正数 n,调用 math.random(n) 等效于 math.random(1,n)。调用 math.random(0) 会产生一个所有位都为(伪)随机的整数。

此函数使用 xoshiro256** 算法生成伪随机 64 位整数,这是使用参数 0 调用的结果。其他结果(范围和浮点数)是从这些整数中无偏提取的。

Lua 使用等效于不带参数调用 math.randomseed 的调用来初始化其伪随机生成器,以便 math.random 应在每次程序运行时生成不同的结果序列。


math.randomseed ([x [, y]])

当使用至少一个参数调用时,整数参数 xy 被连接成一个 128 位的种子,用于重新初始化伪随机生成器;相等的种子会产生相等の数序列。y 的默认值为零。

在不带参数的情况下调用时,Lua 会生成一个弱随机性的种子。

此函数返回实际使用的两个种子组件,以便再次设置它们来重复序列。

为初始状态确保所需的随机性级别(或相反,具有确定性序列,例如在调试程序时),您应使用显式参数调用 math.randomseed


math.sin (x)

返回 x 的正弦值(假定以弧度为单位)。


math.sqrt (x)

返回 x 的平方根。(您还可以使用表达式 x^0.5 来计算此值。)


math.tan (x)

返回 x 的正切值(假定以弧度为单位)。


math.tointeger (x)

如果值 x 可转换为整数,则返回该整数。否则,返回 fail


math.type (x)

如果 x 是整数,则返回“integer”;如果 x 是浮点数,则返回“float”;如果 x 不是数字,则返回 fail


math.ult (m, n)

返回一个布尔值,当且仅当整数 m 在作为无符号整数比较时低于整数 n 时,返回 true

6.8 – 输入和输出工具

I/O 库为文件操作提供了两种不同的样式。第一种使用隐式文件句柄;也就是说,有一些操作可以设置默认输入文件和默认输出文件,并且所有输入/输出操作都是通过这些默认文件完成的。第二种样式使用显式文件句柄。

使用隐式文件句柄时,所有操作都由表 io 提供。使用显式文件句柄时,操作 io.open 返回一个文件句柄,然后所有操作都作为文件句柄的方法提供。

文件句柄的元表为 __gc__close 提供元方法,这些元方法在调用时尝试关闭文件。

io 还提供了三个预定义的文件句柄,它们具有 C 语言中通常的含义:io.stdinio.stdoutio.stderr。I/O 库绝不会关闭这些文件。

除非另有说明,否则所有 I/O 函数在失败时返回 fail,并返回错误消息作为第二个结果和系统相关的错误代码作为第三个结果,并在成功时返回一些非假值。在非 POSIX 系统上,在发生错误时计算错误消息和错误代码可能不是线程安全的,因为它们依赖于全局 C 变量 errno


io.close ([file])

等效于 file:close()。如果没有 file,则关闭默认输出文件。


io.flush ()

等效于 io.output():flush()


io.input ([file])

当使用文件名调用时,它将打开指定的文件(文本模式),并将它的句柄设置为默认输入文件。当使用文件句柄调用时,它将简单地将此文件句柄设置为默认输入文件。当不带参数调用时,它将返回当前的默认输入文件。

如果出现错误,此函数将引发错误,而不是返回错误代码。


io.lines ([filename, ···])

以读取模式打开给定的文件名,并返回一个迭代器函数,该函数在打开的文件中像 file:lines(···) 一样工作。当迭代器函数无法读取任何值时,它将自动关闭文件。除了迭代器函数,io.lines 还返回三个其他值:两个 nil 值作为占位符,以及创建的文件句柄。因此,当在通用 for 循环中使用时,即使循环因错误或 break 而中断,文件也会关闭。

调用 io.lines()(没有文件名)等效于 io.input():lines("l");也就是说,它遍历默认输入文件的行。在这种情况下,迭代器在循环结束时不会关闭文件。

如果在打开文件时出现错误,此函数将引发错误,而不是返回错误代码。


io.open (filename [, mode])

此函数打开一个文件,模式在字符串 mode 中指定。如果成功,它将返回一个新的文件句柄。

mode 字符串可以是以下任何一个

mode 字符串末尾还可以有一个 'b',在某些系统中需要它才能以二进制模式打开文件。


io.output ([file])

类似于 io.input,但作用于默认输出文件。


io.popen (prog [, mode])

此函数依赖于系统,并非所有平台均可用。

在单独的进程中启动程序 prog,并返回一个文件句柄,该句柄可用于从该程序读取数据(如果 mode"r",即默认值)或向该程序写入数据(如果 mode"w")。


io.read (···)

等同于 io.input():read(···)


io.tmpfile ()

如果成功,则返回一个临时文件的句柄。此文件以更新模式打开,并且在程序结束时会自动删除。


io.type (obj)

检查 obj 是否为有效的文件句柄。如果 obj 是一个打开的文件句柄,则返回字符串 "file";如果 obj 是一个已关闭的文件句柄,则返回 "closed file";如果 obj 不是一个文件句柄,则返回 fail


io.write (···)

等同于 io.output():write(···)


file:close ()

关闭 file。请注意,当文件句柄被垃圾回收时,文件会自动关闭,但发生这种情况需要不可预测的时间。

当关闭使用 io.popen 创建的文件句柄时,file:close 返回 os.execute 返回的相同值。


file:flush ()

将任何已写入的数据保存到 file


file:lines (···)

返回一个迭代器函数,每次调用时,都会根据给定的格式读取文件。如果未给出格式,则使用 "l" 作为默认值。例如,构造

     for c in file:lines(1) do body end

将从当前位置开始遍历文件的全部字符。与 io.lines 不同,此函数在循环结束时不会关闭文件。


file:read (···)

根据给定的格式读取文件 file,这些格式指定要读取的内容。对于每种格式,该函数返回一个字符串或一个数字,其中包含读取的字符,或者如果它无法使用指定格式读取数据,则返回 fail。(在后一种情况下,该函数不会读取后续格式。)当不带参数调用时,它使用默认格式来读取下一行(见下文)。

可用的格式有

格式“l”和“L”仅应用于文本文件。


file:seek ([whence [, offset]])

设置和获取文件位置,从文件开头开始,到由offset加上字符串whence指定的基准位置,如下所示

如果成功,seek会返回最终文件位置,以字节为单位从文件开头开始测量。如果seek失败,则会返回失败,以及描述错误的字符串。

whence的默认值为“cur”,offset的默认值为 0。因此,调用file:seek()会返回当前文件位置,而不更改它;调用file:seek("set")会将位置设置为文件开头(并返回 0);调用file:seek("end")会将位置设置为文件结尾,并返回其大小。


file:setvbuf (mode [, size])

设置文件的缓冲模式。有三种可用模式

对于后两种情况,size是缓冲区大小(以字节为单位)的提示。默认值是适当的大小。

每种模式的具体行为不可移植;请查看平台中底层的 ISO C 函数setvbuf以了解更多详细信息。


file:write (···)

将每个参数的值写入file。参数必须是字符串或数字。

如果成功,此函数会返回file

6.9 – 操作系统功能

此库通过表os实现。


os.clock ()

返回程序使用的 CPU 时间量(以秒为单位)的近似值,由底层的 ISO C 函数clock返回。


os.date ([format [, time]])

返回一个字符串或一个表,其中包含日期和时间,格式根据给定的字符串format进行格式化。

如果存在 time 参数,则这是要格式化的时间(有关此值的说明,请参见 os.time 函数)。否则,date 会格式化当前时间。

如果 format 以“!”开头,则日期会以协调世界时格式化。在此可选字符之后,如果 format 是字符串“*t”,则 date 会返回一个包含以下字段的表:yearmonth(1-12)、day(1-31)、hour(0-23)、min(0-59)、sec(0-61,由于闰秒)、wday(星期几,1-7,星期日为 1)、yday(一年中的第几天,1-366)和 isdst(夏令时标志,一个布尔值)。如果信息不可用,则最后一个字段可能不存在。

如果 format 不是“*t”,则 date 会根据与 ISO C 函数 strftime 相同的规则将日期返回为字符串。

如果 format 不存在,则它默认为“%c”,它使用当前语言环境提供人类可读的日期和时间表示形式。

在非 POSIX 系统上,此函数可能不是线程安全的,因为它依赖于 C 函数 gmtime 和 C 函数 localtime


os.difftime (t2, t1)

返回从时间 t1 到时间 t2 的差值(以秒为单位)(其中时间是 os.time 返回的值)。在 POSIX、Windows 和一些其他系统中,此值为 t2-t1


os.execute ([command])

此函数等效于 ISO C 函数 system。它将 command 传递给操作系统 shell 执行。其第一个结果是 true(如果命令成功终止)或 fail(否则)。在此第一个结果之后,函数返回一个字符串和一个数字,如下所示

在没有 command 的情况下调用时,os.execute 返回一个布尔值,如果 shell 可用,则该布尔值为 true。


os.exit ([code [, close]])

调用 ISO C 函数 exit 终止主机程序。如果 codetrue,则返回的状态为 EXIT_SUCCESS;如果 codefalse,则返回的状态为 EXIT_FAILURE;如果 code 为一个数字,则返回的状态为此数字。code 的默认值为 true

如果可选的第二个参数 close 为 true,该函数在退出前关闭 Lua 状态(参见 lua_close)。


os.getenv (varname)

返回进程环境变量 varname 的值,如果变量未定义,则返回 fail


os.remove (filename)

删除具有给定名称的文件(或 POSIX 系统上的空目录)。如果此函数失败,它将返回 fail,以及描述错误和错误代码的字符串。否则,它将返回 true。


os.rename (oldname, newname)

将名为 oldname 的文件或目录重命名为 newname。如果此函数失败,它将返回 fail,以及描述错误和错误代码的字符串。否则,它将返回 true。


os.setlocale (locale [, category])

设置程序的当前语言环境。locale 是指定语言环境的系统相关字符串;category 是一个可选字符串,描述要更改的类别:"all""collate""ctype""monetary""numeric""time";默认类别为 "all"。该函数返回新语言环境的名称,如果请求无法得到满足,则返回 fail

如果 locale 是空字符串,则当前语言环境将设置为实现定义的本机语言环境。如果 locale 是字符串 "C",则当前语言环境将设置为标准 C 语言环境。

当使用 nil 作为第一个参数调用时,此函数仅返回给定类别的当前语言环境的名称。

此函数可能不是线程安全的,因为它依赖于 C 函数 setlocale


os.time ([table])

在不带参数调用时返回当前时间,或返回表示由给定表指定的时间和日期的时间。此表必须具有字段 yearmonthday,并且可以具有字段 hour(默认为 12)、min(默认为 0)、sec(默认为 0)和 isdst(默认为 nil)。其他字段将被忽略。有关这些字段的说明,请参见 os.date 函数。

当调用该函数时,这些字段中的值不需要在其有效范围内。例如,如果 sec 为 -10,则表示其他字段指定的时间之前 10 秒;如果 hour 为 1000,则表示其他字段指定的时间之后 1000 小时。

返回的值是一个数字,其含义取决于你的系统。在 POSIX、Windows 和其他一些系统中,此数字计算自某个给定的开始时间(“纪元”)以来的秒数。在其他系统中,含义未指定,并且 time 返回的数字只能用作 os.dateos.difftime 的参数。

当使用表格调用时,os.time 还会对 os.date 函数中记录的所有字段进行规范化,以便它们表示与调用前相同的时间,但值在其有效范围内。


os.tmpname ()

返回一个字符串,其中包含可用于临时文件的文件名。必须在使用前显式打开文件,并且在不再需要时显式删除文件。

在 POSIX 系统中,此函数还会创建一个具有该名称的文件,以避免安全风险。(在获取名称和创建文件之间的时间内,其他人可能会创建具有错误权限的文件。)你仍然必须打开文件才能使用和删除它(即使你没有使用它)。

如果可能,你可能更愿意使用 io.tmpfile,它会在程序结束时自动删除文件。

6.10 – 调试库

此库向 Lua 程序提供调试接口(§4.7)的功能。使用此库时应小心。它的几个函数违反了 Lua 代码的基本假设(例如,无法从外部访问函数的局部变量;Lua 代码无法更改用户数据元表;Lua 程序不会崩溃),因此可能会损害原本安全的代码。此外,此库中的一些函数可能会很慢。

此库中的所有函数都提供在 debug 表中。所有针对线程操作的函数都有一个可选的第一个参数,即要操作的线程。默认值始终是当前线程。


debug.debug ()

以交互模式进入用户,运行用户输入的每个字符串。使用简单的命令和其他调试工具,用户可以检查全局和局部变量、更改其值、评估表达式等。仅包含单词 cont 的行完成此函数,以便调用者继续执行。

请注意,debug.debug 的命令在任何函数中都没有词法嵌套,因此无法直接访问局部变量。


debug.gethook ([thread])

返回线程的当前钩子设置,作为三个值:当前钩子函数、当前钩子掩码和当前钩子计数,由 debug.sethook 函数设置。

如果当前没有激活的钩子,则返回 fail


debug.getinfo ([thread,] f [, what])

返回一个包含有关函数信息的表。你可以直接给出函数,也可以将一个数字作为 f 的值,这意味着在给定线程的调用堆栈的第 f 级运行的函数:第 0 级是当前函数(getinfo 本身);第 1 级是调用 getinfo 的函数(尾调用除外,尾调用不在堆栈中计算);以此类推。如果 f 是一个大于活动函数数量的数字,则 getinfo 返回 fail

返回的表可以包含 lua_getinfo 返回的所有字段,字符串 what 描述要填写的字段。what 的默认值是获取所有可用的信息,除了有效行表。如果存在,选项 'f' 会添加一个名为 func 的字段,其中包含函数本身。如果存在,选项 'L' 会添加一个名为 activelines 的字段,其中包含有效行表。

例如,表达式 debug.getinfo(1,"n").name 返回当前函数的名称(如果可以找到合理的名称),而表达式 debug.getinfo(print) 返回一个包含有关 print 函数的所有可用信息的表。


debug.getlocal ([thread,] f, local)

此函数返回堆栈中第 f 级函数的索引为 local 的局部变量的名称和值。此函数不仅可以访问显式局部变量,还可以访问参数和临时值。

第一个参数或局部变量的索引为 1,依此类推,按照它们在代码中声明的顺序,仅计算函数当前作用域中处于活动状态的变量。如果编译器对其进行了优化,则编译时常量可能不会出现在此列表中。负索引引用可变参数;-1 是第一个可变参数。如果不存在具有给定索引的变量,则函数返回 fail,并在使用超出范围的级别调用时引发错误。(你可以调用 debug.getinfo 来检查级别是否有效。)

以 '('(左括号)开头的变量名表示没有已知名称的变量(内部变量,如循环控制变量和从不带调试信息保存的块中获取的变量)。

参数 f 也可以是一个函数。在这种情况下,getlocal 仅返回函数参数的名称。


debug.getmetatable (value)

返回给定value的元表,如果没有元表,则返回nil


debug.getregistry ()

返回注册表(参见§4.3)。


debug.getupvalue (f, up)

此函数返回函数f的索引为up的上值的名称和值。如果没有给定索引的上值,则函数返回fail

(对于 Lua 函数,上值是函数使用的外部局部变量,因此包含在它的闭包中。)

对于 C 函数,此函数使用空字符串""作为所有上值的名字。

变量名“?”(问号)表示没有已知名称的变量(从没有调试信息保存的块中保存的变量)。


debug.getuservalue (u, n)

返回与用户数据u关联的第n个用户值,以及一个布尔值,如果用户数据没有该值,则为false


debug.sethook ([thread,] hook, mask [, count])

将给定函数设置为调试钩子。字符串mask和数字count描述了钩子何时被调用。字符串掩码可以具有以下字符的任意组合,具有给定的含义

此外,如果count不为零,则在每count条指令后也会调用钩子。

当不带参数调用时,debug.sethook关闭钩子。

当调用钩子时,它的第一个参数是一个描述触发其调用的事件的字符串:"call""tail call""return""line""count"。对于行事件,钩子还将其第二个参数获取为新行号。在钩子内部,你可以使用 2 级调用getinfo以获取有关正在运行的函数的更多信息。(0 级是getinfo函数,1 级是钩子函数。)


debug.setlocal ([thread,] level, local, value)

此函数将值value分配给堆栈level级的函数的索引为local的局部变量。如果没有给定索引的局部变量,则函数返回fail,并且在使用超出范围的level调用时引发错误。(你可以调用getinfo来检查级别是否有效。)否则,它返回局部变量的名称。

有关变量索引和名称的更多信息,请参见 debug.getlocal


debug.setmetatable (value, table)

将给定 value 的元表设置为给定的 table(可以是 nil)。返回 value


debug.setupvalue (f, up, value)

此函数将值 value 分配给函数 f 的索引为 up 的向上值。如果没有索引为给定索引的向上值,则该函数返回 fail。否则,它将返回向上值名称。

有关上值的更多信息,请参见 debug.getupvalue


debug.setuservalue (udata, value, n)

将给定的 value 设置为与给定的 udata 关联的第 n 个用户值。udata 必须是完整用户数据。

返回 udata,或如果用户数据没有该值,则返回 fail


debug.traceback ([thread,] [message [, level]])

如果 message 存在但既不是字符串也不是 nil,则此函数将返回 message,而不会进一步处理。否则,它将返回一个字符串,其中包含调用堆栈的回溯。可选的 message 字符串附加在回溯的开头。可选的 level 数字告诉从哪个级别开始回溯(默认为 1,即调用 traceback 的函数)。


debug.upvalueid (f, n)

返回给定函数中编号为 n 的向上值的唯一标识符(作为轻量级用户数据)。

这些唯一标识符允许程序检查不同的闭包是否共享 upvalue。共享 upvalue(即访问同一个外部局部变量)的 Lua 闭包将为这些 upvalue 索引返回相同的 id。


debug.upvaluejoin (f1, n1, f2, n2)

使 Lua 闭包 f1 的第 n1 个向上值引用 Lua 闭包 f2 的第 n2 个向上值。

7 – Lua 独立版

虽然 Lua 被设计为扩展语言,用于嵌入到主机 C 程序中,但它也经常被用作独立语言。Lua 作为独立语言的解释器(称为 lua)随标准发行版提供。独立解释器包含所有标准库。其用法是

     lua [options] [script [args]]

选项为

(表单 -l g=mod 在版本 5.4.4 中引入。)

处理完其选项后,lua 运行给定的 脚本。在未带参数调用时,lua 在标准输入 (stdin) 为终端时表现为 lua -v -i,否则表现为 lua -

在未带选项 -E 调用时,解释器在运行任何参数之前检查环境变量 LUA_INIT_5_4(或在未定义版本化名称时检查 LUA_INIT)。如果变量内容具有格式 @filename,则 lua 执行该文件。否则,lua 执行字符串本身。

在带选项 -E 调用时,Lua 不查询任何环境变量。特别是,package.pathpackage.cpath 的值使用 luaconf.h 中定义的默认路径设置。

选项 -e-l-W 按其出现的顺序处理。例如,类似于以下内容的调用

     $ lua -e 'a=1' -llib1 script.lua

将首先将 a 设置为 1,然后需要库 lib1,最后运行文件 script.lua,且无参数。(此处 $ 是 shell 提示符。您的提示符可能不同。)

在运行任何代码之前,lua 将所有命令行参数收集到一个名为 arg 的全局表中。脚本名称转到索引 0,脚本名称后的第一个参数转到索引 1,依此类推。脚本名称之前的任何参数(即解释器名称及其选项)转到负索引。例如,在调用

     $ lua -la b.lua t1 t2

中,表如下所示

     arg = { [-2] = "lua", [-1] = "-la",
             [0] = "b.lua",
             [1] = "t1", [2] = "t2" }

如果调用中没有脚本,则解释器名称转到索引 0,后跟其他参数。例如,调用

     $ lua -e "print(arg[1])"

将打印“-e”。如果存在脚本,则使用参数 arg[1]、···、arg[#arg] 调用该脚本。与 Lua 中的所有块一样,该脚本被编译为可变参数函数。

在交互模式下,Lua 重复提示并等待一行。在读取一行后,Lua 首先尝试将该行解释为表达式。如果成功,它将打印其值。否则,它将该行解释为语句。如果您编写了一个不完整的语句,解释器将通过发出不同的提示来等待其完成。

如果全局变量 _PROMPT 包含一个字符串,则其值将用作提示符。类似地,如果全局变量 _PROMPT2 包含一个字符串,则其值将用作辅助提示符(在不完整语句期间发出)。

如果脚本中存在不受保护的错误,解释器会将错误报告给标准错误流。如果错误对象不是字符串但具有元方法 __tostring,则解释器将调用此元方法来生成最终消息。否则,解释器将错误对象转换为字符串并向其添加堆栈回溯。当警告开启时,它们将简单地打印在标准错误输出中。

在正常完成时,解释器关闭其主 Lua 状态(请参阅 lua_close)。脚本可以通过调用 os.exit 来终止以避免此步骤。

为了允许在 Unix 系统中将 Lua 用作脚本解释器,如果文件块的第一行以 # 开头,Lua 会跳过该行。因此,可以通过使用 chmod +x#! 形式将 Lua 脚本制成可执行程序,如下所示:

     #!/usr/local/bin/lua

当然,Lua 解释器的位置在你的机器上可能不同。如果 lua 在你的 PATH 中,那么

     #!/usr/bin/env lua

是一个更便携的解决方案。

8 – 与以前版本的不兼容性

这里列出了将程序从 Lua 5.3 迁移到 Lua 5.4 时可能遇到的不兼容性。

你可以通过使用适当的选项编译 Lua 来避免一些不兼容性(参见文件 luaconf.h)。但是,所有这些兼容性选项都将在未来移除。通常,当这些兼容性选项被移除时,就会出现兼容性问题。因此,每当你有机会时,你都应该尝试使用已关闭所有兼容性选项的 Lua 版本来测试你的代码。这将简化向 Lua 更新版本的过渡。

Lua 版本总是可以以不会暗示程序中源代码更改的方式更改 C API,例如常量的数字值或函数作为宏的实现。因此,你永远不应该假设二进制文件在不同的 Lua 版本之间是兼容的。在使用新版本时,请务必重新编译 Lua API 的客户端。

类似地,Lua 版本总是可以更改预编译块的内部表示;预编译块在不同的 Lua 版本之间不兼容。

官方发行版中的标准路径可能会在不同版本之间更改。

8.1 – 语言中的不兼容性

8.2 – 库中的不兼容性

8.3 – API 中的不兼容性

9 – Lua 的完整语法

以下是 Lua 在扩展 BNF 中的完整语法。与扩展 BNF 中惯例一样,{A} 表示 0 个或多个 A,[A] 表示一个可选的 A。(有关运算符优先级的说明,请参见 §3.4.8;有关 Name、Numeral 和 LiteralString 终端的说明,请参见 §3.1。)

	chunk ::= block

	block ::= {stat} [retstat]

	stat ::=  ‘;’ | 
		 varlist ‘=’ explist | 
		 functioncall | 
		 label | 
		 break | 
		 goto Name | 
		 do block end | 
		 while exp do block end | 
		 repeat block until exp | 
		 if exp then block {elseif exp then block} [else block] end | 
		 for Name ‘=’ exp ‘,’ exp [‘,’ exp] do block end | 
		 for namelist in explist do block end | 
		 function funcname funcbody | 
		 local function Name funcbody | 
		 local attnamelist [‘=’ explist] 

	attnamelist ::=  Name attrib {‘,’ Name attrib}

	attrib ::= [‘<’ Name ‘>’]

	retstat ::= return [explist] [‘;’]

	label ::= ‘::’ Name ‘::’

	funcname ::= Name {‘.’ Name} [‘:’ Name]

	varlist ::= var {‘,’ var}

	var ::=  Name | prefixexp ‘[’ exp ‘]’ | prefixexp ‘.’ Name 

	namelist ::= Name {‘,’ Name}

	explist ::= exp {‘,’ exp}

	exp ::=  nil | false | true | Numeral | LiteralString | ‘...’ | functiondef | 
		 prefixexp | tableconstructor | exp binop exp | unop exp 

	prefixexp ::= var | functioncall | ‘(’ exp ‘)’

	functioncall ::=  prefixexp args | prefixexp ‘:’ Name args 

	args ::=  ‘(’ [explist] ‘)’ | tableconstructor | LiteralString 

	functiondef ::= function funcbody

	funcbody ::= ‘(’ [parlist] ‘)’ block end

	parlist ::= namelist [‘,’ ‘...’] | ‘...’

	tableconstructor ::= ‘{’ [fieldlist] ‘}’

	fieldlist ::= field {fieldsep field} [fieldsep]

	field ::= ‘[’ exp ‘]’ ‘=’ exp | Name ‘=’ exp | exp

	fieldsep ::= ‘,’ | ‘;’

	binop ::=  ‘+’ | ‘-’ | ‘*’ | ‘/’ | ‘//’ | ‘^’ | ‘%’ | 
		 ‘&’ | ‘~’ | ‘|’ | ‘>>’ | ‘<<’ | ‘..’ | 
		 ‘<’ | ‘<=’ | ‘>’ | ‘>=’ | ‘==’ | ‘~=’ | 
		 and | or

	unop ::= ‘-’ | not | ‘#’ | ‘~