c++编译器笔记 一个简单编译器的实现 中缀表达式 Lisp

  • 编译即将高级语言翻译为机器语言,学习视频为lexer的开发C编译器,up主的开源代码为 https://github.com/iiicp/ccc,但是开源的代码对于理解编译器的处理过程过于高级,所以将up的代码简单修改以配合视频观看。

C++程序的编译流程一般包括以下几个步骤:

  • 预处理(Preprocessing):预处理器首先处理源代码中的预处理指令,例如#include指令和宏定义等。它会根据这些指令修改源代码,生成一个扩展名为".i"的中间文件。

  • 编译(Compilation):编译器将预处理后的文件作为输入,并进行词法分析、语法分析和语义分析,生成针对目标平台的汇编代码。这个阶段的输出是一个扩展名为".s"的汇编代码文件

  • 汇编(Assembly):汇编器将汇编代码转换为机器码指令,生成一个扩展名为".o"的可重定位目标文件。这个目标文件包含了二进制指令、符号表以及其他与目标平台相关的信息。

  • 链接(Linking):链接器将可重定位目标文件与必要的库文件进行合并,生成最终的可执行文件。这个过程涉及符号解析、地址重定向、符号重定位等。最终生成的可执行文件可以在操作系统中运行。

编译器的工作流程

编译器的工作流程可以大致分为以下阶段:

  1. 词法分析(lexical analysis):关键字识别,将源代码分解为词法单元(tokens),如关键字、标识符、运算符等。例如,将代码中的"for"关键字识别为一个词法单元。

  2. 语法分析(syntax analysis):根据语法规则检查词法单元的排列是否符合语言的规范。通常将词法单元组织成抽象语法树(Abstract Syntax Tree,AST)的结构,以表示程序的语法结构。

  3. 语义分析(semantic analysis):检查语法正确的代码是否符合语义规则,包括类型检查和语义约束等。例如,检查变量是否在使用前已经声明。

  4. 中间代码生成(intermediate code generation):将抽象语法树转换为中间表示形式,这种形式比源代码更接近底层机器的表示,但仍比较容易进行优化。

  5. 优化(optimization):对中间代码进行各种优化操作,以提高程序的执行效率和资源利用率。这些优化包括常量折叠、循环展开、无用代码删除等。

  6. 目标代码生成(code generation):将优化后的中间代码转换为目标机器的机器码或汇编代码,以便计算机能够执行。这一阶段还包括寄存器分配、指令选择和代码填充等步骤。

  7. 符号表管理(symbol table management):在整个编译过程中,编译器维护符号表,记录变量、函数等的定义和使用信息,以便在语义分析和代码生成过程中进行查找和处理。

  8. 错误处理(error handling):在编译过程中,如果发现了语法错误、类型错误或其他问题,编译器会生成相应的错误信息,帮助程序员定位和修复问题。

总之,编译器通过一系列的处理阶段将高级语言的源代码转换为可执行的目标代码,以便计算机能够理解和执行。🤖✨

汇编语言

X86-64有16个64位寄存器,分别是:
%rax,%rbx,%rcx,%rdx,%esi,%edi,%rbp,%rsp,%r8,%r9,%r10,%r11,%r12,%r13,%r14,%r15。
其中:
%rax 作为函数返回值使用。
%rsp 栈指针寄存器,指向栈顶
%rdi,%rsi,%rdx,%rcx,%r8,%r9 用作函数参数,依次对应第1参数,第2参数。。。
%rbx,%rbp,%r12,%r13,%14,%15 用作数据存储,遵循被调用者使用规则,简单说就是随便用,调用子函数之前要备份它,以防他被修改
%r10,%r11 用作数据存储,遵循调用者使用规则,简单说就是使用之前要先保存原值

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

         描述语言BNF,程序由表达式组成,表达式由加减法组成,加法由乘法组成(乘法表达式相加或相减),乘法由基础表达式的乘或除组成,基础表达式为数字
在这里插入图片描述

开始程序

在这里插入图片描述

在这里插入图片描述

词法分析 Lex

/**********************************
 * File:     Lexer.h  * * Author:   caipeng ** Email:    iiicp@outlook.com  * * Date:     2022/1/3
 ***********************************/
#ifndef CCC_LEXER_H
#define CCC_LEXER_H

#include <string_view> // C++17,std::string增强版 
#include <memory>
#include <cstdio>
#include <cassert>
#include <unordered_map>

namespace CCC// 定义命名空间
{
  /*符号定义,加减乘除等操作,被计算的数字类型 ,结束*/
  enum class TokenKind { 
  	  Plus, Minus, …, Arrow, Ellipsis, LongLong, Colon, If, Else, While, Do, For,   Num  ,Eof
  };
  
  /* 一个ToKen节点对象*/
  class Token
  {
  public:
    TokenKind Kind; 
    int Value;
    std::string_view Content; //内容
  };



  class Lexer //词法解析
  {
  private:
    char CurChar{' '};// 当前的字符,直接列表初始化  
    int Cursor{0};// 当前的游标,全局变量
    int Line{0};
    int LineHead{0};
    PeekPoint PeekPt;
    std::string_view SourceCode;
    char *CodeBuf{nullptr};
    const char *CurrentFilePath{nullptr};
  public:
    std::shared_ptr<Token> CurrentToken;
  public:
    void GetNextToken();// 获取下一个Token,解析Lexer对象中的code字符串,获取token
    void GetNextChar(); // 获取下一个字符,在解析Token时用到 
    void ExpectToken(TokenKind kind);
    void SkipToken(TokenKind kind);
    bool Match(TokenKind kind);

    void BeginPeekToken();
    void EndPeekToken();

  private:
    bool IsLetter();
    bool IsDigit();
    bool IsLetterOrDigit();

    char PeekChar(int distance);

    void SkipWhiteSpace();
    void SkipComment();

    SourceLocation GetLocation();

    const char *GetTokenSimpleSpelling(TokenKind kind);
  };
}

#endif // CCC_LEXER_H

GetNextToken()

        比如得到一个字符(CurChar == '+')进行{kind = TokenKind::Plus;}等操作即为Token对象赋值currentToken = std::make_shared<Token>(); CurrentToken->Kind = kind;。除了 +,-,*,/ , = 这样的单个字符Token,数字会循环到isdigit(CurChar)== False->这样的则会查看-的下一个字符是什么,因为第一天的课程没有考虑这种,-实现只考虑单个字符)

在这里插入图片描述

语法分析 Parser

Parser.h

/**********************************
 * File:     Parser.h
 *
 * Author:   caipeng
 *
 * Email:    iiicp@outlook.com
 *
 * Date:     2022/1/3
 ***********************************/

#ifndef CCC_PARSER_H
#define CCC_PARSER_H
#include "AstVisitor.h"
#include <set>
#include <unordered_map>
#include "Expr.h"
#include "Stmt.h"
#include "Decl.h"
#include "Basic/Diag.h"

namespace CCC
{
  class Parser
  {
  private:
    Lexer &Lex;// Lex object
    std::set<TokenKind> FirstDeclarationSet;
  public:
    Parser(Lexer &lex);// 使用Lexer构造 
    std std::shared_ptr<AstNode> Parse(); //view this and parse func after you know AstNode std::shared_ptr<TranslationUnit> ParseTranslationUnit();
  private:
    std::shared_ptr<ExprNode> ParseExpr();
    std::shared_ptr<ExprNode> ParseAddExpr();
    std::shared_ptr<ExprNode> ParseMultiExpr();
    std::shared_ptr<ExprNode> ParsePrimaryExpr();
  };
}

#endif // CCC_PARSER_H

BNF section

  • Parser::ParseExpr()
  /**
   expression:
      assignment-expression
      expression , assignment-expression
   */
  std::shared_ptr<ExprNode> Parser::ParseExpr()
  {
    return ParseAssignExpr();
  }

  /**
   assignment-expression:
      conditional-expression
      unary-expression assignment-operator assignment-expression

    assignment-operator: one of
                =  *=  /=  %=  +=  -=  <<=  >>=  &=  ^=  |=
   */
  std::shared_ptr<ExprNode> Parser::ParseAssignExpr()
  {
    auto left = ParseEqualExpr();

    if (Lex.Match(TokenKind::Assign)) {
      auto node = std::make_shared<AssignExpr>(Lex.CurrentToken);
      Lex.SkipToken(TokenKind::Assign);
      node->Lhs = left;
      node->Rhs = ParseAssignExpr();
      return node;
    }
    return left;
  }
  • Parser::ParseExpr()
  /**
   expression:
      assignment-expression
      expression , assignment-expression
   */
  std::shared_ptr<ExprNode> Parser::ParseExpr()
  {
    return ParseAddExpr();
  }

  • Parser::ParseAddExpr() zai shipin de 20 minute
  /**
   additive-expression:
      multiplicative-expression
      additive-expression + multiplicative-expression
      additive-expression - multiplicative-expression
   */
  std::shared_ptr<ExprNode> Parser::ParseAddExpr()
  {
    std::shared_ptr<ExprNode> left = ParseMultiExpr();
    while (Lex.Match(TokenKind::Plus)
        || Lex.Match(TokenKind::Minus)) {

      BinaryOperator bop = BinaryOperator::Add;
      if (Lex.Match(TokenKind::Minus))
        bop = BinaryOperator::Sub;

      auto node = std::make_shared<BinaryExpr>(bop, Lex.CurrentToken);
      Lex.GetNextToken();
      node->Lhs = left;
      node->Rhs = ParseMultiExpr();
      left = node;
    }
    return left;
  }

语法tree AstNode de jiegou

#ifndef CCC_ASTNODE_H
#define CCC_ASTNODE_H

#include <memory>
#include <list>
#include <string_view>
#include <vector>
#include "Lexer.h"

namespace CCC
{
  class AstVisitor;

  class AstNode // Basic class of node
  {
  public:
    //std::shared_ptr<Token> Tok;
    virtual ~AstNode() {}
    AstNode(std::shared_ptr<Token> tok) : Tok(tok) {}
    virtual void Accept(AstVisitor *visitor) = 0; //
  };



  enum class BinaryOperator // from src/Parse/Expr.h
  {
    Add, Sub, Mul, Div  //, Equal, PipeEqual, Greater, GreaterEqual, Lesser, LesserEqual
  };

  class BinaryExpr : public AstNode // from src/Parse/Expr.h and   ExprNode => AstNode
  {
  public:
    BinaryOperator BinOp;// such as BinaryOperator::Add
    std::shared_ptr<ExprNode> Lhs{nullptr};
    std::shared_ptr<ExprNode> Rhs{nullptr};
    BinaryExpr(BinaryOperator op, std::shared_ptr<Token> tok) : ExprNode(tok), BinOp(op) {}
    void Accept(AstVisitor *visitor) override;
  };



  class NumExpr : public ExprNode  // from src/Parse/Expr.h
  {
  public:
    int Value;
    // NumExpr(std::shared_ptr<Token> tok) : ExprNode(tok) {}
    void Accept(AstVisitor *visitor) override;
  };
}

#endif // CCC_ASTNODE_H
  • ProgramNode
  class ProgramNode : public ExprNode  // src/Parse/Expr.h UnaryExpr
  {
  public:
    //UnaryOperator Uop;
    std::shared_ptr<ExprNode> Lhs{nullptr};
    UnaryExpr(UnaryOperator op, std::shared_ptr<Token> tok) : ExprNode(tok), Uop(op) {}
    void Accept(AstVisitor *visitor) override;
  };
  void UnaryExpr::Accept(AstVisitor *visitor) {
    visitor->VisitorUnaryExprNode(this);
  }
  • Accept function
  void BinaryExpr::Accept(AstVisitor *visitor) {// from src/Parse/Expr.cpp
    visitor->VisitorBinaryExprNode(this);
  }

  void NumExpr::Accept(AstVisitor *visitor) {// from src/Parse/Expr.cpp
    visitor->VisitorNumExprNode(this);
  }

AstVisitor

在这里插入图片描述

test parse =>
  void Sema::VisitorBinaryExprNode(BinaryExpr *node) {
      node->Lhs->Accept(this);
      node->Rhs->Accept(this);
      
    switch (node->BinOp) {
    case BinaryOperator::Add:
      printf("+");
      break;
    case BinaryOperator::Sub:
      printf("-");//CheckSubOperator(node);
      break;
    case BinaryOperator::Mul:
    	printf("*");
    	break;
    case BinaryOperator::Div: {
      printf("/");
    	break;
    }
    default:
    assert(0);
      break;
    }
  }
    void Sema::VisitorNumExprNode(NumExpr *node)
  {
    print("%d",node->value);
  }
  void Sema::VisitorBinaryExprNode(BinaryExpr *node) {
    switch (node->BinOp) {
    case BinaryOperator::Add:
      CheckAddOperator(node);
      break;
    case BinaryOperator::Sub:
      CheckSubOperator(node);
      break;
    case BinaryOperator::Mul:
    case BinaryOperator::Div: {
      node->Lhs->Accept(this);
      node->Rhs->Accept(this);
      node->Ty = node->Lhs->Ty;
      if (!node->Lhs->Ty->IsArithTy() && !node->Rhs->Ty->IsArithTy()) {
        SemaDiag(node->Tok->Location, "Invalid operands");
      }
      break;
    }
    case BinaryOperator::Equal:
    case BinaryOperator::PipeEqual:
    case BinaryOperator::Greater:
    case BinaryOperator::GreaterEqual:
    case BinaryOperator::Lesser:
    case BinaryOperator::LesserEqual: {
      node->Lhs->Accept(this);
      node->Rhs->Accept(this);
      node->Ty = Type::LongTy;
      break;
    }
    default:
      break;
    }
  }

  void Sema::VisitorNumExprNode(NumExpr *node)
  {
    node->Ty = Type::LongTy;
  }

CODEGEN

/**********************************
 * File:     Gen.h* * Author:   caipeng * * Email:    iiicp@outlook.com * * Date:     2022/1/3
 ***********************************/

#ifndef CCC_CODEGEN_H
#define CCC_CODEGEN_H

#include "AstVisitor.h"
#include <string>
#include "Type.h"
#include "SymbolTable.h"
#include <stack>

namespace CCC {
  class CodeGen : public AstVisitor
  {
  private:
    int StackLevel{0};
    int Sequence{0};
    const char *Reg8[6] = {"%dil", "%sil", "%dl", "%cl", "%r8b", "%r9b"};
    const char *Reg16[6] = {"%di", "%si", "%dx", "%cx", "%r8w", "%r9w"};
    const char *Reg32[6] = {"%edi", "%esi", "%edx", "%ecx", "%r8d", "%r9d"};
    const char *Reg64[6] = {"%rdi", "%rsi", "%rdx", "%rcx", "%r8", "%r9"};
    std::string CurrentFuncName;
    std::stack<int> LabelStack;
    
  public:
    CodeGen() {}
    
  private:
    void VisitorBinaryExprNode(BinaryExpr *node) override;
    void VisitorUnaryExprNode(UnaryExpr *node) override;
    void VisitorNumExprNode(NumExpr *node) override;
   

    void Push();
    void Pop(const char *reg);

    void GenAddr(AstNode *node);
    void Load(std::shared_ptr<Type> ty);
    void Store(std::shared_ptr<Type> ty);


  };
}

#endif // CCC_CODEGEN_H

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述


void CodeGen::VisitorBinaryExprNode(BinaryExpr *node)
{
  node->Rhs->Accept(this);
  Push();
  node->Lhs->Accept(this);
  Pop("%rdi");

  switch (node->BinOp) {
  case BinaryOperator::Add:
    printf("\tadd %%rdi, %%rax\n");
    break;
  case BinaryOperator::Sub:
    printf("\tsub %%rdi, %%rax\n");
    break;
  case BinaryOperator::Mul:
    printf("\timul %%rdi, %%rax\n");
    break;
  case BinaryOperator::Div: {
    printf("\tcqo\n");
    printf("\tidiv %%rdi\n");
    break;
  }
  case BinaryOperator::Equal:{
    printf("\tcmp %%rdi, %%rax\n");
    printf("\tsete %%al\n");
    printf("\tmovzb %%al, %%rax\n");
    break;
  }
  case BinaryOperator::PipeEqual: {
    printf("\tcmp %%rdi, %%rax\n");
    printf("\tsetne %%al\n");
    printf("\tmovzb %%al, %%rax\n");
    break;
  }
  case BinaryOperator::Greater: {
    printf("\tcmp %%rdi, %%rax\n");
    printf("\tsetg %%al\n");
    printf("\tmovzb %%al, %%rax\n");
    break;
  }
  case BinaryOperator::GreaterEqual: {
    printf("\tcmp %%rdi, %%rax\n");
    printf("\tsetge %%al\n");
    printf("\tmovzb %%al, %%rax\n");
    break;
  }
  case BinaryOperator::Lesser: {
    printf("\tcmp %%rdi, %%rax\n");
    printf("\tsetl %%al\n");
    printf("\tmovzb %%al, %%rax\n");
    break;
  }
  case BinaryOperator::LesserEqual: {
    printf("\tcmp %%rdi, %%rax\n");
    printf("\tsetle %%al\n");
    printf("\tmovzb %%al, %%rax\n");
    break;
  }
  default:
    assert(0);
    break;
  }
}

void CodeGen::VisitorNumExprNode(NumExpr *node)
{
  printf("\tmovabs $%ld, %%rax\n", node->Value);
}

void CodeGen::Push()
{
  printf("\tpush %%rax\n");
  StackLevel++;
}

void CodeGen::Pop(const char *reg)
{
  printf("\tpop %s\n", reg);
  StackLevel--;
}
void CodeGen::VisitorUnaryExprNode(UnaryExpr *node) {
  switch (node->Uop) {
  case UnaryOperator::Plus: {
    node->Lhs->Accept(this);
    break;
  }
  case UnaryOperator::Minus: {
    node->Lhs->Accept(this);
    printf("\tneg %%rax\n");
    break;
  }
  case UnaryOperator::Star: {
    GenAddr(node);
    Load(node->Ty);
    break;
  }
  case UnaryOperator::Amp: {
    GenAddr(node->Lhs.get());
    break;
  }
  default:
    break;
  }
}

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述
在这里插入图片描述

在这里插入图片描述
在这里插入图片描述

  • prong()是汇编生成的函数,在代码生成部分
    在这里插入图片描述
    在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

CG

细节

  • Lexer.cpp 在指向对象时没有使用 std::shared_ptr<Obj> 使用效率更高的 std::make_shared<Obj> https://www.jianshu.com/p/03eea8262c11
  • 使用了很多列表初始化 https://www.bookstack.cn/read/cppreference-language/37048f7c0627974a.md
  • #include <string_view> // C++17,std::string增强版 https://zhuanlan.zhihu.com/p/166359481

minilisp

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值