为什么学习编译器开发?
编译器是计算机科学的明珠之一,掌握编译器开发不仅能深入理解编程语言的运行机制,还能提升对计算机体系结构的认知。LLVM 作为业界领先的编译器框架,为语言开发者提供了强大的基础设施。本系列教程将带您从零开始实现一个名为 Kaleidoscope 的简易编程语言。
初探词法分析器
什么是词法分析?
词法分析器(Lexer)是编译器的第一道关卡,负责将字符流转换为有意义的 词素(Token) 。就像阅读时把字母组合成单词一样,Lexer 需要识别以下元素:
- 标识符(如变量名)
- 数字字面量(如 42、3.14)
- 运算符(如 +、-)
- 关键字(如 def、extern)
实现思路
我们将使用 C++ 实现一个状态机模型,通过 gettok() 函数逐个获取 Token。核心流程如下:
- 跳过空白字符:处理空格、制表符等无效字符
- 识别标识符与关键字:通过首字母判断是否是变量/函数名
- 解析数字字面量:支持整数和小数的识别
- 处理注释:跳过以
#开头的行注释 - 识别特殊符号:返回运算符和文件结束符
核心代码解析
// Token 类型枚举
enum Token {
tok_eof = -1,
tok_def = -2,
tok_extern = -3,
tok_identifier = -4,
tok_number = -5
};
static std::string IdentifierStr; // 存储标识符名称
static double NumVal; // 存储数值
// 核心词法分析函数
static int gettok() {
static int LastChar = ' ';
// 跳过空白字符
while (isspace(LastChar))
LastChar = getchar();
// 识别标识符
if (isalpha(LastChar)) {
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 == '.') {
std::string NumStr;
do {
NumStr += LastChar;
LastChar = getchar();
} while (isdigit(LastChar) || LastChar == '.');
NumVal = strtod(NumStr.c_str(), nullptr);
return tok_number;
}
// 处理注释
if (LastChar == '#') {
do {
LastChar = getchar();
} while (LastChar != EOF && LastChar != '\n' && LastChar != '\r');
if (LastChar != EOF)
return gettok();
}
// 处理文件结束
if (LastChar == EOF)
return tok_eof;
// 返回 ASCII 字符本身作为 Token
int ThisChar = LastChar;
LastChar = getchar();
return ThisChar;
}
关键实现细节
- 状态保持:使用静态变量
LastChar跟踪当前字符 - 错误处理:简单实现暂未包含详细错误报告
- 数值解析:使用
strtod处理浮点数转换 - 扩展性:通过添加新的关键字枚举即可扩展语言特性
下一步计划
完成词法分析器后,我们将在第二章实现语法分析器(Parser),构建抽象语法树(AST)。此时可以测试当前的 Lexer:
# 输入示例
def foo(x) x + 1
# 预期输出序列
tok_def
tok_identifier("foo")
(
tok_identifier("x")
)
tok_identifier("x")
+
tok_number(1)
完整代码获取
访问 LLVM官方教程页面 获取完整代码及测试用例。
通过实现词法分析器,我们完成了语言处理的第一步。虽然这只是一个简单开端,但已涉及编译器开发的核心概念。下一阶段我们将进入更有挑战性的语法分析环节,敬请期待!