上回說到 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 之路已經打通。
在這以前,先看一下路上的其它風景。