c++编译器笔记 一个简单编译器的实现 中缀表达式 Lisp
描述语言BNF,程序由表达式组成,表达式由加减法组成,加法由乘法组成(乘法表达式相加或相减),乘法由基础表达式的乘或除组成,基础表达式为数字。除了 +,-,*,/ , = 这样的单个字符Token,数字会循环到。的下一个字符是什么,因为第一天的课程没有考虑这种,等操作即为Token对象赋值。实现只考虑单个字符)
- 编译即将高级语言翻译为机器语言,学习视频为lexer的开发C编译器,up主的开源代码为 https://github.com/iiicp/ccc,但是开源的代码对于理解编译器的处理过程过于高级,所以将up的代码简单修改以配合视频观看。
C++程序的编译流程一般包括以下几个步骤:
-
预处理(Preprocessing):预处理器首先处理源代码中的预处理指令,例如#include指令和宏定义等。它会根据这些指令修改源代码,生成一个扩展名为".i"的中间文件。
-
编译(Compilation):编译器将预处理后的文件作为输入,并进行词法分析、语法分析和语义分析,生成针对目标平台的汇编代码。这个阶段的输出是一个扩展名为".s"的
汇编代码文件
。 -
汇编(Assembly):汇编器将汇编代码转换为机器码指令,生成一个扩展名为
".o"的可重定位目标文件
。这个目标文件包含了二进制指令、符号表以及其他与目标平台相关的信息。 -
链接(Linking):链接器将可重定位目标文件与必要的库文件进行合并,生成最终的可执行文件。这个过程涉及符号解析、地址重定向、符号重定位等。最终生成的可执行文件可以在操作系统中运行。
编译器的工作流程
编译器的工作流程可以大致分为以下阶段:
-
词法分析(lexical analysis):
关键字识别
,将源代码分解为词法单元(tokens),如关键字、标识符、运算符等。例如,将代码中的"for"关键字识别为一个词法单元。 -
语法分析(syntax analysis):根据语法规则检查词法单元的排列是否符合语言的规范。通常将词法单元组织成
抽象语法树
(Abstract Syntax Tree,AST)的结构,以表示程序的语法结构。 -
语义分析(semantic analysis):检查语法正确的代码是否符合语义规则,包括类型检查和语义约束等。例如,检查变量是否在使用前已经声明。
-
中间代码生成(intermediate code generation):将
抽象语法树转换为中间表示形式
,这种形式比源代码更接近底层机器的表示,但仍比较容易进行优化。 -
优化(optimization):对中间代码进行各种优化操作,以提高程序的执行效率和资源利用率。这些优化包括常量折叠、循环展开、无用代码删除等。
-
目标代码生成(code generation):将优化后的中间代码转换为目标机器的机器码或汇编代码,以便计算机能够执行。这一阶段还包括寄存器分配、指令选择和代码填充等步骤。
-
符号表管理(symbol table management):在整个编译过程中,编译器维护符号表,记录变量、函数等的定义和使用信息,以便在语义分析和代码生成过程中进行查找和处理。
-
错误处理(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)