• linkedu视频
  • 平面设计
  • 电脑入门
  • 操作系统
  • 办公应用
  • 电脑硬件
  • 动画设计
  • 3D设计
  • 网页设计
  • CAD设计
  • 影音处理
  • 数据库
  • 程序设计
  • 认证考试
  • 信息管理
  • 信息安全
菜单
linkedu.com
  • 网页制作
  • 数据库
  • 程序设计
  • 操作系统
  • CMS教程
  • 游戏攻略
  • 脚本语言
  • 平面设计
  • 软件教程
  • 网络安全
  • 电脑知识
  • 服务器
  • 视频教程
  • vbs
  • DOS/BAT
  • hta/htc
  • python
  • perl
  • VBA
  • ColdFusion
  • ruby
  • PowerShell
  • Lua
  • Golang
  • linux shell
您的位置:首页 > 脚本语言 >Lua > lua中赋值类型代码详解

lua中赋值类型代码详解

作者:hebedich 字体:[增加 减小] 来源:互联网

hebedich 通过本文主要向大家介绍了lua源码详解与应用,lua 详解,lua table赋值,lua 数组赋值,lua 赋值等相关知识,希望对您有所帮助,也希望大家支持linkedu.com www.linkedu.com

我们来看看lua vm在解析下面源码并生成bytecode时的整个过程:

 foo = "bar"
 local a, b = "a", "b"
 foo = a
</div>

首先我们先使用ChunkySpy这个工具来看看vm最终会具体生成什么样的vm instructions

在这里,开头为[数字]的行是vm真正生成的字节码,我们看到一共生成了六行字节码。首先loadk将常量表中下标为1的常量即"bar"赋给寄存器0;然后setglobal将寄存器0的内容赋给全局变量表中下标为0的全局变量即foo;loadk再将"a"和"b"分别赋值给了寄存器0、1,在这里寄存器0和1分别表示当前函数的local变量即变量a和b;最后setglobal将变量a的值赋给了全局变量foo;最后一个return01是vm在每一个chunk最后都会生成了,并没有什么用。现在应该比较清除的了解了lua vm生成的字节码的含义了,接下来我们看看vm是怎样且为什么生成这些个字节码的。

当我们用luaL_dofile函数执行这个lua脚本源码时会有两个阶段,第一个是将脚本加载进内存,分词解析并生成字节码并将其整个包裹为main chunk放于lua stack栈顶,第二是调用lua_pcall执行这个chunk,这里我们只会分析第一个过程。

前面几篇文章说了,当dofile时会跑到一个叫做luaY_parser的函数中,

Proto *luaY_parser (lua_State *L, ZIO *z, Mbuffer *buff, const char *name) {
 struct LexState lexstate;
 struct FuncState funcstate;
 -- ... ...
 funcstate.f->is_vararg = VARARG_ISVARARG; /* main func. is always vararg */
 luaX_next(&lexstate); /* read first token */
 chunk(&lexstate);
 -- ... ...
 return funcstate.f;
}
</div>

函数luaY_parser前面两行定义了LexState和FuncState结构体变量,其中LexState不仅用于保存当前的词法分析状态信息,而且也保存了整个编译系统的全局状态,FuncState结构体来保存当前函数编译的状态数据。在lua源码中都会有一个全局的函数执行体,即为main func,在开始解析的时候当前的函数必然是main func函数,此时第三行的funcstate表示了这个函数的状态,由于lua规定这个函数必然会接收不定参数因此第五行将is_vararg标识设为VARARG_ISVARARG。接着第六行luaX_next解析文件流分离出第一个token,将其保存在lexstate的t成员中,此时t为“foo”全局变量。接着调用了chunk函数,这里开始了递归下降解析的全部过程:

static void chunk (LexState *ls) {
 /* chunk -> { stat [`;'] } */
 int islast = 0;
 enterlevel(ls);
 while (!islast && !block_follow(ls->t.token)) {
  islast = statement(ls);//递归下降点
  testnext(ls, ';');
  lua_assert(ls->fs->f->maxstacksize >= ls->fs->freereg &&
        ls->fs->freereg >= ls->fs->nactvar);
  ls->fs->freereg = ls->fs->nactvar; /* free registers */
 }
 leavelevel(ls);
}
</div>

lua是有作用域层次概念的,因此当进入一个层次时会调用enterlevel函数,离开当前层次则会调用leavelevel函数。首先进入while循环,当前token为“foo”,这既不是终结标志也不是一个block开始的词素,因此会进入statement函数,statement函数主体是一个长长的switch...case...代码结构,根据第一个token进入不同的调用解析分支。在我们这个例子中会进入default分支:

static int statement (LexState *ls) {
 -- ... ...
 switch (ls->t.token) {
  case TK_IF: { /* stat -> ifstat */
   ifstat(ls, line);
   return 0;
  }
  case TK_WHILE: { /* stat -> whilestat */
   whilestat(ls, line);
   return 0;
  }
  -- ... ...
  default: {
   exprstat(ls);
   return 0; /* to avoid warnings */
  }
 }
}
</div>

进入exprstate函数:

static void exprstat (LexState *ls) {
 /* stat -> func | assignment */
 FuncState *fs = ls->fs;
 struct LHS_assign v;
 primaryexp(ls, &v.v);
 if (v.v.k == VCALL) /* stat -> func */
  SETARG_C(getcode(fs, &v.v), 1); /* call statement uses no results */
 else { /* stat -> assignment */
  v.prev = NULL;
  assignment(ls, &v, 1);
 }
}
</div>

第四行的LHS_assign结构体是为了处理多变量赋值的情况的,例如a,b,c = ...。在LHS_assign中成员v类型为expdesc描述了等号左边的变量,详情可见上篇文章里对expdesc的介绍。接下来进入primaryexp,来获取并填充“foo”变量的expdesc信息,这会接着进入prefixexp函数中

 static void prefixexp (LexState *ls, expdesc *v) {
  /* prefixexp -> NAME | '(' expr ')' */
  switch (ls->t.token) {
   case '(': {
    int line = ls->linenumber;
    luaX_next(ls);
    expr(ls, v);
    check_match(ls, ')', '(', line);
    luaK_dischargevars(ls->fs, v);
    return;
   }
   case TK_NAME: {
    singlevar(ls, v);
    return;
   }
   default: {
    luaX_syntaxerror(ls, "unexpected symbol");
    return;
   }
  }
 }
</div>

由于当前token是“foo”,因此进入TK_NAME分支,调用singlevar。

static void singlevar (LexState *ls, expdesc *var) {
 TString *varname = str_checkname(ls);
 FuncState *fs = ls->fs;
 if (singlevaraux(fs, varname, var, 1) == VGLOBAL)
  var->u.s.info = luaK_stringK(fs, varname); /* info points to global name */
}
static int singlevaraux (FuncState *fs, TString *n, expdesc *var, int base) {
 if (fs == NULL) { /* no more levels? */
  init_exp(var, VGLOBAL, NO_REG); /* default is global variable */
  return VGLOBAL;
 }
 else {
  int v = searchvar(fs, n); /* look up at current level */
  if (v >= 0) {
   init_exp(var, VLOCAL, v);
   if (!base)
    markupval(fs, v); /* local will be used as an upval */
   return VLOCAL;
  }
  else { /* not found at current level; try upper one */
   if (singlevaraux(fs->prev, n, var, 0) == VGLOBAL)
    return VGLOBAL;
   var->u.s.info = indexupvalue(fs, n, var); /* else was LOCAL or UPVAL */
   var->k = VUPVAL; /* upvalue in this level */
   return VUPVAL;
  }
 }
</div>

在singlevaraux函数中会判断变量是local、upvalue还是global的。如果fs为null了则说明变量为全局的,否则进入searchvar在当前的函数局部变量数组中查找,否则根据fs的prev成员取得其父函数的FuncState并传入singlevaraux中递归查找,如果前面的都没满足则变量为upvlaue。此例中进入第21行中,由于fs已经指向了main func因此其prev为null,“foo”判定为global并返回到exprstate函数中。在取得了“foo”的信息后,因为“foo”不是函数调用,因此接着进入assignment函数中

primaryexp(ls, &v.v);
 if (v.v.k == VCALL) /* stat -> func */
  SETARG_C(getcode(fs, &v.v), 1); /* call statement uses no results */
 else { /* stat -> assignment */
  v.prev = NULL;
  assignment(ls, &v, 1);
 }
</div>

在assignment函数中首先判断下一个token是否为“,",此例中不是则说明是单变量的赋值,接着check下一个token为”=“,成立,接着调用explist1判断等号右边有几个值,此例为1个,然后会判断左边的变量数是否等于右边的值数,不等于则进入adjust_assign函数进行调整,此例是相等的因此依次进入luaK_setoneret和luaK_storevar函数。在luaK_storevar中首先进入int e = luaK_exp2anyreg(fs, ex);函数luaK_exp2anyreg的K代表了此函数是字节码相关的函数,ex为值”bar“,这个函数又调用了discharge2reg,根据ex的类型来生成不同的字节码:

static void discharge2reg (FuncState *fs, expdesc *e, int reg) {
 luaK_dischargevars(fs, e);
 switch (e->k) {
  case VNIL: {
   luaK_nil(fs, reg, 1);
   break;
  }
  case VFALSE: case VTRUE: {
   luaK_codeABC(fs, OP_LOADBOOL, reg, e->k == VTRUE, 0);
   break;
  }
  case VK: {
   luaK_codeABx(fs, OP_LOADK, reg, e->u.s.info);
   break;
  }
//... ...
}
</div>

由于”bar“是常量因此调用luaK_codeABx函数生成loadk字节码。reg为保存载入的常量值的寄存器号,e->u.s.info根据不同类型值代表不同含义,根据注释我们知道此时info为常量数组的下标。

typedef enum {
 //... ...
 VK,    /* info = index of constant in `k' */
 VKNUM,  /* nval = numerical value */
 VLOCAL,  /* info = local register */
 VGLOBAL,  /* inf
  


 

您可能想查找下面的文章:

  • lua文件操作详解
  • 举例详解Lua中的协同程序编程
  • 详解Lua中的元表概念
  • 详解Lua中的数组概念知识
  • 详解Lua中if ... else语句的使用方法
  • 详解Lua中的if语句的使用方法
  • 详解Lua中的while循环语句的使用
  • 详解Lua中的数据类型
  • 详解Lua中的变量相关知识点
  • lua中赋值类型代码详解

相关文章

  • Lua学习笔记之函数、变长参数、closure(闭包)、select等
  • Lua极简入门指南(一):函数篇
  • Lua学习笔记之数据结构
  • Lua中的基本语法、控制语句总结
  • lua闭包的理解以及表与函数的几种表达方法
  • 浅谈Lua的面向对象特性
  • Lua中的loadfile、dofile、require详解
  • Lua中的持久化和序列化详解
  • linux系统安装Nginx Lua环境
  • Cocos2d-x中调用Lua及HelloWorld.lua源码分解

文章分类

  • vbs
  • DOS/BAT
  • hta/htc
  • python
  • perl
  • VBA
  • ColdFusion
  • ruby
  • PowerShell
  • Lua
  • Golang
  • linux shell

最近更新的内容

    • 安装Nginx+Lua开发环境
    • Lua教程(九):元表与元方法详解
    • Lua中使用元表(metatable)执行算术类元方法实例
    • Lua教程(八):数据持久化
    • Lua字符串库(string库)学习笔记
    • lua脚本实现自动生成APK包
    • Lua性能优化技巧(五):削减、重用和回收
    • Lua操作字符串的5个代码片段分享
    • Lua教程(三):C语言、C++中调用Lua的Table示例
    • Lua中的源代码预编译浅析

关于我们 - 联系我们 - 免责声明 - 网站地图

©2020-2025 All Rights Reserved. linkedu.com 版权所有