其中一個(gè)明顯的例子是Eclipse CDT里的parser。
網(wǎng)站建設(shè)哪家好,找創(chuàng)新互聯(lián)!專(zhuān)注于網(wǎng)頁(yè)設(shè)計(jì)、網(wǎng)站建設(shè)、微信開(kāi)發(fā)、微信平臺(tái)小程序開(kāi)發(fā)、集團(tuán)企業(yè)網(wǎng)站建設(shè)等服務(wù)項(xiàng)目。為回饋新老客戶(hù)創(chuàng)新互聯(lián)還提供了定遠(yuǎn)免費(fèi)建站歡迎大家使用!
它是完全用Java實(shí)現(xiàn)的,手寫(xiě)的遞歸下降parser,能把C或C++源碼parse成AST供Eclipse CDT的IDE功能使用。它支持C99語(yǔ)法(包括GCC擴(kuò)展)、C++語(yǔ)法(我沒(méi)仔細(xì)看現(xiàn)在支持到什么版本了)等。
它并不用于實(shí)際的編譯(這跟Eclipse JDT里的Eclipse Compiler for Java不同);實(shí)際編譯還是交給諸如GCC、xlc之類(lèi)的編譯器去完成。
關(guān)于Eclipse CDT里的C與C++ parser的介紹,請(qǐng)參考
不論是物理機(jī)還是虛擬機(jī),大部分的程序代碼從開(kāi)始編譯到最終轉(zhuǎn)化成物理機(jī)的目標(biāo)代碼或虛擬機(jī)能執(zhí)行的指令集之前,都會(huì)按照如下圖所示的各個(gè)步驟進(jìn)行:
其中綠色的模塊可以選擇性實(shí)現(xiàn)。很容易看出,上圖中間的那條分支是解釋執(zhí)行的過(guò)程(即一條字節(jié)碼一條字節(jié)碼地解釋執(zhí)行,如JavaScript),而下面的那條分支就是傳統(tǒng)編譯原理中從源代碼到目標(biāo)機(jī)器代碼的生成過(guò)程。
如今,基于物理機(jī)、虛擬機(jī)等的語(yǔ)言,大多都遵循這種基于現(xiàn)代經(jīng)典編譯原理的思路,在執(zhí)行前先對(duì)程序源碼進(jìn)行詞法解析和語(yǔ)法解析處理,把源碼轉(zhuǎn)化為抽象語(yǔ)法樹(shù)。對(duì)于一門(mén)具體語(yǔ)言的實(shí)現(xiàn)來(lái)說(shuō),詞法和語(yǔ)法分析乃至后面的優(yōu)化器和目標(biāo)代碼生成器都可以選擇獨(dú)立于執(zhí)行引擎,形成一個(gè)完整意義的編譯器去實(shí)現(xiàn),這類(lèi)代表是C/C++語(yǔ)言。也可以把抽象語(yǔ)法樹(shù)或指令流之前的步驟實(shí)現(xiàn)一個(gè)半獨(dú)立的編譯器,這類(lèi)代表是Java語(yǔ)言。又或者可以把這些步驟和執(zhí)行引擎全部集中在一起實(shí)現(xiàn),如大多數(shù)的JavaScript執(zhí)行器。
Javac編譯
在Java中提到“編譯”,自然很容易想到Javac編譯器將*.java文件編譯成為*.class文件的過(guò)程,這里的Javac編譯器稱(chēng)為前端編譯器,其他的前端編譯器還有諸如Eclipse?JDT中的增量式編譯器ECJ等。相對(duì)應(yīng)的還有后端編譯器,它在程序運(yùn)行期間將字節(jié)碼轉(zhuǎn)變成機(jī)器碼(現(xiàn)在的Java程序在運(yùn)行時(shí)基本都是解釋執(zhí)行加編譯執(zhí)行),如HotSpot虛擬機(jī)自帶的JIT(Just?In?Time?Compiler)編譯器(分Client端和Server端)。另外,有時(shí)候還有可能會(huì)碰到靜態(tài)提前編譯器(AOT,Ahead?Of?Time?Compiler)直接把*.java文件編譯成本地機(jī)器代碼,如GCJ、Excelsior?JET等,這類(lèi)編譯器我們應(yīng)該比較少遇到。
下面簡(jiǎn)要說(shuō)下Javac編譯(前端編譯)的過(guò)程。
詞法、語(yǔ)法分析
詞法分析是將源代碼的字符流轉(zhuǎn)變?yōu)闃?biāo)記(Token)集合。單個(gè)字符是程序編寫(xiě)過(guò)程中的的最小元素,而標(biāo)記則是編譯過(guò)程的最小元素,關(guān)鍵字、變量名、字面量、運(yùn)算符等都可以成為標(biāo)記,比如整型標(biāo)志int由三個(gè)字符構(gòu)成,但是它只是一個(gè)標(biāo)記,不可拆分。
語(yǔ)法分析是根據(jù)Token序列來(lái)構(gòu)造抽象語(yǔ)法樹(shù)的過(guò)程。抽象語(yǔ)法樹(shù)是一種用來(lái)描述程序代碼語(yǔ)法結(jié)構(gòu)的樹(shù)形表示方式,語(yǔ)法樹(shù)的每一個(gè)節(jié)點(diǎn)都代表著程序代碼中的一個(gè)語(yǔ)法結(jié)構(gòu),如bao、類(lèi)型、修飾符、運(yùn)算符等。經(jīng)過(guò)這個(gè)步驟后,編譯器就基本不會(huì)再對(duì)源碼文件進(jìn)行操作了,后續(xù)的操作都建立在抽象語(yǔ)法樹(shù)之上。
填充符號(hào)表
完成了語(yǔ)法分析和詞法分析之后,下一步就是填充符號(hào)表的過(guò)程。符號(hào)表是由一組符號(hào)地址和符號(hào)信息構(gòu)成的表格。符號(hào)表中所登記的信息在編譯的不同階段都要用到,在語(yǔ)義分析(后面的步驟)中,符號(hào)表所登記的內(nèi)容將用于語(yǔ)義檢查和產(chǎn)生中間代碼,在目標(biāo)代碼生成階段,黨對(duì)符號(hào)名進(jìn)行地址分配時(shí),符號(hào)表是地址分配的依據(jù)。
語(yǔ)義分析
語(yǔ)法樹(shù)能表示一個(gè)結(jié)構(gòu)正確的源程序的抽象,但無(wú)法保證源程序是符合邏輯的。而語(yǔ)義分析的主要任務(wù)是讀結(jié)構(gòu)上正確的源程序進(jìn)行上下文有關(guān)性質(zhì)的審查。語(yǔ)義分析過(guò)程分為標(biāo)注檢查和數(shù)據(jù)及控制流分析兩個(gè)步驟:
標(biāo)注檢查步驟檢查的內(nèi)容包括諸如變量使用前是否已被聲明、變量和賦值之間的數(shù)據(jù)類(lèi)型是否匹配等。
數(shù)據(jù)及控制流分析是對(duì)程序上下文邏輯更進(jìn)一步的驗(yàn)證,它可以檢查出諸如程序局部變量在使用前是否有賦值、方法的每條路徑是否都有返回值、是否所有的受查異常都被正確處理了等問(wèn)題。
字節(jié)碼生成
字節(jié)碼生成是Javac編譯過(guò)程的最后一個(gè)階段。字節(jié)碼生成階段不僅僅是把前面各個(gè)步驟所生成的信息轉(zhuǎn)化成字節(jié)碼寫(xiě)到磁盤(pán)中,編譯器還進(jìn)行了少量的代碼添加和轉(zhuǎn)換工作。?實(shí)例構(gòu)造器init()方法和類(lèi)構(gòu)造器clinit()方法就是在這個(gè)階段添加到語(yǔ)法樹(shù)之中的(這里的實(shí)例構(gòu)造器并不是指默認(rèn)的構(gòu)造函數(shù),而是指我們自己重載的構(gòu)造函數(shù),如果用戶(hù)代碼中沒(méi)有提供任何構(gòu)造函數(shù),那編譯器會(huì)自動(dòng)添加一個(gè)沒(méi)有參數(shù)、訪問(wèn)權(quán)限與當(dāng)前類(lèi)一致的默認(rèn)構(gòu)造函數(shù),這個(gè)工作在填充符號(hào)表階段就已經(jīng)完成了)。
JIT編譯
Java程序最初是僅僅通過(guò)解釋器解釋執(zhí)行的,即對(duì)字節(jié)碼逐條解釋執(zhí)行,這種方式的執(zhí)行速度相對(duì)會(huì)比較慢,尤其當(dāng)某個(gè)方法或代碼塊運(yùn)行的特別頻繁時(shí),這種方式的執(zhí)行效率就顯得很低。于是后來(lái)在虛擬機(jī)中引入了JIT編譯器(即時(shí)編譯器),當(dāng)虛擬機(jī)發(fā)現(xiàn)某個(gè)方法或代碼塊運(yùn)行特別頻繁時(shí),就會(huì)把這些代碼認(rèn)定為“Hot?Spot?Code”(熱點(diǎn)代碼),為了提高熱點(diǎn)代碼的執(zhí)行效率,在運(yùn)行時(shí),虛擬機(jī)將會(huì)把這些代碼編譯成與本地平臺(tái)相關(guān)的機(jī)器碼,并進(jìn)行各層次的優(yōu)化,完成這項(xiàng)任務(wù)的正是JIT編譯器。
現(xiàn)在主流的商用虛擬機(jī)(如Sun?HotSpot、IBM?J9)中幾乎都同時(shí)包含解釋器和編譯器(三大商用虛擬機(jī)之一的JRockit是個(gè)例外,它內(nèi)部沒(méi)有解釋器,因此會(huì)有啟動(dòng)相應(yīng)時(shí)間長(zhǎng)之類(lèi)的缺點(diǎn),但它主要是面向服務(wù)端的應(yīng)用,這類(lèi)應(yīng)用一般不會(huì)重點(diǎn)關(guān)注啟動(dòng)時(shí)間)。二者各有優(yōu)勢(shì):當(dāng)程序需要迅速啟動(dòng)和執(zhí)行時(shí),解釋器可以首先發(fā)揮作用,省去編譯的時(shí)間,立即執(zhí)行;當(dāng)程序運(yùn)行后,隨著時(shí)間的推移,編譯器逐漸會(huì)返回作用,把越來(lái)越多的代碼編譯成本地代碼后,可以獲取更高的執(zhí)行效率。解釋執(zhí)行可以節(jié)約內(nèi)存,而編譯執(zhí)行可以提升效率。
HotSpot虛擬機(jī)中內(nèi)置了兩個(gè)JIT編譯器:Client?Complier和Server?Complier,分別用在客戶(hù)端和服務(wù)端,目前主流的HotSpot虛擬機(jī)中默認(rèn)是采用解釋器與其中一個(gè)編譯器直接配合的方式工作。
運(yùn)行過(guò)程中會(huì)被即時(shí)編譯器編譯的“熱點(diǎn)代碼”有兩類(lèi):
被多次調(diào)用的方法。
被多次調(diào)用的循環(huán)體。
兩種情況,編譯器都是以整個(gè)方法作為編譯對(duì)象,這種編譯也是虛擬機(jī)中標(biāo)準(zhǔn)的編譯方式。要知道一段代碼或方法是不是熱點(diǎn)代碼,是不是需要觸發(fā)即時(shí)編譯,需要進(jìn)行Hot?Spot?Detection(熱點(diǎn)探測(cè))。目前主要的熱點(diǎn)?判定方式有以下兩種:
基于采樣的熱點(diǎn)探測(cè):采用這種方法的虛擬機(jī)會(huì)周期性地檢查各個(gè)線程的棧頂,如果發(fā)現(xiàn)某些方法經(jīng)常出現(xiàn)在棧頂,那這段方法代碼就是“熱點(diǎn)代碼”。這種探測(cè)方法的好處是實(shí)現(xiàn)簡(jiǎn)單高效,還可以很容易地獲取方法調(diào)用關(guān)系,缺點(diǎn)是很難精確地確認(rèn)一個(gè)方法的熱度,容易因?yàn)槭艿骄€程阻塞或別的外界因素的影響而擾亂熱點(diǎn)探測(cè)。
基于計(jì)數(shù)器的熱點(diǎn)探測(cè):采用這種方法的虛擬機(jī)會(huì)為每個(gè)方法,甚至是代碼塊建立計(jì)數(shù)器,統(tǒng)計(jì)方法的執(zhí)行次數(shù),如果執(zhí)行次數(shù)超過(guò)一定的閥值,就認(rèn)為它是“熱點(diǎn)方法”。這種統(tǒng)計(jì)方法實(shí)現(xiàn)復(fù)雜一些,需要為每個(gè)方法建立并維護(hù)計(jì)數(shù)器,而且不能直接獲取到方法的調(diào)用關(guān)系,但是它的統(tǒng)計(jì)結(jié)果相對(duì)更加精確嚴(yán)謹(jǐn)。
在HotSpot虛擬機(jī)中使用的是第二種——基于計(jì)數(shù)器的熱點(diǎn)探測(cè)方法,因此它為每個(gè)方法準(zhǔn)備了兩個(gè)計(jì)數(shù)器:方法調(diào)用計(jì)數(shù)器和回邊計(jì)數(shù)器。
方法調(diào)用計(jì)數(shù)器用來(lái)統(tǒng)計(jì)方法調(diào)用的次數(shù),在默認(rèn)設(shè)置下,方法調(diào)用計(jì)數(shù)器統(tǒng)計(jì)的并不是方法被調(diào)用的絕對(duì)次數(shù),而是一個(gè)相對(duì)的執(zhí)行頻率,即一段時(shí)間內(nèi)方法被調(diào)用的次數(shù)。
回邊計(jì)數(shù)器用于統(tǒng)計(jì)一個(gè)方法中循環(huán)體代碼執(zhí)行的次數(shù)(準(zhǔn)確地說(shuō),應(yīng)該是回邊的次數(shù),因?yàn)椴⒎撬械难h(huán)都是回邊),在字節(jié)碼中遇到控制流向后跳轉(zhuǎn)的指令就稱(chēng)為“回邊”。
在確定虛擬機(jī)運(yùn)行參數(shù)的前提下,這兩個(gè)計(jì)數(shù)器都有一個(gè)確定的閥值,當(dāng)計(jì)數(shù)器的值超過(guò)了閥值,就會(huì)觸發(fā)JIT編譯。觸發(fā)了JIT編譯后,在默認(rèn)設(shè)置下,執(zhí)行引擎并不會(huì)同步等待編譯請(qǐng)求完成,而是繼續(xù)進(jìn)入解釋器按照解釋方式執(zhí)行字節(jié)碼,直到提交的請(qǐng)求被編譯器編譯完成為止(編譯工作在后臺(tái)線程中進(jìn)行)。當(dāng)編譯工作完成后,下一次調(diào)用該方法或代碼時(shí),就會(huì)使用已編譯的版本。
由于方法計(jì)數(shù)器觸發(fā)即時(shí)編譯的過(guò)程與回邊計(jì)數(shù)器觸發(fā)即時(shí)編譯的過(guò)程類(lèi)似,因此這里僅給出方法調(diào)用計(jì)數(shù)器觸發(fā)即時(shí)編譯的流程:
流程:
Java詞法分析
最開(kāi)始的時(shí)候,高級(jí)語(yǔ)言編寫(xiě)的程序?qū)幾g器來(lái)說(shuō)只是一連串的單個(gè)字符組成的字符串。為了讓編譯器識(shí)別這一連串的字符串,需要逐個(gè)字符的讀取源程序,然后將其切分成有意義的單詞,這些被切分后的單詞在編譯器眼里是以
標(biāo)識(shí),語(yǔ)義值對(duì)的形式存在。
為了從源程序字符串中依次找出單詞,編譯器需要具有掃描功能,通常這種掃描器可以用一組有限狀態(tài)機(jī)來(lái)實(shí)現(xiàn)。為了說(shuō)明有限狀態(tài)機(jī)是怎么一回事,下面給出一個(gè)實(shí)例。
下圖為一個(gè)識(shí)別數(shù)字的有限狀態(tài)機(jī),數(shù)字由整數(shù)部分和可選的小數(shù)部分組成。因此,根據(jù)這個(gè)有限狀態(tài)機(jī),250和3.14159都能被識(shí)別成一個(gè)有效的數(shù)字。
Java語(yǔ)法分析
完成了“詞法分析”后,接下來(lái)就是激動(dòng)人心的“語(yǔ)法分析”階段。通過(guò)語(yǔ)法分析得到語(yǔ)法樹(shù)。
Java翻譯語(yǔ)法樹(shù)
首先,尋找最小表達(dá)式,接下來(lái),將最小表達(dá)式與其周邊的表達(dá)式合并。最后,將所有的表達(dá)式有序的進(jìn)行合并,得到最終的匯編語(yǔ)言描述。
至此,我們便將高級(jí)語(yǔ)言翻譯成了匯編語(yǔ)言。
參照百科解釋
PMD是一種開(kāi)源分析Java代碼錯(cuò)誤的工具。與其他分析工具不同的是,PMD通過(guò)靜態(tài)分析獲知代碼錯(cuò)誤。也就是說(shuō),在不運(yùn)行Java程序的情況下報(bào)告錯(cuò)誤。PMD附帶了許多可以直接使用的規(guī)則,利用這些規(guī)則可以找出Java源程序的許多問(wèn)題。此外,用戶(hù)還可以自己定義規(guī)則,檢查Java代碼是否符合某些特定的編碼規(guī)范。
PMD的核心是JavaCC解析器生成器。PMD結(jié)合運(yùn)用JavaCC和EBNF(擴(kuò)展巴科斯-諾爾范式,Extended Backus-Naur Formal)語(yǔ)法,再加上JJTree,把Java源代碼解析成抽象語(yǔ)法樹(shù)(AST,Abstract Syntax Tree)。
PMD是一款采用BSD協(xié)議發(fā)布的Java程序代碼檢查工具。該工具可以做到檢查Java代碼中是否含有未使用的變量、是否含有空的抓取塊、是否含有不必要的對(duì)象等。該軟件功能強(qiáng)大,掃描效率高
在期貨中應(yīng)該是一種個(gè)人所設(shè)置的或者編程的一個(gè)期貨指標(biāo),或者就是
偏振模色散指單模光纖中偏振色散,簡(jiǎn)稱(chēng)PMD(=Polarization Mode Dispersion),是由光纖橫截面微小的不對(duì)稱(chēng)性引起的色散。這種不對(duì)稱(chēng)性引起兩個(gè)相互垂直的基本偏振模以不同的速度傳播。由于經(jīng)歷了色散,即脈沖擴(kuò)展,當(dāng)接收器接收到這個(gè)合成的脈沖時(shí)要比發(fā)送端的脈沖寬。
起因于實(shí)際的單模光纖中基模含有兩個(gè)相互垂直的偏振模,沿光纖傳播過(guò)程中,由于光纖難免受到外部的作用,如溫度和壓力等因素變化或擾動(dòng),使得兩模式發(fā)生耦合,并且它們的傳播速度也不盡相同,從而導(dǎo)致光脈沖展寬,展寬量也不確定,便相當(dāng)于隨機(jī)的色散。隨著傳輸速率的提高,該色散對(duì)通信系統(tǒng)的影響愈來(lái)愈明,而且越來(lái)越不可低估。有文獻(xiàn)給出由PMD限制的系統(tǒng)最大距離按公式:L最大值=1000/(PMD.比特率)2,式中:L單位為(根號(hào))km,PMD單位為PS/KM,以及比特率單位為Gb/s。國(guó)際上一些標(biāo)準(zhǔn)組織,如IEC、TIA和ITU考慮制定這種隨機(jī)性色散的統(tǒng)計(jì)特性和相應(yīng)的測(cè)試方法。