目录
- Lua语言
- 1 搭建Lua开发环境
- 1.1 安装Lua解释器
- Windows
- Linux
- 1.2 IntelliJ安装Lua插件
- 在线安装
- 本地安装
- 2 Lua语法
- 2.1 数据类型
- 2.2 变量
- 全局变量
- 局部变量
- 命名规范
- 局部变量作用域
- 2.3 注释
- 单行注释
- 多行注释
- 2.4 赋值
- 2.5 操作符
- 数学操作符
- 比较操作符
- 逻辑操作符
- 连接操作符
- 取长度操作符
- 操作符优先级
- 2.6 if语句
- 2.7 循环语句
- while
- repeat
- for
- 2.8 表类型
- 对象
- 数组
- 遍历表
- 2.9 函数
- 3 标准库
- 3.1 Base库
- 3.2 String库
- 3.3 Table库
- 3.4 Math库
- 3.5 Debug库
- debug.getinfo(f, what)
- 参数
- 举例
- 3.6 cjson 库和 cmsgpack库
- 4 安装扩展库
- 4.1 安装LuaRocks
- Windows
- Linux
- 4.2 安装链接库和头文件
- 4.3 配置LuaRocks
- 4.4 安装GCC编译器
- 4.5 安装CJson库
- 4.6 安装cmsgpack库
- 5 构建Lua解释器
- 6 构建LuaRocks
Lua语言
Lua 是一种高效的轻量级脚本语言。Lua 在葡萄牙语中是“月亮”的意思,它的徽标
形似卫星,寓意着Lua是一种“卫星语言”,能够方便地嵌入其他编程语言中使用。lua语法简单,小巧,源码一共才200多K,本身不会有太强的功能。
很多人都希望在Redis中加入各种各样的命令,这些命令中有的确实很实用,但可以使用多个Redis已有的命令实现。在Redis中包含开发者需要的所有命令显然是不现实的,所以Redis在2.6版中提供了Lua脚本功能来让开发者自己扩展Redis。
官方网站:https://www.lua.org/
IntelliJ Lua插件:https://plugins.jetbrains.com/plugin/9768-emmylua
1 搭建Lua开发环境
1.1 安装Lua解释器
Windows
首先从官网上下载可执行程序包:
https://jaist.dl.sourceforge.net/project/luabinaries/5.4.2/Tools%20Executables/lua-5.4.2_Win64_bin.zip
解压到本地目录,并将此目录添加到环境变量path,例如:
D:\00_programming\07_apps\lua-5.4.2_Win64_bin
打开命令行窗口,运行命令:
>lua54
Lua 5.4.2 Copyright (C) 1994-2020 Lua.org, PUC-Rio
>
Linux
To be added…
1.2 IntelliJ安装Lua插件
在线安装
打开插件市场File>Settings>Plugins>Marketplace,搜索EmmyLua和EmmyLuaCodeStyle并安装。
本地安装
首先下载插件包到本地磁盘,然后
https://downloads.marketplace.jetbrains.com/files/9768/517613/IntelliJ-EmmyLua-1.4.13-IDEA241.zip?updateId=517613&pluginId=9768&family=INTELLIJ
https://downloads.marketplace.jetbrains.com/files/21973/506115/EmmyLua-CodeStyle-1.5.0.25-IDEA223.zip?updateId=506115&pluginId=21973&family=INTELLIJ
安装完成后,就可以新建Lua类型的项目了。创建了项目后,需要指定Lua解释器的路径,才可以正确编译。
2 Lua语法
可以使用Lua在线编译器方便的尝试编写简单的Lua程序:https://onecompiler.com/lua
2.1 数据类型
Lua是一种动态类型语言,一个变量可以存储任何类型的值。
类型 | 取值 |
---|---|
空(nil) | 空类型只包含一个值,即nil。nil表示空,所有没有赋值的变量或表的字段都是nil |
布尔(boolean) | 布尔类型包含true和false两个值 |
数字(number) | 整数和浮点数都使用数字类型存储,如1、0.2、3.5e20等 |
字符串(string) | 字符串类型可以存储字符串,且与Redis的键值一样都是二进制安全的。字符串可以使用单引号或双引号表示,两个符号是相同的。例如’a’,"b"都是可以的。字符串中可以包含转义字符,如\n、\r等 |
表(table) | 表类型是Lua语言中唯一的数据结构,既可以当数组又可以当字典,十分灵活 |
函数(function) | 函数在Lua中是一等值(first-class value),可以存储在变量中,也可以作为函数的参数或返回结果 |
2.2 变量
Lua 的变量分为全局变量和局部变量。
全局变量
全局变量无须声明就可以直接使用,默认值是nil。如:
a = 1 -- 为全局变量a赋值
print (b) -- 无须声明即可使用,默认值是nil
a = nil -- 删除全局变量a的方法是将其赋值为nil。全局变量没有声明和未声明之分,只有非nil和nil的区别
局部变量
在Redis脚本中不能使用全局变量,只允许使用局部变量以防止脚本之间相互影响。
声明局部变量的方法为local变量名,就像这样:
local c -- 声明一个局部变量c,默认值是nil
local d = 1 -- 声明一个局部变量d并赋值为1
local e, f -- 可以同时声明多个局部变量
同样,声明一个存储函数的局部变量的方法为:
local say hi = function ()print 'hi'
end
命名规范
- 变量名必须以非数字开头
- 只能包含字母、数字和下画线
- 区分大小写
- 变量名不与Lua的保留关键字相同,保留关键字如下:
and break do else elseif end false for function if
in local nil not or function repeat return then true until while
局部变量作用域
局部变量的作用域为从声明开始到所在层的语句块末尾,例如:
local x = 10
if true thenlocal x = x + 1print (x)dolocal x =x + 1print (x)endprint (x)
end
print (x)
打印结果为:
11
12
11
10
– a的值是1,b的值是2
local e, f = 1
2.3 注释
Lua的注释有单行和多行两种。
单行注释
以 – 开始,到行尾结束,在上面的代码已经使用过了,一般习惯在 – 后面跟上一个空格。
多行注释
以 – [[开始,到]]表示,例如:
-- [[这是一个多行注释]]
2.4 赋值
Lua支持多重赋值,例如:
local a, b = 1, 2 -- a的值是1,b的值是2
local c, d = 1, 2, 3 -- c的值是1,d的值是2,3被舍弃了
local e, f = 1 -- e的值是1,f的值是nil
在执行多重赋值时,Lua会先计算所有表达式的值,例如:
local a = {1, 2, 3}
local i = 1
i, a[i] = i + 1, 5
Lua计算所有表达式的值后,上面最后一个赋值语句变为:
i, a[1] = 2, 5
所以赋值后i的值为2,a则为{5,2,3}。
注意 表类型的索引从1开始
使用函数返回的多个值进行多重赋值:
a, b, c = func1()
2.5 操作符
Lua有以下5类操作符:
数学操作符
操作符 | 说明 |
---|---|
+ | 加号 |
- | 减号 |
* | 乘号 |
/ | 除号 |
% | 取模 |
- | 取负 |
^ | 幂运算 |
数学操作符的操作数如果是字符串会自动转换成数字
print('1' + 1) -- 结果为2
比较操作符
操作符 | 说明 |
---|---|
== | 比较两个操作数的类型和值是否都相等 |
~= | 比较两个操作数的类型和值是否不相等 |
< | 小于 |
> | 大于 |
<= | 小于等于 |
>= | 大于等于 |
比较操作符的结果一定是布尔类型。比较操作符不同于数学操作符,不会对两边的操
作数进行自动类型转换。
print (1 == '1') -- false,二者类型不同,不会进行自动类型转换
print ({'a'} == {'a'}) -- false,对于表类型值比较的是二者的引用
如果需要比较字符串和数字,可以手动进行类型转换。
print(1 == tonumber('1'))
print('1' == tostring(1))
tonumber()函数还可以进行进制转换,例如:
print(tonumber('F',16)) -- 将字符串'F'从十六进制转成十进制结果是15
逻辑操作符
操作符 | 说明 |
---|---|
not | 根据操作数true和false相应地返回false和true |
and | a and b中如果a为真则返回b,否则返回a |
or | a or b中如果a为假则返回b,否则返回a |
只要操作数不是nil或false,逻辑操作符就认为操作数为真,否则为假。特别需
要注意的是,即使是0或空字符串也被当作真。
print(not '') --空字符串为真
print('' and 1) --''为真返回1
print('' or 1) --''为真返回''
print (false and 5) --false为假返回false
print (false or 5) --false为假返回5
print (not 0) --0为真
输出:
false
1
false
5
false
Lua的逻辑操作符支持短路,也就是说对于false and foo(),的数,因为第一个操作数已经决定了无论foo()函数返回的结果是什么,该表达是false,or操作符与之类似。
连接操作符
操作符 | 说明 |
---|---|
.. | 用来连接两个字符串 |
print ('hello' .. ' ' .. 'world!') -- 'hello world!'
连接操作符会自动把数字类型的值转换成字符串类型:
print ('The price is ' .. 25) -- 'The price is 25'
取长度操作符
操作符 | 说明 |
---|---|
# | 用来获取字符串或表的长度 |
例如:
print(#"12345") -- 5
print(#{1, 2, 3, 4, 5}) -- 5
操作符优先级
^
not # -(一元)
* / %
+ -
..
< > <= >= ~= ==
and
or
2.6 if语句
Lua的if语句格式如下:
if conditonal expression thenstatement block
elseif condition thenstatement block
elsestatement block
end
前面提到过在Lua中只有nil和false才为假,其余值,包括空字符串和0,都被认为是真值。这是一个容易出问题的地方,例如Redis的EXISTS 命令的返回值1和0分别表示存在或不存在,所以在条件判断是要判断具体的返回值是0或1:
if redis.call ('exists', 'key') == 1 thenexists = true
elseexists = false
end
2.7 循环语句
Lua 支持while、repeat和for循环语句。
while
while condition dostatement block
end
repeat
repeatstatement block
until conditonal expression
for
数值型 for 循环:用于遍历一个数值区间
for variable = initial value, final value, step length dostatement block
end
泛型 for 循环:用于遍历表(table)或者其他迭代器函数(iterator functions)
for variable1, variable2, ..., variablen in iterator dostatement block
end
例如:
local tab = {'a', 'b', 'c', 'd', 'e'}for i = 1, 5, 1 doprint(tab[i])
endfor k, v in pairs(tab) doprint(k)print(v)
end
2.8 表类型
表是Lua中唯一的数据结构,可以理解为关联数组,任何类型的值(除了空类型)可以作为表的索引。
表的定义方式为:
对象
people = {} -- 将变量people赋值为一个空表
people['name'] = 'Bob' -- 将name字段赋值Bob
print (people.name) -- 打印内容为'Bob', people.field是people['field']的语法糖
也可以这样定义
people = {name = 'Bob',age = 29,emails = {'bob@a.com', 'bob@b.com'},toString = function()return people.name .. ':' .. people.age .. ':' .. people.emails[1] .. ':' .. people.emails[2]end
}
print (people.name) -- 打印的内容为'Bob'
print(people.emails[1]) -- 打印的内容为bob@a.com
print(people.toString()) -- Bob:29:bob@a.com:bob@b.com
数组
当索引为整数的时候,表和传统的数组一样,例如:
a = {}
a [1] = 'Bob'
a[2] = 'Jeff'
还可以写成下面这样:
a = {'Bob', 'Jeff'}
print (a[1]) -- 打印的内容为'Bob'
Lua约定数组的索引是从1开始,而不是0。
遍历表
local tab = {'one',a = 'A','two',b = 'B','three',c = 'C',nil,d = nil,'four',e = 'E','five'
}
-- ipairs(tab)返回迭代器,从1开始迭代到最后一个值不为nil的整形索引
for index, value in ipairs(tab) doprint(index .. ':' .. value)
end
输出,注意four之后的元素并没有输出:
1:one
2:two
3:three
-- pair()返回迭代器,迭代所有值不为nil的索引
for index, value in pairs(tab) doprint(index .. ':' .. value)
end
输出:
1:one
2:two
3:three
5:four
6:five
a:A
c:C
b:B
e:E
for i = 1, #tab, 1 doif tab[i] thenprint(i .. ':' .. tab[i])end
end
输出:
1:one
2:two
3:three
5:four
6:five
2.9 函数
函数的定义为:
function(参数列表)
函数体
end
可以将其赋值给一个局部变量,例如:
local square = function (num)return num * num
end
即使没有参数,括号也不能省略。
Lua还提供了一个语法糖来简化函数的定义,例
local function square (num)return num * num
end
这段代码会被转换为:
local square
square = function (num)return num * num
end
- 因为在赋值前声明了局部变量square,所以可以在函数内部引用自身(实现递归)。
- 如果实参的个数小于形参的个数,则没有匹配到的形参的值为nil。
- 相应地,如果实参的个数大于形参的个数,则多出的实参会被忽略。
- 如果希望捕获多出的实参(即实现可变参数个数),可以让最后一个形参为 … 。
例如,希望传入若干参数计算这些数的平方:
local function square (...)local argv = {...}for i = 1, #argv doargv[i] = argv[i] * argv[i]endreturn table.unpack(argv)
enda, b, c = square(1, 2, 3)
print (a)
print (b)
print (c)
输出结果为:
1
4
9
在Lua中,return和break(用于跳出循环)语句必须是语句块中的最后一条语句,
简单地说在这两条语句后面只能是end、else或until三者之一。如果希望在语句块的
中间使用这两条语句的话,可以人为地使用do和end将其包围。
3 标准库
3.1 Base库
提供了一些基础函数
函数 | 说明 |
---|---|
tonumber() | 字符串转换为数字 |
tostring() | 其它类型转换为字符串 |
ipairs() | 获取表的整形索引迭代器 |
pairs() | 获取表的全部索引迭代器 |
3.2 String库
提供了用于字符串操作的函数
函数 | 说明 |
---|---|
string.len(string) | |
string.lower(string) | |
string.upper(string) | |
string.sub(string, start [, end]) | string.sub()函数可以获取一个字符串从索引start升始到end结束的子字然系引从1开始。索引可以是负数,-1代表最后一个元素。end李效如果省略则默认是,(即截取到字符串末尾)。 |
3.3 Table库
提供了用于表操作的函数,大部分函数都需要的表的形式是数组形式。
函数 | 说明 |
---|---|
table.concat(table [, sep [, i (, j1]]) | 将数组转换为字符串。table.concat()函数与JavaScript中的join()函数类似,可以将一个数组转换成字符串,中间以sep参数指定的字符串分割,默认为空。i和j用来限制要转换的表元素的索引范围,默认分别是1和表的长度,不支持负数索引。 |
table.insert (table, [pos, ] value) | 向数组中插入元素。向指定索引位置pos抽入兀系value,并将后面的元素按顺序后移。默认poST是数组长度加1,即在数组末尾插入。 |
table.remove(table [, pos]) | 从数组中弹出一个元素。从指定的索引删除一个元素,并将后面的元素前移,返回删除的元素值。默认pos的值是数组的长度,即从数组末尾弹出一个元素。 |
3.4 Math库
Math库提供了常用的数学运算函数,如果参数是字符串,会自动尝试转换成数字。
函数 | 说明 |
---|---|
math.abs (x) | 获得数字的绝对值 |
math.sin (x) | 求三角函数sin 值 |
math.cos (x) | 求三角函数 cos值 |
math.tan (x) | 求三角函数 tan值 |
math.ceil (x) | 进一取整,例如1.2取整后是2 |
math.floor (x) | 向下取整,例如1.8取整后是1 |
math.max (x, … ) | 获得参数中的最大值 |
math.min (x, … ) | 获得参数中的最小值 |
math.pow(x, y) | 获得xy的值 |
math.sqrt (x) | 获得x的平方根 |
math.random([m, [, n]]) | 函数用来生成一个随机数,根据参数不同其返回值范围也不同: 没有提供参数:返回[0,1)的实数; 只提供了m参数:返回[1,m]的整数; 同时提供了m和n参数:返回 [m, n]的整数。 |
math.randomseed (x) | math.random()函数生成的随机数是依据种子(seed)计算得来的伪随机数,意味着使用同一种子生成的随机数序列是相同的。可以使用math.randomseed()函数设置种子的值。 |
随机数测试:
math.randomseed(1)
print('Random seed is: 1')
print(tostring(math.random(1, 100)))
print(tostring(math.random(1, 100)))
print(tostring(math.random(1, 100)))
print(tostring(math.random(1, 100)))
print(tostring(math.random(1, 100)))
math.randomseed(1)
print('Random seed is: 1')
print(tostring(math.random(1, 100)))
print(tostring(math.random(1, 100)))
print(tostring(math.random(1, 100)))
print(tostring(math.random(1, 100)))
print(tostring(math.random(1, 100)))
math.randomseed(2)
print('Random seed is: 2')
print(tostring(math.random(1, 100)))
print(tostring(math.random(1, 100)))
print(tostring(math.random(1, 100)))
print(tostring(math.random(1, 100)))
print(tostring(math.random(1, 100)))
Output:
Random seed is: 1
40
79
80
92
20
Random seed is: 1
40
79
80
92
20
Random seed is: 2
81
9
13
35
43
3.5 Debug库
提供了用于调试的函数。
debug.getinfo(f, what)
参数
f:可以给两种模式,函数或数值。第一种则是给函数,获取给定函数的信息表。第二种则是给一个数字作为 f 的值,表示栈层级:当为 0 时表示当前函数( 即 getinfo 本身), 1 表示调用 getinfo 的函数(尾调用除外,它们不计入堆栈),以此类推。如果 f 是一个大于活跃栈层级的数字,则 getinfo 返回 nil。
what: 可选项,表示要获取哪些指定的信息。因为 getinfo 的效率不高,所以为了效率好些,可以只选择需要的内容,如果需要多个值时,可以将多个拼凑,例如 nfS 。
what取值 | 获取的值 |
---|---|
n | 选择 name 和 namewhat |
f | 选择 func |
S | 选择 source、short_src、what、linedefined 和 lastlinedefined |
l | 选择 currentline |
L | 选择 activelines |
u | 选择 nup、nparams 和 isvararg |
获取的值 | 描述 |
---|---|
name | 该字段是该函数的一个适当的名称,例如保存该函数的全局变量的名称。(可能没有值,也可能有多个名称)(只有当 f 为数值时才有该值) |
namewhat | 该字段用于说明 name 字段的含义,可能是 “global”、“local”、“method”、“field” 或 “”(空字符串)。空字符串表示 Lua 语言找不到该函数的名称。 |
func | 该字段是该函数本身 |
source | 该字段用于说明函数定义的位置。如果函数定义在一个字符串中(通过调用 load),那么 source 就是这个字符串;如果函数定义在一个文件中,那么 source 就是使用 @ 作为前缀的文件名 |
short_src | 该字段是 source 的精简版本(最多 60 个字符),对于错误信息十分有用。 |
what | 该字段用于说明函数的类型。 - 如果 foo 是一个普通的 Lua 函数,则为 “Lua” - 如果是一个 C 函数,则为 “C” - 如果是一个 Lua 语言代码段的主要部分,则为 “main” 。 |
linedefined | 该字段是该函数定义在源代码中第一行的行号 |
lastlinedefined | 该字段是该函数定义在源代码中最后一行的行号 |
currentline | 表示当前该函数正在执行的代码所在的行 (只有当 f 为数值时才有该值) |
istailcall | 返回一个布尔值,为真表示函数是被尾调用所调起(尾调用时,函数真正的调用者不在栈中)(只有 f 为数值时才有该值) |
activelines | 该字段是一个包含该函数所有活跃行的集合。活跃行是指除空行和只包含注释的行外的其它行(该字段的典型用法是用于设置断点。大多数调试器不允许在活跃行外设置断点,因为非活跃行是不可达的)。 |
nups | 该字段是该函数的上值的个数 |
nparams | 该字段是该函数的参数个数 |
isvararg | 该字段表明该函数是否为可变长函数 |
举例
function testFunction()print('-----current function:')local info1 = debug.getinfo(1)for k, v in pairs(info1) doprint(k, v)endprint('-----caller:')local info2 = debug.getinfo(2)for k, v in pairs(info2) doprint(k, v)end
endtestFunction()
Output:
-----current function:
func function: 0x55ec6c036770
currentline 3
source @Main.lua
name testFunction
nparams 0
what Lua
linedefined 1
isvararg false
short_src Main.lua
namewhat global
nups 1
istailcall false
lastlinedefined 12
-----caller:
func function: 0x55ec6c036680
currentline 14
source @Main.lua
namewhat
nparams 0
linedefined 0
isvararg true
short_src Main.lua
what main
nups 1
istailcall false
lastlinedefined 0
3.6 cjson 库和 cmsgpack库
除了标准库,Redis还通过cjson库和cmsgpack库提供了对JSON和MessagePack的
支持。Redis自动加载了这两个库,在脚本中可以分别通过cjson和cmsgpack这两个全局变量来访问对应的库。两者的用法如下:
local people = {name = 'Bob',age = 29
}
-- 使用cjson序列化成字符串:
local json_people_str = cjson.encode (people)
-- 使用cjson将序列化后的字符串还原成表:
local json_people_obj = cjson.decode (people)
print (json_people_obj.name)-- 使用cmsgpack序列化成字符串:
local msgpack_people_str = cmsgpack.pack(people)
-- 使用cmsgpack将序列化后的字符串还原成表:
local msgpack_people_obj = cmsgpack.unpack(people)
print (msgpack_people_obj.name)
4 安装扩展库
安装Lua库可以通过在包管理软件LuaRocks中运行命令install
下载库的源码并进行变异构建。所有要求环境中安装有GCC编译器,并且本地有安装有Lua头文件和链接库文件。
4.1 安装LuaRocks
Luarocks是Lua编程语言的软件包管理器。它提供了一种简单的方法来安装和管理Lua模块,这些模块是Lua代码的集合,可用于扩展Lua的功能。
使用Luarocks,您可以轻松地从中央存储库或本地文件或URL安装Lua模块。您还可以管理模块之间的依赖关系,因此,如果一个模块需要另一个模块才能正常工作,Luarocks将自动为您安装所需的模块。
Luarocks还提供了一种创建和发布自己的Lua模块的方法,以便其他人可以轻松地在自己的Lua程序中使用您的代码。
LuaRocks安装包和release源码下载:https://luarocks.github.io/luarocks/releases/
Github代码仓库:https://github.com/luarocks/luarocks
Windows
首先从官网上下载可执行程序包:
https://luarocks.github.io/luarocks/releases/luarocks-3.11.1-windows-64.zip
解压到指定目录,并将此目录添加到环境变量path,例如:D:\00_programming\07_apps\luarocks-3.11.1-windows-64
打开命令行窗口,运行命令:
>luarocks --version
luarocks 3.11.1
LuaRocks main command-line interface
Linux
首先从官网上下载可执行程序包:
https://luarocks.github.io/luarocks/releases/luarocks-3.11.1-linux-x86_64.zip
4.2 安装链接库和头文件
首先从官网上下载链接库文件:
https://jaist.dl.sourceforge.net/project/luabinaries/5.4.2/Tools%20Executable/Dynamic/lua-5.4.2_Win64_bin.zip?viasf=1
然后在Lua解释器目录下新建两个子目录:
头文件目录:D:\00_programming\07_apps\lua-5.4.2_Win64_bin\include
动态链接库目录:D:\00_programming\07_apps\lua-5.4.2_Win64_bin\lib
用压缩包中的include文件夹
覆盖刚刚新建的include文件夹
:
D:\00_programming\07_apps\lua-5.4.2_Win64_bin\include\lauxlib.h
D:\00_programming\07_apps\lua-5.4.2_Win64_bin\include\lua.h
D:\00_programming\07_apps\lua-5.4.2_Win64_bin\include\lua.hpp
D:\00_programming\07_apps\lua-5.4.2_Win64_bin\include\luaconf.h
D:\00_programming\07_apps\lua-5.4.2_Win64_bin\include\lualib.h
将压缩包中的链接文件复制到lib
目录下:
D:\00_programming\07_apps\lua-5.4.2_Win64_bin\lib\liblua54.a
D:\00_programming\07_apps\lua-5.4.2_Win64_bin\lib\lua54.dll
4.3 配置LuaRocks
将上一步安装的Lua链接库文件和头文件所在目录,指定到LuaRocks中:
luarocks config variables.LUA_LIBDIR D:\00_programming\07_apps\lua-5.4.2_Win64_bin\lib
luarocks config variables.LUA_INCDIR D:\00_programming\07_apps\lua-5.4.2_Win64_bin\include
4.4 安装GCC编译器
首先从官网上下GCC编译器:
https://sourceforge.net/projects/mingw-w64/files/Toolchains%20targetting%20Win64/Personal%20Builds/mingw-builds/8.1.0/threads-win32/seh/x86_64-8.1.0-release-win32-seh-rt_v6-rev0.7z
解压到指定目录,例如:
D:\00_programming\07_apps\mingw64
并将此目录下的bin目录添加到环境变量path,例如:
D:\00_programming\07_apps\mingw64\bin
打开命令行窗口,运行命令:
>gcc -v
Using built-in specs.
COLLECT_GCC=gcc
COLLECT_LTO_WRAPPER=D:/00_programming/07_apps/mingw64/bin/../libexec/gcc/x86_64-w64-mingw32/8.1.0/lto-wrapper.exe
Target: x86_64-w64-mingw32
Configured with: ../../../src/gcc-8.1.0/configure --host=x86_64-w64-mingw32 --build=x86_64-w64-mingw32 --target=x86_64-w64-mingw32 --prefix=/mingw64 --with-sysroot=/c/mingw810/x86_64-810-win32-sjlj-rt_v6-rev0/mingw64 --enable-shared --enable-static --enable-targets=all --enable-multilib --enable-languages=c,c++,fortran,lto --enable-libstdcxx-time=yes --enable-threads=win32 --enable-libgomp --enable-libatomic --enable-lto --enable-graphite --enable-checking=release --enable-fully-dynamic-string --enable-version-specific-runtime-libs --enable-sjlj-exceptions --disable-libstdcxx-pch --disable-libstdcxx-debug --enable-bootstrap --disable-rpath --disable-win32-registry --disable-nls --disable-werror --disable-symvers --with-gnu-as --with-gnu-ld --with-arch-32=i686 --with-arch-64=nocona --with-tune-32=generic --with-tune-64=core2 --with-libiconv --with-system-zlib --with-gmp=/c/mingw810/prerequisites/x86_64-w64-mingw32-static --with-mpfr=/c/mingw810/prerequisites/x86_64-w64-mingw32-static --with-mpc=/c/mingw810/prerequisites/x86_64-w64-mingw32-static --with-isl=/c/mingw810/prerequisites/x86_64-w64-mingw32-static --with-pkgversion='x86_64-win32-sjlj-rev0, Built by MinGW-W64 project' --with-bugurl=https://sourceforge.net/projects/mingw-w64 CFLAGS='-O2 -pipe -fno-ident -I/c/mingw810/x86_64-810-win32-sjlj-rt_v6-rev0/mingw64/opt/include -I/c/mingw810/prerequisites/x86_64-zlib-static/include -I/c/mingw810/prerequisites/x86_64-w64-mingw32-static/include' CXXFLAGS='-O2 -pipe -fno-ident -I/c/mingw810/x86_64-810-win32-sjlj-rt_v6-rev0/mingw64/opt/include -I/c/mingw810/prerequisites/x86_64-zlib-static/include -I/c/mingw810/prerequisites/x86_64-w64-mingw32-static/include' CPPFLAGS=' -I/c/mingw810/x86_64-810-win32-sjlj-rt_v6-rev0/mingw64/opt/include -I/c/mingw810/prerequisites/x86_64-zlib-static/include -I/c/mingw810/prerequisites/x86_64-w64-mingw32-static/include' LDFLAGS='-pipe -fno-ident -L/c/mingw810/x86_64-810-win32-sjlj-rt_v6-rev0/mingw64/opt/lib -L/c/mingw810/prerequisites/x86_64-zlib-static/lib -L/c/mingw810/prerequisites/x86_64-w64-mingw32-static/lib '
Thread model: win32
gcc version 8.1.0 (x86_64-win32-sjlj-rev0, Built by MinGW-W64 project)
4.5 安装CJson库
LuaRocks配置好了,下面我们使用它来安装前面用到过的库CJson。打开命令行,运行命令:
>luarocks install lua-cjson
Installing https://luarocks.org/lua-cjson-2.1.0.10-1.src.rock
lua-cjson 2.1.0.10-1 is already installed in C:\Users\qp188\AppData\Roaming\luarocks
Use --force to reinstall.C:\Users\qp188>luarocks install lua-cjson --force
Installing https://luarocks.org/lua-cjson-2.1.0.10-1.src.rocklua-cjson 2.1.0.10-1 depends on lua >= 5.1 (5.4-1 provided by VM: success)
x86_64-w64-mingw32-gcc -O2 -c -o lua_cjson.o -ID:\00_programming\07_apps\lua-5.4.2_Win64_bin\include lua_cjson.c -DDISABLE_INVALID_NUMBERS -DUSE_INTERNAL_ISINF
x86_64-w64-mingw32-gcc -O2 -c -o strbuf.o -ID:\00_programming\07_apps\lua-5.4.2_Win64_bin\include strbuf.c -DDISABLE_INVALID_NUMBERS -DUSE_INTERNAL_ISINF
strbuf.c: In function 'debug_stats':
strbuf.c:98:17: warning: cast from pointer to integer of different size [-Wpointer-to-int-cast](long)s, s->reallocs, s->length, s->size);^
strbuf.c: In function 'strbuf_resize':
strbuf.c:172:17: warning: cast from pointer to integer of different size [-Wpointer-to-int-cast](long)s, s->size, newsize);^
x86_64-w64-mingw32-gcc -O2 -c -o fpconv.o -ID:\00_programming\07_apps\lua-5.4.2_Win64_bin\include fpconv.c -DDISABLE_INVALID_NUMBERS -DUSE_INTERNAL_ISINF
x86_64-w64-mingw32-gcc -shared -o C:\Users\qp188\AppData\Local\Temp\luarocks_build-lua-cjson-2.1.0.10-1-4032167\cjson.dll lua_cjson.o strbuf.o fpconv.o D:\00_programming\07_apps\lua-5.4.2_Win64_bin\lib\lua54.dll -lm
lua-cjson 2.1.0.10-1 is now installed in C:\Users\qp188\AppData\Roaming\luarocks (license: MIT)
下面来调用CJson库。在IntelliJ中添加脚本文件cjson-demo.lua:
-- 加载cjson模块
package.cpath = package.cpath .. ';C:\\Users\\qp188\\AppData\\Roaming\\luarocks\\lib\\lua\\5.4\\cjson.dll'local cjson = require "cjson"-- JSON 编码(将表转换为JSON字符串)
local people = {name = 'Bob',age = 29,emails = {'bob@a.com', 'bob@b.com'}
}
local json_str = cjson.encode(people)
print(json_str)-- JSON 解码(将JSON字符串转换为Lua表)
local bob = cjson.decode(json_str)
print(bob.name .. '-' .. bob.age .. '-' .. bob.emails[1] .. '-' .. bob.emails[2])
运行结果如下:
{"age":29,"emails":["bob@a.com","bob@b.com"],"name":"Bob"}
Bob-29.0-bob@a.com-bob@b.com
4.6 安装cmsgpack库
接下来安装cmsgpack库,打开命令行,运行命令:
>luarocks install lua-cmsgpack --force
Installing https://luarocks.org/lua-cmsgpack-0.4.0-0.rockspec
Cloning into 'lua-cmsgpack'...
remote: Enumerating objects: 257, done.
Receiving objects: 100% (257/257), 99.66 KiB | 689.00 KiB/s, done.Resolving deltas: 100% (129/129), done.
Note: switching to 'dec1810a70d2948725f2e32cc38163de62b9d9a7'.You are in 'detached HEAD' state. You can look around, make experimental
changes and commit them, and you can discard any commits you make in this
state without impacting any branches by switching back to a branch.If you want to create a new branch to retain commits you create, you may
do so (now or later) by using -c with the switch command. Example:git switch -c <new-branch-name>Or undo this operation with:git switch -Turn off this advice by setting config variable advice.detachedHead to falseUpdating files: 100% (12/12), done.lua-cmsgpack 0.4.0-0 depends on lua >= 5.1 (5.4-1 provided by VM: success)
x86_64-w64-mingw32-gcc -O2 -c -o lua_cmsgpack.o -ID:\00_programming\07_apps\lua-5.4.2_Win64_bin\include lua_cmsgpack.c
x86_64-w64-mingw32-gcc -shared -o C:\Users\qp188\AppData\Local\Temp\luarocks_build-lua-cmsgpack-0.4.0-0-8168657\cmsgpack.dll lua_cmsgpack.o D:\00_programming\07_apps\lua-5.4.2_Win64_bin\lib\lua54.dll -lm
lua-cmsgpack 0.4.0-0 is now installed in C:\Users\qp188\AppData\Roaming\luarocks (license: Two-clause BSD)
接下来调用cmsgpack库。在IntelliJ中添加脚本文件cmsgpack-demo.lua:
package.cpath = package.cpath .. ';C:\\Users\\qp188\\AppData\\Roaming\\luarocks\\lib\\lua\\5.4\\cmsgpack.dll'local cmsgpack = require("cmsgpack")-- 将表转换为字符串
local people = {name = 'Bob',age = 29,emails = {'bob@a.com', 'bob@b.com'}
}
local msgpack_str = cmsgpack.pack(people)
print(msgpack_str)-- 将字符串转换为表
local bob = cmsgpack.unpack(msgpack_str)
print(bob.name .. '-' .. bob.age .. '-' .. bob.emails[1] .. '-' .. bob.emails[2])
···
运行结果如下:
```lua
��age�name�Bob�emails��bob@a.com�bob@b.com
Bob-29-bob@a.com-bob@b.com
5 构建Lua解释器
首先从官网下载Lua源码:
https://www.lua.org/ftp/lua-5.4.7.tar.gz
解压到本地目录,例如:
D:\00_programming\02_source_code\lua-5.4.7
在此目录下创建批处理文件build.cmd,内容如下:
@echo off
setlocalset lua_build_dir=%cd%
set lua_install_dir=%lua_build_dir%\targetecho.
echo **** Compilation starting ... ****
echo.mingw32-make PLAT=mingwecho.
echo **** Completed ****
echo.echo.
echo **** Starting moving files ... ****
echo.mkdir %lua_install_dir%
mkdir %lua_install_dir%\doc
mkdir %lua_install_dir%\bin
mkdir %lua_install_dir%\includecopy %lua_build_dir%\src\*.exe %lua_install_dir%\bin\*.*
del %lua_build_dir%\src\*.exe
copy %lua_build_dir%\src\*.dll %lua_install_dir%\bin\*.*
del %lua_build_dir%\src\*.dll
copy %lua_build_dir%\src\liblua.a %lua_install_dir%\bin\*.*
del %lua_build_dir%\src\liblua.a
copy %lua_build_dir%\doc\*.* %lua_install_dir%\doc\*.*
copy %lua_build_dir%\src\luaconf.h %lua_install_dir%\include\*.*
copy %lua_build_dir%\src\lua.h %lua_install_dir%\include\*.*
copy %lua_build_dir%\src\lualib.h %lua_install_dir%\include\*.*
copy %lua_build_dir%\src\lauxlib.h %lua_install_dir%\include\*.*
copy %lua_build_dir%\src\lua.hpp %lua_install_dir%\include\*.*
del %lua_build_dir%\src\*.oecho.
echo **** Completed ... ****
echo.%lua_install_dir%\bin\lua.exe -e "print 'Hello world!'"endlocal
运行批处理文件:
>build.cmd**** Compilation starting ... ****mingw32-make[1]: Entering directory 'D:/00_programming/02_source_code/lua-5.4.7/src'
mingw32-make "LUA_A=lua54.dll" "LUA_T=lua.exe" \
"AR=gcc -std=gnu99 -shared -o" "RANLIB=strip --strip-unneeded" \
"SYSCFLAGS=-DLUA_BUILD_AS_DLL" "SYSLIBS=" "SYSLDFLAGS=-s" lua.exe
mingw32-make[2]: Entering directory 'D:/00_programming/02_source_code/lua-5.4.7/src'
gcc -std=gnu99 -O2 -Wall -Wextra -DLUA_COMPAT_5_3 -DLUA_BUILD_AS_DLL -c -o lua.o lua.c
gcc -std=gnu99 -O2 -Wall -Wextra -DLUA_COMPAT_5_3 -DLUA_BUILD_AS_DLL -c -o lapi.o lapi.c
gcc -std=gnu99 -O2 -Wall -Wextra -DLUA_COMPAT_5_3 -DLUA_BUILD_AS_DLL -c lcode.c
gcc -std=gnu99 -O2 -Wall -Wextra -DLUA_COMPAT_5_3 -DLUA_BUILD_AS_DLL -c -o lctype.o lctype.c
gcc -std=gnu99 -O2 -Wall -Wextra -DLUA_COMPAT_5_3 -DLUA_BUILD_AS_DLL -c -o ldebug.o ldebug.c
gcc -std=gnu99 -O2 -Wall -Wextra -DLUA_COMPAT_5_3 -DLUA_BUILD_AS_DLL -c -o ldo.o ldo.c
gcc -std=gnu99 -O2 -Wall -Wextra -DLUA_COMPAT_5_3 -DLUA_BUILD_AS_DLL -c -o ldump.o ldump.c
gcc -std=gnu99 -O2 -Wall -Wextra -DLUA_COMPAT_5_3 -DLUA_BUILD_AS_DLL -c -o lfunc.o lfunc.c
gcc -std=gnu99 -O2 -Wall -Wextra -DLUA_COMPAT_5_3 -DLUA_BUILD_AS_DLL -c -o lgc.o lgc.c
gcc -std=gnu99 -O2 -Wall -Wextra -DLUA_COMPAT_5_3 -DLUA_BUILD_AS_DLL -c llex.c
gcc -std=gnu99 -O2 -Wall -Wextra -DLUA_COMPAT_5_3 -DLUA_BUILD_AS_DLL -c -o lmem.o lmem.c
gcc -std=gnu99 -O2 -Wall -Wextra -DLUA_COMPAT_5_3 -DLUA_BUILD_AS_DLL -c -o lobject.o lobject.c
gcc -std=gnu99 -O2 -Wall -Wextra -DLUA_COMPAT_5_3 -DLUA_BUILD_AS_DLL -c -o lopcodes.o lopcodes.c
gcc -std=gnu99 -O2 -Wall -Wextra -DLUA_COMPAT_5_3 -DLUA_BUILD_AS_DLL -c lparser.c
gcc -std=gnu99 -O2 -Wall -Wextra -DLUA_COMPAT_5_3 -DLUA_BUILD_AS_DLL -c -o lstate.o lstate.c
gcc -std=gnu99 -O2 -Wall -Wextra -DLUA_COMPAT_5_3 -DLUA_BUILD_AS_DLL -c -o lstring.o lstring.c
gcc -std=gnu99 -O2 -Wall -Wextra -DLUA_COMPAT_5_3 -DLUA_BUILD_AS_DLL -c -o ltable.o ltable.c
gcc -std=gnu99 -O2 -Wall -Wextra -DLUA_COMPAT_5_3 -DLUA_BUILD_AS_DLL -c -o ltm.o ltm.c
gcc -std=gnu99 -O2 -Wall -Wextra -DLUA_COMPAT_5_3 -DLUA_BUILD_AS_DLL -c -o lundump.o lundump.c
gcc -std=gnu99 -O2 -Wall -Wextra -DLUA_COMPAT_5_3 -DLUA_BUILD_AS_DLL -c -o lvm.o lvm.c
gcc -std=gnu99 -O2 -Wall -Wextra -DLUA_COMPAT_5_3 -DLUA_BUILD_AS_DLL -c -o lzio.o lzio.c
gcc -std=gnu99 -O2 -Wall -Wextra -DLUA_COMPAT_5_3 -DLUA_BUILD_AS_DLL -c -o lauxlib.o lauxlib.c
gcc -std=gnu99 -O2 -Wall -Wextra -DLUA_COMPAT_5_3 -DLUA_BUILD_AS_DLL -c -o lbaselib.o lbaselib.c
gcc -std=gnu99 -O2 -Wall -Wextra -DLUA_COMPAT_5_3 -DLUA_BUILD_AS_DLL -c -o lcorolib.o lcorolib.c
gcc -std=gnu99 -O2 -Wall -Wextra -DLUA_COMPAT_5_3 -DLUA_BUILD_AS_DLL -c -o ldblib.o ldblib.c
gcc -std=gnu99 -O2 -Wall -Wextra -DLUA_COMPAT_5_3 -DLUA_BUILD_AS_DLL -c -o liolib.o liolib.c
gcc -std=gnu99 -O2 -Wall -Wextra -DLUA_COMPAT_5_3 -DLUA_BUILD_AS_DLL -c -o lmathlib.o lmathlib.c
gcc -std=gnu99 -O2 -Wall -Wextra -DLUA_COMPAT_5_3 -DLUA_BUILD_AS_DLL -c -o loadlib.o loadlib.c
gcc -std=gnu99 -O2 -Wall -Wextra -DLUA_COMPAT_5_3 -DLUA_BUILD_AS_DLL -c -o loslib.o loslib.c
gcc -std=gnu99 -O2 -Wall -Wextra -DLUA_COMPAT_5_3 -DLUA_BUILD_AS_DLL -c -o lstrlib.o lstrlib.c
gcc -std=gnu99 -O2 -Wall -Wextra -DLUA_COMPAT_5_3 -DLUA_BUILD_AS_DLL -c -o ltablib.o ltablib.c
gcc -std=gnu99 -O2 -Wall -Wextra -DLUA_COMPAT_5_3 -DLUA_BUILD_AS_DLL -c -o lutf8lib.o lutf8lib.c
gcc -std=gnu99 -O2 -Wall -Wextra -DLUA_COMPAT_5_3 -DLUA_BUILD_AS_DLL -c -o linit.o linit.c
gcc -std=gnu99 -shared -o lua54.dll lapi.o lcode.o lctype.o ldebug.o ldo.o ldump.o lfunc.o lgc.o llex.o lmem.o lobject.o lopcodes.o lparser.o lstate.o lstring.o ltable.o ltm.o lundump.o lvm.o lzio.o lauxlib.o lbaselib.o lcorolib.o ldblib.o liolib.o lmathlib.o loadlib.o loslib.o lstrlib.o ltablib.o lutf8lib.o linit.o
strip --strip-unneeded lua54.dll
gcc -std=gnu99 -o lua.exe -s lua.o lua54.dll -lm
mingw32-make[2]: Leaving directory 'D:/00_programming/02_source_code/lua-5.4.7/src'
mingw32-make "LUAC_T=luac.exe" luac.exe
mingw32-make[2]: Entering directory 'D:/00_programming/02_source_code/lua-5.4.7/src'
gcc -std=gnu99 -O2 -Wall -Wextra -DLUA_COMPAT_5_3 -c -o luac.o luac.c
ar rcu liblua.a lapi.o lcode.o lctype.o ldebug.o ldo.o ldump.o lfunc.o lgc.o llex.o lmem.o lobject.o lopcodes.o lparser.o lstate.o lstring.o ltable.o ltm.o lundump.o lvm.o lzio.o lauxlib.o lbaselib.o lcorolib.o ldblib.o liolib.o lmathlib.o loadlib.o loslib.o lstrlib.o ltablib.o lutf8lib.o linit.o
ranlib liblua.a
gcc -std=gnu99 -o luac.exe luac.o liblua.a -lm
mingw32-make[2]: Leaving directory 'D:/00_programming/02_source_code/lua-5.4.7/src'
mingw32-make[1]: Leaving directory 'D:/00_programming/02_source_code/lua-5.4.7/src'**** Completed ******** Starting moving files ... ****D:\00_programming\02_source_code\lua-5.4.7\src\lua.exe
D:\00_programming\02_source_code\lua-5.4.7\src\luac.exe
已复制 2 个文件。
D:\00_programming\02_source_code\lua-5.4.7\src\lua54.dll
已复制 1 个文件。
已复制 1 个文件。
D:\00_programming\02_source_code\lua-5.4.7\doc\contents.html
D:\00_programming\02_source_code\lua-5.4.7\doc\index.css
D:\00_programming\02_source_code\lua-5.4.7\doc\logo.gif
D:\00_programming\02_source_code\lua-5.4.7\doc\lua.1
D:\00_programming\02_source_code\lua-5.4.7\doc\lua.css
D:\00_programming\02_source_code\lua-5.4.7\doc\luac.1
D:\00_programming\02_source_code\lua-5.4.7\doc\manual.css
D:\00_programming\02_source_code\lua-5.4.7\doc\manual.html
D:\00_programming\02_source_code\lua-5.4.7\doc\OSIApproved_100X125.png
D:\00_programming\02_source_code\lua-5.4.7\doc\readme.html
已复制 10 个文件。
已复制 1 个文件。
已复制 1 个文件。
已复制 1 个文件。
已复制 1 个文件。
已复制 1 个文件。**** Completed ... ****Hello world!
6 构建LuaRocks
首先从官网下载LuaRocks源码:
https://luarocks.github.io/luarocks/releases/luarocks-3.11.1-win32.zip
解压到本地目录,例如:
D:\00_programming\02_source_code\luarocks-3.11.1-win32
在此目录下运行命令:
install.bat /F /LUA D:\00_programming\07_apps\lua-5.4.2_Win64_bin /INC D:\00_programming\07_apps\lua-5.4.2_Win64_bin\include /P ./luarocks-3.11.1-install /SELFCONTAINED /Q
此命令会在luarocks-3.11.1-install下生成可执行程序。
运行命令结果如下:
>install.bat /F /LUA D:\00_programming\07_apps\lua-5.4.2_Win64_bin\lib /INC D:\00_programming\07_apps\lua-5.4.2_Win64_bin\include /P ./luarocks-3.11.1-install /SELFCONTAINED /QD:\00_programming\02_source_code\luarocks-3.11.1-win32>rem=rem --[[--lua
LuaRocks 3.11.x installer.========================
== Checking system... ==
========================Need admin privileges, now elevating a new process to continue installing...
Now exiting unprivileged installerD:\00_programming\02_source_code\luarocks-3.11.1-win32>install.bat /F /LUA D:\00_programming\07_apps\lua-5.4.2_Win64_bin\lib /INC D:\00_programming\07_apps\lua-5.4.2_Win64_bin\include /P ./luarocks-3.11.1-install /SELFCONTAINED /QD:\00_programming\02_source_code\luarocks-3.11.1-win32>rem=rem --[[--lua
LuaRocks 3.11.x installer.========================
== Checking system... ==
========================Need admin privileges, now elevating a new process to continue installing...
Now exiting unprivileged installerD:\00_programming\02_source_code\luarocks-3.11.1-win32>install.bat /F /LUA D:\00_programming\07_apps\lua-5.4.2_Win64_bin /INC D:\00_programming\07_apps\lua-5.4.2_Win64_bin\include /P ./luarocks-3.11.1-install /SELFCONTAINED /QD:\00_programming\02_source_code\luarocks-3.11.1-win32>rem=rem --[[--lua
LuaRocks 3.11.x installer.========================
== Checking system... ==
========================Need admin privileges, now elevating a new process to continue installing...
Now exiting unprivileged installer