深入解析OLLVM-TLL项目:实现Kaleidoscope语言的解析器与AST构建
ollvm-tll Ollvm+Armariris+LLVM 6.0.0 项目地址: https://gitcode.com/gh_mirrors/ol/ollvm-tll
前言
在编译器开发领域,构建解析器和抽象语法树(AST)是前端处理的核心环节。本文将基于OLLVM-TLL项目中的Kaleidoscope语言实现,详细讲解如何从零开始构建一个完整的解析器系统,并生成结构化的AST表示。
解析器与AST基础概念
什么是抽象语法树(AST)
AST是源代码的树状表示,它保留了程序的结构信息但省略了不必要的语法细节(如分号、括号等)。在编译流程中,AST作为前端和后端之间的桥梁,具有以下特点:
- 每个语法结构对应一个节点
- 紧密反映语言特性
- 便于后续阶段处理(如代码生成)
解析器的作用
解析器负责将词法分析器生成的token序列转换为AST结构。在Kaleidoscope实现中,我们采用:
- 递归下降解析:处理大部分语法结构
- 运算符优先级解析:专门处理二元表达式
Kaleidoscope的AST设计
表达式基类
class ExprAST {
public:
virtual ~ExprAST() {}
};
所有表达式节点都继承自这个基类,使用虚析构函数支持多态。
具体表达式类型
- 数值常量表达式
class NumberExprAST : public ExprAST {
double Val;
public:
NumberExprAST(double Val) : Val(Val) {}
};
存储具体的数值,如1.0
。
- 变量引用表达式
class VariableExprAST : public ExprAST {
std::string Name;
public:
VariableExprAST(const std::string &Name) : Name(Name) {}
};
表示对变量的引用,如x
。
- 二元运算表达式
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)) {}
};
表示二元运算如x+y
,存储运算符和左右操作数。
- 函数调用表达式
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)) {}
};
表示函数调用如foo(1,2)
,存储函数名和参数列表。
函数相关结构
- 函数原型
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)) {}
};
组合函数原型和函数体构成完整函数定义。
解析器实现详解
基础工具函数
- Token缓冲系统
static int CurTok;
static int getNextToken() {
return CurTok = gettok();
}
维护当前token和获取下一个token的简单缓冲机制。
- 错误处理
std::unique_ptr<ExprAST> LogError(const char *Str) {
fprintf(stderr, "Error: %s\n", Str);
return nullptr;
}
统一的错误报告机制,返回nullptr表示解析失败。
基本表达式解析
- 数值常量解析
static std::unique_ptr<ExprAST> ParseNumberExpr() {
auto Result = std::make_unique<NumberExprAST>(NumVal);
getNextToken(); // 消耗数字token
return std::move(Result);
}
处理简单的数字字面量。
- 括号表达式解析
static std::unique_ptr<ExprAST> ParseParenExpr() {
getNextToken(); // 消耗'('
auto V = ParseExpression();
if (!V) return nullptr;
if (CurTok != ')')
return LogError("expected ')'");
getNextToken(); // 消耗')'
return V;
}
处理括号分组表达式,注意递归调用ParseExpression。
- 标识符解析
static std::unique_ptr<ExprAST> ParseIdentifierExpr() {
std::string IdName = IdentifierStr;
getNextToken(); // 消耗标识符
if (CurTok != '(') // 简单变量引用
return std::make_unique<VariableExprAST>(IdName);
// 函数调用处理
getNextToken(); // 消耗'('
std::vector<std::unique_ptr<ExprAST>> Args;
// ... 参数解析逻辑
return std::make_unique<CallExprAST>(IdName, std::move(Args));
}
通过前瞻判断是变量引用还是函数调用。
二元表达式解析
处理二元表达式的关键在于运算符优先级。我们使用运算符优先级解析算法。
- 优先级定义
static std::map<char, int> BinopPrecedence;
// 初始化优先级
BinopPrecedence['<'] = 10;
BinopPrecedence['+'] = 20;
BinopPrecedence['-'] = 20;
BinopPrecedence['*'] = 40;
- 核心解析逻辑
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(); // 消耗运算符
auto RHS = ParsePrimary();
if (!RHS) return nullptr;
// 处理运算符结合性
int NextPrec = GetTokPrecedence();
if (TokPrec < NextPrec) {
RHS = ParseBinOpRHS(TokPrec+1, std::move(RHS));
if (!RHS) return nullptr;
}
LHS = std::make_unique<BinaryExprAST>(BinOp, std::move(LHS), std::move(RHS));
}
}
该算法能正确处理运算符优先级和结合性,例如将a+b*c
解析为a+(b*c)
。
总结
本文详细介绍了如何在OLLVM-TLL项目中构建Kaleidoscope语言的解析器和AST系统。关键点包括:
- 设计合理的AST类层次结构
- 实现递归下降解析处理基本表达式
- 使用运算符优先级算法处理二元表达式
- 完善的错误处理机制
这套架构不仅适用于Kaleidoscope语言,其核心思想也可以扩展到其他编程语言的实现中。通过AST的构建,我们已经为后续的语义分析和代码生成奠定了坚实基础。
ollvm-tll Ollvm+Armariris+LLVM 6.0.0 项目地址: https://gitcode.com/gh_mirrors/ol/ollvm-tll
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考