上節說到表達式的解析問題,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); }