antlr4注釋處理-使用Antlr設(shè)計(jì)C++預(yù)處理器的提示_第1頁(yè)
antlr4注釋處理-使用Antlr設(shè)計(jì)C++預(yù)處理器的提示_第2頁(yè)
antlr4注釋處理-使用Antlr設(shè)計(jì)C++預(yù)處理器的提示_第3頁(yè)
antlr4注釋處理-使用Antlr設(shè)計(jì)C++預(yù)處理器的提示_第4頁(yè)
antlr4注釋處理-使用Antlr設(shè)計(jì)C++預(yù)處理器的提示_第5頁(yè)
已閱讀5頁(yè),還剩4頁(yè)未讀 繼續(xù)免費(fèi)閱讀

下載本文檔

版權(quán)說明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請(qǐng)進(jìn)行舉報(bào)或認(rèn)領(lǐng)

文檔簡(jiǎn)介

antlr4注釋處理_使?Antlr設(shè)計(jì)C++預(yù)處理器的提??C++創(chuàng)建?定義編譯器的第?步之?就是開發(fā)編譯器的預(yù)處理器引擎。通常,C++編譯器具有單獨(dú)的預(yù)處理引擎,可以完成以下四個(gè)基本任務(wù):定義和重新定義宏(##define,##undef)。?持頭?件(#include指令)。?持條件編譯(#ifdef,#ifndef,#else,#endif)。從輸?源清單中刪除所有注釋。通常,預(yù)處理器引擎的輸出然后饋送到C++詞法分析器/解析器組合。本?討論使?Antlr的C++預(yù)處理器的設(shè)計(jì)。您應(yīng)該對(duì)Antlr和?上?下的遞歸下降解析器的概念有所了解。眾所周知,本?討論的所有代碼都可在Antlr-2.7.2上運(yùn)?,并使?gcc-3.4.4進(jìn)?編譯。為預(yù)處理器創(chuàng)建?個(gè)解析器將Antlr?作創(chuàng)建C++編譯器的?選解析器?成器?具的?個(gè)顯著好處是不需要單獨(dú)的預(yù)處理器引擎。預(yù)處理器引擎可以集成為詞法分析器的?部分。要了解此策略,請(qǐng)回顧詞法分析器和解析器通常是如何?作的。詞法分析器處理原始數(shù)據(jù)(在本例中為.h/.cpp?件),從該數(shù)據(jù)創(chuàng)建令牌,然后將令牌傳遞給解析器。解析器依次處理令牌并針對(duì)語(yǔ)法進(jìn)?驗(yàn)證,進(jìn)?語(yǔ)義檢查并最終?成匯編代碼。使?單個(gè)編譯器和預(yù)處理器引擎的關(guān)鍵在于對(duì)詞法分析器的適當(dāng)修改。Antlr詞法分析器已擴(kuò)展為可以預(yù)處理C++源,并且僅將相關(guān)令牌傳遞給解析器。解析器永遠(yuǎn)不會(huì)意識(shí)到預(yù)處理部分;它僅與它從詞法分析器收到的令牌有關(guān)??紤]以下代碼段:#defineUSE_STREAMS#ifdefUSE_STREAMS#include<iostream>#else#include<stdio.h>#endif假定此代碼段已聲明為C++源?件的?部分。詞法分析器需要通過包含iostream標(biāo)頭將其找到的第?個(gè)令牌傳遞給解析器。#define映射和#ifdef條件評(píng)估是詞法分析器的附加職責(zé)。通過定義新標(biāo)記來增強(qiáng)Antlr詞法分析器使?C++語(yǔ)?的詞法分析器根據(jù)C++語(yǔ)?標(biāo)準(zhǔn)定義令牌。例如,您通常會(huì)找到表?平均lexer?件中定義的變量名稱和語(yǔ)?關(guān)鍵字的標(biāo)記。要將預(yù)處理引擎與詞法分析器結(jié)合使?,必須定義與預(yù)處理器構(gòu)造相對(duì)應(yīng)的新令牌類型。在遇到這些構(gòu)造時(shí),詞法分析器將采取必要的措施。詞法分析器還必須從源代碼中刪除注釋。在這種情況下,詞法分析器在遇到注釋標(biāo)記時(shí),需要忽略該標(biāo)記并繼續(xù)尋找下?個(gè)可?標(biāo)記。重要的是要注意,這些標(biāo)記沒有被定義為語(yǔ)?標(biāo)準(zhǔn)的?部分。它們本質(zhì)上是實(shí)現(xiàn)定義的令牌。在詞法分析器中定義新標(biāo)記該詞法分析器必須包含以下標(biāo)記以及語(yǔ)?要求的標(biāo)記:COMMENT_TOKEN,INCLUDE_TOKEN,DEFINE_TOKEN,UNDEF_TOKEN,IFDEF_TOKEN,ELSE_TOKEN和ENDIF_TOKEN。本?討論了?個(gè)原型詞法分析器,它?持前?提到的預(yù)處理器宏以及整數(shù)和長(zhǎng)數(shù)據(jù)類型的變量聲明。清單1顯?了處理這些令牌的準(zhǔn)系統(tǒng)詞法分析器/解析器組合。清單1.?持預(yù)處理器標(biāo)記和long/int聲明的準(zhǔn)系統(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樣式/**/多?注釋樣式編寫。詞法分析器被擴(kuò)展為包括COMMENT_TOKEN的規(guī)則。遇到此令牌時(shí),需要明確告知詞法分析器不要將此令牌傳遞給解析器,?要跳過它并繼續(xù)尋找下?個(gè)可?的令牌。您可以使?$setType的$setType函數(shù)執(zhí)?此操作。您?需在解析器端添加?持,因?yàn)樗肋h(yuǎn)不會(huì)傳遞注釋令牌。參見清單2。清單2.定義詞法分析器以從C/C++代碼中剝離注釋COMMENT_TOKEN:("http://"(~'\n')*'\n'{newline();}|"/*"({LA(2)!='/'}?'*'|'\n'{newline();}|~('*'|'\n'))*"*/"){$setType(ANTLR_USE_NAMESPACE(antlr)Token::SKIP);};該代碼對(duì)于C++樣式的注釋?夠簡(jiǎn)單。對(duì)于C樣式的注釋,請(qǐng)注意{LA(2)!='/'}?匹配*前使?。這意味著如果*后?有?個(gè)/,則此規(guī)則將不匹配,?詞法分析器最終將匹配*/。這是必需的,因?yàn)?*thisis*/aninvalidcomment*/是?效的C++注釋。這也意味著詞法分析器所需的最?提前量為2。LALA(2)被稱為語(yǔ)義謂詞-提?詞法分析器決定在輸?流中接下來要尋找的字符。包括?件處理如前所述,解析器需要查看連續(xù)的令牌流。因此,當(dāng)詞法分析器遇到包含?件時(shí),需要切換流,并在遇到包含?件的末尾時(shí)切換回流。解析器不了解此流切換,實(shí)際上將包含的?件視為連續(xù)的令牌流。您可以使?Antlr通過多種?式實(shí)現(xiàn)此?為。有關(guān)詳細(xì)討論見“部分。本?使?Antlr的TokenStreamSelector類。與其使?的詞法分析器初始化解析器,不如使?TokenStreamSelector對(duì)象初始化解析器。在內(nèi)部,TokenStreamSelector類維護(hù)?堆詞法分析器。當(dāng)詞法分析器遇到新的輸?流時(shí),它將創(chuàng)建?個(gè)新的詞法分析器?于處理新的流,然后將新的詞法分析器附加到tokenstreamselector對(duì)象,以便使?來獲取所有將來的令牌(直到到達(dá)新流的末尾)。新的詞法分析器類。接下來,調(diào)?TokenStreamSelector類的retry?法,該類現(xiàn)在從新的輸?流中獲取令牌。唯?要做的另?件事是在遇到包含?件中的EOF時(shí)切換回先前的輸?流。為了允許?戶定義的遇到?尾的動(dòng)作,uponEOF提供了預(yù)定義的例程。您必須通過調(diào)?TokenStreamSelector::pop()修改此例程以切換到先前的輸?流。清單3顯?了包含?件處理的代碼段。清單3.使?TokenStreamSelector對(duì)象初始化解析器#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中描述了與詞法分析器相關(guān)的更改。清單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};請(qǐng)注意,TokenStreamSelector對(duì)象是有意全局變量,因?yàn)樗枰鳛閘exeruponEOF?法的?部分進(jìn)?共享。另外,在uponEOF?法?章中,您必須調(diào)?pop?法retry以便TokenStreamSelector再次在當(dāng)前流中查找下?個(gè)標(biāo)記。請(qǐng)注意,此處描述的包含?件處理尚未完成,但取決于條件宏?持。例如,如果?個(gè)源代碼段包括?個(gè)內(nèi)的?件#ifdefA..#endif,其中嵌段A沒有定義,則對(duì)于處理INCLUDE_TOKEN被丟棄。在和部分中對(duì)此進(jìn)?了進(jìn)?步說明。處理宏宏處理主要是在哈希表的幫助下完成的。本?使?標(biāo)準(zhǔn)的STL哈希容器。以最簡(jiǎn)單的形式,對(duì)宏的?#持de意fin味eA著和?#u持ndefA類的構(gòu)造。您可以輕松地做到這?點(diǎn),?法是在遇到#define符串“A”壓?哈希表,并在遇到#undef將其從哈希表中刪除。哈希表通常被定義為詞法分析器的?部分。另外,還要注意的是,令牌#define和#undef不能達(dá)到解析器-因?yàn)檫@個(gè)原因,你添加$setType(ANTLR_USE_NAMESPACE(Antlr)Token::SKIP);作為相應(yīng)令牌處理的?部分。清單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ù)條件來考慮哪些令牌到達(dá)解析器。例如,查看以下代碼?段:#defineA#ifdefA#ifdefBintc;#endifintd;#endif在遇到第?個(gè)#ifdef的令牌時(shí),您必須根據(jù)先前是否已定義A來決定是否將遇到的后續(xù)令牌傳遞到解析器。在此階段,您將使?之前定義的哈希表。接下來,您必須考慮?持嵌套的#ifdef塊。因?yàn)槟枰谟龅矫總€(gè)#ifdef檢查路徑條件,所以為路徑條件維護(hù)堆棧是合乎邏輯的。每個(gè)#ifdef/#elsif將路徑條件添加到堆棧頭;匹配的#endif將從堆棧中刪除路徑條件。在看到更詳細(xì)地解釋該概念的代碼之前,您需要了解Antlrlexer類中的另?個(gè)重要?法:nextToken,解析器連續(xù)調(diào)?該?法以從輸?流中檢索下?個(gè)令牌。您不能直接修改此例程,因此最佳?法是從cppLexer類派??個(gè)類,然后根據(jù)路徑條件重新定義nextToken?法。如果路徑條件為true,則例程將下?個(gè)可?令牌返回到解析器;否則,例程返回解析器。否則,它將繼續(xù)直到在令牌流中找到匹配的#endif為?。清單6顯?了派?的lexer類的源。代碼中不應(yīng)直接實(shí)例化cppLexer;解析器應(yīng)使?派?的lexer類對(duì)象的副本進(jìn)?初始化。清單6.定義?個(gè)新的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中顯?了與詞法分析器相關(guān)的更改。清單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);};查看代碼,您會(huì)看到processToken被定義為?堆布爾值,?于存儲(chǔ)路徑條件。遇到#ifdef,如果代碼已經(jīng)在真實(shí)路徑中,則它將測(cè)試路徑條件是否有效。?持將表達(dá)式作為宏的?部分您可以使?宏來表?包含數(shù)字,標(biāo)識(shí)符,數(shù)學(xué)運(yùn)算符甚?函數(shù)調(diào)?的復(fù)雜表達(dá)式。為此m,a必cro須Ar使gs?字符串替換后處理的C++代碼中每次出現(xiàn)的宏定義。上下?確定此替換在語(yǔ)法上是否有效。例如,考慮這種情況:#defineAb,c。在?的某處,您有intA;,這是有效的C++;但是switch(A)顯然是?效的C++。您需要逐次分析與字符串關(guān)聯(lián)的流,并讓語(yǔ)法規(guī)則確定上下?的語(yǔ)法有效性。此?法的代碼如下:1.在cppAdvancedLexer::nextToken捕獲與macroTex

溫馨提示

  • 1. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請(qǐng)下載最新的WinRAR軟件解壓。
  • 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請(qǐng)聯(lián)系上傳者。文件的所有權(quán)益歸上傳用戶所有。
  • 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁(yè)內(nèi)容里面會(huì)有圖紙預(yù)覽,若沒有圖紙預(yù)覽就沒有圖紙。
  • 4. 未經(jīng)權(quán)益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
  • 5. 人人文庫(kù)網(wǎng)僅提供信息存儲(chǔ)空間,僅對(duì)用戶上傳內(nèi)容的表現(xiàn)方式做保護(hù)處理,對(duì)用戶上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對(duì)任何下載內(nèi)容負(fù)責(zé)。
  • 6. 下載文件中如有侵權(quán)或不適當(dāng)內(nèi)容,請(qǐng)與我們聯(lián)系,我們立即糾正。
  • 7. 本站不保證下載資源的準(zhǔn)確性、安全性和完整性, 同時(shí)也不承擔(dān)用戶因使用這些下載資源對(duì)自己和他人造成任何形式的傷害或損失。

最新文檔

評(píng)論

0/150

提交評(píng)論