上节说到表达式的解析问题,exprstate函数用于解析普通的赋值表达式。lua语言支持多变量赋值。本文先从单变量赋值表达式讲起。

  1. a =
  2. b =
  3. c = a + b

对于简单的两个数的求和过程,lua源码是如何解析的呢?

首先,当词法分析获取到第一个token为‘a’的类型是TK_NAME(285),然后是chunk函数,statment函数,走到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), ); /* call statement uses no results */

else { /* stat -> assignment */

  v.prev = NULL;

  assignment(ls, &v, ); } }

其中,LHS_assign是一个包含expdesc结构体的链表,拥有指向另一个变量的指针*prev。每个expdesc代表一个变量,该链表用于保存等式左边的所有变量。

表达式分割的函数最终会从primaryexp进入到prefixexp函数里,由于当前的token值为TK_NAME=285,走到singlevar,即表示单变量的解析函数。

  1. static void singlevar (LexState *ls, expdesc *var) {
  2. TString *varname = str_checkname(ls);
  3. FuncState *fs = ls->fs;
  4. if (singlevaraux(fs, varname, var, ) == VGLOBAL)
  5. var->u.s.info = luaK_stringK(fs, varname); /* info points to global name *//*指向变量名在寄存器的索引值*/
  6. }

luaK_stringK的最终返回值为变量名'a'在fs->f->k这个数组中的索引值,保存在var->u.s.info。这个值在生成字节码时会用到。

然后是singlevaraux,第一次进入改函数,fs != NULL,进入else,在当前层次查找变量,找不到自动递归到上层,即fs->prev指向的上层fs,最后返回VGLOBAL。

  1. static int singlevaraux (FuncState *fs, TString *n, expdesc *var, int base) {
  2. if (fs == NULL) { /* no more levels? */
  3. init_exp(var, VGLOBAL, NO_REG); /* default is global variable */
  4. return VGLOBAL;
  5. }
  6. else {
  7. int v = searchvar(fs, n); /* look up at current level */
  8. if (v >= ) {
  9. init_exp(var, VLOCAL, v);
  10. if (!base)
  11. markupval(fs, v); /* local will be used as an upval */
  12. return VLOCAL;
  13. }
  14. else { /* not found at current level; try upper one */
  15. if (singlevaraux(fs->prev, n, var, ) == VGLOBAL)/**/
  16. return VGLOBAL;
  17. var->u.s.info = indexupvalue(fs, n, var); /* else was LOCAL or UPVAL */
  18. var->k = VUPVAL; /* upvalue in this level */
  19. return VUPVAL;
  20. }
  21. }
  22. }

最后通过luaK_stringK函数调用addK函数对变量‘a’进行处理。 luaH_set()一开始调用luaH_get()在全局变量表中查找该value是否存在, 存在则直接返回值.不存在则调用newkey()完成添加动作。最终变量名'a'会放到f->k这个数组中,并且会返回对应的索引,然后讲索引保存到字节码中。

  1. static int addk (FuncState *fs, TValue *k, TValue *v) {
  2. lua_State *L = fs->L;
  3. TValue *idx = luaH_set(L, fs->h, k);
  4. Proto *f = fs->f;
  5. int oldsize = f->sizek;
  6. if (ttisnumber(idx)) {
  7. lua_assert(luaO_rawequalObj(&fs->f->k[cast_int(nvalue(idx))], v));
  8. return cast_int(nvalue(idx));
  9. }
  10. else { /* constant not found; create a new entry */
  11. setnvalue(idx, cast_num(fs->nk));
  12. luaM_growvector(L, f->k, fs->nk, f->sizek, TValue,
  13. MAXARG_Bx, "constant table overflow");
  14. while (oldsize < f->sizek) setnilvalue(&f->k[oldsize++]);
  15. setobj(L, &f->k[fs->nk], v);
  16. luaC_barrier(L, f, v);
  17. return fs->nk++;
  18. }
  19. }

这时候,回到exprstat函数,等号左边的变量名处理完了。然后处理等号右边的值,调用assignment函数赋值。如果下一个token是逗号,说明是多变量赋值。本例中是单变量。nexps = explist1(ls, &e);用于处理等号右边的值的表达式,将结果存入&e中,并返回右值的个数,然后判断是表达式的个数是否和右值的个数相等。

  1. static void assignment (LexState *ls, struct LHS_assign *lh, int nvars) {
  2. expdesc e;
  3. check_condition(ls, VLOCAL <= lh->v.k && lh->v.k <= VINDEXED,
  4. "syntax error");
  5. if (testnext(ls, ',')) { /* assignment -> `,' primaryexp assignment */
  6. struct LHS_assign nv;
  7. nv.prev = lh;
  8. primaryexp(ls, &nv.v);
  9. if (nv.v.k == VLOCAL)
  10. check_conflict(ls, lh, &nv.v);
  11. assignment(ls, &nv, nvars+);
  12. }
  13. else { /* assignment -> `=' explist1 */
  14. int nexps;
  15. checknext(ls, '=');
  16. nexps = explist1(ls, &e);/*解析等号右边的值*/
  17. if (nexps != nvars) {
  18. adjust_assign(ls, nvars, nexps, &e);
  19. if (nexps > nvars)
  20. ls->fs->freereg -= nexps - nvars; /* remove extra values */
  21. }
  22. else {
  23. luaK_setoneret(ls->fs, &e); /* close last expression */
  24. luaK_storevar(ls->fs, &lh->v, &e);/*生成指令*/
  25. return; /* avoid default */
  26. }
  27. }
  28. init_exp(&e, VNONRELOC, ls->fs->freereg-); /* default assignment */
  29. luaK_storevar(ls->fs, &lh->v, &e);
  30. }

表达式分析函数是通过subexpr函数进行递归下降分析。这个知识点以后会专门来讲,现在由于只是简单赋值,不会涉及到运算符优先级的问题。本例中最终调用的是simpleexp函数,进入case TK_NUMBER:

  1. static void simpleexp (LexState *ls, expdesc *v) {
  2. /* simpleexp -> NUMBER | STRING | NIL | true | false | ... |
  3. constructor | FUNCTION body | primaryexp */
  4. switch (ls->t.token) {
  5. case TK_NUMBER: {
  6. init_exp(v, VKNUM, );/*传入寄存器位置为0*/
  7. v->u.nval = ls->t.seminfo.r;/*将浮点数1.0赋值给v->u.navl*/
  8. break;
  9. }
  10. case …………………………
  11. }
  12. luaX_next(ls);
  13. }

最后,luaK_storevar函数会将右值保存在寄存器,并生成相应的指令码

  1. void luaK_storevar (FuncState *fs, expdesc *var, expdesc *ex) {
  2. switch (var->k) {
  3. case VLOCAL: {
  4. freeexp(fs, ex);
  5. exp2reg(fs, ex, var->u.s.info);
  6. return;
  7. }
  8. case VUPVAL: {
  9. int e = luaK_exp2anyreg(fs, ex);
  10. luaK_codeABC(fs, OP_SETUPVAL, e, var->u.s.info, );
  11. break;
  12. }
  13. case VGLOBAL: {/*本例中是全局变量*/
  14. int e = luaK_exp2anyreg(fs, ex);//返回寄存器索引
  15. luaK_codeABx(fs, OP_SETGLOBAL, e, var->u.s.info);//生成指令
  16. break;
  17. }
  18. case VINDEXED: {
  19. int e = luaK_exp2RK(fs, ex);
  20. luaK_codeABC(fs, OP_SETTABLE, var->u.s.info, var->u.s.aux, e);
  21. break;
  22. }
  23. default: {
  24. lua_assert(); /* invalid var kind to store */
  25. break;
  26. }
  27. }
  28. freeexp(fs, ex);
  29. }

最后调用luaK_codeABx生成指令,关于指令问题,下回再叙。

  1. int luaK_codeABx (FuncState *fs, OpCode o, int a, unsigned int bc) {
  2. lua_assert(getOpMode(o) == iABx || getOpMode(o) == iAsBx);
  3. lua_assert(getCMode(o) == OpArgN);
  4. return luaK_code(fs, CREATE_ABx(o, a, bc), fs->ls->lastline);
  5. }

lua源码学习篇三:赋值表达式解析的流程的更多相关文章

  1. lua源码学习篇一:环境部署

    研究生即将毕业,答辩完成后,这几天有些时间.开始写一些自己的东西,记录自己的学习历程. --前言 本着学习和交流的原则,本文的内容仅供参考,而不是权威版本,如有任何问题,欢迎指出. --声明 跨专业考 ...

  2. lua源码学习篇四:字节码指令

    在llimits.h文件中定义了指令的类型.其实就是32个字节. typedef lu_int32 Instruction; 上节说到变量最终会存入proto的数组k中,返回的索引放在expdesc ...

  3. lua源码学习篇二:语法分析

    一步步调试,在lparser.c文件中luaY_parser函数是语法分析的重点函数,词法分析也是在这个过程中调用的.在这个过程中,用到一些数据结构,下面会详细说. Proto *luaY_parse ...

  4. mongo源码学习(三)请求接收传输层

    在上一篇博客中(mongo源码学习(二)db.cpp之mongoDbMain方法分析),我们把db.cpp中的mongoDbMain的执行过程分析了一下,最后会调用initAndListen(serv ...

  5. jquery源码学习笔记三:jQuery工厂剖析

    jquery源码学习笔记二:jQuery工厂 jquery源码学习笔记一:总体结构 上两篇说过,query的核心是一个jQuery工厂.其代码如下 function( window, noGlobal ...

  6. Android7.0 Phone应用源码分析(三) phone拒接流程分析

    本文主要分析Android拒接电话的流程,下面先来看一下拒接电话流程时序图 步骤1:滑动按钮到拒接图标,会调用到AnswerFragment的onDecline方法 com.android.incal ...

  7. <转>赋值表达式解析的流程

    转自:http://www.cnblogs.com/nazhizq/p/6520072.html 上节说到表达式的解析问题,exprstate函数用于解析普通的赋值表达式.lua语言支持多变量赋值.本 ...

  8. Java集合源码学习(三)LinkedList分析

    前面学习了ArrayList的源码,数组是顺序存储结构,存储区间是连续的,占用内存严重,故空间复杂度很大.但数组的二分查找时间复杂度小,为O(1),数组的特点是寻址容易,插入和删除困难.今天学习另外的 ...

  9. Java集合源码学习(三)LinkedList

    前面学习了ArrayList的源码,数组是顺序存储结构,存储区间是连续的,占用内存严重,故空间复杂度很大.但数组的二分查找时间复杂度小,为O(1),数组的特点是寻址容易,插入和删除困难.今天学习另外的 ...

随机推荐

  1. ajax异步 —— javascript

    目录 ajax是什么 原生ajax jquery ajax ajax跨域 ajax是什么 作用:不必重新加载整个页面,更新部分页面内容. 大概使用过程:通过后台提供的数据接口,ajax获取数据,动态修 ...

  2. python-scp-上传文件到服务器

    python中使用scp,将文件上传到服务器 def ssh_scp_put(ip, username, password, local_file, remote_path): "" ...

  3. PHP WEB 引擎缓存加速优化

    PHP 缓存加速器介绍 操作码缓存 请求一个 PHP 程序时,PHP 引擎会解析程序,并且将编译码作为特定操作码.这是要执行的代 码的一种二进制表示形式.随后,此操作码有 PHP 引擎执行并丢弃.操作 ...

  4. awk 快速批量kill 进程

    ps -ef|grep aaa|grep -v grep|awk '{print "kill -9 " $2}' |sh

  5. ARM系统时钟初始化

    2440时钟体系,12MHz的晶振 6410时钟体系,12MHz的晶振 210时钟体系,24MHz晶振 时钟初始化:1.设置locktime 2.设置分频系数 4.设置CPU到异步工作模式 3.设置f ...

  6. 一个web应用的诞生(7)

    现在所有的Py代码均写在default.py文件中,很明显这种方法下,一旦程序变的负责,那么无论对于开发和维护来说,都会带来很多问题. Flask框架并不强制要求项目使用特定的组织结构,所以这里使用的 ...

  7. Runnable和Thread比较

    看代码 public static void main(String[] args) { // TODO Auto-generated method stub new MyThread().start ...

  8. python实例31[列出目录下所有的文件到txt]

    代码: (使用os.listdir) import os def ListFilesToTxt(dir,file,wildcard,recursion):     exts = wildcard.sp ...

  9. 设计模式-Interpreter(行为模式) 使用解释器给用户提供一个一门定义语言的语法表示的解释器,通过该解释器解释语言中的句子。

    //以下代码来源: 设计模式精解-GoF 23种设计模式解析附C++实现源码 //Context.h #pragma once class Context { public: Context(); ~ ...

  10. ideal 工具jdk环境配置

    1.File  >>  Other Settings >> Default Project Structure ... 2.Project  >>  jdk_vie ...