精選分類 書庫 完本 排行 原創專區
欣可小說 > 曆史 > 腳本語言 > ◎第二章 腳本語言

腳本語言 ◎第二章 腳本語言

作者:賈德江 分類:曆史 更新時間:2026-07-28 12:30:02

{

\"code\": 200,

\"title\": \"\",

\"content\": \"編譯原理\\n\\n編譯原理是計算機專業的一門重要專業課,旨在介紹編譯程式構造的一般原理和基本方法。內容包括語言和文法、詞法分析、語法分析、語法製導翻譯、中間代碼生成、存儲管理、代碼優化和目標代碼生成。編譯原理是計算機專業設置的一門重要的專業課程。雖然隻有少數人從事編譯方麵的工作,但是這門課在理論、技術、方法上都對學生提供了係統而有效的訓練,有利於提高軟件人員的素質和能力。目前各個大學使用的教材機械工業出版社、國防工業出版社出版的《編譯原理》。\\n\\n編譯器是將一種語言翻譯為另一種語言的計算機程式。編譯器將源程式(sourcelanguage)編寫的程式作為輸入,而產生用目標語言(targetlanguage)編寫的等價程式。通常地,源程式為高級語言(high-levellanguage),如C或C ,而目標語言則是目標機器的目標代碼(objectcode,有時也稱作機器代碼(machinecode)),也就是寫在計算機機器指令中的用於運行的代碼。這一過程可以表示為:\\n\\n源程式→編譯器→目標程式\\n\\n發展曆程\\n\\n編譯原理實驗程式\\n\\n在20世紀40年代,由於馮·諾伊曼在存儲-程式計算機方麵的先鋒作用,編寫一串代碼或程式已成必要,這\\n\\n編譯原理實驗程式樣\\n\\n計算機就可以執行所需的計算。開始時,這些程式都是用機器語言(machinelanguage)編寫的。機器語言就是表示機器實際操作的數字代碼,例如:\\n\\nC70600000002表示在IBMPC上使用的Intel8x86處理器將數字2移至地址0000(16進製)的指令。\\n\\n但編寫這樣的代碼是十分費時和乏味的,這種代碼形式很快就被組合語言(assemblylanguage)代替了。在組合語言中,都是以符號形式給出指令和存儲地址的。例如,組合語言指令MOVX,2就與前麵的機器指令等價(假設符號存儲地址X是0000)。彙程式設計序(assembler)將組合語言的符號代碼和存儲地址翻譯成與機器語言相對應的數字代碼。\\n\\n組合語言大大提高了編程的速度和準確度,人們至今仍在使用著它,在編碼需要極快的速度和極高的簡潔程度時尤為如此。但是,組合語言也有許多缺點:編寫起來也不容易,閱讀和理解很難;而且組合語言的編寫嚴格依賴於特定的機器,所以為一台計算機編寫的代碼在應用於另一台計算機時必須完全重寫。\\n\\n發展編程技術的下一個重要步驟就是以一個更類似於數學定義或自然語言的簡潔形式來編寫程式的操作,它應與任何機器都無關,而且也可由一個程式翻譯為可執行的代碼。例如,前麵的組合語言代碼可以寫成一個簡潔的與機器無關的形式x=2。\\n\\n在1954年至1957年期間,IBM的JohnBackus帶領的一個研究小組對FORTRAN語言及其編譯器的開發,使得上麵的擔憂不必要了。但是,由於當時處理中所涉及到的大多數程式設計語言的翻譯並不為人所掌握,所以這個項目的成功也伴隨著巨大的辛勞。幾乎與此同時,人們也在開發著第一個編譯器,NoamChomsky開始了他的自然語言結構的研究。他的發現最終使得編譯器結構異常簡單,甚至還帶有了一些自動化。Chosmky的研究導致了根據語言文法(grammar,指定其結構的規則)的難易程度以及識彆它們所需的演算法來為語言分類。正如現在所稱的-與喬姆斯基分類結構(Chomskyhierarchy)一樣-包括了文法的4個層次:0型、1型、2型和3型文法,且其中的每一個都是其前者的專門化。2型(或上下文無關文法(context-freegrammar))被證明是程式設計語言中最有用的,而且今天它已代表著程式設計語言結構的標準方式。\\n\\n分析問題(parsingproblem,用於限定上下文無關語言的識彆的有效演算法)的研究是在20世紀60年代和70年代,它相當完善地解決了這一問題,現在它已是編譯理論的一個標準部分。它們與喬姆斯基的3型文法相對應。對它們的研究與喬姆斯基的研究幾乎同時開始,並且引出了表示程式設計語言的單詞(或稱為記號)的符號方式。\\n\\n人們接著又深化了生成有效的目標代碼的方法,這就是最初的編譯器,它們被一直使用至今。人們通常將其誤稱為優化技術(optimizationtechnique),但因其從未真正地得到過被優化了的目標代碼而僅僅改進了它的有效性,因此實際上應稱作代碼改進技術(codeimprovementtechnique)。\\n\\n這些程式最初被稱為編譯程式-編譯器,但更確切地應稱為分析程式生成器(parsergenerator),這是因為它們僅僅能夠自動處理編譯的一部分。這些程式中最著名的是Yacc(yetanothercompiler-compiler),它是由SteveJohnson在1975年為Unix係統編寫的。\\n\\n類似地,有窮自動機的研究也發展了另一種稱為掃描程式生成器(scannergenerator)的工具,Lex(與Yacc同時,由MikeLesk為Unix係統開發的)是這其中的佼佼者。在20世紀70年代後期和80年代早期,大量的項目都關注於編譯器其他部分的生成自動化,這其中就包括代碼生成。這些嘗試並未取得多少成功,這大概是因為操作太複雜而人們又對其不甚瞭解。\\n\\n編譯器設計最近的發展包括:首先,編譯器包括了更為複雜的演算法的應用程式,它用於推斷或簡化程式中的資訊;這又與更為複雜的程式設計語言(可允許此類分析)的發展結合在一起。其中典型的有用於函數語言編譯的Hindley-Milner類型檢查的統一演算法。\\n\\n其次,編譯器已越來越成為基於視窗的互動開發環境(interactivedevelopmentenvironment,IDE)的一部分,它包括了編輯器、鏈接程式、調試程式以及項目管理程式。這樣的IDE的標準並冇有多少,但是已沿著這一方向對標準的視窗環境進行開發了。\\n\\n相關程式\\n\\n1.解釋程式(interpreter)\\n\\n解釋程式是如同編譯器的一種語言翻譯程式。它與編譯器的不同之處在於:它立即執行源程式而不是生成在翻譯完成之後才執行的目標代碼。從原理上講,任何程式設計語言都可被解釋或被編譯,但是根據所使用的語言和翻譯情況,很可能會選用解釋程式而不用編譯器。例如,我們經常解釋BASIC語言而不是去編譯它。類似地,諸如LISP的函數語言也常常是被解釋的。\\n\\n解釋程式也經常用於教育和軟件的開發,此處的程式很有可能被翻譯若乾次。而另一方麵,當執行的速度是最為重要的因素時就使用編譯器,這是因為被編譯的目標代碼比被解釋的源代碼要快得多,有時要快10倍或更多。但是,解釋程式具有許多與編譯器共享的操作,而兩者之間也有一些混合之處。\\n\\n2.彙程式設計序(assembler)\\n\\n彙程式設計序是用於特定計算機上的組合語言的翻譯程式。正如前麵所提到的,組合語言是計算機的機器語言的符號形式,它極易翻譯。有時,編譯器會生成組合語言以作為其目標語言,然後再由一個彙程式設計序將它翻譯成目標代碼。\\n\\n3.連接程式(linker)\\n\\n編譯器和彙程式設計序都經常依賴於連接程式,它將分彆在不同的目標檔案中編譯或彙編的代碼收集到一個可直接執行的檔案中。在這種情況下,目標代碼,即還未被連接的機器代碼,與可執行的機器代碼之間就有了區彆。連接程式還連接目標程式和用於標準庫函數的代碼,以及連接目標程式和由計算機的操作係統提供的資源(例如,存儲分配程式及輸入與輸出設備)。連接程式現在正在完成編譯器最早的一個主要活動(這也是“編譯”一詞的用法,即通過收集不同的來源來構造)。連接過程對操作係統和處理器有極大的依賴性,。\\n\\n4.裝入程式(loader)\\n\\n編譯器、彙程式設計序或連接程式生成的代碼經常還不完全適用或不能執行,但是它們的主要存儲器訪問卻可以在存儲器的任何位置中且與一個不確定的起始位置相關。這樣的代碼被稱為是可重定位的(relocatable),而裝入程式可處理所有的與指定的基地址或起始地址有關的可重定位的地址。裝入程式使得可執行代碼更加靈活,但是裝入處理通常是在後台(作為操作環境的一部分)或與連接相聯合時才發生,裝入程式極少會是實際的獨立程式。\\n\\n5.預處理器(preprocessor)\\n\\n預處理器是在真正的翻譯開始之前由編譯器調用的獨立程式。預處理器可以刪除註釋、包含其他檔案以及執行宏(宏macro是一段重複文字的簡短描寫)替代。預處理器可由語言(如C)要求或以後作為提供額外功能(諸如為FORTRAN提供Ratfor預處理器)的附加軟件。\\n\\n6.編輯器(editor)\\n\\n編譯器通常接受由任何生成標準檔案(例如ASCII檔案)的編輯器編寫的源程式。現在,編譯器已與另一個編輯器和其他程式捆綁進一個互動的開發環境-IDE中。此時,儘管編輯器仍然生成標準檔案,但會轉向正被討論的程式設計語言的格式或結構。這樣的編輯器稱為基於結構的(structurebased),且它早已包括了編譯器的某些操作;因此,程式員就會在程式的編寫時而不是在編譯時就得知錯誤了。從編輯器中也可調用編譯器以及與它共用的程式,這樣程式員無需離開編輯器就可執行程式。\\n\\n7.調試程式(debugger)\\n\\n調試程式是可在被編譯了的程式中判定執行錯誤的程式,它也經常與編譯器一起放在IDE中。運行一個帶有調試程式的程式與直接執行不同,這是因為調試程式儲存著所有的或大多數源代碼資訊(諸如行數、變量名和過程)。它還可以在預先指定的位置(稱為斷點(breakpoint))暫停執行,並提供有關已調用的函數以及變量的當前值的資訊。為了執行這些函數,編譯器必須為調試程式提供恰當的符號資訊,而這有時卻相當困難,尤其是在一個要優化目標代碼的編譯器中。因此,調試又變成了一個編譯問題。\\n\\n8.描述器(profiler)\\n\\n描述器是在執行中蒐集目標程式行為統計的程式。程式員特彆感興趣的統計是每一個過程的調用次數和每一個過程執行時間所占的百分比。這樣的統計對於幫助程式員提高程式的執行速度極為有用。有時編譯器也甚至無需程式員的乾涉就可利用描述器的輸出來自動改進目標代碼。\\n\\n9.項目管理程式(projectmanager)\\n\\n現在的軟件項目通常大到需要由一組程式員來完成,這時對那些由不同人員操作的檔案進行整理就非常重要了,而這正是項目管理程式的任務。例如,項目管理程式應將由不同的程式員製作的檔案的各個獨立版本整理在一起,它還應儲存一組檔案的更改曆史,這樣就能維持一個正在開發的程式的連貫版本了(這對那些由單個程式員管理的項目也很有用)。項目管理程式的編寫可與語言無關,但當其與編譯器捆綁在一起時,它就可以保持有關特定的編譯器和建立一個完整的可執行程式的鏈接程式操作的資訊。在Unix係統中有兩個流行的項目管理程式:sccs(sourcecodecontrolsystem)和rcs(revisioncontrolsystem)。\\n\\n步驟\\n\\n編譯器內部包括了許多步驟或稱為階段源代碼(phase),它們執行不同的邏輯操作。將這些階設想為編譯器中一個個單獨的片斷是很有用的,儘管在應用中它們是經常組合在一起的,但它們掃描程式確實是作為單獨的代碼操作來編寫的。\\n\\n掃描程式\\n\\n(scanner)\\n\\n在這個階段編譯器實際閱讀源程式(通常以析程式字元流的形式表示)。掃描程式執行詞法分析註釋樹符號表(Lexicalanalysis):它將字元序列收集到稱作記號錯誤處(token)的有意義單元中,記號同自然語言,如英源代碼理器語中的字詞相似。因此可以認為掃描程式執行與優化程式拚寫相似的任務。中間代碼例如在下麵的代碼行(它可以是C程式的一部分)中:代碼生成器a[index]=4 2這個代碼包括了12個非空字元,但隻有8個目標代碼記號:a標識符目標代碼優化程式[左括號index標識符]右括號=賦值目標代碼4數字編譯器的階段 加號2數字每一個記號均由一個或多個字元組成,在進一步處理之前它已被收集在一個單元中。掃描程式還可完成與識彆記號一起執行的其他操作。例如,它可將標識符輸入到符號表中,將文字(litral)輸入到文字表中(文字包括諸如3.1415926535的數字常量,以及諸如“Hello,world!”的引用字串)。\\n\\n語法分析程式\\n\\n(parser)\\n\\n語法分析程式從掃描程式中獲取記號形式的源代碼,並完成定義程式結構的語法分析(syntaxanalysis),這與自然語言中句子的語法分析類似。語法分析定義了程式的結構元素及其關係。通常將語法分析的結果表示為分析樹(parsetree)或語法樹(syntaxtree)。例如,還是那行C代碼,它表示一個稱為表達式的結構元素,該表達式是一個由左邊為下標表達式、右邊為整型表達式的賦值表達式組成。這個結構可按下麵的形式表示為一個分析樹:請注意,分析樹的內部節點均由其表示的結構名標示出,而分析樹的葉子則表示輸入中的記號序列(結構名以不同字體表示以示與記號的區彆)。分析樹對於顯示程式的語法或程式元素很有幫助,但是對於表示該結構卻顯得力不從心了。分析程式更趨向於生成語法樹,語法樹是分析樹中所含資訊的濃縮(有時因為語法樹表示從分析樹中的進一步抽取,所以也被稱為抽象的語法樹(abstractsyntaxtree))。下麵是一個C賦值語句的抽象語法樹的例子:請注意,在語法樹中,許多節點(包括記號節點在內)已經消失。例如,如果知道表達式是一個下標運算,則不再需要用括號“[”和“]”來表示該操作是在原始輸入中。\\n\\n語義分析程式\\n\\n(semanticanalyzer)\\n\\n程式的語義就是它的“意思”,它與語法或結構不同。程式的語義確定程式的運行,但是大多數的程式設計語言都具有在執行之前被確定而不易由語法表示和由分析程式分析的特征。這些特征被稱作靜態語義(staticsemantic),而語義分析程式的任務就是分析這樣的語義(程式的“動態”語義具有隻有在程式執行時才能確定的特性,由於編譯器不能執行程式,所以它不能由編譯器來確定)。一般的程式設計語言的典型靜態語義包括聲明和類型檢查。由語義分析程式計算的額外資訊(諸如數據類型)被稱為屬性(attribute),它們通常是作為註釋或“裝飾”增加到樹中(還可將屬性新增到符號表中)。在正運行的C表達式a[index]=4 2中,該行分析之前收集的典型類型資訊可能是:a是一個整型值的數組,它帶有來自整型子範圍的下標;index則是一個整型變量。接著,語義分析程式將用所有的子表達式類型來標註語法樹,並檢查賦值是否使這些類型有意義了,如若冇有,則聲明一個類型匹配錯誤。在上例中,所有的類型均有意義,有關語法樹的語義分析結果可用以下註釋了的樹來表示。\\n\\n源代碼優化程式\\n\\n(sourcecodeoptimizer)\\n\\n編譯器通常包括許多代碼改進或優化步驟。絕大多數最早的優化步驟是在語義分析之後完成的,而此時代碼改進可能隻依賴於源代碼。這種可能性是通過將這一操作提供為編譯過程中的單獨階段指出的。每個編譯器不論在已完成的優化種類方麵還是在優化階段的定位中都有很大的差異。在上例中,我們包括了一個源代碼層次的優化機會,也就是:表達式4 2可由編譯器計算先得到結果6(這種優化稱為常量合併(constantfolding))。當然,還會有更複雜的情況。還是在上例中,通過將根節點右麵的子樹合併爲它的常量值,這個優化就可以直接在(註釋)語法樹上完成:儘管許多優化可以直接在樹上完成,但是在很多情況下,優化接近於彙編代碼線性化形式的樹更為簡便。這樣節點的變形有許多,但是三元式代碼(three-addresscode)(之所以這樣稱呼是因為它在存儲器中包含了3個(或3個以上)位置的地址)卻是標準選擇。另一個常見的選擇是P-代碼(P-code),它常用於Pascal編譯器中。在前麵的例子中,原先的C表達式的三元式代碼應是:t=4 2a[index]=t(請注意,這裡利用了一個額外的臨時變量t存放加法的中間值)。這樣,優化程式就將這個代碼改進為兩步。首先計算加法的結果:t=6a[index]=t接著,將t替換為該值以得到三元語句a[index]=6,指出源代碼優化程式可能通過將其輸出稱為中間代碼(intermediatecode)來使用三元式代碼。中間代碼一直是指一種位於源代碼和目標代碼(例如三元式代碼或類似的線性表示)之間的程式碼錶示形式。但是,我們可以更概括地認為它是編譯器使用的源代碼的任何一個內部表示。此時,也可將語法樹稱作中間代碼,源代碼優化程式則確實能繼續在其輸出中使用這個表示。有時,這箇中間代碼也稱作中間表示(intermediaterepresentation,IR)。\\n\\n代碼生成器\\n\\n(codegenerator)\\n\\n代碼生成器得到中間代碼(IR),並生成目標機器的代碼。正是在編譯的這個階段中,目標機器的特性成為了主要因素。當它存在於目標機器時,使用指令不僅是必須的而且數據的形式表示也起著重要的作用。例如,整型數據類型的變量和浮點數據類型的變量在存儲器中所占的字節數或字數也很重要。在上麵的示例中,現在必須決定怎樣存儲整型數來為數組索引生成代碼。例如,下麵是所給表達式的一個可能的樣本代碼序列(在假設的組合語言中):\\n\\nMOVR0,index;;\\n\\nvalueofindex->R0MULR0,2;;\\n\\ndoublevalueinR0MOVR1,&a;;\\n\\naddressofa->R1ADDR1,R0;;\\n\\naddR0toR1MOV*R1,6;;\\n\\nconstant6->addressinR1\\n\\n在以上代碼中,為編址模式使用了一個類似C的協定,因此&a是a的地址(也就是數組的基地址),*R1則意味著間接暫存器地址(因此最後一條指令將值6存放在R1包含的地址中)。這個代碼還假設機器執行字節編址,並且整型數占據存儲器的兩個字節(所以在第2條指令中用2作為乘數)。\\n\\n目標代碼優化程式\\n\\n(targetcodeoptimizer)\\n\\n在這個階段中,編譯器嘗試著改進由代碼生成器生成的目標代碼。這種改進包括選擇編址模式以提高效能、將速度慢的指令更換成速度快的,以及刪除多餘的操作。在上麵給出的樣本目標代碼中,還可以做許多更改:在第2條指令中,利用移位指令替代乘法(通常地,乘法很費時間),還可以使用更有效的編址模式(例如用索引地址來執行數組存儲)。使用了這兩種優化後,目標代碼就變成:\\n\\nMOVR0,index;;\\n\\nvalueofindex->R0SHLR0;;\\n\\ndoublevalueinR0MOV&a[R0],6;;\\n\\nconstant6->addressa R0\\n\\n到這裡就是編譯原理的簡要描述,但還應特彆強調編譯器在其結構細節上差彆很大。\\n\\n主要數據結構\\n\\n編譯原理一直是計算機學習的必修課\\n\\n當然,由編譯器的階段使用的演算法與支援這些階段的數據結構之間的互動是非常強大的。編譯器的編寫者儘可能有效實施這些方法且不引起複雜性。理想的情況是:與程式大小成線性比例的時間內編譯器,換言之就是,在0(n)時間內,n是程式大小的度量(通常是字元數)。本節將講述一些主要的數據結構,它們是其操作部分階段所需要的,並用來在階段中交流資訊。\\n\\n1.記號(token)\\n\\n當掃描程式將字元收集到一個記號中時,它通常是以符號表示這個記號;這也就是說,作為一個枚舉數據類型的值來表示源程式的記號集。有時還必須保留字串本身或由此派生出的其他資訊(例如:與標識符記號相關的名字或數字記號值)。在大多數語言中,掃描程式一次隻需要生成一個記號(這稱為單符號先行(singlesymbollookahead))。在這種情況下,可以用全程變量放置記號資訊;而在彆的情況(最為明顯的是FORTRAN)下,則可能會需要一個記號數組。\\n\\n2.語法樹(syntaxtree)\\n\\n如果分析程式確實生成了語法樹,它的構造通常為基於指針的標準結構,在進行分析時動態分配該結構,則整棵樹可作為一個指向根節點的單個變量儲存。結構中的每一個節點都是一個記錄,它的域表示由分析程式和之後的語義分析程式收集的資訊。例如,一個表達式的數據類型可作為表達式的語法樹節點中的域。有時為了節省空間,這些域也是動態分配或存放在諸如符號表的其他數據結構中,這樣就可以有選擇地進行分配和釋放。實際上,根據它所表示的語言結構的類型(例如:表達式節點對於語句節點或聲明節點都有不同的要求),每一個語法樹節點本身都可能要求存儲不同的屬性。在這種情況下,可由不同的記錄表示語法樹中的每個節點,每個節點類型隻包含與本身相關的資訊。\\n\\n3.符號表(symboltable)\\n\\n這個數據結構中的資訊與標識符有關:函數、變量、常量以及數據類型。符號表幾乎與編譯器的所有階段互動:掃描程式、分析程式或將標識符輸入到表格中的語義分析程式;語義分析程式將增加數據類型和其他資訊;優化階段和代碼生成階段也將利用由符號表提供的資訊選出恰當的代碼。因為對符號表的訪問如此頻繁,所以插入、刪除和訪問操作都必須比常規操作更有效。儘管可以使用各種樹的結構,但雜湊表卻是達到這一要求的標準數據結構。有時在一個列表或棧中可使用若乾個表格。\\n\\n4.常數表(literaltable)\\n\\n常數表的功能是存放在程式中用到的常量和字串,因此快速插入和查詢在常數表中也十分重要。但是,在其中卻無需刪除,這是因為它的數據全程應用於程式而且常量或字串在該表中隻出現一次。通過允許重複使用常量和字串,常數表對於縮小程式在存儲器中的大小顯得非常重要。在代碼生成器中也需要常數表來構造用於常數和在目標代碼檔案中輸入數據定義的符號地址。\\n\\n5.中間代碼(intermediatecode)\\n\\n根據中間代碼的類型(例如三元式代碼和P-代碼)和優化的類型,該代碼可以是文字串的數組、臨時文字檔案或是結構的連接列表。對於進行複雜優化的編譯器,應特彆注意選擇允許簡單重組的表示。\\n\\n6.臨時檔案(temporaryfile)\\n\\n計算機過去一直未能在編譯器時將整個程式保留在存儲器中。這一問題已經通過使用臨時檔案來儲存翻譯時中間步驟的結果或通過“匆忙地”編譯(也就是隻保留源程式早期部分的足夠資訊用以處理翻譯)解決了。存儲器的限製現在也隻是一個小問題了,現在可以將整個編譯單元放在存儲器之中,特彆是在可以分彆編譯的語言中時。但是偶爾還是會發現需要在某些運行步驟中生成中間檔案。其中典型的是代碼生成時需要反填(backpatch)地址。例如,當翻譯如下的條件語句時ifx=0then...else...在知道else部分代碼的位置之前必須由文字跳到else部分:\\n\\nCMPX,0JNENEXT;;\\n\\nlocationofNEXTnotyetknownNEXT:\\n\\n通常,必須為NEXT的值留出一個空格,一旦知道該值後就會將該空格填上,利用臨時檔案可以很容易地做到這一點。\\n\\n其他問題\\n\\n可從許多不同的角度來觀察編譯器的結構,還有其他一些可能的觀點:編譯器的物理結構、操作的順序等等。由於編譯器的結構對其可靠性、有效性、可用性以及可維護性都有很大的影響,所以編譯器的編寫者應熟悉儘可能多的有關編譯器結構的觀點。\\n\\n1.分析和綜合\\n\\n在這個觀點中,已將分析源程式以計算其特性的編譯器操作歸為編譯器的分析(analysis)部分,而將生成翻譯代碼時所涉及到的操作稱作編譯器的綜合(synthesis)部分。當然,詞法分析、語法分析和語義分析均屬於分析部分,而代碼生成卻是綜合部分。在優化步驟中,分析和綜合都有。分析正趨向於易懂和更具有數學性,而綜合則要求更深的專業技術。因此,將分析步驟和綜合步驟兩者區分開來以便發生變化時互不影響是很有用的。\\n\\n2.前端和後端\\n\\n本觀點認為,將編譯器分成了隻依賴於源語言(前端(frontend))的操作和隻依賴於目標語言(後端(backend))的操作兩部分。這與將其分成分析和綜合兩部分是類似的:掃描程式、分析程式和語義分析程式是前端,代碼生成器是後端。但是一些優化分析可以依賴於目標語言,這樣就是屬於後端了,然而中間代碼的綜合卻經常與目標語言無關,因此也就屬於前端了。在理想情況下,編譯器被嚴格地分成這兩部分,而中間表示則作為其間的交流媒介。這一結構對於編譯器的可移植性(portability)十分重要,此時設計的編譯器既能改變源代碼(它涉及到重寫前端),又能改變目標代碼(它還涉及到重寫後端)。在實際中,這是很難做到的,而且稱作可移植的編譯器仍舊依賴於源語言和目標語言。其部分原因是程式設計語言和機器構造的快速發展以及根本性的變化,但是有效地保持移植一個新的目標語言所需的資訊或使數據結構普遍地適合改變為一個新的源語言所需的資訊卻十分困難。然而人們不斷分離前端和後端的努力會帶來更方便的可移植性。\\n\\n3.遍\\n\\n編譯器發現,在生成代碼之前多次處理整個源程式很方便。這些重複就是遍(pass)。首遍是從源中構造一個語法樹或中間代碼,在它之後的遍是由處理中間表示、向它增加資訊、更換結構或生成不同的表示組成。遍可以和階段相應,也可無關-遍中通常含有若乾個階段。實際上,根據語言的不同,編譯器可以是一遍(onepass)-所有的階段由一遍完成,其結果是編譯得很好,但(通常)代碼卻不太有效。Pascal語言和C語言均允許單遍編譯。(Modula-2語言的結構則要求編譯器至少有兩遍)。大多數帶有優化的編譯器都需要超過一遍:典型的安排是將一遍用於掃描和分析,將另一遍用於語義分析和源代碼層優化,第3遍用於代碼生成和目標層的優化。更深層的優化則可能需要更多的遍:5遍、6遍、甚至8遍都是可能的。\\n\\n4.語言定義和編譯器\\n\\n程式設計語言的詞法和語法結構通常用形式的術語指定,並使用正則表達式和上下文無關文法。但是,程式設計語言的語義通常仍然是由英語(或其他的自然語言)描述的。這些描述(與形式的詞法及語法結構一起)一般是集中在一個語言參考手冊(languagereferencemanual)或語言定義(languagedefinition)之中。因為編譯器的編寫者掌握的技術對於語言的定義有很大的影響,所以在使用了一種新的語言之後,語言的定義和編譯器同時也能夠得到開發。類似地,一種語言的定義對於構造編譯器所需的技術也有很大的關係。編譯器的編寫者更經常遇到的情況是:正在實現的語言是眾所周知的並已有了語言定義。有時這個語言定義已達到了某個語言標準(languagestandard)的層次,語言標準是指得到諸如美國國家標準協會(AmericanNationalStandardsInstitute,ANSI)或國際標準化組織(InternationalOrganizationforStandardization,ISO)的官方標準組織批準的標準。FORTRAN、Pascal和C語言就具有ANSI標準,Ada有一個通過了美國政府批準的標準。在這種情況下,編譯器的編寫者必須解釋語言的定義並執行符合語言定義的編譯器。通常做到這一點並不容易,但是有時由於有了標準測試程式集(測試組(testsuite)),就能夠測試編譯器(Ada有這樣一個測試組),這又變得簡單起來了。有時候,一種語言可從數學術語的形式定義(formaldefinition)中得到它的語義。現在人們已經使用了許多方法,儘管一個稱作表示語義(denotationalsemantics)的方法已經成為較為常用的方法,在函數編程共同體中尤為如此,但現在仍然冇有一種可成為標準的方法。當語言有一個形式定義時,那麼在理論上就有可能給出編譯器與該定義一致的數學證明,但是由於這太難了,而幾乎從未有人做過。無論怎樣,運行時環境的結構和行為是尤其受到語言定義影響的編譯器構造的一個方麵。\\n\\n\"

}

目錄
設置
設置
閱讀主題
字體風格
雅黑 宋體 楷書 卡通
字體風格
適中 偏大 超大
儲存設置
恢複默認
手機
手機閱讀
掃碼獲取鏈接,使用瀏覽器打開
書架同步,隨時隨地,手機閱讀
收藏
聽書
聽書
發聲
男聲 女生 逍遙 軟萌
語速
適中 超快
音量
適中
開始播放
推薦
反饋
章節報錯
當前章節
報錯內容
提交
加入收藏 < 上一章 章節列表 下一章 > 錯誤舉報