Skip to content

Commit

Permalink
Make a progress.
Browse files Browse the repository at this point in the history
  • Loading branch information
atom-l committed Mar 22, 2024
1 parent 67f8cee commit 0d81072
Showing 1 changed file with 90 additions and 0 deletions.
90 changes: 90 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2979,3 +2979,93 @@ print 函数不适用于格式化输出,而是一个展示值的快捷方法

### coroutine.yield (···)
挂起当前运行的协程。任何传入 yield 的参数都会作为之前 resume 的调用结果返回。


## 6.3 – 模块
包相关库提供了Lua中加载模块相关的基础工具。其直接将一个函数[require](#)导出到了全局环境中,其他都导出到了表 package 中。

### require (modname)
加载给定的模块。此函数一开始会在表[package.loaded](#)中确认名为参数 modname 模块是否已经加载。如果是,那么 require 会返回存在 package.loaded\[modname\] 中的值。(这种情况下是没有第二个返回值的,以表示此次调用没有加载任何模块。)否则将尝试查找模块的*加载器 loader*

[package.searchers](#)会引导查找加载器。该表中的每一个元素都是一个查找函数,通过其各自的方式查找模块。所以通过改变该表我们就可以改变 require 查找模块的方式。下边解释了默认配置下的 package.searchers 。

首先 require 会查询 package.preload\[modname\]。如果找到了,那么其对应的值(一定是个函数)就是加载器。否则 require 会使用另一个加载器,这个加载器使用存在 [package.path](#) 的路径查找模块。如果又失败了,则会使用一个C加载器,其会在 [package.cpath](#) 中寻找。如果还是失败了,则会尝试使用 *all-in-one* 加载器(参见[package.searchers](#packagesearchers))。

对于每个找到的加载器, require 都会使用两个参数调用它:modname 和一个扩展值(是一个 *加载器数据 loader data*,也是由查找器返回的) 。这个加载器数据是模块所用到的一个任意值;对于默认的查找器,它是加载器被查找到的位置。(例如,如果是一个来自文件中的加载器,这个值就是文件路径。)如果加载器返回了任何非空的值,require 会将其赋值给 package.loaded\[modname\] 。如果加载器没有返回有效值并没有任何 package.loaded\[modname\] 的赋值,那么 require 会将该条目置为*true*。任何情况下,require 都会返回 package.loaded\[modname\] 最终的值。除这个值以外,require 还会将查找器返回的加载器数据作为第二个结果返回,其表明了 require 是如何找到的模块。

如果在加载或运行模块的过程中发生了任何错误,或者没有找到任何加载器,那么 require 都会抛出一个错误。

### package.config
该字符串描述了关于包的一些编译时设置。此字符串由这些行构成:
* 第一行是目录的分割符。Windows下默认是 '\' ,其他系统默认是 '/' 。
* 第二行是各个路径的分割符。默认为 ';' 。
* 第三行是标记匹配替换的符号。默认为 '?' 。
* 第四行是表示在Windows中会被替换为可执行文件所在的目录的符号。默认为 '!' 。
* 第五行是一个标记,会使得在构建 luaopen_ 函数名时忽略其后边的文本。默认为 '-' 。

### package.cpath
该字符串是用于[reqire](#require-modname)中查找 C 加载器的路径。

Lua初始化[package.cpath](#packagecpath)的方式和[package.path](#packagepath)是一样的,使用环境变量 LUA_CPATH_5_4 ,或使用环境变量 LUA_CPATH,或者使用定义在 luaconf.h 文件中的默认路径。

### package.loaded
用于[require](#require-modname)的一个表,可以用来确认哪些模块已经加载了。当你需要一个名为 modname 的模块时,如果 package.loaded[modname] 存在,那么[require](#require-modname)就会之间返回存在此处的值。

这个全局变量只是真正的 loaded 表的引用;给这个变量赋值并不会改变[require](#require-modname)的行为。真正的 loaded 表是存在C注册表中的(参见[4.3](#43---注册表)),索引为 LUA_LOADED_TABLE, 一个预定义的字符串。

### package.loadlib (libname, funcname)
将一个名为 libname 的C库动态链接到宿主程序中。

如果 funcname 为 "*" ,那么只会链接这个库,将其符号导出给其他的动态链接库。否则,会在这个库中查找名为 funcname 的函数并找到的函数作为Lua定义的C函数形式返回。所以, funcname 函数必须符合[lua_CFunction](#lua_cfunction)的约定类型。

这是一个底层接口。其完全绕开了包和模块系统。与[require](#require-modname)不同,其不会执行任何路径查找和紫红添加扩展名。参数 libname 必须是C库的完整文件名,其包括包括必要的路径和扩展名。参数 funcname 必须是C库中已经导出的确切名称(可能取决于所使用的C编译器和链接器)。

ISO标准C并不支持此函数。因为函数的功能只在部分支持动态链接的操作系统上可用(Windows、Linux、Mac OS X、Solaris、BSD以及其他支持 dlfcn 的类Unix系统)。

此函数本质上是不安全的,因为其允许Lua在系统中的任何可读动态库中调用任何函数。(Lua说的函数都是假设符合约定类型并遵守调用约定的,可以参见[lua_CFunction](#lua_cfunction)。因此在任意库中调用任意函数通常会引起访问冲突。)

### package.path
该字符串变量存储了供[require](#require-modname)查找加载器的路径。

在启动时Lua会初始化这个变量的值,其值来自于环境变量 LUA_PATH_5_4 或是 LUA_PATH ,如果没有找到这些环境变量,则使用定义在 luaconf.h 文件中的默认路径。环境变量中的 ";;" 会被替换为默认路径。

### package.preload
存储了一些特殊模块的加载器的表(参见[require](#require-modname))。

该变量只是引用了真正的表;给这个变量赋值不会改变[require](#require-modname)的行为。真正的表是存在C注册表中的(参见[4.3](#43---注册表)),索引是一个预定义的字符串 LUA_LOADED_TABLE 。

### package.searchers
控制了[require](#require-modname)如何查找模块的表。

该表中的每个条目都是一个*查找函数*。每当查找一个模块,[require](#require-modname)会先后调用其中的每个查找函数,并将模块名(由传进[require](#require-modname)的参数而来)作为唯一参数传入。查找器如果找到了模块,则返回另一个函数,即该模块的*加载器*,以及一个额外的值,即*加载数据*,将会传入加载器并作为[require](#require-modname)的第二个参数。如果没找到模块,会返回一个字符串以解释原因(或是返回**nil**以表示无话可说)。

Lua会使用四个函数来初始化这个表。

第一个函数会直接在[package.preload](#packagepreload)表中查找加载器。

第二个函数会使用[package.path](#packagepath)中的路径来查找有没有相应的Lua库。搜索方式可以参照[package.searchpath](#packagesearchpath-name-path--sep--rep)中的描述。

第三个函数会使用[package.cpath](#packagecpath)中所给出的路径来查找有没有相应的C库。同样,其搜索方式可以参照[package.searchpath](#packagesearchpath-name-path--sep--rep)中的描述。例如,如果有一个如下的C路径:

<pre>"./?.so;./?.dll;/usr/local/?/init.so"</pre>

对于名为 foo 的模块,查找器将会先后尝试打开这些文件:./foo.so、./foo.dll、以及 /usr/local/foo/init.so 。一旦找到了C库,查找器就会使用动态链接工具将这个库链接到程序中。然后在其中搜索一个C函数作为加载器。这个C函数的名称是一个以 "luaopen_" 开头、后跟模块名的字符串,其中模块名中的 '.' 要替换成下划线。此外,如果模块名中含有连字符 '-' ,那么第一个连字符本身以及其后的内容都要被移除,如果有模块名为 a.b.c-v2.1 ,那么函数名应当为 luaopen_a_b_c 。

第四个函数会尝试使用 *all-in-one* 加载器。其搜索给定模块的根名称的C库。例如导入 a.b.c 库是,其将会搜索名为 a 的库。如果找到了,会在其中搜索子模块的 open 函数;在本例中,这个 open 函数会是 luaopen_a_b_c 。通过这种方式,一个包可以将多个C子模块打包到一个库中,其中每个子模块都有各自的 open 函数。

除了第一个查找函数之外,所有的查找函数都会返回一个记录了模块文件路径的额外字符串,由[package.searchpath](#packagesearchpath-name-path--sep--rep)返回。第一个查找函数始终返回字符串 ":preload:" 。

查找函数应该不会抛出错误并且对Lua没有副作用。(可能会因为将库连接到了程序,从而对C代码产生副作用。)

### package.searchpath (name, path [, sep [, rep]])
在给定路径 path 中查找名称 name 。

路径是一个包含了多个*模板 templates* 的字符串,各模板之间使用分号隔开。对于每个模板,此函数会将每个模板中的问号替换由参数 name 得来的文件路径,其中会将 name 中的每个遇到的 sep(默认是 '.')替换为 rep (默认为系统中的目录分隔符),然后再尝试打开最后得到的各个文件路径。

例如一个路径为如下的字符串:

<pre>"./?.lua;./?.lc;/usr/local/?/init.lua"</pre>

如果在其中查找名称 foo.a ,则会先后尝试打开这些文件: ./foo/a.lua、./foo/a.lc、以及 /usr/local/foo/a/init.lua 。

如果成功了,则返回第一个遇到的可以用读模式打开的文件名(在此之后会关闭文件)。否则返回**fail**加上一个错误消息。(这个错误消息列出了每个文件打不开的原因。)

0 comments on commit 0d81072

Please sign in to comment.