Lua5.0 語法解析之路

上回說到 luaL_loadfile ,此次把它的調用展開到語法分析器 parser.數據結構

先一下它的函數定義閉包

LUALIB_API int luaL_loadfile (lua_State *L, const char *filename) {
  LoadF lf;
  int status, readstatus;
  int c;
  int fnameindex = lua_gettop(L) + 1;  /* index of filename on the stack */
  if (filename == NULL) {
    lua_pushliteral(L, "=stdin");
    lf.f = stdin;
  }
  else {
    lua_pushfstring(L, "@%s", filename);
    lf.f = fopen(filename, "r");
  }
  if (lf.f == NULL) return errfile(L, fnameindex);  /* unable to open file */
  c = ungetc(getc(lf.f), lf.f);
  if (!(isspace(c) || isprint(c)) && lf.f != stdin) {  /* binary file? */
    fclose(lf.f);
    lf.f = fopen(filename, "rb");  /* reopen in binary mode */
    if (lf.f == NULL) return errfile(L, fnameindex); /* unable to reopen file */
  }
  status = lua_load(L, getF, &lf, lua_tostring(L, -1));
  readstatus = ferror(lf.f);
  if (lf.f != stdin) fclose(lf.f);  /* close file (even in case of errors) */
  if (readstatus) {
    lua_settop(L, fnameindex);  /* ignore results from `lua_load' */
    return errfile(L, fnameindex);
  }
  lua_remove(L, fnameindex);
  return status;
}


上來先定義一個 LoadF 數據結構。函數

typedef struct LoadF {
  FILE *f;
  char buff[LUAL_BUFFERSIZE];
} LoadF;


能夠看到,裏面一個 FILE 指針,一個字符串 buffer。lua

這會兒還看不出來這個結構體是幹什麼的。spa

接下來給 LoadF 結構體 lf 變量賦值。線程

若是 filename 爲空,表示從 stdin 輸入。指針

不然,打開文件 filename。rest

這裏能夠看到,lf 變量的 f 字段被賦值爲打開的文件 FILE 。code

以後讀文件的第一個字符,判斷文件是否爲二進制文件,也就是已經編譯了的 Lua 字節碼文件。orm

若是是二進制文件,把原來的文件關閉。以二進制格式打開。

而後就是調用 lua_load 進行解析。

注意它的第二個和第三個參數分別爲 getF, lf,一下子下面會用到。

LUA_API int lua_load (lua_State *L, lua_Chunkreader reader, void *data,
                      const char *chunkname) {
  ZIO z;
  int status;
  int c;
  lua_lock(L);
  if (!chunkname) chunkname = "?";
  luaZ_init(&z, reader, data, chunkname);
  c = luaZ_lookahead(&z);
  status = luaD_protectedparser(L, &z, (c == LUA_SIGNATURE[0]));
  lua_unlock(L);
  return status;
}


lua_load 的第二個參數是個函數指針,用來進行塊讀取。

函數指針的簽名以下:

typedef const char * (*lua_Chunkreader) (lua_State *L, void *ud, size_t *sz);

這裏傳入的是 getF。

static const char *getF (lua_State *L, void *ud, size_t *size) {
  LoadF *lf = (LoadF *)ud;
  (void)L;
  if (feof(lf->f)) return NULL;
  *size = fread(lf->buff, 1, LUAL_BUFFERSIZE, lf->f);
  return (*size > 0) ? lf->buff : NULL;
}


這個函數作的事情就是從 LoadF 結構體的 FILE 指針 f 中讀取一個塊到其 buff 中。

這裏的 ud 就是上面 lua_load 傳入的第三個參數。

回到 lua_load 函數。

看到裏面有 lua_lock, lua_unlock,這是作線程同步的,如今不用管它。

接着調用 luaZ_init 來初始化 ZIO 結構體。

void luaZ_init (ZIO *z, lua_Chunkreader reader, void *data, const char *name) {
  z->reader = reader;
  z->data = data;
  z->name = name;
  z->n = 0;
  z->p = NULL;
}


能夠看到,在調用 luaZ_int 的時候把 reader 和 data 傳過去了。

reader 和 data 就是 getF 和 lf。

在 luaZ_fill 能夠看到一個 reader 的調用,

int luaZ_fill (ZIO *z) {
  size_t size;
  const char *buff = z->reader(NULL, z->data, &size);
  if (buff == NULL || size == 0) return EOZ;
  z->n = size - 1;
  z->p = buff;
  return char2int(*(z->p++));
}


這裏經過 reader 來獲取一個 buff。

這個 z->reader(NULL, z->data, &size) 的調用就至關於

getF(NULL, lf, &size)。

讀數據這塊就通了。

回到 lua_load 函數。

c = luaZ_lookahead(&z);

取第一個字符。

status = luaD_protectedparser(L, &z, (c == LUA_SIGNATURE[0]));

這裏取第一個字符後,就是用來和保存的字節碼的第一個字符進行對比。

編譯器 dump 出來的字節碼的頭幾個字符是 LUA_SIGNATURE

#define LUA_SIGNATURE "\033Lua" /* binary files start with "<esc>Lua" */

看下 luaD_protectedparser

int luaD_protectedparser (lua_State *L, ZIO *z, int bin) {
  struct SParser p;
  int status;
  ptrdiff_t oldtopr = savestack(L, L->top);  /* save current top */
  p.z = z; p.bin = bin;
  luaZ_initbuffer(L, &p.buff);
  status = luaD_rawrunprotected(L, f_parser, &p);
  luaZ_freebuffer(L, &p.buff);
  if (status != 0) {  /* error? */
    StkId oldtop = restorestack(L, oldtopr);
    seterrorobj(L, status, oldtop);
  }
  return status;
}


這裏上來定義告終構體變量 p。

/*
** Execute a protected parser.
*/
struct SParser {  /* data to `f_parser' */
  ZIO *z;
  Mbuffer buff;  /* buffer to be used by the scanner */
  int bin;
};


接下來給結構體賦值。

luaZ_initbuffer(L, &p.buff); // 初始化 buff。

status = luaD_rawrunprotected(L, f_parser, &p); // 調用

luaD_rawrunprotected 的定義以下:

int luaD_rawrunprotected (lua_State *L, Pfunc f, void *ud) {
  struct lua_longjmp lj;
  lj.status = 0;
  lj.previous = L->errorJmp;  /* chain new error handler */
  L->errorJmp = &lj;
  if (setjmp(lj.b) == 0)
    (*f)(L, ud);
  L->errorJmp = lj.previous;  /* restore old error handler */
  return lj.status;
}


這個主要是爲把調用放到一個 C 語言版的 try catch 裏去。

(*f)(L, ud); // 調用 f_parser

static void f_parser (lua_State *L, void *ud) {
  struct SParser *p;
  Proto *tf;
  Closure *cl;
  luaC_checkGC(L);
  p = cast(struct SParser *, ud);
  tf = p->bin ? luaU_undump(L, p->z, &p->buff) : luaY_parser(L, p->z, &p->buff);
  cl = luaF_newLclosure(L, 0, gt(L));
  cl->l.p = tf;
  setclvalue(L->top, cl);
  incr_top(L);
}


經過判斷傳入的是否爲二進制格式而進行不一樣的處理:

若是是二進制則 luaU_undump

不然調用語法解析 luaY_parser 。

解析完後,放到閉包裏,壓棧。

通向語法解析 luaY_parser 之路已經打通。

在這以前,先看一下路上的其它風景。

相關文章
相關標籤/搜索