从零开始构建编程语言:实现词法分析器(基于LLVM教程)

206 阅读3分钟

为什么学习编译器开发?

编译器是计算机科学的明珠之一,掌握编译器开发不仅能深入理解编程语言的运行机制,还能提升对计算机体系结构的认知。LLVM 作为业界领先的编译器框架,为语言开发者提供了强大的基础设施。本系列教程将带您从零开始实现一个名为 Kaleidoscope 的简易编程语言。

初探词法分析器

什么是词法分析?

词法分析器(Lexer)是编译器的第一道关卡,负责将字符流转换为有意义的 词素(Token) 。就像阅读时把字母组合成单词一样,Lexer 需要识别以下元素:

  • 标识符(如变量名)
  • 数字字面量(如 42、3.14)
  • 运算符(如 +、-)
  • 关键字(如 def、extern)

实现思路

我们将使用 C++ 实现一个状态机模型,通过 gettok() 函数逐个获取 Token。核心流程如下:

  1. 跳过空白字符:处理空格、制表符等无效字符
  2. 识别标识符与关键字:通过首字母判断是否是变量/函数名
  3. 解析数字字面量:支持整数和小数的识别
  4. 处理注释:跳过以 # 开头的行注释
  5. 识别特殊符号:返回运算符和文件结束符

核心代码解析

// 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;
}

关键实现细节

  1. 状态保持:使用静态变量 LastChar 跟踪当前字符
  2. 错误处理:简单实现暂未包含详细错误报告
  3. 数值解析:使用 strtod 处理浮点数转换
  4. 扩展性:通过添加新的关键字枚举即可扩展语言特性

下一步计划

完成词法分析器后,我们将在第二章实现语法分析器(Parser),构建抽象语法树(AST)。此时可以测试当前的 Lexer:

# 输入示例
def foo(x) x + 1

# 预期输出序列
tok_def
tok_identifier("foo")
( 
tok_identifier("x")
)
tok_identifier("x")
+
tok_number(1)

完整代码获取

访问 LLVM官方教程页面 获取完整代码及测试用例。


通过实现词法分析器,我们完成了语言处理的第一步。虽然这只是一个简单开端,但已涉及编译器开发的核心概念。下一阶段我们将进入更有挑战性的语法分析环节,敬请期待!