生活随笔
收集整理的這篇文章主要介紹了
MYC编译器源码分析之程序入口
小編覺得挺不錯的,現在分享給大家,幫大家做個參考.
前文.NET框架源碼解讀之MYC編譯器講了MyC編譯器的架構,整個編譯器是用C#語言寫的,上圖列出了MyC編譯器編譯一個C源文件的過程,編譯主路徑如下:
首先是入口Main函數用來解析命令行參數,讀取源文件,并開始編譯過程。Main函數在MyC.cs文件,而IO.cs文件主要保存讀取源碼文件的相關操作。下表是Main函數的源碼(批注用注釋的方式顯示),IO.cs文件用單獨的一個小節說明: public static void Main()
{try
{// 看源碼注釋,代碼是99年寫的,也就是說.NET當年正在開發中// 可能那個時候虛擬機都沒有做好向Main函數傳遞命令行參數的開發// 用了下面這個奇葩方法獲取程序的命令行參數String[] args = Environment.GetCommandLineArgs();// 初始化讀取源文件的IO對象,該對象負責將源文件以字節流的方式// 輸出給下一個對象 – 詞法分析Io prog = new Io(args);// 詞法分析對象,該對象的工作是過濾掉源碼中不必要的字符,比如空格// 注釋之類的,并且把源碼中的字符歸類 – Tokenize,以便語法分析器// 更方便的解析語法Tok tok = new Tok(prog);// 語法分析對象,解析完畢后即是代碼生成階段,但一般語法分析過程// 都只會生成語法樹,這樣的設計可以對接多種結果文件輸出手段。比如// 說,本例中生成可執行文件的exe.cs和生成IL源碼的asm.cs都是通過// 遍歷語法樹,使用不同的輸出策略生成結果文件的Parse p = new Parse(prog, tok);// 采用自頂向下的方式進行語法解析p.program();// 編譯工作已經完成,關閉打開的源文件句柄等資源prog.Finish();}catch (Exception e)
{// 編譯過程中有任何錯誤,即中斷處理,打印錯誤消息并退出程序Console.WriteLine("Compiler aborting: " + e.ToString());}
}
MyC的語法很簡單,因此編譯過程是很干凈的詞法分析、語法分析、代碼生成和結果輸出的過程。其中詞法分析代碼在tok.cs,語法分析代碼在parse.cs中,Emit.cs處理代碼生成,而Asm.cs和Exe.cs分別根據命令行參數的設置,來生成最終的可執行文件并選擇是否輸出IL源碼。 IO.cs - IO處理 將源文件讀取進內存,并采用流式處理的代碼都放在IO這個類里面,IO的構造函數解析命令行參數,并打開源文件,等待Tok.cs里面代碼的指令將源文件的字符一個個讀進內存并處理,下面是它的構造函數的源碼: public Io(String[] a){int i;args = a;// 解析命令行參數,并根據參數打開內部的控制開關,詳情請看下面對ParseArgs// 函數的源碼解讀ParseArgs();// 打開要編譯的源文件ifile = new FileStream(ifilename, FileMode.Open,FileAccess.Read, FileShare.Read, 8192);// 如果源文件不存在,報錯退出if (ifile == null){Abort("Could not open file '"+ifilename+"'\n");
}// 采用流式處理方式讀取源文件rfile = new StreamReader(ifile); // open up a stream for reading// 根據源文件的名稱設定結果輸出文件的文件名i = ifilename.LastIndexOf('.');if (i < 0)Abort("Bad filename '"+ifilename+"'");int j = ifilename.LastIndexOf('\\');if (j < 0)j = 0;elsej++;classname = ifilename.Substring(j,i-j);// 根據命令行參數決定是生成.exe、.dll等可執行文件,還是輸出包含// IL源碼的.lst文件 if (genexe)ofilename = classname+".exe";if (gendll)ofilename = classname+".dll";if (genlist)
{
// 如果是要輸出IL源碼,因為原來的可執行文件也要輸出,需要創建一個新的文件lst_ofilename = classname+".lst";lst_ofile = new FileStream(lst_ofilename, FileMode.Create,FileAccess.Write, FileShare.Write, 8192);if (lst_ofile == null)Abort("Could not open file '"+ofilename+"'\n");lst_wfile = new StreamWriter(lst_ofile);}}
編譯器是在IO類里處理命令行參數的,參數解析實際上是一些字符串處理的活,本文解釋下關鍵代碼:
void ParseArgs(){int i = 1;// 程序至少需要兩個參數,否則就輸出幫助文字并退出if (args.Length < 2){Abort("myc [/debug] [/nodebug] [/list] [/dll] [/exe] [/outdir:path] filename.myc\n");}// 逐個遍歷命令行參數while (true){if (args[i][0] != '/')break;// 處理 /? 這個參數,即輸出幫助文本if (args[i].Equals("/?")){Console.WriteLine("Compiler options:\n myc [/debug] [/nodebug] [/list] [/dll] [/exe] [/outdir:path] filename.myc\n");Environment.Exit(1);}
// 如果有 /debug 參數,則打開內部的 gendebug 開關,這個開關在代碼生成的過程
// 中會用到if (args[i].Equals("/debug")){gendebug = true;i++;continue;}
// ... ... 跳過類似的代碼
// 如果有 /outdir 參數,則獲取命令行中指定的目錄路徑if (args[i].Length > 8 && args[i].Substring(0,8).Equals("/outdir:")){genpath = args[i].Substring(8);i++;continue;}
// 前面那么多的if相當于switch … case … default 塊里面的 case 處理路徑
// 下面這段代碼即是 default 處理路徑 – 如果命令行參數符合前面的if條件
// 都會執行里面的 continue 子句跳出循環,能執行到這里,說明參數
// 是無法識別的參數,因此報告錯誤并退出執行Abort("Unmatched switch = '"+args[i]+"'\nArguments are:\nmyc [/debug] [/nodebug] [/list] [/dll] [/exe] [/outdir:path] filename.myc\n");}// 如果前面的循環執行完畢,還有參數列表未處理,說明輸入了不支持的參數if (args.Length-i != 1){Abort("myc [/debug] [/nodebug] [/list] [/dll] [/exe] [/outdir:path] filename.myc\n");
}// 最后一個參數是要編譯的源文件路徑ifilename = args[args.Length-1]; // filename is last}
IO類中大部分函數都是為Tok.cs服務的,因此其它函數在解釋詞法分析的時候說明
轉載于:https://www.cnblogs.com/vowei/p/4329568.html
總結
以上是生活随笔為你收集整理的MYC编译器源码分析之程序入口的全部內容,希望文章能夠幫你解決所遇到的問題。
如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。