lua源碼學習篇三:賦值表達式解析的流程


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

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM