Lua Lua 5.5 参考手册

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

版权所有 © 2020–2025 Lua.org, PUC-Rio。根据 Lua 许可证条款自由分发。

1 – 引言

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

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

Lua 以库的形式实现,用纯 C 语言编写,纯 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 的著作《Programming in Lua》。

2 – 基本概念

本节描述了该语言的基本概念。

2.1 – 值和类型

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

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

Lua 中有八种基本类型:nilbooleannumberstringfunctionuserdatathreadtable。类型 nil 只有一个值,即 nil,其主要特性是与任何其他值都不同;它通常表示有用值的缺失。类型 boolean 有两个值,falsetruenilfalse 都使条件为假;它们统称为假值。任何其他值都使条件为真。尽管名为 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 整数,并且字符串加上一个小标头必须适合 size_t

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 实现关联数组,也就是说,其索引不仅可以是数字,还可以是除了 nil 和 NaN 之外的任何 Lua 值。(Not a Number 是 IEEE 754 标准用于表示未定义数值结果(例如 0/0)的特殊浮点值。)表可以是异构的;也就是说,它们可以包含所有类型的值(除了 nil)。任何关联到值 nil 的键都不被视为表的一部分。相反,任何不属于表的键都具有关联值 nil

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

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

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

表、函数、线程和(完整)userdata 值都是对象:变量实际上不包含这些值,只包含对它们的引用。赋值、参数传递和函数返回总是操作这些值的引用;这些操作不涉及任何形式的复制。

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

2.2 – 作用域、变量和环境

变量名根据代码该点上下文中的声明引用全局变量或局部变量。(为了讨论的目的,函数的形参等同于一个局部变量。)

所有代码块都以隐式声明 global * 开始,该声明将所有自由名称声明为全局变量;此序言声明在任何其他 global 声明的作用域内失效,如下例所示:

     X = 1       -- Ok, global by default
     do
       global Y  -- voids the implicit initial declaration
       Y = 1     -- Ok, Y declared as global
       X = 1     -- ERROR, X not declared
     end
     X = 2       -- Ok, global by default again

因此,在任何全局声明之外,Lua 默认为全局变量。在任何全局声明之内,Lua 不设置默认值:所有变量都必须声明。

Lua 是一种词法作用域语言。变量声明的作用域从声明后的第一条语句开始,持续到包含该声明的最内层块的最后一个非空语句。(空语句是标签和空语句。)

声明会遮蔽在声明点上下文中具有相同名称的任何声明。在此遮蔽内部,该名称的任何外部声明均无效。请参阅下一个示例:

     global print, x
     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

正如我们将在 §3.2§3.3.3 中进一步讨论的,对全局变量 var 的任何引用在语法上都会转换为 _ENV.var。此外,每个代码块都在一个名为 _ENV 的外部局部变量的作用域内编译(参见 §2.2),因此 _ENV 本身在一个代码块中从不是一个自由名称。

尽管存在这个外部 _ENV 变量和自由名称的转换,_ENV 仍然是一个常规名称。特别是,你可以使用这个名称定义新的变量和参数。(但是,你不应该将 _ENV 定义为全局变量,否则 _ENV.var 将转换为 _ENV._ENV.var,依此类推,进入无限循环。)对全局变量名称的每次引用都使用程序在该点可见的 _ENV

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

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

当 Lua 加载一个代码块时,其 _ENV 变量的默认值是全局环境(参见 load)。因此,默认情况下,Lua 代码中的全局变量引用全局环境中的条目,并因此充当传统的全局变量。此外,所有标准库都加载到全局环境中,其中一些函数在该环境中操作。你可以使用 load(或 loadfile)加载具有不同环境的代码块。(在 C 中,你必须加载代码块,然后更改其第一个上值的值;参见 lua_setupvalue。)

2.3 – 错误处理

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

Lua 代码可以通过调用 error 函数明确地引发错误。(此函数永不返回。)

要在 Lua 中捕获错误,你可以进行受保护调用,使用 pcall(或 xpcall)。函数 pcall受保护模式下调用给定函数。在运行函数期间发生的任何错误都会停止其执行,并将控制立即返回给 pcallpcall 返回一个状态码。

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

每当发生错误时,都会传播一个错误对象,其中包含有关错误的信息。Lua 本身只生成错误对象为字符串的错误,但程序可以生成以任何值作为错误对象的错误,除了 nil。(Lua 会将 nil 作为错误对象更改为字符串消息。)由 Lua 程序或其宿主来处理此类错误对象。由于历史原因,错误对象通常被称为错误消息,尽管它不一定是字符串。

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

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

2.4 – 元表和元方法

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

元表中每个事件的键是一个以两个下划线作为前缀的事件名称字符串;对应的值称为元值。对于大多数事件,元值必须是一个函数,该函数被称为元方法。在前面的例子中,键是字符串 "__add",元方法是执行加法的函数。除非另有说明,元方法实际上可以是任何可调用值,它要么是一个函数,要么是一个具有 __call 元方法的值。

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

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

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

元表控制的操作的详细列表如下所示。每个事件都由其对应的键标识。按照惯例,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 使用的所有内存都受自动管理:字符串、表、userdata、函数、线程、内部结构等。

一旦垃圾回收器确信对象在程序的正常执行中不会再次被访问,该对象就被认为是死亡的。(这里的“正常执行”不包括终结器,终结器会复活死亡对象(参见 §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 的值意味着收集器等待总字节数翻倍后才开始新周期。

垃圾回收器步长大小控制每个增量步长的大小,具体来说是解释器在执行一步之前分配多少字节:值 n 意味着解释器在步长之间将分配大约 n 字节。

垃圾回收器步长乘数控制每个增量步长所做的工作量。值为 n 意味着解释器每分配一个字就会执行 n% 工作单元。一个工作单元大致对应于遍历一个槽或清扫一个对象。较大的值使收集器更具侵略性。请注意,过小的值可能导致收集器速度太慢,无法完成一个周期。作为一个特例,零值表示无限工作,有效地产生一个非增量式的、停止-世界(stop-the-world)的收集器。

2.5.2 – 分代垃圾回收

在分代模式下,收集器会频繁地进行次要回收,这只遍历最近创建的对象。如果次要回收后字节数超过限制,收集器会切换到主要回收,这会遍历所有对象。然后,收集器将一直进行主要回收,直到它检测到程序正在生成足够的垃圾,以证明回到次要回收是合理的。

分代模式使用三个参数:次要乘数次要-主要乘数主要-次要乘数

次要乘数控制次要回收的频率。对于次要乘数 x,当字节数比上次主要回收后的使用量增加 x% 时,将进行一次新的次要回收。例如,对于乘数 20,当字节数比上次主要回收后的总数增加 20% 时,收集器将进行一次次要回收。

次要-主要乘数控制向主要回收的切换。对于乘数 x,当旧对象的字节数比上次主要回收后的总数增加 x% 时,收集器将切换到主要回收。例如,对于乘数 100,当旧字节数是上次主要回收后总数的两倍以上时,收集器将进行一次主要回收。作为一个特例,值为 0 会阻止收集器进行主要回收。

主要-次要乘数控制切换回次要收集。对于乘数 x,收集器将在一次主要收集至少回收了上一个周期分配的字节的 x% 后,切换回次要收集。特别地,对于乘数 0,收集器将在完成一次主要收集后立即切换回次要收集。

2.5.3 – 垃圾回收元方法

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

为了在收集对象(表或 userdata)时进行终结,你必须将其标记为终结。当你设置其元表并且元表具有 __gc 元方法时,你就会将对象标记为终结。请注意,如果你设置了一个没有 __gc 字段的元表,然后稍后在元表中创建该字段,则该对象将不会被标记为终结。

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

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

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

当你关闭一个状态(参见 lua_close)时,Lua 会按照所有被标记为终结的对象的标记顺序的逆序调用它们的终结器。如果在此阶段有任何终结器标记对象进行回收,这些标记将无效。

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

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

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 的额外参数将作为参数传递给该函数。协程开始运行后,它会一直运行直到终止或让出

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

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

coroutine.create 类似,coroutine.wrap 函数也创建协程,但它不返回协程本身,而是返回一个函数,该函数在被调用时恢复协程。传递给此函数的任何参数都将作为额外参数传递给 coroutine.resumecoroutine.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。非终结符显示为 non-terminal,关键字显示为 kword,其他终结符显示为 ‘=’。Lua 的完整语法可在本手册末尾的 §9 中找到。

3.1 – 词法约定

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

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

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

     and       break     do        else      elseif    end
     false     for       function  global    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 中有三种变量:全局变量、局部变量和表字段。

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

	var ::= Name

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

由于变量是词法作用域的,局部变量可以被其作用域内定义的函数自由访问(参见 §2.2)。

在对变量进行第一次赋值之前,它的值为 nil

方括号用于索引表:

	var ::= prefixexp ‘[’ exp ‘]

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

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

	var ::= prefixexp ‘.’ Name

对全局变量 x 的访问等同于 _ENV.x

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 都测试为假。所有与 nilfalse 不同的值都测试为真。特别是,数字 0 和空字符串也测试为真。

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

goto 语句将程序控制转移到标签。出于语法原因,Lua 中的标签也被认为是语句:

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

标签在其定义的整个块中可见,嵌套函数内部除外。goto 可以跳转到任何可见标签,只要它不进入变量声明的作用域。标签不应在具有相同名称的先前标签可见的地方声明,即使此其他标签已在包含块中声明。

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

	stat ::= break

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

return 语句用于从函数或代码块(被视为匿名函数)返回R值。函数可以返回多个值,因此 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

给定的标识符 (Name) 定义了控制变量,它是一个新的、只读 (const) 的局部于循环体(block)的变量。

循环首先一次性评估三个控制表达式。它们的值分别称为初始值限制步长。如果步长缺失,则默认为 1。

如果初始值和步长都是整数,则循环以整数执行;请注意,限制可能不是整数。否则,这三个值将转换为浮点数,循环以浮点数执行。在这种情况下,请注意浮点精度问题。

初始化之后,循环体将重复执行,控制变量的值以初始值开始,以步长给定的公差递增。负步长产生递减序列;步长等于零会引发错误。当值小于或等于限制时(如果步长为负,则大于或等于),循环继续。如果初始值已经大于限制(如果步长为负,则小于),则不执行循环体。

对于整数循环,控制变量永不环绕;相反,循环在溢出时结束。

通用 for 循环

通用 for 语句作用于函数,称为迭代器。在每次迭代中,迭代器函数被调用以生成一个新值,当新值为 nil 时停止。通用 for 循环具有以下语法:

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

一个 for 语句,例如:

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

工作方式如下。

名称 var_i 声明了循环变量,它们是局部于循环体的。这些变量中的第一个是控制变量,它是一个只读 (const) 变量。

循环通过评估 explist 来开始,以产生四个值:一个迭代器函数、一个状态、控制变量的初始值和一个关闭值

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

关闭值行为类似于一个待关闭变量(参见 §3.3.8),它可以在循环结束时用于释放资源。否则,它不干扰循环。

3.3.6 – 作为语句的函数调用

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

	stat ::= functioncall

在这种情况下,所有返回值都会被丢弃。函数调用在 §3.4.10 中解释。

3.3.7 – 变量声明

局部变量和全局变量可以在块内的任何地方声明。声明可以包含初始化:

	stat ::= local attnamelist [‘=’ explist]
	stat ::= global attnamelist [‘=’ explist]

如果没有初始化,局部变量用 nil 初始化;全局变量保持不变。否则,初始化会进行与多重赋值相同的调整(参见 §3.3.3)。此外,对于全局变量,如果变量已定义(即,它具有非 nil 值),初始化将引发运行时错误。

名称列表可以以一个属性(尖括号之间的名称)为前缀,每个变量名称可以以一个属性为后缀:

	attnamelist ::=  [attrib] Name [attrib] {‘,’ Name [attrib]}
	attrib ::= ‘<’ Name ‘>

前缀属性应用于列表中的所有名称;后缀属性应用于其特定名称。有两种可能的属性:const,声明一个常量只读变量,即不能用作赋值左侧的变量;和 close,声明一个待关闭变量(参见 §3.3.8)。只有局部变量可以具有 close 属性。变量列表最多可以包含一个待关闭变量。

Lua 还提供了全局变量的集体声明:

	stat ::= global [attrib] ‘*

这种特殊形式隐式地将所有以前未明确声明的名称声明为全局变量。特别是,global<const> * 隐式地将所有以前未明确声明的名称声明为只读全局变量;请参阅以下示例:

     global X
     global<const> *
     print(math.pi)   -- Ok, 'print' and 'math' are read-only
     X = 1            -- Ok, declared as read-write
     Y = 1            -- Error, Y is read-only

§2.2 中所述,所有代码块都以隐式声明 global * 开始,但此序言声明在任何其他 global 声明的作用域内失效。因此,不使用全局声明或不以 global * 开头的程序可以自由读写任何全局变量;以 global<const> * 开头的程序可以自由只读访问任何全局变量;而以任何其他全局声明(例如,global none)开头的程序只能引用已声明的变量。

请注意,对于全局变量,任何声明的效果仅限于语法(除了可选的赋值):

     global X <const>, _G
     X = 1           -- ERROR
     _ENV.X = 1      -- Ok
     _G.print(X)     -- Ok
     foo()         -- 'foo' can freely change any global

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

变量声明的可见性规则在 §2.2 中解释。

3.3.8 – 待关闭变量

待关闭变量的行为类似于常量局部变量,不同之处在于,无论何时变量超出作用域,包括正常块终止、通过 break/goto/return 退出其块,或因错误退出,其值都会被关闭

这里,关闭一个值意味着调用其 __close 元方法。调用元方法时,值本身作为第一个参数传递。如果发生错误,导致退出的错误对象作为第二个参数传递;否则,没有第二个参数。

分配给待关闭变量的值必须具有 __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)、一元逻辑 not(参见 §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 为 false,而 "1"<1 会引发错误(参见 §3.4.4)。这些强制转换主要为了兼容性而存在,并可能在语言的未来版本中移除。

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

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

3.4.4 – 关系运算符

Lua 支持以下关系运算符:

这些运算符总是返回 falsetrue

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

表、userdata 和线程按引用比较:只有当两个对象是同一个对象时,才认为它们相等。每次创建新对象(表、userdata 或线程)时,此新对象都与任何先前存在的对象不同。函数总是等于它自己。任何具有可检测差异(不同行为、不同定义)的函数总是不同的。在不同时间创建但没有可检测差异的函数可能会被分类为相等或不相等(取决于内部缓存细节)。

您可以使用 __eq 元方法(参见 §2.4)更改 Lua 比较表和 userdata 的方式。

相等比较不会将字符串转换为数字,反之亦然。因此,"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 视为假,将其他任何值视为真。

否定运算符 not 总是返回 falsetrue。合取运算符 and 如果其第一个参数为 falsenil 则返回该值;否则,and 返回其第二个参数。析取运算符 or 如果其第一个参数不为 nil 且不为 false 则返回该值;否则,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)

换句话说,边界是表中存在的任何正整数索引,后跟一个不存在的索引,加上两种极限情况:当索引 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
	stat ::= global 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 的递归引用时才会有所不同。)类似地,语句

     global function f () body end

转换为

     global f; global f = function () body end

第二个 global 使赋值成为初始化,如果该全局变量已经定义,则会引发错误。

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

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

是以下内容的语法糖

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

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

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

函数可能返回的值的数量受系统限制。此限制保证至少为 1000。

参数

参数充当局部变量,用参数值进行初始化

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

当 Lua 函数被调用时,它会调整其参数列表以匹配其参数列表的长度(参见 §3.4.12),除非该函数是 可变参数函数,这由其参数列表末尾的三个点('...')表示。可变参数函数不会调整其参数列表;相反,它会收集所有额外参数并通过 可变参数表 将它们提供给函数。在该表中,索引 1、2 等处的值是额外参数,索引“n”处的值是额外参数的数量。

举例来说,考虑以下定义

     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, va. table ->  {n = 0}
     g(3, 4)          a=3, b=4,   va. table ->  {n = 0}
     g(3, 4, 5, 8)    a=3, b=4,   va. table ->  {5, 8, n = 2}
     g(5, r())        a=5, b=1,   va. table ->  {2, 3, n = 2}

可变参数函数中的可变参数表可以有一个可选名称,位于三个点之后。当存在时,该名称表示一个只读局部变量,它引用可变参数表。如果可变参数表没有名称,则只能通过可变参数表达式访问它。

可变参数表达式也写成三个点,其值是可变参数表中从 1 到索引“n”处的整数值的值列表。(因此,如果代码不修改可变参数表,则此列表对应于函数调用中的额外参数。)此列表的行为类似于具有多个结果的函数的结果(参见 §3.3.4)。

作为一项优化,如果变参表满足某些条件,代码不会创建实际的表,而是将索引表达式和变参表达式转换为对内部变参数据的访问。条件如下:如果变参表有名称,该名称不是嵌套函数中的上值,并且仅用作语法结构 t[exp]t.id 中的基本表。请注意,匿名变参表总是满足这些条件。

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.

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 语言之间传递值。栈中的每个元素都代表一个 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 ≤ abs(index) ≤ top)加上 伪索引,伪索引表示 C 代码可以访问但不在栈中的一些位置。伪索引用于访问注册表(参见 §4.3)和 C 函数的上值(参见 §4.2)。

不需要特定可变位置,而只需要一个值的函数(例如,查询函数)可以与可接受索引一起调用。可接受索引 可以是任何有效索引,但它也可以是栈顶之后、栈分配空间内的任何正索引,即直到栈大小的索引。(请注意,0 永远不是可接受索引。)大于当前 C 函数中上值真实数量的上值索引(参见 §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 的垃圾收集可能会释放或移动内存,从而使指向由 Lua 状态处理的字符串的指针失效。为了安全地使用这些指针,API 保证只要栈索引处的字符串值未从栈中移除,指向该字符串的任何指针就有效。(尽管它可以移动到另一个索引。)当索引是伪索引(指向一个上值)时,只要相应的调用处于活动状态且相应的上值未被修改,该指针就有效。

调试接口中的一些函数也返回指向字符串的指针,即 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 库都可以在此表中存储数据,但它必须注意选择与其他库使用的键不同的键,以避免冲突。通常,您应该使用包含您的库名称的字符串作为键,或者使用一个轻量级 userdata,其中包含您代码中 C 对象的地址,或者您的代码创建的任何 Lua 对象。与变量名一样,以“下划线后跟大写字母”开头的字符串键保留给 Lua 使用。

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

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

4.4 – C 中的错误处理

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

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

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

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

恐慌函数,顾名思义,是最后一道防线。程序应该避免使用它。一般而言,当 C 函数通过 Lua 状态被 Lua 调用时,它可以在该 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。所有这些函数都接收一个 continuation function(参数名为 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 正在分配的对象的类型。当 Lua 创建该类型的新对象时(且仅当此时),osizeLUA_TSTRINGLUA_TTABLELUA_TFUNCTIONLUA_TUSERDATALUA_TTHREAD 之一。当 osize 为其他值时,Lua 正在为其他事物分配内存。

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

nsize 为零时,分配器必须像 free 一样行为,然后返回 NULL

nsize 不为零时,分配器必须像 realloc 一样行为。特别地,分配器仅当无法满足请求时才返回 NULL

这里是一个简单的分配器函数实现,对应于辅助库中的函数 luaL_alloc

     void *luaL_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);

设置一个新的恐慌函数并返回旧的恐慌函数(参见 §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 会确保返回的值适合栈空间,但不确保栈中有任何额外的空间。函数结果按直接顺序推入栈中(第一个结果最先推入),因此调用后最后一个结果位于栈顶。

nresults 的最大值为 250。

在调用和运行函数时发生的任何错误都会向上传播(通过 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 元方法不能挂起。


lua_closethread

[-0, +?, –]

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

重置一个线程,清理其调用栈并关闭所有待处理的待关闭变量。参数 from 表示正在重置 L 的协程。如果没有这样的协程,此参数可以为 NULL

除非 L 等于 from,否则调用会返回一个状态码:如果线程中没有错误(无论是停止线程的原始错误还是关闭方法中的错误),则为 LUA_OK,否则为错误状态。如果发生错误,错误对象会被放在栈顶。

如果 L 等于 from,则表示线程正在关闭自身。在这种情况下,调用不会返回;相反,重新启动线程的 resume 返回。线程必须在 resume 内部运行。


lua_compare

[-0, +0, e]

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

比较两个 Lua 值。如果索引 index1 处的值与索引 index2 处的值进行比较时满足 op(遵循相应 Lua 运算符的语义,即它可能调用元方法),则返回 1。否则返回 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 nseq, int nrec);

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


lua_dump

[-0, +0, –]

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

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

函数 lua_dump 在调用写入器函数期间完全保留 Lua 栈,只是它可能在第一次调用之前推入一些值供内部使用,并在最后一次调用之后将栈大小恢复到其原始大小。

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

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


lua_error

[-1, +0, v]

int lua_error (lua_State *L);

引发一个 Lua 错误,使用栈顶的值作为错误对象。此函数执行 long jump,因此永远不会返回(参见 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 中一样,此函数可能会触发“索引”事件的元方法(参见 §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 中一样,此函数可能会触发“索引”事件的元方法(参见 §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 中一样,此函数可能会触发“索引”事件的元方法(参见 §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);

将与给定索引处的完整 userdata 关联的第 n 个用户值推入栈中,并返回被推入值的类型。

如果 userdata 没有该值,则推入 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);

如果给定索引处的值是一个轻量级 userdata,则返回 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);

如果给定索引处的值是 userdata(完整或轻量级),则返回 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),并且可能触发“长度”事件的元方法(参见 §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"。此外,它可能有一个 'B' 而不是 'b',表示带有二进制转储的 固定缓冲区

固定缓冲区表示 reader 函数返回的地址将包含块,直到块创建的所有内容都被收集;因此,Lua 可以避免将块的某些部分复制到内部结构中。(通常,固定缓冲区会将其内容保留到程序结束,例如 ROM 中的块。)此外,对于固定缓冲区,reader 函数应在第一次读取时返回整个块。(例如,luaL_loadbufferx 就是这样做的,这意味着您可以使用它来加载固定缓冲区。)

函数 lua_load 在调用 reader 函数期间完全保留 Lua 栈,只是它可能在第一次调用之前推入一些值供内部使用,并在最后一次调用之后将栈大小恢复到其原始大小加一(用于推入的结果)。

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

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


lua_newstate

[-0, +0, –]

lua_State *lua_newstate (lua_Alloc f, void *ud,
                                   unsigned int seed);

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


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);

此函数在栈上创建并推入一个新的完整 userdata,带有 nuvalue 个关联的 Lua 值,称为 user values,以及一个关联的 size 字节的原始内存块。(用户值可以使用函数 lua_setiuservaluelua_getiuservalue 进行设置和读取。)

该函数返回内存块的地址。Lua 确保只要相应的 userdata 存活,此地址就有效(参见 §2.5)。此外,如果 userdata 已标记为终结(参见 §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 中的浮点数类型。

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


lua_numbertointeger

int lua_numbertointeger (lua_Number n, lua_Integer *p);

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

此宏可能会多次评估其参数。


lua_numbertocstring

[-0, +0, –]

unsigned lua_numbertocstring (lua_State *L, int idx,
                                        char *buff);

将可接受索引 idx 处的值转换为字符串,并将结果放入 buff。缓冲区大小至少应为 LUA_N2SBUFFSZ 字节。转换遵循非指定格式(参见 §3.4.3)。函数返回写入缓冲区的字节数(包括末尾的零),如果 idx 处的值不是数字,则返回零。


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 表示此函数将有多少个 upvalue(参见 §4.2)。

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

创建 C 函数时,可以为其关联一些值,即所谓的 upvalue;这些 upvalue 在函数被调用时都可以访问。这种关联称为 C 闭包(参见 §4.2)。要创建一个 C 闭包,首先必须将其 upvalue 的初始值推入堆栈。(当有多个 upvalue 时,第一个值首先被推入。)然后调用 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 函数推入堆栈。此函数等价于没有 upvalue 的 lua_pushcclosure


lua_pushexternalstring

[-0, +1, m]

const char *lua_pushexternalstring (lua_State *L,
                const char *s, size_t len, lua_Alloc falloc, void *ud);

创建一个外部字符串,即使用非 Lua 管理的内存的字符串。指针 s 指向保存字符串内容的外部缓冲区,len 是字符串的长度。字符串末尾应有一个零,即应满足条件 s[len] == '\0'。与 Lua 中的任何字符串一样,长度必须适合 Lua 整数。

如果 fallocNULL 不同,当不再需要外部缓冲区时,Lua 将调用该函数。在这次调用之前,缓冲区的内容不应改变。该函数将以给定的 ud、字符串 s 作为块、长度加一(考虑末尾的零)作为旧大小,以及 0 作为新大小进行调用。

即使使用外部缓冲区,Lua 仍然需要为字符串分配一个头。在发生内存分配错误的情况下,Lua 将在引发错误之前调用 falloc

该函数返回指向字符串的指针(即 s)。


lua_pushfstring

[-0, +1, v]

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

将格式化字符串推入堆栈并返回指向该字符串的指针(参见 §4.1.3)。结果是 fmt 的副本,其中每个转换说明符都替换为其各自额外参数的字符串表示。转换说明符(及其对应的额外参数)可以是“%%”(插入字符“%”)、“%s”(插入零终止字符串,无大小限制)、“%f”(插入 lua_Number)、“%I”(插入 lua_Integer)、“%p”(插入 void 指针)、“%d”(插入 int)、“%c”(将 int 插入为单字节字符)和“%U”(将 unsigned long 插入为 UTF-8 字节序列)。

字符串 fmt 中每次出现的“%”都必须构成一个有效的转换说明符。

除了内存分配错误,如果生成的字符串过大,此函数可能会引发错误。


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);

将一个轻量级 userdata 推入堆栈。

userdata 在 Lua 中表示 C 值。一个轻量级 userdata 表示一个指针,一个 void*。它是一个值(像一个数字):你不需要创建它,它没有独立的元表,并且它不被收集(因为它从未被创建)。一个轻量级 userdata 等于具有相同 C 地址的“任何”轻量级 userdata。


lua_pushliteral

[-0, +1, v]

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

此宏等效于 lua_pushstring,但仅在 s 是字面字符串时使用。(Lua 可能会优化此情况。)


lua_pushlstring

[-0, +1, v]

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, –]

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

等效于 lua_pushfstring,但它接收一个 va_list 而不是可变数量的参数,并且不会引发错误。相反,如果发生错误,它会推入错误消息并返回 NULL


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 是表示为轻量级 userdata 的指针 p。访问是原始的;也就是说,它不使用 __index 元值。

返回被推入值的类型。


lua_rawlen

[-0, +0, –]

lua_Unsigned lua_rawlen (lua_State *L, int index);

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


lua_rawset

[-2, +0, m]

void lua_rawset (lua_State *L, int index);

类似于 lua_settable,但执行原始赋值(即,不使用元方法)。index 处的值必须是一个表。


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 编码为轻量级 userdata,v 是堆栈顶部的值。

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


lua_Reader

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

lua_load 使用的读取器函数。每当 lua_load 需要另一部分 chunk 时,它都会调用读取器,并传入其 data 参数。读取器必须返回指向内存块的指针,其中包含 chunk 的新部分,并将 size 设置为块大小。该块必须存在直到读取器函数再次被调用。为了表示 chunk 的结束,读取器必须返回 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_resume

[-?, +?, –]

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

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

要启动协程,您将主函数和任何参数推入线程的空堆栈。然后调用 lua_resume,其中 nargs 是参数的数量。当协程暂停、完成执行或引发未受保护的错误时,函数返回。当它无错误返回时,*nresults 会更新,堆栈顶部包含传递给 lua_yield 或由主体函数返回的 *nresults 值。lua_resume 在协程让出时返回 LUA_YIELD,在协程无错误完成执行时返回 LUA_OK,或者在发生错误时返回错误代码(参见 §4.4.1)。在发生错误时,错误对象被推入堆栈顶部。(在这种情况下,nresults 不会更新,因为对于唯一的错误对象,其值必须为 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);

从堆栈中弹出一个值,并将其设置为与给定索引处的完整 userdata 关联的第 n 个用户值。如果 userdata 不具有该值,则返回 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, v]

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 元方法也不是假值,则此函数会引发错误。

此函数不应针对等于或低于活动的“待关闭”槽的索引进行调用。

请注意,无论是出现错误还是正常返回,在 __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 字符串。Lua 值必须是字符串或数字;否则,函数返回 NULL。如果该值是数字,则 lua_tolstring 还会将堆栈中的实际值更改为字符串。(当在表遍历期间对键应用 lua_tolstring 时,此更改会混淆 lua_next。)

如果 len 不为 NULL,则函数将 *len 设置为字符串长度。返回的 C 字符串在其最后一个字符后总是有一个零('\0'),但其主体中可以包含其他零。

如果对应的 Lua 值从堆栈中移除,lua_tolstring 返回的指针可能会被垃圾回收器失效(参见 §4.1.3)。

此函数仅在将数字转换为字符串时可能引发内存错误(因为那时它可能会创建新字符串)。


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*)。该值可以是 userdata、表、线程、字符串或函数;否则,lua_topointer 返回 NULL。不同的对象将给出不同的指针。无法将指针转换回其原始值。

通常此函数仅用于哈希和调试信息。


lua_tostring

[-0, +0, m]

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

等效于 len 等于 NULLlua_tolstring


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);

如果给定索引处的值是完整的 userdata,则返回其内存块地址。如果该值是轻量级 userdata,则返回其值(一个指针)。否则,返回 NULL


lua_type

[-0, +0, –]

int lua_type (lua_State *L, int index);

返回给定有效索引处值的类型,或者对于无效但可接受的索引返回 LUA_TNONElua_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 编码的类型名称,tp 必须是 lua_type 返回的值之一。


lua_Unsigned

typedef ... lua_Unsigned;

lua_Integer 的无符号版本。


lua_upvalueindex

[-0, +0, –]

int lua_upvalueindex (int i);

返回表示正在运行函数的第 i 个 upvalue 的伪索引(参见 §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 生成另一部分 chunk 时,它都会调用写入器,传入要写入的缓冲区(p)、其大小(sz)以及提供给 lua_dumpud 参数。

lua_dump 写入最后一部分后,它将通过再次调用写入器函数(缓冲区为 NULL,大小为 0)来发出信号。

写入器返回一个错误代码: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_yieldkctx 值。

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

如果此函数是从具有待处理 C 调用但没有延续函数(所谓的C 调用边界)的线程中调用,或者从未在 resume 中运行的线程中调用(通常是主线程),则它可能会引发错误。

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) */
  unsigned char extraargs;    /* (t) number of extra arguments */
  char istailcall;            /* (t) */
  int ftransfer;              /* (r) index of first value transferred */
  int 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_getinfo 的参数 what 中。)

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 个 upvalue 的信息。它将 upvalue 的值推入堆栈并返回其名称。当索引 n 大于 upvalue 的数量时,返回 NULL(并且不推入任何内容)。

有关 upvalue 的更多信息,请参见 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 执行函数或 chunk,此执行将在不调用任何钩子的情况下发生。

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

钩子函数可以在以下条件下让出:只有计数和行事件可以让出;要让出,钩子函数必须调用 lua_yieldnresults 等于零(即,没有值)来完成其执行。


lua_sethook

[-0, +0, –]

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

设置调试钩子函数。

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

通过将 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);

设置闭包 upvalue 的值。它将堆栈顶部的值赋给 upvalue 并返回其名称。它还会从堆栈中弹出该值。

当索引 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 函数参数。由于错误消息是针对参数格式化的(例如,“bad argument #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),将字符串 p 的任何出现替换为字符串 r


luaL_addlstring

[-?, +?, m]

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

s 指向的长度为 l 的字符串添加到缓冲区 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 是否为 true。如果不是,则引发带有标准消息的错误(参见 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 是否为 true。如果不是,则引发有关参数 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 的 userdata(参见 luaL_newmetatable),并返回 userdata 的内存块地址(参见 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);

确保值 t[fname](其中 t 是索引 idx 处的值)是一个表,并将该表推入堆栈。如果找到先前存在的表,则返回 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);

等效于 mode 等于 NULLluaL_loadbufferx


luaL_loadbufferx

[-0, +1, –]

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

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

此函数返回与 lua_load 相同的结果。name 是 chunk 名称,用于调试信息和错误消息。字符串 mode 的作用与函数 lua_load 中的相同。特别是,此函数支持固定缓冲区的模式“B”。


luaL_loadfile

[-0, +1, m]

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

等效于 mode 等于 NULLluaL_loadfilex


luaL_loadfilex

[-0, +1, m]

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

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

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

此函数返回与 lua_load 相同的结果,或者对于文件相关错误返回 LUA_ERRFILE

lua_load 一样,此函数只加载 chunk;它不运行它。


luaL_loadstring

[-0, +1, –]

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

将字符串加载为 Lua chunk。此函数使用 lua_load 加载零终止字符串 s 中的 chunk。

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

lua_load 一样,此函数只加载 chunk;它不运行它。


luaL_makeseed

[-0, +0, –]

unsigned int luaL_makeseed (lua_State *L);

返回一个具有微弱随机性的值。如果 Lua 状态不可用,参数 L 可以为 NULL


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。否则,创建一个新表作为 userdata 的元表,将键值对 __name = tname 添加到此新表,将键值对 [tname] = new table 添加到注册表,并返回 1。

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


luaL_newstate

[-0, +0, –]

lua_State *luaL_newstate (void);

创建一个新的 Lua 状态。它调用 lua_newstate,使用 luaL_alloc 作为分配器函数,并将 luaL_makeseed(NULL) 的结果作为种子,然后设置一个警告函数和一个 panic 函数(参见 §4.4),这些函数会将消息打印到标准错误输出。

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


luaL_opt

[-0, +0, –]

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

此宏定义如下

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

换句话说,如果参数 arg 为 nil 或缺失,则宏的结果为默认值 dflt。否则,其结果为调用 func,并以状态 L 和参数索引 arg 作为参数。请注意,它只在需要时才评估表达式 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_addsize, luaL_pushresult


luaL_ref

[-1, +0, m]

int luaL_ref (lua_State *L, int t);

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

引用系统使用表的整数键。引用是唯一的整数键;luaL_ref 确保其返回的键的唯一性。条目 1 保留供内部使用。在使用 luaL_ref 之前,表的整数键应形成一个正确的序列(无空洞),并且条目 1 处的值应为假:如果序列为空,则为 nil;否则为 false。在使用 luaL_ref 之后,您不应手动设置表中的整数键。

您可以通过调用 lua_rawgeti(L,t,r)lua_geti(L,t,r) 来检索由引用 r 引用的对象。luaL_unref 函数释放引用。

如果堆栈顶部的对象是 nilluaL_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)注册到堆栈顶部的表中(可选的 upvalue 以下,参见下一段)。

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

一个值为 NULL 的函数代表一个占位符,它将被填充为 false


luaL_setmetatable

[-0, +0, –]

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

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


luaL_alloc

void *luaL_alloc (void *ud, void *ptr, size_t osize, size_t nsize);

Lua 的标准分配器函数(参见 lua_Alloc),建立在 C 函数 reallocfree 之上。


luaL_Stream

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

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

文件句柄作为完整的 userdata 实现,其元表名为 LUA_FILEHANDLE(其中 LUA_FILEHANDLE 是实际元表名称的宏)。该元表由 I/O 库创建(参见 luaL_newmetatable)。

此 userdata 必须以 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);

释放一个引用(参见 luaL_ref)。整数 ref 必须是 LUA_NOREFLUA_REFNIL,或者是之前由 luaL_ref 返回且尚未释放的引用。如果 refLUA_NOREFLUA_REFNIL,此函数不执行任何操作。否则,该条目将从表中移除,以便可以收集引用的对象,并且 luaL_ref 可以再次使用引用 ref


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 包含以下标准库

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

6.1 – 在 C 代码中加载库

如果 C 主机程序希望其脚本使用标准库,则必须显式地将标准库加载到状态中。为此,主机程序可以调用函数 luaL_openlibs。或者,主机可以使用 luaL_openselectedlibs 选择要打开的库。这两个函数都在头文件 lualib.h 中声明。

独立的解释器 lua(参见 §7)已经打开了所有标准库。


luaL_openlibs

[-0, +0, e]

void luaL_openlibs (lua_State *L);

将所有标准 Lua 库打开到给定状态。


luaL_openselectedlibs

[-0, +0, e]

void luaL_openselectedlibs (lua_State *L, int load, int preload);

打开(加载)并预加载选定的标准库到状态 L 中。(“预加载”意味着将库加载器添加到表 package.preload 中,以便程序以后可以请求该库。请记住,require 本身是由“包”库提供的。如果程序不加载该库,它将无法请求任何东西。)

整数 load 选择要加载的库;整数 preload 选择在未加载的库中哪些要预加载。两者都是由以下常量进行位或运算形成的掩码

6.2 – 基本函数

基本库为 Lua 提供了核心函数。如果您的应用程序不包含此库,您应该仔细检查是否需要为其某些功能提供实现。


assert (v [, message])

如果参数 v 的值为假(即 nilfalse),则引发错误;否则,返回所有参数。如果发生错误,message 是错误对象;如果省略,则默认为“assertion failed!


collectgarbage ([opt [, arg]])

此函数是垃圾回收器的通用接口。它根据其第一个参数 opt 执行不同的功能

有关垃圾回收和其中一些选项的更多详细信息,请参见 §2.5

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


dofile ([filename])

打开指定文件并将其内容作为 Lua 块执行,返回该块返回的所有值。当不带参数调用时,dofile 执行标准输入(stdin)的内容。如果发生错误,dofile 会将错误传播给其调用者。(也就是说,dofile 不在保护模式下运行。)


error (message [, level])

引发一个错误(参见 §2.3),其中 message 作为错误对象。此函数从不返回。

通常,如果 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 上值。)

无论如何,如果结果函数有任何 upvalue,其第一个 upvalue 将设置为 env 的值(如果给定该参数),或设置为全局环境的值。其他 upvalue 用 nil 初始化。所有 upvalue 都是新的,也就是说,它们不与任何其他函数共享。

chunkname 用作错误消息和调试信息(参见 §4.7)的块名称。如果省略,如果 chunk 是字符串,则默认为 chunk,否则默认为“=(load)”。

字符串 mode 控制块可以是文本还是二进制(即预编译块)。它可以是字符串“b”(仅二进制块)、“t”(仅文本块)或“bt”(二进制和文本)。默认值为“bt”。

Lua 不检查二进制块的一致性。恶意制作的二进制块可能会使解释器崩溃。您可以使用 mode 参数来防止加载二进制块。


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 函数、表 t,以及两个 nil 值,以便构造

     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.11)。


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.5”。


warn (msg1, ···)

发出一个警告,其消息由所有参数(应为字符串)的连接组成。

按照惯例,以“@”开头的一段消息被视为“控制消息”,即发送给警告系统本身的消息。特别是,Lua 中的标准警告函数识别控制消息“@off”以停止警告的发出,以及“@on”以(重新)开始发出警告;它忽略未知的控制消息。


xpcall (f, msgh [, arg1, ···])

此函数类似于 pcall,不同之处在于它设置了一个新的消息处理程序 msgh

6.3 – 协同程序操作

此库包含用于操作协同程序的操作,这些操作位于表 coroutine 中。有关协同程序的通用描述,请参见 §2.6


coroutine.close ([co])

关闭协同程序 co,即关闭其所有待关闭的变量并使协同程序处于死亡状态。co 的默认值是正在运行的协同程序。

给定的协同程序必须是已死的、已挂起的或正在运行的协同程序。对于正在运行的协同程序,此函数不返回。相反,重新启动协同程序的 resume 返回。

对于其他协同程序,如果发生错误(无论是停止协同程序的原始错误还是关闭方法中的错误),此函数返回 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.4 – 模块

包库提供了在 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],则 require 会将 true 赋给此条目。在任何情况下,require 都会返回 package.loaded[modname] 的最终值。除了该值之外,require 还会返回搜索器返回的加载器数据作为第二个结果,该数据指示 require 如何找到模块。

如果加载或运行模块时出现任何错误,或者无法找到模块的任何加载器,则 require 会引发错误。


package.config

一个描述包的一些编译时配置的字符串。此字符串是一个行序列


package.cpath

一个字符串,其中包含 require 用于搜索 C 加载器的路径。

Lua 初始化 C 路径 package.cpath 的方式与初始化 Lua 路径 package.path 的方式相同,使用环境变量 LUA_CPATH_5_5,或环境变量 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 不支持此功能。因此,loadlib 仅在某些平台可用:Linux、Windows、Mac OS X、Solaris、BSD 以及支持 dlfcn 标准的其他 Unix 系统。

此功能本质上不安全,因为它允许 Lua 调用系统中任何可读动态库中的任何函数。(Lua 调用任何函数时都假定该函数具有正确的原型并遵循正确的协议(参见 lua_CFunction)。因此,调用任意动态库中的任意函数通常会导致访问冲突。)


package.path

一个字符串,其中包含 require 用于搜索 Lua 加载器的路径。

启动时,Lua 使用环境变量 LUA_PATH_5_5 或环境变量 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 库。如果找到,它将在其中查找子模块的打开函数;在我们的例子中,这将是 luaopen_a_b_c。通过此功能,一个包可以将多个 C 子模块打包到一个库中,每个子模块都保留其原始的打开函数。

除了第一个搜索器(预加载)之外,所有搜索器都将模块的查找路径作为额外值返回,该路径由 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.5 – 字符串操作

此库提供了通用的字符串操作函数,例如查找和提取子字符串以及模式匹配。在 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 会返回该函数的副本(但具有新的上值)。如果 strip 为真值,则二进制表示可能不包含关于函数的所有调试信息,以节省空间。

带有 upvalue 的函数只保存其 upvalue 的数量。当(重新)加载时,这些 upvalue 会接收到新的实例。(有关这些 upvalue 如何初始化的详细信息,请参见 load 函数。您可以使用调试库以适合您需求的方式序列化和重新加载函数的 upvalue。)


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

在字符串 s 中查找 pattern(参见 §6.5.1)的第一个匹配项。如果找到匹配项,则 find 返回此匹配项在 s 中开始和结束的索引;否则,它返回 fail。第三个可选的数字参数 init 指定从哪里开始搜索;其默认值为 1,可以是负数。第四个可选的布尔参数 plaintrue 时会关闭模式匹配功能,因此该函数执行普通的“查找子字符串”操作,pattern 中的任何字符都不会被视为特殊字符。

如果模式有捕获,那么在成功匹配中,捕获的值也会在两个索引之后返回。


string.format (formatstring, ···)

返回其可变数量参数的格式化版本,遵循其第一个参数(必须是字符串)中给出的描述。格式字符串遵循 ISO C 函数 sprintf 的相同规则。接受的转换说明符有 AacdEefGgiopsuXx 和“%”,以及一个非 C 说明符 q。接受的标志有“-”、“+”、“#”、“0”和“ ”(空格)。宽度和精度(如果存在)都限制为两位数。

说明符 q 以一种结果是 Lua 源代码中有效常量的方式格式化布尔值、nil、数字和字符串。布尔值和 nil 以显而易见的方式写入(truefalsenil)。浮点数以十六进制写入,以保留全部精度。字符串用双引号括起来,必要时使用转义序列以确保 Lua 解释器可以安全地读回。例如,调用

     string.format('%q', 'a string with "quotes" and \n new line')

可能会生成字符串

     "a string with \"quotes\" and \
      new line"

此说明符不支持修饰符(标志、宽度、精度)。

转换说明符 AaEefGg 都期望一个数字作为参数。说明符 cdiouXx 期望一个整数。当 Lua 使用 C89 编译器编译时,说明符 Aa(十六进制浮点数)不支持修饰符。

说明符 s 期望一个字符串;如果其参数不是字符串,它将按照 tostring 的相同规则转换为字符串。如果说明符有任何修饰符,则相应的字符串参数不应包含嵌入的零。

说明符 p 格式化由 lua_topointer 返回的指针。这为表、userdata、线程、字符串和函数提供了唯一的字符串标识符。对于其他值(数字、nil、布尔值),此说明符将生成一个表示 NULL 指针的字符串。


string.gmatch (s, pattern [, init])

返回一个迭代器函数,该函数每次被调用时,返回字符串 spattern(参见 §6.5.1)的下一个捕获。如果 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 的副本,其中所有(或前 n 个,如果给定)pattern(参见 §6.5.1)的出现都被 repl 指定的替换字符串替换,repl 可以是字符串、表或函数。gsub 还返回匹配发生的总数作为其第二个值。名称 gsub 来自 Global SUBstitution

如果 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.5"}
     x = string.gsub("$name-$version.tar.gz", "%$(%w+)", t)
     -- x="lua-5.5.tar.gz"


string.len (s)

接收一个字符串并返回其长度。空字符串 "" 的长度为 0。嵌入的零也会被计算在内,因此 "a\000bc\000" 的长度为 5。


string.lower (s)

接收一个字符串并返回此字符串的副本,其中所有大写字母都更改为小写。所有其他字符保持不变。大写字母的定义取决于当前语言环境。


string.match (s, pattern [, init])

在字符串 s 中查找 pattern(参见 §6.5.1)的第一个“匹配”。如果找到匹配项,则 match 返回模式的捕获;否则返回 fail。如果 pattern 没有指定捕获,则返回整个匹配。第三个可选的数字参数 init 指定从哪里开始搜索;其默认值为 1,可以是负数。


string.pack (fmt, v1, v2, ···)

返回一个二进制字符串,其中包含根据格式字符串 fmt(参见 §6.5.2)以二进制形式序列化(打包)的值 v1v2 等。


string.packsize (fmt)

返回使用给定格式通过 string.pack 生成的字符串的长度。格式字符串不能有可变长度选项“s”或“z”(参见 §6.5.2)。


string.rep (s, n [, sep])

返回一个字符串,该字符串是由 n 个字符串 s 的副本以字符串 sep 分隔连接而成的。sep 的默认值是空字符串(即没有分隔符)。如果 n 不是正数,则返回空字符串。

(请注意,通过一次调用此函数,很容易耗尽您的机器内存。)


string.reverse (s)

返回一个字符串,该字符串是字符串 s 的反转。


string.sub (s, i [, j])

返回从 i 开始到 j 结束的 s 的子字符串;ij 可以是负数。如果 j 省略,则假定它等于 -1(这与字符串长度相同)。特别是,调用 string.sub(s,1,j) 返回 s 的长度为 j 的前缀,而 string.sub(s,-i)(对于正 i)返回 s 的长度为 i 的后缀。

在负索引转换后,如果 i 小于 1,则将其更正为 1。如果 j 大于字符串长度,则将其更正为该长度。如果经过这些更正后,i 大于 j,则函数返回空字符串。


string.unpack (fmt, s [, pos])

根据格式字符串 fmt(参见 §6.5.2)返回字符串 s 中打包的值(参见 string.pack)。可选的 pos 标记在 s 中从何处开始读取(默认为 1)。在读取值之后,此函数还会返回 s 中第一个未读取字节的索引。


string.upper (s)

接收一个字符串并返回此字符串的副本,其中所有小写字母都更改为大写。所有其他字符保持不变。小写字母的定义取决于当前语言环境。

6.5.1 – 模式

Lua 中的模式由普通字符串描述,这些字符串由模式匹配函数 string.findstring.gmatchstring.gsubstring.match 解释为模式。本节描述这些字符串的语法和含义(即它们匹配什么)。

字符类

“字符类”用于表示一组字符。允许以下组合来描述字符类

对于所有由单个字母表示的类(%a%c 等),对应的大写字母表示该类的补集。例如,%S 表示所有非空格字符。

字母、空格和其他字符组的定义取决于当前语言环境。特别是,类 [a-z] 可能不等于 %l

模式项

“模式项”可以是

模式

“模式”是模式项的序列。模式开头的插入符“^”将匹配锚定在主题字符串的开头。模式末尾的“$”将匹配锚定在主题字符串的末尾。在其他位置,“^”和“$”没有特殊含义,表示它们本身。

捕获

一个模式可以包含用括号括起来的子模式;它们描述“捕获”。当匹配成功时,主题字符串中匹配捕获的子字符串被存储(“捕获”)以备将来使用。捕获根据其左括号进行编号。例如,在模式 "(a*(.)%w(%s*))" 中,匹配 "a*(.)%w(%s*)" 的字符串部分被存储为第一个捕获,因此编号为 1;匹配“.”的字符捕获编号为 2,匹配“%s*”的部分捕获编号为 3。

作为特殊情况,捕获 () 捕获当前字符串位置(一个数字)。例如,如果我们将模式 "()aa()" 应用于字符串 "flaaap",将有两个捕获:3 和 5。

多重匹配

函数 string.gsub 和迭代器 string.gmatch 匹配主题中给定模式的多个出现。对于这些函数,只有当新匹配结束至少在上次匹配结束一个字节之后才被视为有效。换句话说,模式机从不接受紧接在另一个匹配之后的空字符串作为匹配。例如,考虑以下代码的结果

     > string.gsub("abc", "()a*()", print);
     --> 1   2
     --> 3   3
     --> 4   4

第二个和第三个结果来自 Lua 在“b”之后和“c”之后匹配一个空字符串。Lua 不会在“a”之后匹配空字符串,因为它会在与前一个匹配相同的位置结束。

6.5.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.6 – UTF-8 支持

此库提供对 UTF-8 编码的基本支持。它将所有函数都放在表 utf8 中。此库除了处理编码之外,不提供任何 Unicode 支持。任何需要字符含义的操作,例如字符分类,都不在此范围之内。

除非另有说明,所有期望字节位置作为参数的函数都假定给定位置要么是字节序列的开始,要么是主题字符串长度加一。与字符串库一样,负索引从字符串末尾开始计数。

创建字节序列的函数接受所有值,直到 0x7FFFFFFF,如原始 UTF-8 规范中定义;这意味着字节序列最多可达六个字节。

解释字节序列的函数只接受有效的序列(格式正确且不超长)。默认情况下,它们只接受生成有效 Unicode 码点的字节序列,拒绝大于 10FFFF 和代理的值。布尔参数 lax(如果可用)会取消这些检查,以便接受所有值直到 0x7FFFFFFF。(格式不正确和超长的序列仍然会被拒绝。)


utf8.char (···)

接收零个或多个整数,将每个整数转换为其对应的 UTF-8 字节序列,并返回一个包含所有这些序列连接而成的字符串。


utf8.charpattern

模式(一个字符串,不是函数)"[\0-\x7F\xC2-\xFD][\x80-\xBF]*"(参见 §6.5.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 计数)作为两个整数:其编码开始的索引(以字节为单位)和其结束的索引(以字节为单位)。

如果指定的字符紧跟在 s 的末尾,则函数表现得好像那里有一个“\0”。如果指定的字符既不在主题中也不紧跟在主题的末尾,则函数返回 fail

负数 n 获取位置 i 之前的字符。i 的默认值是当 n 为非负数时为 1,否则为 #s + 1,因此 utf8.offset(s,-n) 获取从字符串末尾开始的第 n 个字符的偏移量。

作为特殊情况,当 n 为 0 时,函数返回包含 s 的第 i 个字节的字符编码的开始和结束位置。

此函数假定 s 是有效的 UTF-8 字符串。

6.7 – 表操作

此库提供通用的表操作函数。它将其所有函数都放在表 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.create (nseq [, nrec])

创建一个新的空表,预分配内存。当您提前知道表将有多少元素时,这种预分配有助于提高性能并节省内存。

参数 nseq 提示表将有多少个序列元素。可选参数 nrec 提示表将有多少个其他元素;其默认值为零。


table.insert (list, [pos,] value)

将元素 value 插入到 listpos 位置,将元素 list[pos],list[pos+1],···,list[#list] 向上移动。pos 的默认值是 #list+1,因此调用 table.insert(t,x) 会将 x 插入到列表 t 的末尾。


table.move (a1, f, e, t [,a2])

将元素从表 a1 移动到表 a2,执行相当于以下多个赋值的操作:a2[t],··· = a1[f],···,a1[e]a2 的默认值是 a1。目标范围可以与源范围重叠。要移动的元素数量必须适合 Lua 整数。如果 f 大于 e,则不移动任何内容。

返回目标表 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 时,索引 pos 也可以是 0 或 #list + 1

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.8 – 数学函数

此库提供基本的数学函数。它在表 math 中提供了所有函数和常量。带有“integer/float”注解的函数对于整数参数返回整数结果,对于非整数参数返回浮点数结果。舍入函数 math.ceilmath.floormath.modf 在结果适合整数范围时返回整数,否则返回浮点数。


math.abs (x)

返回 x-x 之间的最大值。(整数/浮点数)


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 的余数,该余数将商向零舍入。(整数/浮点数)


math.frexp (x)

返回两个数字 me,使得 x = m2e,其中 e 是一个整数。当 x 为零、NaN、+inf 或 -inf 时,m 等于 x;否则,m 的绝对值在区间 [0.5, 1) 中。


math.huge

浮点值 HUGE_VAL,一个大于任何其他数值的值。


math.ldexp (m, e)

返回 m2e,其中 e 是一个整数。


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 被组合成一个种子,用于重新初始化伪随机生成器;相等的种子会产生相等的数字序列。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 比较时小于 n,返回 true

6.9 – 输入和输出设施

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 失败,它返回 fail,外加描述错误的字符串。

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。否则,它返回四个值:fail、错误消息、错误代码以及它能够写入的字节数。

6.10 – 操作系统设施

此库通过表 os 实现。


os.clock ()

返回程序使用的 CPU 时间(以秒为单位)的近似值,由底层 ISO C 函数 clock 返回。


os.date ([format [, time]])

返回一个字符串或一个包含日期和时间的表,根据给定的字符串 format 进行格式化。

如果存在 time 参数,这是要格式化的时间(有关此值的描述,请参见 os.time 函数)。否则,date 格式化当前时间。

如果 format 以 '!' 开头,则日期以世界协调时间 (Coordinated Universal Time) 格式化。在此可选字符之后,如果 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.11 – 调试库

此库向 Lua 程序提供了调试接口 (§4.7) 的功能。使用此库时应格外小心。它的某些功能违反了关于 Lua 代码的基本假设(例如,函数本地变量无法从外部访问;userdata 元表无法通过 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 的字段,其中包含有效行表,前提是该函数是一个 Lua 函数。如果该函数没有调试信息,则该表为空。

例如,表达式 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 的upvalue的名称和值。如果不存在具有给定索引的upvalue,则函数返回 fail

(对于 Lua 函数,upvalues是函数使用的外部局部变量,因此包含在其闭包中。)

对于 C 函数,此函数使用空字符串 "" 作为所有upvalues的名称。

变量名“?”(问号)表示没有已知名称的变量(来自没有调试信息保存的块中的变量)。


debug.getuservalue (u, n)

返回与userdata u 关联的第 n 个用户值以及一个布尔值,如果userdata没有该值则返回 false


debug.sethook ([thread,] hook, mask [, count])

将给定函数设置为调试钩子。字符串 mask 和数字 count 描述了何时调用钩子。字符串掩码可以包含以下任意组合的字符,并具有给定含义

此外,如果 count 不为零,则钩子也会在每 count 条指令之后被调用。

当不带参数调用时,debug.sethook 会关闭钩子。

调用钩子时,其第一个参数是一个字符串,描述触发其调用的事件:“call”、“tail call”、“return”、“line”和“count”。对于行事件,钩子还会将其新行号作为第二个参数。在钩子内部,您可以调用 getinfo 级别 2 以获取有关运行函数的更多信息。(级别 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 的upvalue。如果不存在具有给定索引的upvalue,则函数返回 fail。否则,它返回upvalue的名称。

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


debug.setuservalue (udata, value, n)

将给定 value 设置为与给定 udata 关联的第 n 个用户值。udata 必须是完整userdata。

返回 udata,如果 userdata 没有该值则返回 fail


debug.traceback ([thread,] [message [, level]])

如果 message 存在但既不是字符串也不是 nil,则此函数直接返回 message。否则,它返回一个包含调用堆栈回溯的字符串。可选的 message 字符串会附加在回溯的开头。可选的 level 数字指示回溯从哪个级别开始(默认为 1,即调用 traceback 的函数)。


debug.upvalueid (f, n)

返回给定函数的编号为 n 的upvalue的唯一标识符(作为轻量级userdata)。

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


debug.upvaluejoin (f1, n1, f2, n2)

使 Lua 闭包 f1 的第 n1 个upvalue引用 Lua 闭包 f2 的第 n2 个upvalue。

7 – Lua 独立版

尽管 Lua 被设计为一种扩展语言,嵌入到宿主 C 程序中,但它也经常被用作独立语言。标准发行版提供了一个名为 lua 的独立 Lua 解释器。独立解释器包含所有标准库。其用法如下

     lua [options] [script [args]]

选项如下

处理完选项后,lua 运行给定的 script。当不带参数调用时,如果标准输入 (stdin) 是终端,lua 的行为类似于 lua -v -i,否则类似于 lua -

当不带选项 -E 调用时,解释器在运行任何参数之前会检查环境变量 LUA_INIT_5_5(如果未定义版本化名称,则检查 LUA_INIT)。如果变量内容格式为 @filename,则 lua 执行该文件。否则,lua 执行字符串本身。

当使用 -E 选项调用时,Lua 不会查询任何环境变量。特别是,package.pathpackage.cpath 的值会设置为 luaconf.h 中定义的默认路径。为了向库发出此选项已开启的信号,独立解释器会在注册表中将字段 "LUA_NOENV" 设置为真值。其他库可以出于相同目的查询此字段。

选项 -e-l-W 按照它们出现的顺序处理。例如,以下调用

     $ lua -e 'a=1' -llib1 script.lua

将首先将 a 设置为 1,然后 require 库 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 首先尝试将该行解释为表达式。如果成功,它会打印其值。否则,它会将该行解释为一个块。如果您编写了一个不完整的块,解释器会通过发出不同的提示等待其完成。

请注意,由于每行完整行都作为新块读取,因此局部变量不会超出行。为避免混淆,如果一行以保留字 local 开头,解释器会发出警告。

     > x = 20      -- global 'x'
     > local x = 10; print(x)
         --> warning: locals do not survive across lines in interactive mode
         --> 10
     > print(x)     -- back to global 'x'
         --> 20
     > do       -- incomplete chunk
     >> local x = 10; print(x)    -- '>>' prompts for line completion
     >> print(x)
     >> end     -- chunk completed
        --> 10
        --> 10

如果全局变量 _PROMPT 包含一个字符串,则其值将用作提示符。类似地,如果全局变量 _PROMPT2 包含一个字符串,则其值将用作二级提示符(在不完整语句期间发出)。

如果脚本中出现未受保护的错误,解释器会将错误报告给标准错误流。如果错误对象不是字符串但具有元方法 __tostring,则解释器会调用此元方法生成最终消息。否则,解释器会将错误对象转换为字符串并添加堆栈回溯。当警告开启时,它们只是打印到标准错误输出。

正常结束时,解释器会关闭其主 Lua 状态(参见 lua_close)。脚本可以通过调用 os.exit 终止来避免此步骤。

为了允许在 Unix 系统中使用 Lua 作为脚本解释器,如果文件块的第一行以 # 开头,Lua 会跳过该行。因此,Lua 脚本可以通过使用 chmod +x#! 形式制成可执行程序,如下所示

     #!/usr/local/bin/lua

当然,Lua 解释器的位置在您的机器上可能不同。如果 lua 在您的 PATH 中,那么

     #!/usr/bin/env lua

是一种更便携的解决方案。

8 – 与上一版本的不兼容性

这里列出了将程序从 Lua 5.4 迁移到 Lua 5.5 时可能遇到的不兼容性。

您可以通过使用适当的选项编译 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 | 
		 global function Name funcbody | 
		 local attnamelist [‘=’ explist] | 
		 global attnamelist | 
		 global [attrib] ‘*’ 

	attnamelist ::=  [attrib] 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 [‘,’ varargparam] | varargparam

	varargparam ::= ‘...’ [Name]

	tableconstructor ::= ‘{’ [fieldlist] ‘}’

	fieldlist ::= field {fieldsep field} [fieldsep]

	field ::= ‘[’ exp ‘]’ ‘=’ exp | Name ‘=’ exp | exp

	fieldsep ::= ‘,’ | ‘;’

	binop ::=  ‘+’ | ‘-’ | ‘*’ | ‘/’ | ‘//’ | ‘^’ | ‘%’ | 
		 ‘&’ | ‘~’ | ‘|’ | ‘>>’ | ‘<<’ | ‘..’ | 
		 ‘<’ | ‘<=’ | ‘>’ | ‘>=’ | ‘==’ | ‘~=’ | 
		 and | or

	unop ::= ‘-’ | not | ‘#’ | ‘~