<转>赋值表达式解析的流程
转自:http://www.cnblogs.com/nazhizq/p/6520072.html
上节说到表达式的解析问题,exprstate函数用于解析普通的赋值表达式。lua语言支持多变量赋值。本文先从单变量赋值表达式讲起。
a =
b =
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,即表示单变量的解析函数。
static void singlevar (LexState *ls, expdesc *var) {
TString *varname = str_checkname(ls);
FuncState *fs = ls->fs;
if (singlevaraux(fs, varname, var, ) == 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 >= ) {
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, ) == 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+);
}
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-); /* 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*/
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, );
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(); /* 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);
}
<转>赋值表达式解析的流程的更多相关文章
- lua源码学习篇三:赋值表达式解析的流程
上节说到表达式的解析问题,exprstate函数用于解析普通的赋值表达式.lua语言支持多变量赋值.本文先从单变量赋值表达式讲起. a = b = c = a + b 对于简单的两个数的求和过程,lu ...
- java字符串应用之表达式解析器
一.表达式的组成 1.数字 2.运算符:+ - / * ^ % = 3.圆括号 4.变量二.运算符优先级 由高到低分别为:+-(正负号).^.*/%.+-.= 优先 ...
- 【前端知识体系-JS相关】你真的了解JavaScript编译解析的流程吗?
1. JS编译解析的流程 1.1 JS运行分三步 语法分析(通篇扫描是否有语法错误),预编译(发生在函数执行的前一刻),解释执行(一行行执行). 1.2 预编译执行分五步 创建AO对象(Activat ...
- python爬虫---爬虫的数据解析的流程和解析数据的几种方式
python爬虫---爬虫的数据解析的流程和解析数据的几种方式 一丶爬虫数据解析 概念:将一整张页面中的局部数据进行提取/解析 作用:用来实现聚焦爬虫的吧 实现方式: 正则 (针对字符串) bs4 x ...
- SPEL 表达式解析
Spring Expression Language 解析器 SPEL解析过程 使用 ExpressionParser 基于 ParserContext 将字符串解析为 Expression, Exp ...
- JavaScript—赋值表达式
赋值表达式的运算顺序是从右到左的,因此,可以通过以下方法对多个变量赋值 1 i=j=k=0;//也就是把三个变量初始化为0 赋值表达式中的递增和递减 n++和++n的区别: 简单来说,根据 ...
- Java学习随笔2:Java复合赋值表达式的小问题
问题:i += j只是i = i + j的简写么? 答案:非也!看下面的程序: int i = 5; long j = 8; i += j; // 可以通过编译且结果正确 i = i + j; // ...
- JavaScript—赋值表达式-1
赋值表达式的运算顺序是从右到左的,因此,可以通过以下方法对多个变量赋值 i=j=k=0;//也就是把三个变量初始化为0 赋值表达式中的递增和递减 n++和++n的区别: 简单来说,根据运算顺序,n++ ...
- C++赋值运算符与赋值表达式
赋值运算符 赋值符号“=”就是赋值运算符,它的作用是将一个数据赋给一个变量.如“a=3”的作用是执行一次赋值操作(或称赋值运算).把常量3赋给变量a.也可以将一个表达式的值赋给一个变量. 赋值过程中的 ...
随机推荐
- Js计算指定日期加上多少天,加多少月,加多少年的日期
function DateAdd(interval,number,date) { /* * 功能:实现VBScript的DateAdd功能. * 参数:interval,字符串表达式,表示要添加的时间 ...
- C语言学习 - 字节对齐
字节对齐 字节对齐就是数据在内存中的位置. 假设一个变量的内存地址正好位于它长度的整数倍,他就被称做自然对齐.比方在32位cpu下.假设一个整型变量的地址为0x00000004,那它就是自然对齐的. ...
- Android -- onMeasure
onMeasure调用次数 当Activity获取焦点的时候,它就需要绘制布局.Android框架会处理绘制过程,但这个Activity必须提供它布局树的根节点. 绘制过程是从布局的根节点开始的.这个 ...
- Zend Studio 实用快捷键大全
编辑功能 组合键 实现功能 适用条件 Ctrl+/ 单行注释.当前为php代码时,则在光标所在行添加双斜杠行注释,选择多行则每一行都添加双斜杠:而当代码为html时则在行前后添加<!-- --& ...
- C语言变长数组data[0]【总结】
1.前言 今天在看代码中遇到一个结构中包含char data[0],第一次见到时感觉很奇怪,数组的长度怎么可以为零呢?于是上网搜索一下这样的用法的目的,发现在linux内核中,结构体中经常用到data ...
- PHP 自定义方法实现数组合并
在PHP中提供了强大的数组功能,对于数组的合并也提供了两个方法:array_merge 和 array_merge_recursive 但对于我们千变万化的业务来说这些内置的方法并不完全能满足我们的要 ...
- sublime同步文件与siderbar
有时候,打开了sider bar,想和Eclipse.idea一样,每打开一个tab,就可以在左侧的sider bar 目录上面看到我当前的位置,于是找到了一个插件. SyncedSideBar 安装 ...
- CSDN博客QQ加群、微信
对于QQ加群我一直是拒绝的,不是自己摆姿态,而是实在没有这种影响力和能力.然而今日一朋友的话改变了我这种想法,所以尝试在CSDN博客首页上增加QQ加群功能.此博客也就权当一个记录(主要是为了上传一张二 ...
- android中使用SharedPreferences存储数据
使用SharedPreferences存储数据还是比较简单的 1.添加或修改数据(没有数据就添加,有数据就是修改): SharedPreferences.Editor editor = getShar ...
- 【树莓派】使用VNC进行远程控制
之前有进行过VNC以及xrdp连接树莓派,并成功了. 这里看到一篇比较新的,基于mac的连接,文章转载收藏,实践可参考. 这一课里我们将学习如何在树莓派上安装和使用VNC.它可以使你通过图形界面的方式 ...