婷婷综合国产,91蜜桃婷婷狠狠久久综合9色 ,九九九九九精品,国产综合av

主頁 > 知識庫 > lua中賦值類型代碼詳解

lua中賦值類型代碼詳解

熱門標簽:電話機器人每天搜索多少次 海口智能語音電銷機器人好用嗎 免費門店地圖標注注冊入駐 衡水外呼線路解決 外呼系統一天耗費多少流量 艾比利外呼系統 昆明電話外呼系統好么 陜西便宜電銷機器人軟件 杞縣地圖標注app

我們來看看lua vm在解析下面源碼并生成bytecode時的整個過程:

 foo = "bar"
 local a, b = "a", "b"
 foo = a

首先我們先使用ChunkySpy這個工具來看看vm最終會具體生成什么樣的vm instructions

在這里,開頭為[數字]的行是vm真正生成的字節碼,我們看到一共生成了六行字節碼。首先loadk將常量表中下標為1的常量即"bar"賦給寄存器0;然后setglobal將寄存器0的內容賦給全局變量表中下標為0的全局變量即foo;loadk再將"a"和"b"分別賦值給了寄存器0、1,在這里寄存器0和1分別表示當前函數的local變量即變量a和b;最后setglobal將變量a的值賦給了全局變量foo;最后一個return01是vm在每一個chunk最后都會生成了,并沒有什么用。現在應該比較清除的了解了lua vm生成的字節碼的含義了,接下來我們看看vm是怎樣且為什么生成這些個字節碼的。

當我們用luaL_dofile函數執行這個lua腳本源碼時會有兩個階段,第一個是將腳本加載進內存,分詞解析并生成字節碼并將其整個包裹為main chunk放于lua stack棧頂,第二是調用lua_pcall執行這個chunk,這里我們只會分析第一個過程。

前面幾篇文章說了,當dofile時會跑到一個叫做luaY_parser的函數中,

Proto *luaY_parser (lua_State *L, ZIO *z, Mbuffer *buff, const char *name) {
 struct LexState lexstate;
 struct FuncState funcstate;
 -- ... ...
 funcstate.f->is_vararg = VARARG_ISVARARG; /* main func. is always vararg */
 luaX_next(lexstate); /* read first token */
 chunk(lexstate);
 -- ... ...
 return funcstate.f;
}

函數luaY_parser前面兩行定義了LexState和FuncState結構體變量,其中LexState不僅用于保存當前的詞法分析狀態信息,而且也保存了整個編譯系統的全局狀態,FuncState結構體來保存當前函數編譯的狀態數據。在lua源碼中都會有一個全局的函數執行體,即為main func,在開始解析的時候當前的函數必然是main func函數,此時第三行的funcstate表示了這個函數的狀態,由于lua規定這個函數必然會接收不定參數因此第五行將is_vararg標識設為VARARG_ISVARARG。接著第六行luaX_next解析文件流分離出第一個token,將其保存在lexstate的t成員中,此時t為“foo”全局變量。接著調用了chunk函數,這里開始了遞歸下降解析的全部過程:

static void chunk (LexState *ls) {
 /* chunk -> { stat [`;'] } */
 int islast = 0;
 enterlevel(ls);
 while (!islast  !block_follow(ls->t.token)) {
  islast = statement(ls);//遞歸下降點
  testnext(ls, ';');
  lua_assert(ls->fs->f->maxstacksize >= ls->fs->freereg 
        ls->fs->freereg >= ls->fs->nactvar);
  ls->fs->freereg = ls->fs->nactvar; /* free registers */
 }
 leavelevel(ls);
}

lua是有作用域層次概念的,因此當進入一個層次時會調用enterlevel函數,離開當前層次則會調用leavelevel函數。首先進入while循環,當前token為“foo”,這既不是終結標志也不是一個block開始的詞素,因此會進入statement函數,statement函數主體是一個長長的switch...case...代碼結構,根據第一個token進入不同的調用解析分支。在我們這個例子中會進入default分支:

static int statement (LexState *ls) {
 -- ... ...
 switch (ls->t.token) {
  case TK_IF: { /* stat -> ifstat */
   ifstat(ls, line);
   return 0;
  }
  case TK_WHILE: { /* stat -> whilestat */
   whilestat(ls, line);
   return 0;
  }
  -- ... ...
  default: {
   exprstat(ls);
   return 0; /* to avoid warnings */
  }
 }
}

進入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結構體是為了處理多變量賦值的情況的,例如a,b,c = ...。在LHS_assign中成員v類型為expdesc描述了等號左邊的變量,詳情可見上篇文章里對expdesc的介紹。接下來進入primaryexp,來獲取并填充“foo”變量的expdesc信息,這會接著進入prefixexp函數中

 static void prefixexp (LexState *ls, expdesc *v) {
  /* prefixexp -> NAME | '(' expr ')' */
  switch (ls->t.token) {
   case '(': {
    int line = ls->linenumber;
    luaX_next(ls);
    expr(ls, v);
    check_match(ls, ')', '(', line);
    luaK_dischargevars(ls->fs, v);
    return;
   }
   case TK_NAME: {
    singlevar(ls, v);
    return;
   }
   default: {
    luaX_syntaxerror(ls, "unexpected symbol");
    return;
   }
  }
 }

由于當前token是“foo”,因此進入TK_NAME分支,調用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 */
}
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;
  }
 }

在singlevaraux函數中會判斷變量是local、upvalue還是global的。如果fs為null了則說明變量為全局的,否則進入searchvar在當前的函數局部變量數組中查找,否則根據fs的prev成員取得其父函數的FuncState并傳入singlevaraux中遞歸查找,如果前面的都沒滿足則變量為upvlaue。此例中進入第21行中,由于fs已經指向了main func因此其prev為null,“foo”判定為global并返回到exprstate函數中。在取得了“foo”的信息后,因為“foo”不是函數調用,因此接著進入assignment函數中

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

在assignment函數中首先判斷下一個token是否為“,",此例中不是則說明是單變量的賦值,接著check下一個token為”=“,成立,接著調用explist1判斷等號右邊有幾個值,此例為1個,然后會判斷左邊的變量數是否等于右邊的值數,不等于則進入adjust_assign函數進行調整,此例是相等的因此依次進入luaK_setoneret和luaK_storevar函數。在luaK_storevar中首先進入int e = luaK_exp2anyreg(fs, ex);函數luaK_exp2anyreg的K代表了此函數是字節碼相關的函數,ex為值”bar“,這個函數又調用了discharge2reg,根據ex的類型來生成不同的字節碼:

static void discharge2reg (FuncState *fs, expdesc *e, int reg) {
 luaK_dischargevars(fs, e);
 switch (e->k) {
  case VNIL: {
   luaK_nil(fs, reg, 1);
   break;
  }
  case VFALSE: case VTRUE: {
   luaK_codeABC(fs, OP_LOADBOOL, reg, e->k == VTRUE, 0);
   break;
  }
  case VK: {
   luaK_codeABx(fs, OP_LOADK, reg, e->u.s.info);
   break;
  }
//... ...
}

由于”bar“是常量因此調用luaK_codeABx函數生成loadk字節碼。reg為保存載入的常量值的寄存器號,e->u.s.info根據不同類型值代表不同含義,根據注釋我們知道此時info為常量數組的下標。

typedef enum {
 //... ...
 VK,    /* info = index of constant in `k' */
 VKNUM,  /* nval = numerical value */
 VLOCAL,  /* info = local register */
 VGLOBAL,  /* info = index of table; aux = index of global name in `k' */
 //... ...
} expkind;

生成了loadk后返回到上面的函數中接著進入luaK_codeABx(fs, OP_SETGLOBAL, e, var->u.s.info);其中e為luaK_exp2anyreg的返回值表示常量保存在的寄存器標號,info根據注釋當為global類型時表示global table的相應下標,因此luaK_codeABx函數將生成setglobal字節碼,將剛剛用loadk將常量加載到寄存器中的值保存到global table相應的位置上。因此foo = "bar"語句就完整的生成了相應的字節碼了。

接下來將生成local a,b = "a","b"語句的字節碼了。過程大致相同,不同的是a,b是local變量且這個賦值語句是多變量賦值語句,因此前面的函數會用LHS_assign鏈表將a,b變量連接起來。如圖所示:

以上所述就是本文都全部內容了,希望大家能夠喜歡。

您可能感興趣的文章:
  • Lua基礎教程之賦值語句、表達式、流程控制、函數學習筆記
  • Lua中的基本數據類型詳細介紹
  • Lua變量類型簡明總結
  • Lua數據類型介紹
  • Lua教程(三):值與類型介紹

標簽:臨滄 昌都 海口 西寧 宿遷 南京 泰安 營口

巨人網絡通訊聲明:本文標題《lua中賦值類型代碼詳解》,本文關鍵詞  lua,中,賦值,類型,代碼,詳解,;如發現本文內容存在版權問題,煩請提供相關信息告之我們,我們將及時溝通與處理。本站內容系統采集于網絡,涉及言論、版權與本站無關。
  • 相關文章
  • 下面列出與本文章《lua中賦值類型代碼詳解》相關的同類信息!
  • 本頁收集關于lua中賦值類型代碼詳解的相關信息資訊供網民參考!
  • 推薦文章
    主站蜘蛛池模板: 军事| 佛教| 安陆市| 新平| 娱乐| 根河市| 罗甸县| 弥渡县| 洪江市| 铜川市| 集贤县| 大庆市| 昌江| 凌云县| 肇州县| 林周县| 绥芬河市| 民县| 绥江县| 合川市| 嵊州市| 绥芬河市| 南漳县| 筠连县| 会理县| 蕉岭县| 墨玉县| 巩留县| 蓝山县| 鞍山市| 科技| 邢台市| 正安县| 赫章县| 石首市| 尉犁县| 饶平县| 华阴市| 鹤岗市| 乌拉特前旗| 天等县|