前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >好玩的lua

好玩的lua

作者头像
theanarkh
发布2021-08-24 15:15:53
6080
发布2021-08-24 15:15:53
举报
文章被收录于专栏:原创分享原创分享

前言:最近体验了一下Openresty,了解到Openresty里使用lua语言来增强了Nginx的能力,所以又去了解了一下lua,lua语言小而精悍,lua引擎也值得学习。周末看了一下lua引擎的一些实现,也体验了一下lua语言的一些东西,本文简单介绍一下,后续有时间的话再写文章分析引擎的实现。

1 在c语言中嵌入lua引擎

lua引擎本身是一个库,类似V8一样,我们可以把它嵌入到其他项目中,我们首先安装相关文档安装lua(我安装的是5.1.5)。然后写个demo体验一下。

代码语言:javascript
复制
#include <lua.h>#include <lualib.h>#include <lauxlib.h>#include<stdio.h>
int echo(lua_State *L) {    printf("world");}
int main(int argc, char *argv[]) {    int s = 0;
    lua_State *L = lua_open();
    // 注册个自定义的函数
    lua_register(L,"echo", echo);
    luaL_openlibs(L);
    // 执行lua脚本
    luaL_dofile(L, "hello.lua");
    lua_close(L);
    return 1;}

编译上面的代码

代码语言:javascript
复制
gcc hello.c -llua -lm -ldl

然后写个hello.lua脚本。

代码语言:javascript
复制
print("hello");echo();

执行./a.out,我们看到输出了hello world。这个是个简单的体验demo,和直接使用lua提供的命令行工具类似,只不过我们这里还拓展了一个自定义的echo函数给lua脚本调用。如果我们想动态地执行一段脚本,而不是执行一个lua文件,也是可以的。

代码语言:javascript
复制
#include <lua.h>#include <lualib.h>#include <lauxlib.h>const char * script = "print('hi');";int main(int argc, char *argv[]) {    lua_State *L = lua_open();
    luaL_openlibs(L);
    luaL_dostring(L, script);
    lua_close(L);
    return 1;}

编译执行以上代码我们会看到输出hi。以上这些似乎没什么大的作用,因为我们执行简单地使用lua语言提供的能力。而lua的能力绝不止于此,lua称为胶水语言,除了可以嵌入其他语言中,还支持拓展。下面我们看如果拓展lua的能力。

2 基于lua的demo运行时

虽然这里只是简单地拓展lua,但是这里称之为运行时是因为类似Node.js基于V8一样,我们也可以通过拓展lua来实现一个基于lua的运行时。下面我们看看怎么拓展(也就是怎么调用其他语言的代码,这里是c)。新建一个test.c文件。

代码语言:javascript
复制
#include <lua.h>#include <lualib.h>#include <lauxlib.h>static int test(lua_State* L){    //取栈第一个参数
    const char *a = luaL_checkstring(L, 1); 
    //返回值入栈
    lua_pushstring(L, (const char *)"hi");
    return 1;}
static const struct luaL_Reg reg_test[] = {    {"test", test},
    {NULL, NULL}};
int luaopen_test(lua_State *L) {    const char* libName = "test";
    luaL_register(L, libName, reg_test);
    return 1;}

lua和c是通过一个栈进行通信的,lua调用c函数的时候,c函数可以从栈中获取lua的参数,也可也从栈中返回执行结果给lua。我们把以上代码编译成一个动态库。

代码语言:javascript
复制
gcc test.c -fPIC -shared -o test.so

然后写个测试lua demo。

代码语言:javascript
复制
local test = require "test"
a = test.test("hello world!")print(a)

我们可以看到在lua中成功调用了test模块的test函数,并输出hi。当我们require"test"的时候,lua会去当前目录找test.o,并且执行其中的luaopen_test函数。luaopen_前缀是约定,test则是模块名称。当前去哪里找需要加载的模块这个我们可以设置。我们分析一下c文件的代码,看看拓展lua时的一些内容。首先看luaL_register。

代码语言:javascript
复制
LUALIB_API void (luaL_register) (lua_State *L, const char *libname, const luaL_Reg *l) {
  luaI_openlib(L, libname, l, 0);}

我们主要关注luaL_register的第二第三个参数libname和luaL_Reg。因为知道这个参数的格式,我们才知道怎么写c代码。其中name是库名称,也就是我们require时传的字符串。luaL_Reg的定义如下

代码语言:javascript
复制
typedef int (*lua_CFunction) (lua_State *L);
typedef struct luaL_Reg {  const char *name;
  lua_CFunction func;} luaL_Reg;

luaL_Reg是封装了kv的一个结构体,。name是导出的函数名称,即在lua中可以调用的函数。func则是对应的函数,当在lua执行name函数时就会执行func的代码。

3 lua变量存储的设计

lua是动态类型的语言,意味着一个变量的值的类型是可以改变的,下面看一下lua中是如何设计底层的存储的。lua所有变量都使用TValue结构体来表示。

代码语言:javascript
复制
#define TValuefields    Value value; int tt
typedef struct lua_TValue {  TValuefields;} TValue;

里面只有两个字段。tt是表示变量类型。lua的类型比较简单。如下

代码语言:javascript
复制
#define LUA_TNIL        0#define LUA_TBOOLEAN        1#define LUA_TLIGHTUSERDATA  2#define LUA_TNUMBER     3#define LUA_TSTRING     4// 数组和对象都使用一种类型#define LUA_TTABLE      5#define LUA_TFUNCTION       6#define LUA_TUSERDATA       7#define LUA_TTHREAD     8

接下来我们看看Value的定义。

代码语言:javascript
复制
typedef union {
  GCObject *gc;
  void *p;
  lua_Number n;
  int b;} Value;

Value里分为两种类型,一种是不需要gc的,比如数字,一种是需要gc的,比如数组,lua是带gc的语言。我们继续看GCObject。

代码语言:javascript
复制
union GCObject {
  GCheader gch;
  union TString ts;
  union Udata u;
  union Closure cl;
  struct Table h;
  struct Proto p;
  struct UpVal uv;
  struct lua_State th;  /* thread */};

我们看到GCObject是一个联合体,可以存储不同类型的变量。我们再看看TString的定义。

代码语言:javascript
复制
typedef union TString {
  L_Umaxalign dummy;  /* 内存对齐,性能优化 */
  struct {
    CommonHeader;
    lu_byte reserved;
    unsigned int hash;
    size_t len;
  } tsv;} TString;

字符串结构体里面主要的字段时len和hash,len就是字符串的长度,hash类似一个索引,lua中的字符串不是存储在结构体本身的,而是统一管理起来,主要是为了复用,比如有两个变量的值都是同一个字符串,那么lua中,只会存储一个字符串值,而这两个变量都会通过hash指向这个字符串的值。我们可以看一下一段代码大概了解一下。

代码语言:javascript
复制
// 新建字符串,如果存在则直接复用
TString *luaS_newlstr (lua_State *L, const char *str, size_t l) {
  GCObject *o;
  unsigned int h = cast(unsigned int, l);  /* seed */
  size_t step = (l>>5)+1;  
  size_t l1;
  // 计算字符串的哈希值
  for (l1=l; l1>=step; l1-=step)  /* compute hash */
    h = h ^ ((h<<5)+(h>>2)+cast(unsigned char, str[l1-1]));
  // 判断是否有一样的字符串存在了,是则共享,直接返回,否则新建
  for (o = G(L)->strt.hash[lmod(h, G(L)->strt.size)];
       o != NULL;
       o = o->gch.next) {
    TString *ts = rawgco2ts(o);
    if (ts->tsv.len == l && (memcmp(str, getstr(ts), l) == 0)) {
      if (isdead(G(L), o)) changewhite(o);
      return ts;
    }
  }
  // 找不到则新建
  return newlstr(L, str, l, h);  /* not found */}

我们看到lua的变量存储设计中是一种树状结构,通过上层的变量类型,再进行不同的存取操作。从而我们也可以了解到动态语言在变量存储中的一些设计思想。

后记:这是周末学习lua的一些内容,后续有时间会继续更新,lua是一个非常有意思的项目。有兴趣的同学可以关注仓库

https://github.com/theanarkh/read-lua-code。

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2021-08-01,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 编程杂技 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 1 在c语言中嵌入lua引擎
  • 2 基于lua的demo运行时
  • 3 lua变量存储的设计
相关产品与服务
对象存储
对象存储(Cloud Object Storage,COS)是由腾讯云推出的无目录层次结构、无数据格式限制,可容纳海量数据且支持 HTTP/HTTPS 协议访问的分布式存储服务。腾讯云 COS 的存储桶空间无容量上限,无需分区管理,适用于 CDN 数据分发、数据万象处理或大数据计算与分析的数据湖等多种场景。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档