版權說明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權,請進行舉報或認領
文檔簡介
antlr4注釋處理_使?Antlr設計C++預處理器的提??C++創(chuàng)建?定義編譯器的第?步之?就是開發(fā)編譯器的預處理器引擎。通常,C++編譯器具有單獨的預處理引擎,可以完成以下四個基本任務:定義和重新定義宏(##define,##undef)。?持頭?件(#include指令)。?持條件編譯(#ifdef,#ifndef,#else,#endif)。從輸?源清單中刪除所有注釋。通常,預處理器引擎的輸出然后饋送到C++詞法分析器/解析器組合。本?討論使?Antlr的C++預處理器的設計。您應該對Antlr和?上?下的遞歸下降解析器的概念有所了解。眾所周知,本?討論的所有代碼都可在Antlr-2.7.2上運?,并使?gcc-3.4.4進?編譯。為預處理器創(chuàng)建?個解析器將Antlr?作創(chuàng)建C++編譯器的?選解析器?成器?具的?個顯著好處是不需要單獨的預處理器引擎。預處理器引擎可以集成為詞法分析器的?部分。要了解此策略,請回顧詞法分析器和解析器通常是如何?作的。詞法分析器處理原始數(shù)據(jù)(在本例中為.h/.cpp?件),從該數(shù)據(jù)創(chuàng)建令牌,然后將令牌傳遞給解析器。解析器依次處理令牌并針對語法進?驗證,進?語義檢查并最終?成匯編代碼。使?單個編譯器和預處理器引擎的關鍵在于對詞法分析器的適當修改。Antlr詞法分析器已擴展為可以預處理C++源,并且僅將相關令牌傳遞給解析器。解析器永遠不會意識到預處理部分;它僅與它從詞法分析器收到的令牌有關??紤]以下代碼段:#defineUSE_STREAMS#ifdefUSE_STREAMS#include<iostream>#else#include<stdio.h>#endif假定此代碼段已聲明為C++源?件的?部分。詞法分析器需要通過包含iostream標頭將其找到的第?個令牌傳遞給解析器。#define映射和#ifdef條件評估是詞法分析器的附加職責。通過定義新標記來增強Antlr詞法分析器使?C++語?的詞法分析器根據(jù)C++語?標準定義令牌。例如,您通常會找到表?平均lexer?件中定義的變量名稱和語?關鍵字的標記。要將預處理引擎與詞法分析器結(jié)合使?,必須定義與預處理器構(gòu)造相對應的新令牌類型。在遇到這些構(gòu)造時,詞法分析器將采取必要的措施。詞法分析器還必須從源代碼中刪除注釋。在這種情況下,詞法分析器在遇到注釋標記時,需要忽略該標記并繼續(xù)尋找下?個可?標記。重要的是要注意,這些標記沒有被定義為語?標準的?部分。它們本質(zhì)上是實現(xiàn)定義的令牌。在詞法分析器中定義新標記該詞法分析器必須包含以下標記以及語?要求的標記:COMMENT_TOKEN,INCLUDE_TOKEN,DEFINE_TOKEN,UNDEF_TOKEN,IFDEF_TOKEN,ELSE_TOKEN和ENDIF_TOKEN。本?討論了?個原型詞法分析器,它?持前?提到的預處理器宏以及整數(shù)和長數(shù)據(jù)類型的變量聲明。清單1顯?了處理這些令牌的準系統(tǒng)詞法分析器/解析器組合。清單1.?持預處理器標記和long/int聲明的準系統(tǒng)詞法分析器/解析器header{#include"Main.hpp"#include<string>#include<iostream>}options{language=Cpp;}classcppParserextendsParser;start:(declaration)+;declaration:typea:IDENTIFIER(COMMAb:IDENTIFIER)*SEMI;type:INT|LONG;{#include<fstream>#include"cppParser.hpp"}classcppLexerextendsLexer;options{charVocabulary='\3'..'\377';k=5;}tokens{INT="int";LONG="long";}DEFINE_TOKEN:"#define"WSmacroText:IDENTIFIERWSmacroArgs:MACRO_TEXT;UNDEF_TOKEN:"#undef"IDENTIFIER;IFDEF_TOKEN:("ifdef"|"#ifndef")WSIDENTIFIER;ELSE_TOKEN:("else"|"elsif"WSIDENTIFIER);ENDIF_TOKEN:"endif";INCLUDE_TOKEN:"#include"(WS)?f:STRING;COMMENT_TOKEN:("http://"(~'\n')*'\n'{newline();}|"/*"({LA(2)!='/'}?'*'|'\n'{newline();}|~('*'|'\n'))*"*/");IDENTIFIER:('a'..'z'|'A'..'Z'|'_')('a'..'z'|'A'..'Z'|'_'|'0'..'9')*;STRING:'"'!(~'"')*'"'!SEMI:';';COMMA:',';WS:(''|'\t'|'\f'|'\n'{newline();})+;MACRO_TEXT:(~'\n')*;定義詞法分析器/解析器組合以去除注釋注釋可以?C++//樣式或C樣式/**/多?注釋樣式編寫。詞法分析器被擴展為包括COMMENT_TOKEN的規(guī)則。遇到此令牌時,需要明確告知詞法分析器不要將此令牌傳遞給解析器,?要跳過它并繼續(xù)尋找下?個可?的令牌。您可以使?$setType的$setType函數(shù)執(zhí)?此操作。您?需在解析器端添加?持,因為它永遠不會傳遞注釋令牌。參見清單2。清單2.定義詞法分析器以從C/C++代碼中剝離注釋COMMENT_TOKEN:("http://"(~'\n')*'\n'{newline();}|"/*"({LA(2)!='/'}?'*'|'\n'{newline();}|~('*'|'\n'))*"*/"){$setType(ANTLR_USE_NAMESPACE(antlr)Token::SKIP);};該代碼對于C++樣式的注釋?夠簡單。對于C樣式的注釋,請注意{LA(2)!='/'}?匹配*前使?。這意味著如果*后?有?個/,則此規(guī)則將不匹配,?詞法分析器最終將匹配*/。這是必需的,因為/*thisis*/aninvalidcomment*/是?效的C++注釋。這也意味著詞法分析器所需的最?提前量為2。LALA(2)被稱為語義謂詞-提?詞法分析器決定在輸?流中接下來要尋找的字符。包括?件處理如前所述,解析器需要查看連續(xù)的令牌流。因此,當詞法分析器遇到包含?件時,需要切換流,并在遇到包含?件的末尾時切換回流。解析器不了解此流切換,實際上將包含的?件視為連續(xù)的令牌流。您可以使?Antlr通過多種?式實現(xiàn)此?為。有關詳細討論見“部分。本?使?Antlr的TokenStreamSelector類。與其使?的詞法分析器初始化解析器,不如使?TokenStreamSelector對象初始化解析器。在內(nèi)部,TokenStreamSelector類維護?堆詞法分析器。當詞法分析器遇到新的輸?流時,它將創(chuàng)建?個新的詞法分析器?于處理新的流,然后將新的詞法分析器附加到tokenstreamselector對象,以便使?來獲取所有將來的令牌(直到到達新流的末尾)。新的詞法分析器類。接下來,調(diào)?TokenStreamSelector類的retry?法,該類現(xiàn)在從新的輸?流中獲取令牌。唯?要做的另?件事是在遇到包含?件中的EOF時切換回先前的輸?流。為了允許?戶定義的遇到?尾的動作,uponEOF提供了預定義的例程。您必須通過調(diào)?TokenStreamSelector::pop()修改此例程以切換到先前的輸?流。清單3顯?了包含?件處理的代碼段。清單3.使?TokenStreamSelector對象初始化解析器#include<iostream>#include<fstream>…TokenStreamSelectorselector;cppParser*parser;cppLexer*mainLexer;intmain(intargc,char**argv){try{std::ifstreaminputstream("test.c",std::ifstream::in);mainLexer=newcppLexer(inputstream);//notifyselectoraboutstartinglexer;nameforconvenienceselector.addInputStream(mainLexer,"main");selector.select("main");//startwithmainlexer//Createparserattachedtoselectorparser=newcppParser(selector);parser->setFilename("test.c");parser->startRule();}catch(exception&e){cerr<<"exception:"<<e.what()<<endl;}return0;}清單4中描述了與詞法分析器相關的更改。清單4.在詞法分析器中包含?件處理classcppLexerextendsLexer;…{public:voiduponEOF(){if(selector.getCurrentStream()!=mainLexer){selector.pop();//returntooldlexer/streamselector.retry();}else{ANTLR_USE_NAMESPACE(std)cout<<"HitEOFofmainfile\n";}}}INCLUDE_TOKEN:"#include"(WS)?f:STRING{ANTLR_USING_NAMESPACE(std)//createlexertohandleincludestringname=f->getText();ifstream*input=newifstream(name.c_str());if(!*input){cerr<<"cannotfindfile"<<name<<endl;}cppLexer*sublexer=newcppLexer(*input);//makesureerrorsarereportedinrightfilesublexer->setFilename(name);parser->setFilename(name);//pushthepreviouslexerstreamandmakesublexercurrentselector.push(sublexer);//ignorethistoken,re-lookfortokenintheswitchedstreamselector.retry();//throwsTokenStreamRetryException};請注意,TokenStreamSelector對象是有意全局變量,因為它需要作為lexeruponEOF?法的?部分進?共享。另外,在uponEOF?法?章中,您必須調(diào)?pop?法retry以便TokenStreamSelector再次在當前流中查找下?個標記。請注意,此處描述的包含?件處理尚未完成,但取決于條件宏?持。例如,如果?個源代碼段包括?個內(nèi)的?件#ifdefA..#endif,其中嵌段A沒有定義,則對于處理INCLUDE_TOKEN被丟棄。在和部分中對此進?了進?步說明。處理宏宏處理主要是在哈希表的幫助下完成的。本?使?標準的STL哈希容器。以最簡單的形式,對宏的?#持de意fin味eA著和?#u持ndefA類的構(gòu)造。您可以輕松地做到這?點,?法是在遇到#define符串“A”壓?哈希表,并在遇到#undef將其從哈希表中刪除。哈希表通常被定義為詞法分析器的?部分。另外,還要注意的是,令牌#define和#undef不能達到解析器-因為這個原因,你添加$setType(ANTLR_USE_NAMESPACE(Antlr)Token::SKIP);作為相應令牌處理的?部分。清單5顯?了這種?為。清單5.?持#define和#undefclasscppLexerextendsLexer;…{boolprocessingMacro;std::map<std::string,std::string>macroDefns;public:voiduponEOF(){…//Codeforincludeprocessing}}…DEFINE_TOKEN:"#define"{processingMacro=true;}WSmacroText:IDENTIFIERWSmacroArgs:MACRO_TEXT{macroDefns[macroText->getText()]=string(macroArgs->getText());$setType(ANTLR_USE_NAMESPACE(antlr)Token::SKIP);};UNDEF_TOKEN:"#undef"WSmacroText:IDENTIFIER{macroDefns.erase(macroText->getText());$setType(ANTLR_USE_NAMESPACE(antlr)Token::SKIP);};MACRO_TEXT:{processingMacro}?(~'\n')*{processingMacro=false;};條件宏?持?義地說,?持條件宏意味著您需要根據(jù)條件來考慮哪些令牌到達解析器。例如,查看以下代碼?段:#defineA#ifdefA#ifdefBintc;#endifintd;#endif在遇到第?個#ifdef的令牌時,您必須根據(jù)先前是否已定義A來決定是否將遇到的后續(xù)令牌傳遞到解析器。在此階段,您將使?之前定義的哈希表。接下來,您必須考慮?持嵌套的#ifdef塊。因為您需要在遇到每個#ifdef檢查路徑條件,所以為路徑條件維護堆棧是合乎邏輯的。每個#ifdef/#elsif將路徑條件添加到堆棧頭;匹配的#endif將從堆棧中刪除路徑條件。在看到更詳細地解釋該概念的代碼之前,您需要了解Antlrlexer類中的另?個重要?法:nextToken,解析器連續(xù)調(diào)?該?法以從輸?流中檢索下?個令牌。您不能直接修改此例程,因此最佳?法是從cppLexer類派??個類,然后根據(jù)路徑條件重新定義nextToken?法。如果路徑條件為true,則例程將下?個可?令牌返回到解析器;否則,例程返回解析器。否則,它將繼續(xù)直到在令牌流中找到匹配的#endif為?。清單6顯?了派?的lexer類的源。代碼中不應直接實例化cppLexer;解析器應使?派?的lexer類對象的副本進?初始化。清單6.定義?個新的lexer類classcppAdvancedLexer:publiccppLexer{public:cppAdvancedLexer(ANTLR_USE_NAMESPACE(std)istream&in):cppLexer(in){}RefTokennextToken(){//keeplookingforatokenuntilyoudon't//getaretryexceptionfor(;;){try{RefToken_next=cppLexer::nextToken();if(processToken.empty()||processToken.top())//definedincppLexerreturn_next;}catch(TokenStreamRetryException&/*r*/){//justretry"forever"}}}};清單7中顯?了與詞法分析器相關的更改。清單7.詞法分析器中的條件宏?持{public:std::stack<bool>processToken;std::stack<bool>pathProcessed;std::map<std::string,std::list<std::string>>macroDefns;voiduponEOF(){…//Codeforincludeprocessingdefinedearlier}}IFDEF_TOKEN{boolnegate=false;}:("#ifdef"|"#ifndef"{negate=true;})WSmacroText:IDENTIFIER{boolmacroAlreadyDefined=false;if(macroDefns.find(macroText->getText())!=macroDefns.end())macroAlreadyDefined=true;processToken.push(negate?!macroAlreadyDefined:macroAlreadyDefined);pathProcessed(processToken.top());$setType(ANTLR_USE_NAMESPACE(antlr)Token::SKIP);};ELSE_TOKEN:("#else"{bool&pathCondition=processToken.top();pathCondition=!processToken.top();//nootherpathistrue}|"#elsif"WSmacroText:IDENTIFIER{if(!processToken.top()&&!pathProcessed.top()){if(macroDefns.find(macroText->getText())!=macroDefns.end()){processToken.push(true);pathProcessed.push(true);}}else{bool&condition=pathProcessed.top();condition=false;}){$setType(ANTLR_USE_NAMESPACE(antlr)Token::SKIP);};ENDIF_TOKEN:"#endif"{processToken.pop();pathProcessed.pop();$setType(ANTLR_USE_NAMESPACE(antlr)Token::SKIP);};查看代碼,您會看到processToken被定義為?堆布爾值,?于存儲路徑條件。遇到#ifdef,如果代碼已經(jīng)在真實路徑中,則它將測試路徑條件是否有效。?持將表達式作為宏的?部分您可以使?宏來表?包含數(shù)字,標識符,數(shù)學運算符甚?函數(shù)調(diào)?的復雜表達式。為此m,a必cro須Ar使gs?字符串替換后處理的C++代碼中每次出現(xiàn)的宏定義。上下?確定此替換在語法上是否有效。例如,考慮這種情況:#defineAb,c。在?的某處,您有intA;,這是有效的C++;但是switch(A)顯然是?效的C++。您需要逐次分析與字符串關聯(lián)的流,并讓語法規(guī)則確定上下?的語法有效性。此?法的代碼如下:1.在cppAdvancedLexer::nextToken捕獲與macroTex
溫馨提示
- 1. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請下載最新的WinRAR軟件解壓。
- 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請聯(lián)系上傳者。文件的所有權益歸上傳用戶所有。
- 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁內(nèi)容里面會有圖紙預覽,若沒有圖紙預覽就沒有圖紙。
- 4. 未經(jīng)權益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
- 5. 人人文庫網(wǎng)僅提供信息存儲空間,僅對用戶上傳內(nèi)容的表現(xiàn)方式做保護處理,對用戶上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對任何下載內(nèi)容負責。
- 6. 下載文件中如有侵權或不適當內(nèi)容,請與我們聯(lián)系,我們立即糾正。
- 7. 本站不保證下載資源的準確性、安全性和完整性, 同時也不承擔用戶因使用這些下載資源對自己和他人造成任何形式的傷害或損失。
最新文檔
- 教育培訓機構(gòu)探班制度
- 規(guī)定完善教育培訓制度
- 化妝培訓機構(gòu)財務制度
- 安全生產(chǎn)車輛培訓制度
- 對外培訓收費制度
- 技術教育培訓制度
- 工廠治安防范培訓制度
- 輔導員進修培訓制度
- 電工合金熔煉及熱變形工崗前工作意識考核試卷含答案
- 化工洗滌工操作評估水平考核試卷含答案
- 新零售模式下人才培養(yǎng)方案
- 上海市徐匯區(qū)2026屆初三一?;瘜W試題(含答案)
- 電力工程課程設計-某機床廠變電所設計
- 馬鞍山經(jīng)濟技術開發(fā)區(qū)建設投資有限公司馬鞍山城鎮(zhèn)南部污水處理廠擴建工程項目環(huán)境影響報告書
- Unit 2 Reading and Thinking教學課件(英語選擇性必修第一冊人教版)
- 兒童常用補液
- GB/T 615-2006化學試劑沸程測定通用方法
- GB/T 22085.2-2008電子束及激光焊接接頭缺欠質(zhì)量分級指南第2部分:鋁及鋁合金
- GB/T 19939-2005光伏系統(tǒng)并網(wǎng)技術要求
- GB/T 18853-2015液壓傳動過濾器評定濾芯過濾性能的多次通過方法
- 工業(yè)管道施工與驗收規(guī)范
評論
0/150
提交評論