1. 程式人生 > >lua函數定義

lua函數定義

也有 它的 block desc 般的 osi oba () tar

FuncState

proto結構數組保存函數原型信息;prev保存父函數體指針;actvar保存定義的局部變量;upvalues保存upvalue

Lua源碼中,專門有一個結構體FuncState用來保存函數相關的信息.其實,即使沒有創建任何函數,對於Lua而言也有一個最外層的FuncState數據.這個結構體的定義:

typedef struct FuncState {
  Proto *f;  /* current function header */
  Table *h;  /* table to find (and reuse) elements in  */
  struct FuncState *prev;  /* enclosing function */
  struct LexState *ls;  /* lexical state */
  struct lua_State *L;  /* copy of the Lua state */
  struct BlockCnt *bl;  /* chain of current blocks */
  int pc;  /* next position to code (equivalent to `ncode‘) */
  int lasttarget;   /* `pc‘ of last `jump target‘  */
  int jpc;  /* list of pending jumps to `pc‘ */
  int freereg;  /* first free register */
  int nk;  /* number of elements in `k‘  */
  int np;  /* number of elements in `p‘  */
  short nlocvars;  /* number of elements in `locvars‘ */
  lu_byte nactvar;  /* number of active local variables  */
  upvaldesc upvalues[LUAI_MAXUPVALUES];  /* upvalues */
  unsigned short actvar[LUAI_MAXVARS];  /* declared-variable stack */
} FuncState;

其中的Proto結構體數組用於保存函數原型信息,包括函數體代碼(opcode),之所以使用數組,是因為在某個函數內,可能存在多個局部函數.而prev指針就是指向這個函數的”父函數體”的指針.

比如以下代碼:

function fun()
   function test()
   end
end

那麽,在保存test函數原型的Proto數據就存放在保存fun函數的FuncState結構體的p數組中,反之,保存test函數的FuncState.prev指針就指向保存func函數的FuncState指針.

接著看Funcstate結構體的成員,actvar數組用於保存局部變量,比如函數的參數就是保存在這裏.另外還有一個存放upval值的upvalues數組.這裏有兩種不同的處理.如果這個upval是父函數內的局部變量,則生成的是MOVE指令用於賦值;如果對於父函數而言也是它的upval,則生成GET_UPVAL指令用於賦值.

當開始處理一個函數的定義時,首先調用open_func函數,創建一個新的Proto結構體用於保存函數原型信息,接著將該函數的FuncState的prev指針指向父函數.
最後當函數處理完畢時,調用pushclosure函數將這個新的函數的信息push到父函數的Proto數組中.

函數也是第一類值 可以存在變量裏

最後,由於函數在Lua中是所謂的”first class type”,所以其實以下兩段Lua代碼是等價的:

local function test()
  -- 可以test
end

--以上相當於 local test; test = function() ...  end

local test = function ()
   --不可以調用test 以為第一類之定義完成之後才可以使用
end

也就是說,其實是生成一段代碼,用於保存函數test的相關信息,之後再將這些信息賦值給變量test,這裏的test可以是local,也可以是global的,這一點跟一般的變量無異.

函數定義詞法分析

所以在與函數定義相關的詞法分析代碼中:

static void funcstat (LexState *ls, int line) {
  /* funcstat -> FUNCTION funcname body */
  int needself;
  expdesc v, b;
  luaX_next(ls);  /* skip FUNCTION */
  needself = funcname(ls, &v);
  body(ls, &b, needself, line);
  luaK_storevar(ls->fs, &v, &b);
  luaK_fixline(ls->fs, line);  /* definition `happens‘ in the first line */
}

上面的變量v首先在funcname函數中獲得該函數的函數名,變量b在進入函數body之後可以得到函數體相關的內容.在這之後的luaK_storevar調用,就是把b的值賦值給v,也就是前面提到的函數體賦值給函數名.

lua函數定義