Lua语言入门

目录

  • Lua语言
  • [1 搭建Lua开发环境](#1 搭建Lua开发环境)
  • [2 Lua语法](#2 Lua语法)
  • [3 标准库](#3 标准库)
    • [3.1 Base库](#3.1 Base库)
    • [3.2 String库](#3.2 String库)
    • [3.3 Table库](#3.3 Table库)
    • [3.4 Math库](#3.4 Math库)
    • [3.5 Debug库](#3.5 Debug库)
      • [debug.getinfo(f, what)](#debug.getinfo(f, what))
    • [3.6 cjson 库和 cmsgpack库](#3.6 cjson 库和 cmsgpack库)
  • [4 安装扩展库](#4 安装扩展库)
    • [4.1 安装LuaRocks](#4.1 安装LuaRocks)
    • [4.2 安装链接库和头文件](#4.2 安装链接库和头文件)
    • [4.3 配置LuaRocks](#4.3 配置LuaRocks)
    • [4.4 安装GCC编译器](#4.4 安装GCC编译器)
    • [4.5 安装CJson库](#4.5 安装CJson库)
    • [4.6 安装cmsgpack库](#4.6 安装cmsgpack库)
  • [5 构建Lua解释器](#5 构建Lua解释器)
  • [6 构建LuaRocks](#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 Executables/lua-5.4.2_Win64_bin.zip

解压到本地目录,并将此目录添加到环境变量path,例如:
D:\00_programming\07_apps\lua-5.4.2_Win64_bin

打开命令行窗口,运行命令:

bash 复制代码
>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项目后,就可以添加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。如:

lua 复制代码
a = 1 -- 为全局变量a赋值
print (b) -- 无须声明即可使用,默认值是nil
a = nil -- 删除全局变量a的方法是将其赋值为nil。全局变量没有声明和未声明之分,只有非nil和nil的区别

局部变量

在Redis脚本中不能使用全局变量,只允许使用局部变量以防止脚本之间相互影响。

声明局部变量的方法为local变量名,就像这样:

lua 复制代码
local c -- 声明一个局部变量c,默认值是nil
local d = 1 -- 声明一个局部变量d并赋值为1
local e, f -- 可以同时声明多个局部变量

同样,声明一个存储函数的局部变量的方法为:

lua 复制代码
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

局部变量作用域

局部变量的作用域为从声明开始到所在层的语句块末尾,例如:

lua 复制代码
local x = 10
if true then
  local x = x + 1
  print (x)
  do
    local x =x + 1
    print (x)
  end
  print (x)
end
print (x)

打印结果为:

11

12

11

10

-- a的值是1,b的值是2

local e, f = 1

2.3 注释

Lua的注释有单行和多行两种。

单行注释

以 -- 开始,到行尾结束,在上面的代码已经使用过了,一般习惯在 -- 后面跟上一个空格。

多行注释

以 -- [[开始,到]]表示,例如:

lua 复制代码
  -- [[
  这是一个多行注释
  ]]

2.4 赋值

Lua支持多重赋值,例如:

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会先计算所有表达式的值,例如:

lua 复制代码
local a = {1, 2, 3}
local i = 1
i, a[i] = i + 1, 5

Lua计算所有表达式的值后,上面最后一个赋值语句变为:

lua 复制代码
i, a[1] = 2, 5

所以赋值后i的值为2,a则为{5,2,3}。

注意 表类型的索引从1开始

使用函数返回的多个值进行多重赋值:

lua 复制代码
a, b, c = func1()

2.5 操作符

Lua有以下5类操作符:

数学操作符

操作符 说明
+ 加号
- 减号
* 乘号
/ 除号
% 取模
- 取负
^ 幂运算

数学操作符的操作数如果是字符串会自动转换成数字

lua 复制代码
print('1' + 1) -- 结果为2

比较操作符

操作符 说明
== 比较两个操作数的类型和值是否都相等
~= 比较两个操作数的类型和值是否不相等
< 小于
> 大于
<= 小于等于
>= 大于等于

比较操作符的结果一定是布尔类型。比较操作符不同于数学操作符,不会对两边的操

作数进行自动类型转换。

lua 复制代码
print (1 == '1') -- false,二者类型不同,不会进行自动类型转换
print ({'a'} == {'a'}) -- false,对于表类型值比较的是二者的引用

如果需要比较字符串和数字,可以手动进行类型转换。

lua 复制代码
print(1 == tonumber('1'))
print('1' == tostring(1))

tonumber()函数还可以进行进制转换,例如:

lua 复制代码
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或空字符串也被当作真。

lua 复制代码
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操作符与之类似。

连接操作符

操作符 说明
.. 用来连接两个字符串
lua 复制代码
print ('hello' .. ' ' .. 'world!') -- 'hello world!'

连接操作符会自动把数字类型的值转换成字符串类型:

lua 复制代码
print ('The price is ' .. 25) -- 'The price is 25'

取长度操作符

操作符 说明
# 用来获取字符串或表的长度

例如:

lua 复制代码
print(#"12345") -- 5
print(#{1, 2, 3, 4, 5}) -- 5

操作符优先级

^
not # -(一元)
* / %
+ -
..
< > <= >= ~= ==
and
or

2.6 if语句

Lua的if语句格式如下:

lua 复制代码
if conditonal expression then
  statement block
elseif condition then
  statement block
else
  statement block
end

前面提到过在Lua中只有nil和false才为假,其余值,包括空字符串和0,都被认为是真值。这是一个容易出问题的地方,例如Redis的EXISTS 命令的返回值1和0分别表示存在或不存在,所以在条件判断是要判断具体的返回值是0或1:

lua 复制代码
if redis.call ('exists', 'key') == 1 then
  exists = true
else
  exists = false
end

2.7 循环语句

Lua 支持while、repeat和for循环语句。

while

lua 复制代码
while condition do
  statement block
end

repeat

lua 复制代码
repeat
  statement block
until conditonal expression

for

数值型 for 循环:用于遍历一个数值区间

lua 复制代码
for variable = initial value, final value, step length do
  statement block
end

泛型 for 循环:用于遍历表(table)或者其他迭代器函数(iterator functions)

lua 复制代码
for variable1, variable2, ..., variablen in iterator do
  statement block
end

例如:

lua 复制代码
local tab = {'a', 'b', 'c', 'd', 'e'}

for i = 1, 5, 1 do
  print(tab[i])
end

for k, v in pairs(tab) do
  print(k)
  print(v)
end

2.8 表类型

表是Lua中唯一的数据结构,可以理解为关联数组,任何类型的值(除了空类型)可以作为表的索引。

表的定义方式为:

对象

lua 复制代码
people = {} -- 将变量people赋值为一个空表
people['name'] = 'Bob' -- 将name字段赋值Bob
print (people.name) -- 打印内容为'Bob', people.field是people['field']的语法糖

也可以这样定义

lua 复制代码
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

数组

当索引为整数的时候,表和传统的数组一样,例如:

lua 复制代码
a = {}
a [1] = 'Bob'
a[2] = 'Jeff'

还可以写成下面这样:

lua 复制代码
a = {'Bob', 'Jeff'}
print (a[1]) -- 打印的内容为'Bob'

Lua约定数组的索引是从1开始,而不是0。

遍历表

lua 复制代码
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) do
  print(index .. ':' .. value)
end

输出,注意four之后的元素并没有输出:

1:one

2:two

3:three

lua 复制代码
-- pair()返回迭代器,迭代所有值不为nil的索引
for index, value in pairs(tab) do
  print(index .. ':' .. value)
end

输出:

1:one

2:two

3:three

5:four

6:five

a:A

c:C

b:B

e:E

lua 复制代码
for i = 1, #tab, 1 do
  if tab[i] then
    print(i .. ':' .. tab[i])
  end
end

输出:

1:one

2:two

3:three

5:four

6:five

2.9 函数

函数的定义为:

lua 复制代码
function(参数列表)
函数体
end

可以将其赋值给一个局部变量,例如:

lua 复制代码
local square = function (num)
  return num * num
end

即使没有参数,括号也不能省略。

Lua还提供了一个语法糖来简化函数的定义,例

lua 复制代码
local function square (num)
  return num * num
end

这段代码会被转换为:

lua 复制代码
local square
square = function (num)
  return num * num
end
  • 因为在赋值前声明了局部变量square,所以可以在函数内部引用自身(实现递归)。
  • 如果实参的个数小于形参的个数,则没有匹配到的形参的值为nil。
  • 相应地,如果实参的个数大于形参的个数,则多出的实参会被忽略。
  • 如果希望捕获多出的实参(即实现可变参数个数),可以让最后一个形参为 ... 。

例如,希望传入若干参数计算这些数的平方:

lua 复制代码
local function square (...)
  local argv = {...}
  for i = 1, #argv do
    argv[i] = argv[i] * argv[i]
  end
  return table.unpack(argv)
end

a, 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()函数设置种子的值。

随机数测试:

lua 复制代码
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 该字段表明该函数是否为可变长函数
举例
lua 复制代码
function testFunction()
		print('-----current function:')
    local info1 = debug.getinfo(1)
    for k, v in pairs(info1) do
        print(k, v)
    end
    print('-----caller:')
    local info2 = debug.getinfo(2)
    for k, v in pairs(info2) do
        print(k, v)
    end
end

testFunction()

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这两个全局变量来访问对应的库。两者的用法如下:

lua 复制代码
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

打开命令行窗口,运行命令:

bash 复制代码
>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 Executable/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中:

bash 复制代码
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 targetting Win64/Personal Builds/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

打开命令行窗口,运行命令:

bash 复制代码
>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。打开命令行,运行命令:

bash 复制代码
>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.rock

lua-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:

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

运行结果如下:

lua 复制代码
{"age":29,"emails":["bob@a.com","bob@b.com"],"name":"Bob"}
Bob-29.0-bob@a.com-bob@b.com

4.6 安装cmsgpack库

接下来安装cmsgpack库,打开命令行,运行命令:

bash 复制代码
>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 false

Updating 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:

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,内容如下:

shell 复制代码
@echo off
setlocal

set lua_build_dir=%cd%
set lua_install_dir=%lua_build_dir%\target

echo.
echo **** Compilation starting ... ****
echo.

mingw32-make PLAT=mingw

echo.
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%\include

copy %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\*.o

echo.
echo **** Completed ... ****
echo.

%lua_install_dir%\bin\lua.exe -e "print 'Hello world!'"

endlocal

运行批处理文件:

shell 复制代码
>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

在此目录下运行命令:

shell 复制代码
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下生成可执行程序。

运行命令结果如下:

shell 复制代码
>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 /Q

D:\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

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\lib /INC D:\00_programming\07_apps\lua-5.4.2_Win64_bin\include /P ./luarocks-3.11.1-install /SELFCONTAINED /Q

D:\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

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

D:\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
相关推荐
@淡 定12 分钟前
责任链模式+CompletableFuture异步处理
开发语言·python·责任链模式
吃猫包的鱼20 分钟前
python关于excel常用函数(pandas篇)
开发语言·python·excel·pandas
hope_wisdom28 分钟前
Python面试宝典第14题:背包问题
开发语言·python·面试·贪心算法·背包问题
(eleven)36 分钟前
Java小白入门到实战应用教程-基本概念了解
java·开发语言
古德new38 分钟前
完美解决ImportError: cannot import name ‘Imputer‘的正确解决方法,亲测有效!!!
linux·运维·服务器·开发语言·python
2401_8574245240 分钟前
精通Postman响应解析:正则表达式的实战应用
正则表达式·lua·postman
阿里嘎多8711 小时前
JVM OutOfMemoryError异常模拟
java·开发语言·jvm·spring boot·redis·mysql·缓存
埋头编程~1 小时前
【C语言】详解结构体(中)(结构体的内存对齐,重点中的重点)
c语言·开发语言·学习
castlooo1 小时前
Qt 使用发布工具 windeployqt 来release
开发语言·qt
酸奶代码1 小时前
搞清c++中的队列(queue)以及双端队列(deque),以及常用的接口!
开发语言·c++