luac.c 是編譯器 luac 的入口文件。
老規矩,從 main 函數看起,看看這個過程當中程序都作了些什麼?
函數
int main(int argc, char* argv[]) { char* d="luac.out"; /* default output file */ int i; for (i=1; i<argc; i++) { if (argv[i][0]!='-') /* end of options */ break; else if (IS("-")) /* use stdin */ break; else if (IS("-d")) /* debug */ lua_debug=1; else if (IS("-l")) /* list */ listing=1; else if (IS("-o")) /* output file */ d=argv[++i]; else if (IS("-p")) /* parse only (for timing purposes) */ dumping=0; else if (IS("-v")) /* show version */ printf("%s %s\n(written by %s)\n\n",LUA_VERSION,LUA_COPYRIGHT,LUA_AUTHORS); else /* unknown option */ usage(); } --i; /* fake new argv[0] */ argc-=i; argv+=i; if (argc<2) usage(); for (i=1; i<argc; i++) if (IS(d)) { fprintf(stderr,"luac: will not overwrite input file \"%s\"\n",d); exit(1); } D=(dumping) ? fopen(d,"wb") : stdout; /* must open in binary mode */ if (D==NULL) { fprintf(stderr,"luac: cannot open "); perror(d); exit(1); } for (i=1; i<argc; i++) compile(IS("-")? NULL : argv[i]); fclose(D); return 0; }
看這個代碼的時候,最好參考一下 luac 的手冊,對比各類選項能看的更清楚點。
程序一開始就定義了一個默認的輸出文件,"luac.out"。
接下來,開始遍歷命令行的輸入,以得到用戶從命令行輸入的選項。
一旦程序遇到一個不是中劃線(減號 '-')打頭的選項,遍歷結束。
luac 的命令行選項的格式都是中劃線後加一個字符,以空白分割,這點和 unix 的傳統是同樣的。而且和通常的命令行程序操做界面也是一致的。
選項若是隻是一箇中劃線,使用標準輸入作爲輸入文件,遍歷結束。
這裏的 IS 是一個宏,這是 C 語言裏面的一個令代碼更有實際意義及更加容易閱讀的一個方法,Lua 的源代碼裏用到了很多的宏,除了能夠減小代碼量外,最重要的就是讓代碼更易讀,以及更有意義。
'-d' 調試選項,若是打開的話,程序會在生成字節碼的時候生成一些調試相關的信息。好比行號和其它一些方便調試的內容。有一些調試接口是隻在你打開調試時,它纔有意義。
'-l' 是否打印字節碼。
'-o' 設置輸出文件,輸出文件句直接在選項的後面,若是不使用這個選項,則使用上面提到的那個 luac.out 文件作爲輸出文件。
'-p' 只進行語法分析。
'-v' 顯示 Lua 的版本號,版權信息及做者。
不然,若是有錯誤的選項,調用 usage,打印使用方法。
ui
static void usage(void) { fprintf(stderr,"usage: luac [-dlpv] [-o output] file ...\n"); exit(0); }
命令行選項遍歷退出時,說明這時命令行選項應該是到了 Lua 腳本的源代碼文件了。
若是參數個數不對,則也一樣調用 usage , 打印使用方法。
在對文件進行編譯以前,要先檢查一下 Lua 腳本文件是否和輸出文件同名了,若是同名,打印出錯信息並退出。就是這個 for 循環:
lua
for (i=1; i<argc; i++) if (IS(d)) { fprintf(stderr,"luac: will not overwrite input file \"%s\"\n",d); exit(1); }
打開輸出文件,若是不須要輸出的話,打開標準輸出做爲輸出。若是打開文件出錯,則打印錯誤並退出。
這裏的 dumping 標誌隻影響編譯後字節碼的輸出,其它過程無影響。
命令行
D=(dumping) ? fopen(d,"wb") : stdout; /* must open in binary mode */ if (D==NULL) { fprintf(stderr,"luac: cannot open "); perror(d); exit(1); }
最後一個 for 循環,是編譯全部的 Lua 腳本文件。
debug
for (i=1; i<argc; i++) compile(IS("-")? NULL : argv[i]);
compile 函數的做用就是打開文件,編譯,並關閉文件。
unix
static void compile(char* filename) { if (lua_openfile(filename)==NULL) { fprintf(stderr,"luac: cannot open "); perror(filename); exit(1); } do_compile(); lua_closefile(); }
do_compile 編譯,並輸出。
調試
static void do_compile(void) { TFunc* tf=new(TFunc); luaI_initTFunc(tf); tf->fileName = lua_parsedfile; lua_parse(tf); do_dump(tf); }
do_dump 看名字能夠看出,作實際的 dump 工做的,也就是輸出字節碼,或者叫轉存字節碼。
code
static void do_dump(TFunc* tf) /* only for tf==main */ { if (dumping) DumpHeader(D); while (tf!=NULL) { TFunc* nf; if (listing) PrintFunction(tf); if (dumping) DumpFunction(tf,D); nf=tf->next; /* list only built after first main */ luaI_freefunc(tf); tf=nf; } }
這個文件結束了,不過,這裏有好幾個東西都沒有說,好比,上面的那個打開關閉文件是幹什麼的,以及爲何要那麼作?
do_compile 裏的 TFunc 是什麼?那個初始化是什麼?lua_parser 是什麼? do_dump 方法裏調的那幾個方法又分別是幹什麼的?
這些東西都會根據這裏調用的順序一點點慢慢的展示出來的。
接口