因爲llvm的資料比較稀缺, 因此品讀官方文檔就成了最佳瞭解llvm的方法, tutorial的整個流程目的是實現一個語法簡單, 但較爲完整的實現一個編譯器. 而其中第一個步驟即是簡單地實現一個前端parser, parser算法整個編譯器流程中最爲簡單的一部分, 但也是整個編譯器的入口(話說當時學編譯原理的時候老師主要講的就是這一部分).前端
tutorial中這個Parser主要分爲三個部分, lexer, AST(abstract syntax tree) 與 parser主體, 代碼很清晰, 咱們來一部分一部分看.git
enum Token { tok_eof = -1, tok_def = -2, tok_extern = -3, tok_identifier = -4, tok_number = -5 }; // 返回token類型 static std::string IdentifierStr; static double NumVal; // 值標記 static int gettok() { // 去空格 static int Lastc = ' '; while(isspace(Lastc)) Lastc = getchar(); // 判斷標識符 if(isalpha(Lastc)) { IdentifierStr = Lastc; while(isalnum(Lastc = getchar())) IdentifierStr += Lastc; if(IdentifierStr == "def") return tok_def; if(IdentifierStr == "extern") return tok_extern; return tok_identifier; } // 判斷數字 if(isdigit(Lastc) || Lastc == '.') { std::string Numstr; do { Numstr += Lastc; Lastc = getchar(); }while(isdigit(Lastc) || Lastc == '.'); NumVal = strtod(Numstr.c_str(), nullptr); return tok_number; } // 判斷註釋 if(Lastc == '#') { do Lastc = getchar(); while(Lastc != EOF || Lastc != '\n' || Lastc != '\r'); if(Lastc != EOF) return gettok(); } // 判斷結尾 if(Lastc == EOF) return tok_eof; // 判斷特殊字符(';', '(', ')', ',') int Thischar = Lastc; Lastc = getchar(); return Thischar; }
這個lexer的目的十分清楚, 識別token, 這個token能夠是數字, 變量或關鍵字, 識別後若是是變量或數字就返回變量名或值. 其中枚舉類型列舉了全部返回的類型.算法
namespace { class ExprAST { public: virtual ~ExprAST() = default; }; // 數值類 class NumberExprAST : public ExprAST{ double Val; public: NumberExprAST(double Val) : Val(Val) {} }; // 簡單變量類 class VariableExprAST : public ExprAST { std::string Name; public: VariableExprAST(const std::string &Name) : Name(Name) {} }; // 運算表達式類 class BinaryExprAST { char Op; std::unique_ptr<ExprAST> LHS, RHS; public: BinaryExprAST(char Op, std::unique_ptr<ExprAST> LHS, std::unique_ptr<ExprAST> RHS) : Op(Op), LHS(std::move(LHS)), RHS(std::move(RHS)) {} }; // 數組類 class CallExprAST { std::string Callee; std::vector<std::unique_ptr<ExprAST>> Args; public: CallExprAST(std::string Callee, std::vector<std::unique_ptr<ExprAST>> Args) : Callee(Callee), Args(std::move(Args)) {} }; // 函數原型類(函數名, 參數名數組) class PrototypeAST { std::string Name; std::vector<std::string> Args; public: PrototypeAST(const std::string &Name, std::vector<std::string> Args) : Name(Name), Args(std::move(Args)) {} const std::string &getName() const {return Name;} }; // 函數類(函數原型, 函數體) class FunctionAST { std::unique_ptr<PrototypeAST> Proto; std::unique_ptr<ExprAST> Body; public: FunctionAST(std::unique_ptr<PrototypeAST> Proto, std::unique_ptr<ExprAST> Body) : Proto(std::move(Proto)), Body(std::move(Body)) {} }; }
抽象語法樹也比較清晰, 一個有七個類, 分別表達了從簡單變量到函數的定義方法express
/===---------------------------------------------------------------------------------------------===// // parser // //===---------------------------------------------------------------------------------------------===// static int CurTok; static int getNextToken() { return CurTok = gettok(); } static std::map<char,int> BinopPrecedence; /* * 返回操做符優先級 * 不吞token */ static int GetTokPrecedence() { if(!isascii(CurTok)) return -1; int TokPrec = BinopPrecedence[CurTok]; if(TokPrec <= 0) return -1; return TokPrec; } std::unique_ptr<ExprAST> LogError(const char *Str) { fprintf(stderr, "Error: %s\n", Str); return nullptr; } // PrototypeAST是ExprAST上層的一個point std::unique_ptr<PrototypeAST> LogErrorP(const char *Str) { LogError(Str); return nullptr; } static std::unique_ptr<ExprAST> ParseExpression(); /* * 最底層函數 --- 識別數字 * 1.生成返回 * 2.下一token * 3.返回 */ static std::unique_ptr<ExprAST> ParseNumberExpr() { auto Result = llvm::make_unique<NumberExprAST>(NumVal); getNextToken(); return std::move(Result); } /* * 最底層函數 --- 識別括號體 * 1.eat '(' * 2.識別主體 * 3.找 ')' */ static std::unique_ptr<ExprAST> ParseParenExpr() { getNextToken(); auto V = ParseExpression(); if (!V) return nullptr; if(CurTok != ')') return LogError("expected ')'"); getNextToken(); return V; } /* * 最底層函數 --- 識別標識符 // 單個 或 數組類型 * 1.判斷'(' * 2.判斷')' * 3.while,push進vector * 4.判斷')'與',' * 5.eat')' * 6.返回 */ static std::unique_ptr<ExprAST> ParseIdentifierExpr() { std::string IdName = IdentifierStr; getNextToken(); if(CurTok != '(') // 單標識符, 無括號 return llvm::make_unique<VariableExprAST>(IdName); getNextToken(); std::vector<std::unique_ptr<ExprAST>> Args; if(CurTok != ')') { while(true) { if(auto Arg = ParseExpression()) Args.push_back(std::move(Arg)); else return nullptr; // (a,b, null if(CurTok == ')') break; if(CurTok != ',') return LogError("Expected ')' or ',' in argument list"); // 標識符支持',', 參數列表不支持',' getNextToken(); // eat ',' } } getNextToken(); return llvm::make_unique<CallExprAST>(IdName, std::move(Args)); } /* * 識別主函數(一個expression的子部分): 標識符 或 數字 或 括號 * 不吞token */ static std::unique_ptr<ExprAST> ParsePrimary() { switch(CurTok) { default: return LogError("unknown token when expecting an expression"); case tok_identifier: return ParseIdentifierExpr(); case tok_number: return ParseNumberExpr(); case '(': return ParseParenExpr(); } } /* * 返回當前LHS所在的完整表達式(對高優先級進行嵌套). * 1.當前優先級 * 2.與前一級比較 * 3.保存op * 4.找RHS * 5.下一個優先級 * 6.兩個優先級比較 * 7.返回 */ static std::unique_ptr<ExprAST> ParseBinOpRHS(int ExprPrec, std::unique_ptr<ExprAST> LHS) { while(true) { // 優先級 int TokPrec = GetTokPrecedence(); if(TokPrec < ExprPrec) return LHS; // 標識符 int BinOp = CurTok; getNextToken(); // RHS auto RHS = ParsePrimary(); if(!RHS) return nullptr; int NextPrec = GetTokPrecedence(); if(TokPrec < NextPrec) { RHS = ParseBinOpRHS(TokPrec + 1, std::move(RHS)); if(!RHS) return nullptr; } //merge LHS/RHS LHS = llvm::make_unique<BinaryExprAST>(BinOp, std::move(LHS), std::move(RHS)); } } /* * 獲取當前完整表達式 */ static std::unique_ptr<ExprAST> ParseExpression() { auto LHS = ParsePrimary(); if(!LHS) return nullptr; return ParseBinOpRHS(0, std::move(LHS)); } /* * 獲取函數名及各個參數名 * 1. 函數名 * 2. eat'(' * 3. 獲取各參數名 * 4. eat')' */ static std::unique_ptr<PrototypeAST> ParsePrototype() { // 判斷名字並存名字 if(CurTok != tok_identifier) return LogErrorP("Expected function name in prototype"); std::string FnName = IdentifierStr; // 判斷( getNextToken(); // eat ( if(CurTok != '(') return LogErrorP("Expected '(' in prototype"); // 存參數 std::vector<std::string> ArgNames; while(getNextToken() == tok_identifier) ArgNames.push_back(IdentifierStr); //判斷並eat) if(CurTok != ')') return LogErrorP("Expected ')' in prototype"); getNextToken(); // 跳 ) return llvm::make_unique<PrototypeAST>(FnName, std::move(ArgNames)); } /* * 函數定義 * 1. 獲取函數名及參數列表(Proto) * 2. 獲取函數體 */ static std::unique_ptr<FunctionAST> ParseDefinition() { // 換行 getNextToken(); // 函數proto並檢測 auto Proto = ParsePrototype(); if(!Proto) return nullptr; // 函數體並檢測 if(auto E = ParseExpression()) return llvm::make_unique<FunctionAST>(std::move(Proto), std::move(E)); return nullptr; } /* * parse函數主體 */ static std::unique_ptr<FunctionAST> ParseTopLevelExpr() { if(auto E = ParseExpression()) { auto Proto = llvm::make_unique<PrototypeAST>("__anon_expr", std::vector<std::string>()); return llvm::make_unique<FunctionAST>(std::move(Proto), std::move(E)); } return nullptr; } /* * 獲取函數定義 */ static std::unique_ptr<PrototypeAST> ParseExtern() { getNextToken(); return ParsePrototype(); } /* * 子層parse都會提早getToken(); */ //===----------------------------===// // 頂層parse //===----------------------------===// /* * parse定義 */ static void HandleDefinition() { if(ParseDefinition()) { fprintf(stderr, "Parsed a function definition.\n"); }else{ getNextToken(); } } static void HandleExtern() { if(ParseExtern()) { fprintf(stderr, "Parsed an extern\n"); }else{ getNextToken(); } } static void HandleTopLevelExpression() { if(ParseTopLevelExpr()) { fprintf(stderr, "Parsed a top_level expr.\n"); }else{ getNextToken(); } } /* * 判斷當前token性質 * 1. EOF ---> 返回 * 2. ; ---> 下一個token * 3. def ---> HandleDefinition() * 4. extern ---> HandleExtern() */ static void MainLoop() { while(true) { fprintf(stderr, "ready> "); switch(CurTok) { case tok_eof: return; case ';': getNextToken(); break; case tok_def: HandleDefinition(); break; case tok_extern: HandleExtern(); break; default: HandleTopLevelExpression(); break; } } }
其中最多的,同時邏輯也較爲複雜的就是這個parser
我畫了一張各個函數之間的調用關係數組
這樣一整個parser就算簡單的表達出來了ide