转自:http://www.cnblogs.com/nazhizq/p/6520072.html

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

a = 1
b = 2
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), 1); /* call statement uses no results */else { /* stat -> assignment */v.prev = NULL; assignment(ls, &v, 1); } }

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

表达式分割的函数最终会从primaryexp进入到prefixexp函数里,由于当前的token值为TK_NAME=285,走到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 *//*指向变量名在寄存器的索引值*/
}

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

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

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;}}
}

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

static int addk (FuncState *fs, TValue *k, TValue *v) {lua_State *L = fs->L;TValue *idx = luaH_set(L, fs->h, k);Proto *f = fs->f;int oldsize = f->sizek;if (ttisnumber(idx)) {lua_assert(luaO_rawequalObj(&fs->f->k[cast_int(nvalue(idx))], v));return cast_int(nvalue(idx));}else {  /* constant not found; create a new entry */setnvalue(idx, cast_num(fs->nk));luaM_growvector(L, f->k, fs->nk, f->sizek, TValue,MAXARG_Bx, "constant table overflow");while (oldsize < f->sizek) setnilvalue(&f->k[oldsize++]);setobj(L, &f->k[fs->nk], v);luaC_barrier(L, f, v);return fs->nk++;}
}

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

static void assignment (LexState *ls, struct LHS_assign *lh, int nvars) {expdesc e;check_condition(ls, VLOCAL <= lh->v.k && lh->v.k <= VINDEXED,"syntax error");if (testnext(ls, ',')) {  /* assignment -> `,' primaryexp assignment */struct LHS_assign nv;nv.prev = lh;primaryexp(ls, &nv.v);if (nv.v.k == VLOCAL)check_conflict(ls, lh, &nv.v);assignment(ls, &nv, nvars+1);}else {  /* assignment -> `=' explist1 */int nexps;checknext(ls, '=');nexps = explist1(ls, &e);/*解析等号右边的值*/if (nexps != nvars) {adjust_assign(ls, nvars, nexps, &e);if (nexps > nvars)ls->fs->freereg -= nexps - nvars;  /* remove extra values */}else {luaK_setoneret(ls->fs, &e);  /* close last expression */luaK_storevar(ls->fs, &lh->v, &e);/*生成指令*/return;  /* avoid default */}}init_exp(&e, VNONRELOC, ls->fs->freereg-1);  /* default assignment */luaK_storevar(ls->fs, &lh->v, &e);
}

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

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

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

void luaK_storevar (FuncState *fs, expdesc *var, expdesc *ex) {switch (var->k) {case VLOCAL: {freeexp(fs, ex);exp2reg(fs, ex, var->u.s.info);return;}case VUPVAL: {int e = luaK_exp2anyreg(fs, ex);luaK_codeABC(fs, OP_SETUPVAL, e, var->u.s.info, 0);break;}case VGLOBAL: {/*本例中是全局变量*/int e = luaK_exp2anyreg(fs, ex);//返回寄存器索引luaK_codeABx(fs, OP_SETGLOBAL, e, var->u.s.info);//生成指令break;}case VINDEXED: {int e = luaK_exp2RK(fs, ex);luaK_codeABC(fs, OP_SETTABLE, var->u.s.info, var->u.s.aux, e);break;}default: {lua_assert(0);  /* invalid var kind to store */break;}}freeexp(fs, ex);
}

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

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

转赋值表达式解析的流程相关推荐

  1. 【Java工具类】学会MVEL2.0,表达式解析再不怕

    文章目录 常见的表达式引擎 一.什么是MVEL? 二.快速入门 三.语法 1.基本语法 1.1 简单属性表达式 1.2 布尔表达式 1.3 复合语句表达式 1.4 返回值 2. 操作符 2.1 一元操 ...

  2. python赋值表达式_6. 表达式

    6.表达式¶ 本章将解释 Python 中组成表达式的各种元素的的含义. 语法注释: 在本章和后续章节中,会使用扩展 BNF 标注来描述语法而不是词法分析. 当(某种替代的)语法规则具有如下形式 na ...

  3. Postgresql源码(85)查询执行——表达式解析器分析(select 1+1如何执行)

    相关 <Postgresql源码(61)查询执行--最外层Portal模块> <Postgresql源码(62)查询执行--子模块ProcessUtility> <Pos ...

  4. 黑马程序员C语言基础(第五天)运算符与表达式、程序流程结构、数组和字符串、函数

    https://www.bilibili.com/video/BV15W411K7k6?p=93&spm_id_from=pageDriver 黑马程序员C语言基础(第五天)运算符与表达式.程 ...

  5. PostgreSQL 源码解读(160)- 查询#80(如何实现表达式解析)

    本节介绍了PostgreSQL如何解析查询语句中的表达式列并计算得出该列的值.表达式列是指除关系定义中的系统列/定义列之外的其他投影列.比如: testdb=# create table t_expr ...

  6. 凹语言版本 yacc 简介 - 以表达式解析为例

    yacc 是用于生成语法解析器的程序,是编译器爱好者的工具.凹语言的 yacc 从 goyacc 移植而来,目前可以初步支持输出 凹语言 版本解析器代码.本文以以表达式解析为例展示下用法. 完整的例子 ...

  7. 表达式解析器(MVEL)

    Jeval 在运行时解析计算静态和动态表达式:支持数学,布尔,字符串,函数表达式:支持大部分的数学和布尔运算符:支持自定义函数:支持嵌套函数:支持解析:支持自定义变量解析器: 官网:http://je ...

  8. 【68】JS(4)——表达式和语句②流程控制语句(1)条件分支语句

    本篇学习目标: 1.掌握各个条件分支语句的用法: 2.理解常见案例实现思路. 目录 一.if 语句 1. 介绍 2. 语法 3. 注意事项 (1)实现选择功能 (2)殊途同归 (3)不建议省略大括号 ...

  9. 海象赋值表达式减少重复变量

    海象赋值表达式减少重复变量 1.概述 在写代码过程中,定义局部变量操作频率非常高.如果在一个函数中定义了太多的变量会让程序不易维护且阅读起来困难. 当我们在函数里面要定义变量时,而且这个变量要在函数里 ...

最新文章

  1. 《深入理解计算机系统》学习心得二:关于show-bytes的 学习
  2. Java 理论与实践: 正确使用 Volatile 变量
  3. 怎么在我的世界服务器注册,我的世界服务器怎么注册
  4. mysql jar jdk1.6_Windows下JDK1.6+MySQL+MyEclipse开发环境的配置
  5. opencv3.1.0+VS2013 环境配置
  6. day11【过渡】SpringBoot
  7. 前端验证码绘制(canvas)
  8. 分布式数据库的优越性
  9. MongoDB 数据库_集合_文档 操作
  10. 硅谷课堂 13_公众号点播课程和直播管理
  11. web前端如何才能成为架构师
  12. pcb成型板aoi检测_PCB设备:一款新的自动光学检测(AOI)系统
  13. pdf文档动态插入水印,45度角,位于文档中央,可插入中文
  14. blowfish java_blowfish加密算法
  15. RAID技术全解图解-RAID0、RAID1、RAID5、RAID100【转】
  16. 文件末尾eof_什么是EOF(文件末尾)? PHP,C ++,C,Python,Java的示例
  17. 华米手表2 是android,手表 | 续航怪兽 华米AMAZFIT智能运动手表2代深度评测(二)...
  18. 麒麟操作系统(Linux)使用和维护:进程相关的操作命令
  19. 必看!开源众包最新IM功能上线了。实时项目跟进不再烦恼!
  20. 控件注册失败原因汇集

热门文章

  1. js和 jquery对象
  2. 【原创】源智工作流聚合步骤模型
  3. 一起学 c++(二)
  4. 什么才是软件开发的葵花宝典?
  5. 【Android 逆向】Dalvik 函数抽取加壳 ( 类加载流程分析 | ClassLoader#loadClass 函数分析 | BaseDexClassLoader#findClass 分析 )
  6. 【开发环境】Windows 中安装 Python 各个版本 ( 下载 Python 各版本 SDK | 安装 Python )
  7. 【Java 并发编程】线程池机制 ( 线程池状态分析 | 线程池状态转换 | RUNNING | SHUTDOWN | STOP | TIDYING | TERMINATED )
  8. 【计算机网络】网络层 : IP 组播 ( IP 数据报传输方式 | 组播 IP 地址 | 组播 MAC 地址 | IGMP 协议 | 组播路由选择协议 )
  9. Bugku——Web——矛盾
  10. 关于String函数