第2章:Kaleidoscope实现解析器和ast

admin 2024年2月23日18:40:38评论9 views字数 18220阅读60分44秒阅读模式
第2章:Kaleidoscope实现解析器和ast
第2章:Kaleidoscope实现解析器和ast
第2章:Kaleidoscope实现解析器和ast

2.1简介

2.2抽象语法树

2.3语法解析器基础知识

⊙2.4基本表达式解析

2.5二进制表达式解析

⊙2.6解析其余部分

2.7 调度循环部分

2.8 完整代码

2.1 第2章简介

第一章我们介绍了万花筒语言的代码实现与实现了词法分析,本章介绍下如何使用第 1 章中构建的词法分析器为我们的 Kaleidoscopy 语言构建完整的解析器,一旦我们有了解析器,我们将定义并构建抽象语法树(AST)。

本章结束后,我们实现的效果如下图所示。

第2章:Kaleidoscope实现解析器和ast

我们将构建的解析器使用递归下降解析和运算符优先级解析的组合来解析万花筒语言(后者用于二进制表达式,前者用于其他所有内容),在我们开始解析之前,让我们先讨论一下解析器的输出:抽象语法树。

2.2 抽象语法树

首先我们得知道它的定义

抽象语法树源代码的抽象语法结构的树状表现形式,用于表示程序的结构,使得编译器或解释器能够更容易地处理和分析代码。

后续抽象语法树后续都称作成ast。

我们的目的是构造 ast,使用ast对语言进行建模,在设计的这个万花筒语言中,我们有表达式、原型和函数对象,我们首先从表达式开始:

在这之前需要创建个所有表达式节点的基类:

class ExprAST {public:  virtual ~ExprAST() = default;};

然后创建数字的表达式类:

class NumberExprAST : public ExprAST {  double Val;public:  NumberExprAST(double Val) : Val(Val) {}};

上方代码中,需要注意的一点是我们将数字的数值作为实例变量,编译起后续阶段知晓存储的数值是什么。

接下来我们逐步创建用于引用变量的表达式类,如"a"

第2章:Kaleidoscope实现解析器和ast

class VariableExprAST : public ExprAST {  std::string Name;public:  VariableExprAST(const std::string &Name) : Name(Name) {}};

二元运算符的表达式类(捕获的例如+):

第2章:Kaleidoscope实现解析器和ast

class BinaryExprAST : public ExprAST {  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)) {}};

用于函数调用的表达式类(捕获的下面的fib和4):

捕获函数名称及其,参数表达式的列表

第2章:Kaleidoscope实现解析器和ast

class CallExprAST : public ExprAST {  std::string Callee;  std::vector<std::unique_ptr<ExprAST>> Args;public:  CallExprAST(const std::string &Callee,              std::vector<std::unique_ptr<ExprAST>> Args)    : Callee(Callee), Args(std::move(Args)) {}};

定义函数的原型及其函数的定义:

//这个类代表一个函数的原型,它包括函数的名称和参数名称。因此,它也隐含地表示了函数接受的参数数量//类中有两个私有成员:一个是存储函数名的字符串 Name,另一个是存储参数名的字符串向量 Args。它有一个构造函数,用于初始化这两个成员,以及一个公共成员函数 getName,用于返回函数名。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; }};//这个类代表一个函数定义。//它由一个函数原型(Proto)和函数体(Body)组成。//这两个部分都是指向相应类型的 unique_ptr,这意味着 FunctionAST 对象拥有这些对象的唯一所有权,构造函数接受这两个 unique_ptr 作为参数,并使用 std::move 函数将所有权转移到 FunctionAST 对象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)) {}};

在上方我们已经定义了一些结构,下面我们要在语法解析器中解析表达式和函数体了。

2.3 语法解析器基础知识

在上方我们构建了一些ast的一些组成,计入我们要解析x+y,转成ast的节点

第2章:Kaleidoscope实现解析器和ast

我们经过解析器解析后应该变成下方的类似代码功能的:

auto LHS = std::make_unique<VariableExprAST>("x");auto RHS = std::make_unique<VariableExprAST>("y");auto Result = std::make_unique<BinaryExprAST>('+', std::move(LHS),                                              std::move(RHS));

这上面所说的就是解析器要干的内容。

在解析器把词法解析成ast的时候会涉及到我们要解析当前token的类别

static int CurTok;static int getNextToken() {  return CurTok = gettok();}

在上面的定义中,我们相当于实现了一个缓冲区,这让我们可以提前看到词法解析器返回的内容,解析器的中的每个函数都会假设curtok式当前需要解析的token。

同时为了错误,还需要创建个错误处理的函数

std::unique_ptr<ExprAST> LogError(const char *Str) {  fprintf(stderr, "Error: %sn", Str);  return nullptr;}std::unique_ptr<PrototypeAST> LogErrorP(const char *Str) {  LogError(Str);  return nullptr;}

2.4 基本表达式解析

我们从数字开始,对于语法中的每一个,我们将定义一个解析该产生式的函数。

/// numberexpr ::= numberstatic std::unique_ptr<ExprAST> ParseNumberExpr() {  auto Result = std::make_unique<NumberExprAST>(NumVal);  getNextToken(); // consume the number  return std::move(Result);}

这个例子非常简单,它在当前token是tok_number时被调用,同步获取当前数字值,创建NumberExprAST节点,并且将词法分析器前进到下一个标记,然后返回。

ps:getNextToken在我们第一节中,解释过,相当于获取下一个token

static int getNextToken() { return CurTok = gettok(); }

接下来要解析括号表达式:

/// parenexpr ::= '(' expression ')'static std::unique_ptr<ExprAST> ParseParenExpr() {  getNextToken(); // eat (.  auto V = ParseExpression();  if (!V)    return nullptr;  if (CurTok != ')')    return LogError("expected ')'");  getNextToken(); // eat ).  return V;}

第2章:Kaleidoscope实现解析器和ast

解析括号表达式的时候,双边都有括号的才可以解析,如上图这种的解析才能正确,第二种左边一个括号,右边一个| 这个不属于括号表达式的定义,所以我们要打印错误信息。

在这个函数里面ParseExpression,这个是解析表达式,

static std::unique_ptr<ExprAST> ParseExpression() {  auto LHS = ParsePrimary();  if (!LHS)    return nullptr;  return ParseBinOpRHS(0, std::move(LHS));}
/// primary///   ::= identifierexpr///   ::= numberexpr///   ::= parenexprstatic 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();  }
static std::unique_ptr<ExprAST> ParseBinOpRHS(int ExprPrec,                                              std::unique_ptr<ExprAST> LHS) {  // 无限循环,用于处理表达式中的所有二元操作符及其右侧表达式  while (true) {    // 获取当前令牌的优先级    int TokPrec = GetTokPrecedence();    // 如果当前操作符的优先级低于已处理的操作符的优先级,返回当前的LHS    // 这意味着当前令牌不是应该与LHS结合的二元操作符    if (TokPrec < ExprPrec)      return LHS;    // 保存当前令牌作为二元操作符    int BinOp = CurTok;    // 消费(移除)当前的二元操作符,为解析右侧表达式做准备    getNextToken(); // eat binop    // 解析二元操作符右侧的表达式    auto RHS = ParsePrimary();    // 如果解析失败(即RHS为nullptr),则返回nullptr表示解析错误    if (!RHS)      return nullptr;    // 检查下一个令牌的优先级,以确定当前操作符与右侧表达式的结合性    int NextPrec = GetTokPrecedence();    // 如果当前操作符的优先级低于下一个操作符的优先级    if (TokPrec < NextPrec) {      // 递归调用ParseBinOpRHS处理右侧表达式,确保操作符优先级正确处理      RHS = ParseBinOpRHS(TokPrec + 1, std::move(RHS));      // 如果递归解析失败,返回nullptr      if (!RHS)        return nullptr;    }    // 合并LHS和RHS,创建表示二元操作表达式的AST节点    LHS = std::make_unique<BinaryExprAST>(BinOp, std::move(LHS), std::move(RHS));  }}

上面的代码可以不好理解:讲个案例

1 + 5 * 2 * (3 + 4)

解析开始:首先从表达式的最左侧开始,首先遇到数组1。

随后ParseExpression()=》ParsePrimary()=>ParseNumberExpr()得到一个

NumberExprAST

auto LHS = ParsePrimary(); lhs的返回值就是NumberExprAST,因为LHS存在,所以要调用ParseBinOpRHS,

插播一下:我们先定义上优先级

  BinopPrecedence['<'] = 10;  BinopPrecedence['+'] = 20;  BinopPrecedence['-'] = 20;  BinopPrecedence['*'] = 40; // highest.
    1. 解析 1

      1. 解析开始,首先识别出数字 1

    2. 遇到第一个 +

      1. 解析器读取到加法操作符 +。这时,左侧的表达式(LHS)是 1

    3. 解析 5 和第一个 *

      1. 接下来,解析器识别出数字 5,随后遇到乘法操作符 *。这里,* 的优先级高于之前的 +,所以解析器准备处理乘法操作。

    4. 处理 5 * 2

      1. 解析器继续向右,识别出数字 2。由于当前的操作符是乘法 *,并且乘法右侧没有立即遇到更高优先级的操作符,所以 5 * 2 被计算为一部分。

    5. 遇到第二个 *

      1. 5 * 2 后,解析器遇到另一个乘法操作符 *。由于乘法操作符之间的优先级相同,解析器将继续按顺序处理。

    6. 处理括号内的 (3 + 4)

      1. 解析器遇到左括号 (,这意味着需要解析括号内的表达式。在这一步,ParseParenExpr 被调用来处理 (3 + 4)。由于加法没有遇到更高优先级的中断,所以括号内的表达式 3 + 4 被作为一个整体解析。

    7. 合并表达式

      1. 完成对括号内表达式的解析后,解析器回到乘法操作 5 * 2 * (结果)。此时,由于 * 的优先级高于 +,所以首先解决乘法操作,然后将整个乘法表达式的结果与 1 相加。

但是这里当时我看到代码的时候有个疑问

RHS = ParseBinOpRHS(TokPrec + 1, std::move(RHS));这里第一个参数为什么要+1?

回答:

具体例子:a + b * c + d

假设我们有一个表达式 a + b * c + d,并且 +* 的优先级分别是 20 和 40。我们从左向右开始解析:

  1. 遇到第一个 +:优先级为 20。

  2. 遇到 *:优先级为 40,高于 +。这时,TokPrec + 1 的作用是:当我们开始递归解析 b * c 时,TokPrec 被设置为 21(假设原始 TokPrec 为 20)。这表示只有遇到优先级大于 21 的操作符时,我们才会继续深入递归。由于没有操作符的优先级大于 40,我们正确地首先解析 b * c

  3. 返回并遇到第二个 +:优先级同样为 20,但由于我们已经正确处理了 b * c,现在可以将其作为整体与 a 通过加法结合,然后再处理 + d

通过这种方式,TokPrec + 1 确保了即使在多个操作符连续出现时,每个操作符都根据其优先级正确地解析,同时也保持了操作符的正确结合性。这样,即便在复杂表达式中,每个部分也都能按照期望的顺序和规则被准确解析。

2.5 二进制表达式解析

二进制表达式非常难以解析,因为他们通常是很没有规律的,我们2.4中其实使用了二进制表达式,当然我们必须弄明白,所以要狠狠的分析代码。

有很多方法可以处理这个问题,但一种优雅且有效的方法是使用运算符优先级解析,这种解析结束使用二元运算符的优先级来知道递归。

首先定义一个优先级表其中定一个函数返回Tok的优先级:

/// BinopPrecedence - This holds the precedence for each binary operator that is/// defined.static std::map<char, int> BinopPrecedence;/// GetTokPrecedence - Get the precedence of the pending binary operator token.static int GetTokPrecedence() {  if (!isascii(CurTok))    return -1;  // Make sure it's a declared binop.  int TokPrec = BinopPrecedence[CurTok];  if (TokPrec <= 0) return -1;  return TokPrec;}int main() {  // Install standard binary operators.  // 1 is lowest precedence.  BinopPrecedence['<'] = 10;  BinopPrecedence['+'] = 20;  BinopPrecedence['-'] = 20;  BinopPrecedence['*'] = 40;  // highest.  ...}

通过上面定义的帮助器,我们现在可以开始解析二进制表达式。运算符优先级解析的基本思想是将具有可能不明确的二元运算符的表达式分解为多个片段。

/// expression///   ::= primary binoprhs///static std::unique_ptr<ExprAST> ParseExpression() {  auto LHS = ParsePrimary();  if (!LHS)    return nullptr;  return ParseBinOpRHS(0, std::move(LHS));}

ParseBinOpRHS 是为我们解析对序列的函数。它需要一个优先级和一个指向迄今为止已解析部分的表达式的指针。

例如,考虑表达式“a+b+(c+d)*e*f+g”。运算符优先级解析将其视为由二元运算符分隔的主表达式流。因此,它将首先解析前导主表达式“a”,然后它将看到 [+, b] [+, (c+d)] [*, e] [*, f] 和 [+, g] 对]。

这个的目的为了处理表达式,让其进行组装,2.4中已经描述了其操作,这里不在进行描述。

2.6 解析其余部分

接下来是函数原型的处理,用于外部函数声明,也用于函数体定义

///   ::= id '(' id* ')'static std::unique_ptr<PrototypeAST> ParsePrototype() {  if (CurTok != tok_identifier)    return LogErrorP("Expected function name in prototype");  std::string FnName = IdentifierStr;  getNextToken();  if (CurTok != '(')    return LogErrorP("Expected '(' in prototype");  // Read the list of argument names.  std::vector<std::string> ArgNames;  while (getNextToken() == tok_identifier)    ArgNames.push_back(IdentifierStr);  if (CurTok != ')')    return LogErrorP("Expected ')' in prototype");  // success.  getNextToken();  // eat ')'.  return std::make_unique<PrototypeAST>(FnName, std::move(ArgNames));}

代码分为两部分,处理有参数和无参数的函数,对其进行分别的处理

第2章:Kaleidoscope实现解析器和ast

然后定义一个函数就非常简单了,一个原型再加上一个表达式就可以实现函数体了:

/// definition ::= 'def' prototype expressionstatic std::unique_ptr<FunctionAST> ParseDefinition() {  getNextToken();  // eat def.  auto Proto = ParsePrototype();  if (!Proto) return nullptr;  if (auto E = ParseExpression())    return std::make_unique<FunctionAST>(std::move(Proto), std::move(E));  return nullptr;}

此外我们还支持extern来声明sin和cos等函数,并支持用户函数的前向声明。

/// external ::= 'extern' prototypestatic std::unique_ptr<PrototypeAST> ParseExtern() {  getNextToken();  // eat extern.  return ParsePrototype();}

最后,我们还将让用户输入任意顶级表达式并即时计算它们。我们将通过为它们定义匿名空(零参数)函数来处理这个问题:

/// toplevelexpr ::= expressionstatic std::unique_ptr<FunctionAST> ParseTopLevelExpr() {  if (auto E = ParseExpression()) {    // Make an anonymous proto.    auto Proto = std::make_unique<PrototypeAST>("", std::vector<std::string>());    return std::make_unique<FunctionAST>(std::move(Proto), std::move(E));  }  return nullptr;}

2.7 调度循环部分

/// top ::= definition | external | expression | ';'static void MainLoop() {  while (true) {    fprintf(stderr, "ready> ");    switch (CurTok) {    case tok_eof:      return;    case ';': // ignore top-level semicolons.      getNextToken();      break;    case tok_def:      HandleDefinition();      break;    case tok_extern:      HandleExtern();      break;    default:      HandleTopLevelExpression();      break;    }  }}

2.8 完整代码

#include <cctype>#include <cstdio>#include <cstdlib>#include <map>#include <memory>#include <string>#include <utility>#include <vector>//===----------------------------------------------------------------------===//// Lexer//===----------------------------------------------------------------------===//// The lexer returns tokens [0-255] if it is an unknown character, otherwise one// of these for known things.enum Token {  tok_eof = -1,  // commands  tok_def = -2,  tok_extern = -3,  // primary  tok_identifier = -4,  tok_number = -5};static std::string IdentifierStr; // Filled in if tok_identifierstatic double NumVal;             // Filled in if tok_number/// gettok - Return the next token from standard input.static int gettok() {  static int LastChar = ' ';  // Skip any whitespace.  while (isspace(LastChar))    LastChar = getchar();  if (isalpha(LastChar)) { // identifier: [a-zA-Z][a-zA-Z0-9]*    IdentifierStr = LastChar;    while (isalnum((LastChar = getchar())))      IdentifierStr += LastChar;    if (IdentifierStr == "def")      return tok_def;    if (IdentifierStr == "extern")      return tok_extern;    return tok_identifier;  }  if (isdigit(LastChar) || LastChar == '.') { // Number: [0-9.]+    std::string NumStr;    do {      NumStr += LastChar;      LastChar = getchar();    } while (isdigit(LastChar) || LastChar == '.');    NumVal = strtod(NumStr.c_str(), nullptr);    return tok_number;  }  if (LastChar == '#') {    // Comment until end of line.    do      LastChar = getchar();    while (LastChar != EOF && LastChar != 'n' && LastChar != 'r');    if (LastChar != EOF)      return gettok();  }  // Check for end of file.  Don't eat the EOF.  if (LastChar == EOF)    return tok_eof;  // Otherwise, just return the character as its ascii value.  int ThisChar = LastChar;  LastChar = getchar();  return ThisChar;}//===----------------------------------------------------------------------===//// Abstract Syntax Tree (aka Parse Tree)//===----------------------------------------------------------------------===//namespace {/// ExprAST - Base class for all expression nodes.class ExprAST {public:  virtual ~ExprAST() = default;};/// NumberExprAST - Expression class for numeric literals like "1.0".class NumberExprAST : public ExprAST {  double Val;public:  NumberExprAST(double Val) : Val(Val) {}};/// VariableExprAST - Expression class for referencing a variable, like "a".class VariableExprAST : public ExprAST {  std::string Name;public:  VariableExprAST(const std::string &Name) : Name(Name) {}};/// BinaryExprAST - Expression class for a binary operator.class BinaryExprAST : public ExprAST {  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)) {}};/// CallExprAST - Expression class for function calls.class CallExprAST : public ExprAST {  std::string Callee;  std::vector<std::unique_ptr<ExprAST>> Args;public:  CallExprAST(const std::string &Callee,              std::vector<std::unique_ptr<ExprAST>> Args)      : Callee(Callee), Args(std::move(Args)) {}};/// PrototypeAST - This class represents the "prototype" for a function,/// which captures its name, and its argument names (thus implicitly the number/// of arguments the function takes).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; }};/// FunctionAST - This class represents a function definition itself.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)) {}};} // end anonymous namespace//===----------------------------------------------------------------------===//// Parser//===----------------------------------------------------------------------===///// CurTok/getNextToken - Provide a simple token buffer.  CurTok is the current/// token the parser is looking at.  getNextToken reads another token from the/// lexer and updates CurTok with its results.static int CurTok;static int getNextToken() { return CurTok = gettok(); }/// BinopPrecedence - This holds the precedence for each binary operator that is/// defined.static std::map<char, int> BinopPrecedence;/// GetTokPrecedence - Get the precedence of the pending binary operator token.static int GetTokPrecedence() {  if (!isascii(CurTok))    return -1;  // Make sure it's a declared binop.  int TokPrec = BinopPrecedence[CurTok];  if (TokPrec <= 0)    return -1;  return TokPrec;}/// LogError* - These are little helper functions for error handling.std::unique_ptr<ExprAST> LogError(const char *Str) {  fprintf(stderr, "Error: %sn", Str);  return nullptr;}std::unique_ptr<PrototypeAST> LogErrorP(const char *Str) {  LogError(Str);  return nullptr;}static std::unique_ptr<ExprAST> ParseExpression();/// numberexpr ::= numberstatic std::unique_ptr<ExprAST> ParseNumberExpr() {  auto Result = std::make_unique<NumberExprAST>(NumVal);  getNextToken(); // consume the number  return std::move(Result);}/// parenexpr ::= '(' expression ')'static std::unique_ptr<ExprAST> ParseParenExpr() {  getNextToken(); // eat (.  auto V = ParseExpression();  if (!V)    return nullptr;  if (CurTok != ')')    return LogError("expected ')'");  getNextToken(); // eat ).  return V;}/// identifierexpr///   ::= identifier///   ::= identifier '(' expression* ')'static std::unique_ptr<ExprAST> ParseIdentifierExpr() {  std::string IdName = IdentifierStr;  getNextToken(); // eat identifier.  if (CurTok != '(') // Simple variable ref.    return std::make_unique<VariableExprAST>(IdName);  // Call.  getNextToken(); // eat (  std::vector<std::unique_ptr<ExprAST>> Args;  if (CurTok != ')') {    while (true) {      if (auto Arg = ParseExpression())        Args.push_back(std::move(Arg));      else        return nullptr;      if (CurTok == ')')        break;      if (CurTok != ',')        return LogError("Expected ')' or ',' in argument list");      getNextToken();    }  }  // Eat the ')'.  getNextToken();  return std::make_unique<CallExprAST>(IdName, std::move(Args));}/// primary///   ::= identifierexpr///   ::= numberexpr///   ::= parenexprstatic 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();  }}/// binoprhs///   ::= ('+' primary)*static std::unique_ptr<ExprAST> ParseBinOpRHS(int ExprPrec,                                              std::unique_ptr<ExprAST> LHS) {  // If this is a binop, find its precedence.  while (true) {    int TokPrec = GetTokPrecedence();    // If this is a binop that binds at least as tightly as the current binop,    // consume it, otherwise we are done.    if (TokPrec < ExprPrec)      return LHS;    // Okay, we know this is a binop.    int BinOp = CurTok;    getNextToken(); // eat binop    // Parse the primary expression after the binary operator.    auto RHS = ParsePrimary();    if (!RHS)      return nullptr;    // If BinOp binds less tightly with RHS than the operator after RHS, let    // the pending operator take RHS as its LHS.    int NextPrec = GetTokPrecedence();    if (TokPrec < NextPrec) {      RHS = ParseBinOpRHS(TokPrec + 1, std::move(RHS));      if (!RHS)        return nullptr;    }    // Merge LHS/RHS.    LHS =        std::make_unique<BinaryExprAST>(BinOp, std::move(LHS), std::move(RHS));  }}/// expression///   ::= primary binoprhs///static std::unique_ptr<ExprAST> ParseExpression() {  auto LHS = ParsePrimary();  if (!LHS)    return nullptr;  return ParseBinOpRHS(0, std::move(LHS));}/// prototype///   ::= id '(' id* ')'static std::unique_ptr<PrototypeAST> ParsePrototype() {  if (CurTok != tok_identifier)    return LogErrorP("Expected function name in prototype");  std::string FnName = IdentifierStr;  getNextToken();  if (CurTok != '(')    return LogErrorP("Expected '(' in prototype");  std::vector<std::string> ArgNames;  while (getNextToken() == tok_identifier)    ArgNames.push_back(IdentifierStr);  if (CurTok != ')')    return LogErrorP("Expected ')' in prototype");  // success.  getNextToken(); // eat ')'.  return std::make_unique<PrototypeAST>(FnName, std::move(ArgNames));}/// definition ::= 'def' prototype expressionstatic std::unique_ptr<FunctionAST> ParseDefinition() {  getNextToken(); // eat def.  auto Proto = ParsePrototype();  if (!Proto)    return nullptr;  if (auto E = ParseExpression())    return std::make_unique<FunctionAST>(std::move(Proto), std::move(E));  return nullptr;}/// toplevelexpr ::= expressionstatic std::unique_ptr<FunctionAST> ParseTopLevelExpr() {  if (auto E = ParseExpression()) {    // Make an anonymous proto.    auto Proto = std::make_unique<PrototypeAST>("__anon_expr",                                                std::vector<std::string>());    return std::make_unique<FunctionAST>(std::move(Proto), std::move(E));  }  return nullptr;}/// external ::= 'extern' prototypestatic std::unique_ptr<PrototypeAST> ParseExtern() {  getNextToken(); // eat extern.  return ParsePrototype();}//===----------------------------------------------------------------------===//// Top-Level parsing//===----------------------------------------------------------------------===//static void HandleDefinition() {  if (ParseDefinition()) {    fprintf(stderr, "Parsed a function definition.n");  } else {    // Skip token for error recovery.    getNextToken();  }}static void HandleExtern() {  if (ParseExtern()) {    fprintf(stderr, "Parsed an externn");  } else {    // Skip token for error recovery.    getNextToken();  }}static void HandleTopLevelExpression() {  // Evaluate a top-level expression into an anonymous function.  if (ParseTopLevelExpr()) {    fprintf(stderr, "Parsed a top-level exprn");  } else {    // Skip token for error recovery.    getNextToken();  }}/// top ::= definition | external | expression | ';'static void MainLoop() {  while (true) {    fprintf(stderr, "ready> ");    switch (CurTok) {    case tok_eof:      return;    case ';': // ignore top-level semicolons.      getNextToken();      break;    case tok_def:      HandleDefinition();      break;    case tok_extern:      HandleExtern();      break;    default:      HandleTopLevelExpression();      break;    }  }}//===----------------------------------------------------------------------===//// Main driver code.//===----------------------------------------------------------------------===//int main() {  // Install standard binary operators.  // 1 is lowest precedence.  BinopPrecedence['<'] = 10;  BinopPrecedence['+'] = 20;  BinopPrecedence['-'] = 20;  BinopPrecedence['*'] = 40; // highest.  // Prime the first token.  fprintf(stderr, "ready> ");  getNextToken();  // Run the main "interpreter loop" now.  MainLoop();  return 0;}

学习逆向和爬虫可以关注我朋友:

我是BestToYou,分享工作或日常学习中关于Android、iOS逆向及安全防护的一些思路和一些自己闲暇时刻调试的一些程序,文中若有错误或者不足的地方,恳请大家联系我批评指正。

第2章:Kaleidoscope实现解析器和ast

扫码加我为好友

原文始发于微信公众号(二进制科学):第2章:Kaleidoscope实现解析器和ast

  • 左青龙
  • 微信扫一扫
  • weinxin
  • 右白虎
  • 微信扫一扫
  • weinxin
admin
  • 本文由 发表于 2024年2月23日18:40:38
  • 转载请保留本文链接(CN-SEC中文网:感谢原作者辛苦付出):
                   第2章:Kaleidoscope实现解析器和asthttp://cn-sec.com/archives/2520020.html

发表评论

匿名网友 填写信息