llvm樣例parser解析

    因爲llvm的資料比較稀缺, 因此品讀官方文檔就成了最佳瞭解llvm的方法, tutorial的整個流程目的是實現一個語法簡單, 但較爲完整的實現一個編譯器. 而其中第一個步驟即是簡單地實現一個前端parser, parser算法整個編譯器流程中最爲簡單的一部分, 但也是整個編譯器的入口(話說當時學編譯原理的時候老師主要講的就是這一部分).前端

    tutorial中這個Parser主要分爲三個部分, lexer, AST(abstract syntax tree) 與 parser主體, 代碼很清晰, 咱們來一部分一部分看.git

lexer

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能夠是數字, 變量或關鍵字, 識別後若是是變量或數字就返回變量名或值. 其中枚舉類型列舉了全部返回的類型.算法

AST

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

/===---------------------------------------------------------------------------------------------===//
//                                                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

相關文章
相關標籤/搜索