C++11利用regex輕鬆實現詞法分析器mini-lexer

來源:互聯網
上載者:User

標籤:name   第一個   span   osi   rac   進一步   添加   規則   程式員   

最近看完了<c++ primer>,發現c++11標準庫已經有Regex了(本人落後編譯器多年,現在都已經c++17了),正好我最近想擼個compiler,索性就先擼個詞法分析器,類似flex。項目代碼量很小,一共不到400行,但是如果不用Regex庫,自己寫NFA,DFA滿足最簡陋的正則文法也是挺輕鬆的,但是想要滿足Posix標準的文法,那就相當煩了,比如對大括弧{}的處理,\d{3}表示3位元字等等。那麼為何不直接用flex呢?一是因為flex不能很好地支援c++,第二是想拿c++練練手。下面簡單地介紹下項目實現:

github:  https://github.com/Yuandong-Chen/mini-lexer

 

項目一共就三個cpp檔案和三個與之對應的hpp標頭檔。用過flex的都知道,我們可以定義自己的Regex宏比如: A [a-z],然後再套用這個宏A去構建更複雜的Regex比如: {A}+(ok)\$ 去構建我們的token結構。然後還能在這個token上加上action,比如:{A}+(ok)\$  {printf("ok!\n"); return 1;} 然後對一系列這樣的token,根據他們的優先順序(前後順序),去分割文本內的字串。對外介面是yylex(), yytext,yyleng,yyline,yyin,yyout等等。於是我們就把這一系列的實現分為三個步驟,第一個檔案實現Regex宏的存取,我們用map<string, string>去儲存,標頭檔如下:

 1 #pragma once 2 #include <map> 3  4 namespace minilex { 5      6     class MacroHandler 7     { 8     private: 9         std::map<std::string, std::string> macrotab;10 11     public:12         MacroHandler() = default;13         ~MacroHandler() = default;14         std::string expandMacro(const std::string& macroname);15         void addMacro(std::pair<std::string, std::string> macro);16     };17 }

 

其中addMacro是存宏定義比如 {string("A"), string("[a-z]")}這樣一個pair結構,expandMacro取宏定義,但是會在最外層加上括弧,如果在map裡頭沒發現就返回Null 字元串。具體實現就不貼了,完全是上面的描述,具體參考項目代碼即可。

 

第二個檔案用於處理類似 {A}+(ok)\$這樣的運算式,已經對給定的字串s,我們根據已有的Regex規則和對應的順序去分割字串,標頭檔如下:

 1 #pragma once 2 #include <functional> 3 #include <list> 4 #include <regex> 5 #include "MacroHandler.hpp" 6  7 namespace minilex { 8      9     class RegularExp10     {11     private:12         bool success = false;13         std::string currentMatch = "";14         std::unique_ptr<MacroHandler> macrohp;15         std::list<std::pair<std::unique_ptr<std::regex>, std::string> > regularexps;16     public:17         std::string expandRegularExp(const std::string& rexp);18         std::string extractMacroName(const std::string& rexp, int &index, int max);19         std::string expandMacro(const std::string& macroname);20     public:21         RegularExp(std::unique_ptr<MacroHandler>&& macrorp);22         ~RegularExp() = default;23         void addRegularExp(std::string rexp);24         void removeRegularExp(std::string rexp);25         std::string eat(std::string& txt);26         bool isEaten(){return success;};27         std::string matchPattern(){return currentMatch;};28     };29 30 }

注意到,這裡也有個addRegularExp,我們可以這樣調用 addRegularExp("{A}+(ok)\$"); 我們可以用expandRegularExp函數完全展開Regex, 在這個函數裡頭就會去尋找並替換宏定義,如果A定義為{B},那麼函數會遞迴地解析宏定義,如果出現A A{A}這樣的遞迴宏,函數會無限遞迴調用並拋出棧溢出錯誤。removeRegularExp函數用於動態地移除規則,比如我們添加了"[a-z]+(ok)\$"運算式,但是文本分割到某個程度時,因為某些原因(比如undef等等)我們不需要這條規則了,我們就可以移除這個運算式。eat函數用於吃字串,吃掉的字串返回,吃剩的放在參數中。matchPattern函數告訴我們匹配了哪條Regex(或說規則)。具體實現由於利用了c++11的regex,非常簡單,可以查看項目具體代碼。

 

第三個檔案就是用於實現yylex()等對外介面了,這裡就貼出yylex這個函數的實現:

 1 int MiniLex::yylex() { 2         std::string eaten; 3         std::string pattern; 4         if(yyin.eof() && linebuffer.empty()) 5         { 6             return 0; 7         } 8  9         if(linebuffer.empty())10         {11             std::getline(yyin, linebuffer);12             yyline++;13         }14 15         eaten = reup->eat(linebuffer);16         yyleng = eaten.size();17         if(!reup->isEaten())18         {19             std::cerr<<"STOP, CANNOT INTERPRET STRING: "<<linebuffer<<std::endl;20             return 0;21         }22 23         pattern = reup->matchPattern();24 25         /* you are required to modify the following code for your own purposes */26         if(pattern == std::string("{Digit}+")) {27             std::cerr<<"RECOGNIZE: "<<yyleng<<‘ ‘<<eaten<<std::endl;28             return 1;29         }30         else if(pattern == std::string("{Alpha}+")) {31             std::cerr<<"RECOGNIZE: "<<yyleng<<‘ ‘<<eaten<<std::endl;32             return 2;33         }34         else if(pattern == std::string("{Equal}")) {35             std::cerr<<"RECOGNIZE: "<<yyleng<<‘ ‘<<eaten<<std::endl;36             return 3;37         }38         else if(pattern == std::string("{CAL}")) {39             std::cerr<<"RECOGNIZE: "<<yyleng<<‘ ‘<<eaten<<std::endl;40             return 4;41         }42         else if(pattern == std::string(".")) {43             std::cerr<<"UNRECOGNIZE: "<<yyleng<<‘ ‘<<eaten<<std::endl;44             return 5;45         }46 47         return 0;48     }

這裡有個小的問題,就是無法正確匹配如 [a-z]"\n"[0-9]這樣的Regex,因為我們是一行一行地讀入,一行匹配完了才去讀下一行,並匹配下一個token,但是我想沒人會定義這樣一個奇怪的跨行的token吧。另外我沒有進一步去實現讀取配置文檔產生代碼方式,而是讓程式員直接去修改我們的原始碼,我覺得這樣更加自由,你甚至可以大改特改我們的源檔案,而非估摸一堆古怪的配置文法。

C++11利用regex輕鬆實現詞法分析器mini-lexer

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.