標籤:io os 使用 ar for 檔案 sp c on
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,列印使用方法。
static void usage(void){ fprintf(stderr,"usage: luac [-dlpv] [-o output] file ...\n"); exit(0);}
命令列選項遍曆退出時,說明這時命令列選項應該是到了 Lua 指令碼的原始碼檔案了。
如果參數個數不對,則也同樣調用 usage , 列印使用方法。
在對檔案進行編譯之前,要先檢查一下 Lua 指令檔是否和輸出檔案同名了,如果同名,列印出錯資訊並退出。就是這個 for 迴圈:
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 指令檔。
for (i=1; i<argc; i++) compile(IS("-")? NULL : argv[i]);
compile 函數的作用就是開啟檔案,編譯,並關閉檔案。
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 工作的,也就是輸出位元組碼,或者叫轉存位元組碼。
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 方法裡調的那幾個方法又分別是幹什麼的?
這些東西都會根據這裡調用的順序一點點慢慢的展現出來的。
Lua2.4 編譯器入口 luac.c