神图镇楼
代码文件夹的内容:
首先,开头附上神图一张,这是为什么本人手撸完可能是全网最适合初学者学习的c语言教程之后,收录了将近两万行代码,突然心血来潮搞这么一出编译器、手撸os、手撸最小化系统、手撸100例趣味例程:
语法、100例子、牛客大厂题目单100、课程自学到现在的《巨作》
其实也就是我个人技术博客的一个延申和mark见证,两个月的时间写了将近两万行c,从语法到大场面试到最终的自学c语言所有常见考点,再写这么一个超级硬核超级底层的内容,也更多是为了给自己一个挑战
第一章:初探编译奥秘与词法分析的基石)
朋友们,你们有没有想过,我们每天敲打的C语言代码,那些看似简单的int main() { ... }
,那些熟悉的printf()
,它们究竟是如何从一行行人类可读的文本,最终变成计算机中央处理器(CPU)能够直接执行的二进制指令的?这中间的魔法,就是编译器!
今天,我们要干一件大事!不是简单地使用编译器,而是要亲手撸一个编译器!是的,你没听错,我们要从零开始,一步步构建一个属于我们自己的C语言编译器。这不仅仅是一个技术挑战,更是一场对C语言“底裤”的终极解密,一次彻底吃透代码到机器码极致蜕变的深度之旅!
为什么我们要“手撸”编译器?
你可能会问:“现在不是有GCC、Clang这些强大的编译器了吗?为什么还要自己造轮子?” 问得好!这就是我们今天要点燃你激情的地方:
-
掀开C语言的“底裤”: 只有当你亲手去实现每一个编译阶段,你才会真正明白C语言的每一个语法结构、每一个数据类型,是如何被编译器处理和转化的。你将看到变量如何分配内存,函数调用如何压栈弹栈,循环和条件语句如何转换为跳转指令。这是一种前所未有的透彻理解,让你对C语言的认知从“会用”跃升到“看透”!
-
吃透计算机底层原理: 编译器是连接高级语言和机器语言的桥梁。手撸编译器,意味着你将直面计算机体系结构、指令集、内存管理等核心概念。你会发现,那些抽象的计算机原理,在编译器的实现中变得如此具体和生动。
-
提升解决复杂问题的能力: 编译器是一个庞大而复杂的系统工程。从词法分析到语法分析,从语义检查到代码生成,每一步都充满了挑战。这个过程将极大地锻炼你的系统设计能力、抽象思维能力和调试排错能力。当你完成这个项目,你会发现,其他任何复杂的编程任务都将变得小菜一碟。
-
享受创造的极致快感: 想象一下,你写下一段C代码,然后用自己亲手打造的编译器将其编译成可执行文件,并成功运行!那种从无到有,将思想变为现实的成就感,是任何其他编程体验都无法比拟的!这不仅仅是写代码,这是在创造一个“生命”!
所以,朋友们,这不是一次简单的学习,这是一场冒险,一次蜕变!准备好你的键盘,点燃你的激情,让我们一起“肝爆”这4万行代码,彻底看透C语言的红尘!
编译器的宏伟蓝图:我们正在建造什么?
在深入细节之前,我们先来鸟瞰一下编译器的全貌。一个典型的编译器通常包含以下几个核心阶段:
-
词法分析(Lexical Analysis / Scanning): 这是编译器的第一步,它像一个“阅读器”,将源代码字符流分解成一个个有意义的“词素”(lexeme),并将其归类为“令牌”(token)。例如,
int
是一个关键字令牌,main
是一个标识符令牌,=
是一个赋值操作符令牌。 -
语法分析(Syntax Analysis / Parsing): 这一阶段像一个“语法学家”,它接收词法分析器生成的令牌流,并根据语言的语法规则(比如C语言的语法)来检查令牌序列是否合法。如果合法,它会构建一个抽象语法树(Abstract Syntax Tree, AST),这棵树是源代码结构的一种抽象表示。
-
语义分析(Semantic Analysis): 语义分析器像一个“逻辑检查员”,它在AST的基础上进行更深层次的检查,比如类型检查(你不能把一个字符串赋值给一个整型变量)、变量作用域检查(你不能使用一个未声明的变量)等。它还会收集类型信息,并可能对AST进行一些转换。
-
中间代码生成(Intermediate Code Generation): 编译器不会直接从AST生成机器码。通常,它会先将AST转换成一种与特定机器无关的中间表示(Intermediate Representation, IR),比如三地址码(Three-Address Code)。这种IR更接近机器指令,但仍然是抽象的,便于后续的优化和跨平台。
-
代码优化(Code Optimization): 这一阶段像一个“性能调优师”,它对中间代码进行各种转换,以提高生成的目标代码的执行效率,比如消除冗余计算、常量折叠、循环优化等。
-
目标代码生成(Target Code Generation): 这是编译器的最后一步,它将优化后的中间代码翻译成特定目标机器(如x86、ARM)的机器指令或汇编代码。
-
链接与加载(Linking & Loading): 虽然严格来说不属于编译器本身,但链接器和加载器是编译过程不可或缺的一部分。链接器将编译好的多个目标文件和库文件组合成一个最终的可执行文件。加载器则负责将可执行文件加载到内存中并启动程序的执行。
我们的目标: 在这个系列中,我们将主要聚焦于前六个阶段,从C语言源代码出发,最终生成一个简单的自定义虚拟机(VM)的字节码。选择VM字节码而不是直接生成汇编,是为了简化目标代码生成阶段的复杂性,让我们能更专注于编译器的核心原理。
编译器干了啥?
一张图总结!
第一章:词法分析的基石——扫描器(Lexer)
好了,理论铺垫到此为止,是时候撸起袖子干活了!我们的第一站是词法分析器,也就是我们常说的扫描器(Scanner)。
词法分析器是做什么的?
想象一下,你正在阅读一本厚厚的C语言书。你不会一个字符一个字符地去理解它,你会识别出“int
”是一个类型,“main
”是一个函数名,“=
”是一个赋值符号。词法分析器做的就是类似的事情。
它接收C语言的源代码文件,这个文件本质上就是一个巨大的字符流。词法分析器的任务就是:
-
识别词素(Lexeme): 从字符流中识别出构成语言的最小有意义单元。例如,
int
,main
,(
,)
,{
,}
,return
,0
,;
,+
,*
,myVariable
等。 -
生成令牌(Token): 为每个识别出的词素创建一个“令牌”。令牌是一个结构化的数据,它至少包含两部分信息:
-
令牌类型(Token Type): 表示这个词素的类别,例如
KEYWORD_INT
(整数关键字),IDENTIFIER
(标识符),OPERATOR_ASSIGN
(赋值运算符),NUMBER_INTEGER
(整数常量) 等。 -
词素值(Lexeme Value / Attribute): 原始的字符序列,例如对于
IDENTIFIER
类型的令牌,它的值就是myVariable
;对于NUMBER_INTEGER
,它的值就是123
。
-
-
过滤无用字符: 忽略源代码中的空格、制表符、换行符以及注释。这些字符对于语法和语义没有直接意义。
-
错误报告: 当遇到无法识别的字符序列时,报告词法错误。
简单来说,词法分析器就是把一堆“散沙”(字符)组织成一个个“砖块”(令牌),为后续的语法分析打下基础。
令牌的结构与定义
在C语言中,我们可以用enum
来定义令牌的类型,用struct
来定义令牌的结构。
// token.h
#ifndef TOKEN_H
#define TOKEN_H
// 定义所有可能的令牌类型
typedef enum {
// 特殊令牌
TOKEN_EOF = 0, // 文件结束符 (End Of File)
TOKEN_UNKNOWN, // 未知令牌,用于错误处理
// 关键字
TOKEN_KEYWORD_INT, // int
TOKEN_KEYWORD_RETURN, // return
TOKEN_KEYWORD_IF, // if
TOKEN_KEYWORD_ELSE, // else
TOKEN_KEYWORD_WHILE, // while
TOKEN_KEYWORD_FOR, // for
TOKEN_KEYWORD_VOID, // void
TOKEN_KEYWORD_CHAR, // char
TOKEN_KEYWORD_SHORT, // short
TOKEN_KEYWORD_LONG, // long
TOKEN_KEYWORD_FLOAT, // float
TOKEN_KEYWORD_DOUBLE, // double
TOKEN_KEYWORD_STRUCT, // struct
TOKEN_KEYWORD_UNION, // union
TOKEN_KEYWORD_ENUM, // enum
TOKEN_KEYWORD_TYPEDEF, // typedef
TOKEN_KEYWORD_SIZEOF, // sizeof
TOKEN_KEYWORD_BREAK, // break
TOKEN_KEYWORD_CONTINUE, // continue
TOKEN_KEYWORD_SWITCH, // switch
TOKEN_KEYWORD_CASE, // case
TOKEN_KEYWORD_DEFAULT, // default
TOKEN_KEYWORD_DO, // do
TOKEN_KEYWORD_GOTO, // goto
TOKEN_KEYWORD_CONST, // const
TOKEN_KEYWORD_VOLATILE, // volatile
TOKEN_KEYWORD_EXTERN, // extern
TOKEN_KEYWORD_STATIC, // static
TOKEN_KEYWORD_REGISTER, // register
TOKEN_KEYWORD_AUTO, // auto
TOKEN_KEYWORD_SIGNED, // signed
TOKEN_KEYWORD_UNSIGNED, // unsigned
// 标识符
TOKEN_IDENTIFIER, // 变量名、函数名等
// 字面量 (常量)
TOKEN_INTEGER_LITERAL, // 整数常量,如 123, 0xAF
TOKEN_FLOAT_LITERAL, // 浮点数常量,如 3.14, 1.0e-5
TOKEN_STRING_LITERAL, // 字符串常量,如 "hello world"
TOKEN_CHAR_LITERAL, // 字符常量,如 'a', '\n'
// 运算符
TOKEN_OP_ASSIGN, // =
TOKEN_OP_PLUS, // +
TOKEN_OP_MINUS, // -
TOKEN_OP_MULTIPLY, // *
TOKEN_OP_DIVIDE, // /
TOKEN_OP_MODULO, // %
TOKEN_OP_EQ, // ==
TOKEN_OP_NE, // !=
TOKEN_OP_LT, // <
TOKEN_OP_LE, // <=
TOKEN_OP_GT, // >
TOKEN_OP_GE, // >=
TOKEN_OP_AND, // &&
TOKEN_OP_OR, // ||
TOKEN_OP_NOT, // !
TOKEN_OP_BIT_AND, // &
TOKEN_OP_BIT_OR, // |
TOKEN_OP_BIT_XOR, // ^
TOKEN_OP_BIT_NOT, // ~
TOKEN_OP_LSHIFT, // <<
TOKEN_OP_RSHIFT, // >>
TOKEN_OP_INC, // ++
TOKEN_OP_DEC, // --
TOKEN_OP_ARROW, // ->
TOKEN_OP_DOT, // .
TOKEN_OP_COMMA, // ,
TOKEN_OP_COLON, // :
TOKEN_OP_QUESTION, // ?
TOKEN_OP_ASSIGN_PLUS, // +=
TOKEN_OP_ASSIGN_MINUS, // -=
TOKEN_OP_ASSIGN_MULTIPLY, // *=
TOKEN_OP_ASSIGN_DIVIDE, // /=
TOKEN_OP_ASSIGN_MODULO, // %=
TOKEN_OP_ASSIGN_LSHIFT, // <<=
TOKEN_OP_ASSIGN_RSHIFT, // >>=
TOKEN_OP_ASSIGN_BIT_AND, // &=
TOKEN_OP_ASSIGN_BIT_OR, // |=
TOKEN_OP_ASSIGN_BIT_XOR, // ^=
// 分隔符/标点符号
TOKEN_LPAREN, // (
TOKEN_RPAREN, // )
TOKEN_LBRACE, // {
TOKEN_RBRACE, // }
TOKEN_LBRACKET, // [
TOKEN_RBRACKET, // ]
TOKEN_SEMICOLON, // ;
TOKEN_POUND, // # (用于预处理器指令)
TOKEN_ELLIPSIS, // ... (用于可变参数)
} TokenType;
// 令牌结构体
typedef struct {
TokenType type; // 令牌类型
char* lexeme; // 词素的字符串表示 (原始文本)
int line; // 令牌所在的行号
int column; // 令牌所在的列号
// 字面量的值 (根据类型存储不同数据)
union {
long long int_val; // 整数值
double float_val; // 浮点数值
char char_val; // 字符值
// 字符串字面量的值直接存储在lexeme中,因为它可能很长
} value;
} Token;
// 辅助函数:获取令牌类型的字符串表示,方便调试
const char* token_type_to_string(TokenType type);
#endif // TOKEN_H
token.c
(辅助函数实现):
// token.c
#include "token.h"
#include <stdio.h> // 用于printf
// 辅助函数:将TokenType枚举值转换为可读的字符串
// 这对于调试和错误报告非常有用
const char* token_type_to_string(TokenType type) {
switch (type) {
case TOKEN_EOF: return "EOF";
case TOKEN_UNKNOWN: return "UNKNOWN";
// 关键字
case TOKEN_KEYWORD_INT: return "KEYWORD_INT";
case TOKEN_KEYWORD_RETURN: return "KEYWORD_RETURN";
case TOKEN_KEYWORD_IF: return "KEYWORD_IF";
case TOKEN_KEYWORD_ELSE: return "KEYWORD_ELSE";
case TOKEN_KEYWORD_WHILE: return "KEYWORD_WHILE";
case TOKEN_KEYWORD_FOR: return "KEYWORD_FOR";
case TOKEN_KEYWORD_VOID: return "KEYWORD_VOID";
case TOKEN_KEYWORD_CHAR: return "KEYWORD_CHAR";
case TOKEN_KEYWORD_SHORT: return "KEYWORD_SHORT";
case TOKEN_KEYWORD_LONG: return "KEYWORD_LONG";
case TOKEN_KEYWORD_FLOAT: return "KEYWORD_FLOAT";
case TOKEN_KEYWORD_DOUBLE: return "KEYWORD_DOUBLE";
case TOKEN_KEYWORD_STRUCT: return "KEYWORD_STRUCT";
case TOKEN_KEYWORD_UNION: return "KEYWORD_UNION";
case TOKEN_KEYWORD_ENUM: return "KEYWORD_ENUM";
case TOKEN_KEYWORD_TYPEDEF: return "KEYWORD_TYPEDEF";
case TOKEN_KEYWORD_SIZEOF: return "KEYWORD_SIZEOF";
case TOKEN_KEYWORD_BREAK: return "KEYWORD_BREAK";
case TOKEN_KEYWORD_CONTINUE: return "KEYWORD_CONTINUE";
case TOKEN_KEYWORD_SWITCH: return "KEYWORD_SWITCH";
case TOKEN_KEYWORD_CASE: return "KEYWORD_CASE";
case TOKEN_KEYWORD_DEFAULT: return "KEYWORD_DEFAULT";
case TOKEN_KEYWORD_DO: return "KEYWORD_DO";
case TOKEN_KEYWORD_GOTO: return "KEYWORD_GOTO";
case TOKEN_KEYWORD_CONST: return "KEYWORD_CONST";
case TOKEN_KEYWORD_VOLATILE: return "KEYWORD_VOLATILE";
case TOKEN_KEYWORD_EXTERN: return "KEYWORD_EXTERN";
case TOKEN_KEYWORD_STATIC: return "KEYWORD_STATIC";
case TOKEN_KEYWORD_REGISTER: return "KEYWORD_REGISTER";
case TOKEN_KEYWORD_AUTO: return "KEYWORD_AUTO";
case TOKEN_KEYWORD_SIGNED: return "KEYWORD_SIGNED";
case TOKEN_KEYWORD_UNSIGNED: return "KEYWORD_UNSIGNED";
// 标识符
case TOKEN_IDENTIFIER: return "IDENTIFIER";
// 字面量
case TOKEN_INTEGER_LITERAL: return "INTEGER_LITERAL";
case TOKEN_FLOAT_LITERAL: return "FLOAT_LITERAL";
case TOKEN_STRING_LITERAL: return "STRING_LITERAL";
case TOKEN_CHAR_LITERAL: return "CHAR_LITERAL";
// 运算符
case TOKEN_OP_ASSIGN: return "OP_ASSIGN";
case TOKEN_OP_PLUS: return "OP_PLUS";
case TOKEN_OP_MINUS: return "OP_MINUS";
case TOKEN_OP_MULTIPLY: return "OP_MULTIPLY";
case TOKEN_OP_DIVIDE: return "OP_DIVIDE";
case TOKEN_OP_MODULO: return "OP_MODULO";
case TOKEN_OP_EQ: return "OP_EQ";
case TOKEN_OP_NE: return "OP_NE";
case TOKEN_OP_LT: return "OP_LT";
case TOKEN_OP_LE: return "OP_LE";
case TOKEN_OP_GT: return "OP_GT";
case TOKEN_OP_GE: return "OP_GE";
case TOKEN_OP_AND: return "OP_AND";
case TOKEN_OP_OR: return "OP_OR";
case TOKEN_OP_NOT: return "OP_NOT";
case TOKEN_OP_BIT_AND: return "OP_BIT_AND";
case TOKEN_OP_BIT_OR: return "OP_BIT_OR";
case TOKEN_OP_BIT_XOR: return "OP_BIT_XOR";
case TOKEN_OP_BIT_NOT: return "OP_BIT_NOT";
case TOKEN_OP_LSHIFT: return "OP_LSHIFT";
case TOKEN_OP_RSHIFT: return "OP_RSHIFT";
case TOKEN_OP_INC: return "OP_INC";
case TOKEN_OP_DEC: return "OP_DEC";
case TOKEN_OP_ARROW: return "OP_ARROW";
case TOKEN_OP_DOT: return "OP_DOT";
case TOKEN_OP_COMMA: return "OP_COMMA";
case TOKEN_OP_COLON: return "OP_COLON";
case TOKEN_OP_QUESTION: return "OP_QUESTION";
case TOKEN_OP_ASSIGN_PLUS: return "OP_ASSIGN_PLUS";
case TOKEN_OP_ASSIGN_MINUS: return "OP_ASSIGN_MINUS";
case TOKEN_OP_ASSIGN_MULTIPLY: return "OP_ASSIGN_MULTIPLY";
case TOKEN_OP_ASSIGN_DIVIDE: return "OP_ASSIGN_DIVIDE";
case TOKEN_OP_ASSIGN_MODULO: return "OP_ASSIGN_MODULO";
case TOKEN_OP_ASSIGN_LSHIFT: return "OP_ASSIGN_LSHIFT";
case TOKEN_OP_ASSIGN_RSHIFT: return "OP_ASSIGN_RSHIFT";
case TOKEN_OP_ASSIGN_BIT_AND: return "OP_ASSIGN_BIT_AND";
case TOKEN_OP_ASSIGN_BIT_OR: return "OP_ASSIGN_BIT_OR";
case TOKEN_OP_ASSIGN_BIT_XOR: return "OP_ASSIGN_BIT_XOR";
// 分隔符/标点符号
case TOKEN_LPAREN: return "LPAREN";
case TOKEN_RPAREN: return "RPAREN";
case TOKEN_LBRACE: return "LBRACE";
case TOKEN_RBRACE: return "RBRACE";
case TOKEN_LBRACKET: return "LBRACKET";
case TOKEN_RBRACKET: return "RBRACKET";
case TOKEN_SEMICOLON: return "SEMICOLON";
case TOKEN_POUND: return "POUND";
case TOKEN_ELLIPSIS: return "ELLIPSIS";
default: return "UNKNOWN_TYPE";
}
}
词法分析器(Lexer)的核心实现
词法分析器需要维护当前扫描到的位置,并提供前进、回溯、查看下一个字符等功能。
// lexer.h
#ifndef LEXER_H
#define LEXER_H
#include "token.h" // 包含令牌定义
#include <stdio.h> // 用于文件操作
// 定义词法分析器结构体
typedef struct {
FILE* source_file; // 输入的源代码文件指针
char* source_code; // 存储整个源代码的缓冲区
size_t source_size; // 源代码文件大小
int current_pos; // 当前正在处理的字符在source_code中的位置
int line; // 当前字符所在的行号
int column; // 当前字符所在的列号
// 关键字映射表 (用于快速查找标识符是否是关键字)
// 这是一个简单的哈希表或排序数组,这里我们先用一个简单的数组模拟
// 实际编译器会用更高效的结构
struct {
const char* lexeme;
TokenType type;
} keywords[40]; // 预留足够的空间给关键字
int num_keywords; // 实际关键字数量
} Lexer;
// 函数声明
Lexer* init_lexer(const char* filepath); // 初始化词法分析器
void free_lexer(Lexer* lexer); // 释放词法分析器资源
Token get_next_token(Lexer* lexer); // 获取下一个令牌
void lexer_error(Lexer* lexer, const char* message); // 报告词法错误
#endif // LEXER_H
现在,是时候揭开lexer.c
的神秘面纱了!这将是词法分析的核心逻辑,我们将在这里实现如何识别各种C语言元素。
// lexer.c
#include "lexer.h"
#include <stdlib.h> // 用于 malloc, free
#include <string.h> // 用于 strdup, strcmp, strncmp
#include <ctype.h> // 用于 isalnum, isalpha, isdigit, isspace
// --- 辅助宏和函数 ---
// 报告词法错误并退出
void lexer_error(Lexer* lexer, const char* message) {
fprintf(stderr, "词法错误 (%d:%d): %s\n", lexer->line, lexer->column, message);
if (lexer->source_code) {
// 尝试打印错误行上下文
int start_col = 0;
int end_col = lexer->current_pos;
while (start_col < lexer->current_pos && lexer->source_code[start_col] != '\n') {
start_col++;
}
if (start_col < lexer->current_pos) start_col++; // 跳过换行符
while (end_col < lexer->source_size && lexer->source_code[end_col] != '\n') {
end_col++;
}
fprintf(stderr, " %.*s\n", end_col - start_col, lexer->source_code + start_col);
for (int i = 0; i < lexer->column - 1; ++i) {
fprintf(stderr, " ");
}
fprintf(stderr, "^\n");
}
free_lexer(lexer); // 释放资源
exit(EXIT_FAILURE); // 终止程序
}
// 获取当前字符
static char current_char(Lexer* lexer) {
if (lexer->current_pos >= lexer->source_size) {
return '\0'; // 文件结束
}
return lexer->source_code[lexer->current_pos];
}
// 查看下一个字符 (不移动指针)
static char peek_char(Lexer* lexer, int offset) {
if (lexer->current_pos + offset >= lexer->source_size) {
return '\0'; // 文件结束
}
return lexer->source_code[lexer->current_pos + offset];
}
// 前进一个字符,并更新行号和列号
static void advance_char(Lexer* lexer) {
if (lexer->current_pos < lexer->source_size) {
if (lexer->source_code[lexer->current_pos] == '\n') {
lexer->line++;
lexer->column = 1; // 新行从第一列开始
} else {
lexer->column++;
}
lexer->current_pos++;
}
}
// 跳过空白字符 (空格、制表符、换行符等)
static void skip_whitespace(Lexer* lexer) {
while (lexer->current_pos < lexer->source_size && isspace(current_char(lexer))) {
advance_char(lexer);
}
}
// 创建一个新令牌
static Token create_token(Lexer* lexer, TokenType type, const char* start_ptr, int length) {
Token token;
token.type = type;
// 为词素字符串分配内存并复制
token.lexeme = (char*)malloc(length + 1);
if (!token.lexeme) {
lexer_error(lexer, "内存分配失败 (lexeme)");
}
strncpy(token.lexeme, start_ptr, length);
token.lexeme[length] = '\0'; // 确保字符串以null结尾
// 记录令牌的位置信息
// 注意:这里记录的是令牌的起始位置,而不是当前扫描到的位置
// 需要根据实际情况调整,通常是记录词素的起始行和列
// 为了简化,这里先用当前lexer的行和列,实际应在识别词素前记录
token.line = lexer->line;
token.column = lexer->column - length; // 减去词素长度得到起始列
// 初始化字面量值
token.value.int_val = 0;
token.value.float_val = 0.0;
token.value.char_val = '\0';
return token;
}
// 释放令牌内存 (特别是lexeme)
static void free_token_lexeme(Token* token) {
if (token && token->lexeme) {
free(token->lexeme);
token->lexeme = NULL;
}
}
// --- 关键字映射表初始化 ---
static void init_keywords(Lexer* lexer) {
// 这里使用一个简单的数组,实际编译器会使用哈希表来提高查找效率
lexer->num_keywords = 0;
#define ADD_KEYWORD(str, type) \
lexer->keywords[lexer->num_keywords].lexeme = str; \
lexer->keywords[lexer->num_keywords].type = type; \
lexer->num_keywords++;
ADD_KEYWORD("int", TOKEN_KEYWORD_INT);
ADD_KEYWORD("return", TOKEN_KEYWORD_RETURN);
ADD_KEYWORD("if", TOKEN_KEYWORD_IF);
ADD_KEYWORD("else", TOKEN_KEYWORD_ELSE);
ADD_KEYWORD("while", TOKEN_KEYWORD_WHILE);
ADD_KEYWORD("for", TOKEN_KEYWORD_FOR);
ADD_KEYWORD("void", TOKEN_KEYWORD_VOID);
ADD_KEYWORD("char", TOKEN_KEYWORD_CHAR);
ADD_KEYWORD("short", TOKEN_KEYWORD_SHORT);
ADD_KEYWORD("long", TOKEN_KEYWORD_LONG);
ADD_KEYWORD("float", TOKEN_KEYWORD_FLOAT);
ADD_KEYWORD("double", TOKEN_KEYWORD_DOUBLE);
ADD_KEYWORD("struct", TOKEN_KEYWORD_STRUCT);
ADD_KEYWORD("union", TOKEN_KEYWORD_UNION);
ADD_KEYWORD("enum", TOKEN_KEYWORD_ENUM);
ADD_KEYWORD("typedef", TOKEN_KEYWORD_TYPEDEF);
ADD_KEYWORD("sizeof", TOKEN_KEYWORD_SIZEOF);
ADD_KEYWORD("break", TOKEN_KEYWORD_BREAK);
ADD_KEYWORD("continue", TOKEN_KEYWORD_CONTINUE);
ADD_KEYWORD("switch", TOKEN_KEYWORD_SWITCH);
ADD_KEYWORD("case", TOKEN_KEYWORD_CASE);
ADD_KEYWORD("default", TOKEN_KEYWORD_DEFAULT);
ADD_KEYWORD("do", TOKEN_KEYWORD_DO);
ADD_KEYWORD("goto", TOKEN_KEYWORD_GOTO);
ADD_KEYWORD("const", TOKEN_KEYWORD_CONST);
ADD_KEYWORD("volatile", TOKEN_KEYWORD_VOLATILE);
ADD_KEYWORD("extern", TOKEN_KEYWORD_EXTERN);
ADD_KEYWORD("static", TOKEN_KEYWORD_STATIC);
ADD_KEYWORD("register", TOKEN_KEYWORD_REGISTER);
ADD_KEYWORD("auto", TOKEN_KEYWORD_AUTO);
ADD_KEYWORD("signed", TOKEN_KEYWORD_SIGNED);
ADD_KEYWORD("unsigned", TOKEN_KEYWORD_UNSIGNED);
#undef ADD_KEYWORD
}
// --- 词法分析器初始化与清理 ---
// 初始化词法分析器
Lexer* init_lexer(const char* filepath) {
Lexer* lexer = (Lexer*)malloc(sizeof(Lexer));
if (!lexer) {
fprintf(stderr, "错误: 内存分配失败 (Lexer)\n");
return NULL;
}
lexer->source_file = fopen(filepath, "rb"); // 以二进制读取模式打开文件
if (!lexer->source_file) {
fprintf(stderr, "错误: 无法打开文件 '%s'\n", filepath);
free(lexer);
return NULL;
}
// 获取文件大小
fseek(lexer->source_file, 0, SEEK_END);
lexer->source_size = ftell(lexer->source_file);
fseek(lexer->source_file, 0, SEEK_SET);
// 为源代码缓冲区分配内存
lexer->source_code = (char*)malloc(lexer->source_size + 1); // +1 用于null终止符
if (!lexer->source_code) {
fprintf(stderr, "错误: 内存分配失败 (source_code)\n");
fclose(lexer->source_file);
free(lexer);
return NULL;
}
// 读取整个文件内容到缓冲区
if (fread(lexer->source_code, 1, lexer->source_size, lexer->source_file) != lexer->source_size) {
fprintf(stderr, "错误: 读取文件失败 '%s'\n", filepath);
free(lexer->source_code);
fclose(lexer->source_file);
free(lexer);
return NULL;
}
lexer->source_code[lexer->source_size] = '\0'; // 确保缓冲区以null终止
// 关闭文件,因为我们已经将内容读入内存
fclose(lexer->source_file);
lexer->source_file = NULL; // 将文件指针置空,避免误用
lexer->current_pos = 0;
lexer->line = 1;
lexer->column = 1;
init_keywords(lexer); // 初始化关键字映射表
return lexer;
}
// 释放词法分析器资源
void free_lexer(Lexer* lexer) {
if (lexer) {
if (lexer->source_code) {
free(lexer->source_code);
lexer->source_code = NULL;
}
// source_file 已经在 init_lexer 中关闭,这里无需再次关闭
free(lexer);
}
}
// --- 核心词法分析逻辑 ---
// 识别标识符或关键字
static Token parse_identifier_or_keyword(Lexer* lexer) {
int start_pos = lexer->current_pos;
int start_col = lexer->column; // 记录词素起始列
// 标识符由字母、数字或下划线组成,但必须以字母或下划线开头
while (isalnum(current_char(lexer)) || current_char(lexer) == '_') {
advance_char(lexer);
}
int length = lexer->current_pos - start_pos;
const char* lexeme_start = lexer->source_code + start_pos;
// 检查是否是关键字
for (int i = 0; i < lexer->num_keywords; ++i) {
if (length == strlen(lexer->keywords[i].lexeme) &&
strncmp(lexeme_start, lexer->keywords[i].lexeme, length) == 0) {
Token token = create_token(lexer, lexer->keywords[i].type, lexeme_start, length);
token.column = start_col; // 修正列号
return token;
}
}
// 如果不是关键字,那就是标识符
Token token = create_token(lexer, TOKEN_IDENTIFIER, lexeme_start, length);
token.column = start_col; // 修正列号
return token;
}
// 识别数字字面量 (整数和浮点数)
static Token parse_number(Lexer* lexer) {
int start_pos = lexer->current_pos;
int start_col = lexer->column; // 记录词素起始列
TokenType type = TOKEN_INTEGER_LITERAL; // 默认是整数
// 处理整数部分
while (isdigit(current_char(lexer))) {
advance_char(lexer);
}
// 处理小数点和浮点数部分
if (current_char(lexer) == '.') {
// 确保小数点后有数字,避免将 `1.` 识别为浮点数
if (isdigit(peek_char(lexer, 1))) {
type = TOKEN_FLOAT_LITERAL;
advance_char(lexer); // 跳过小数点
while (isdigit(current_char(lexer))) {
advance_char(lexer);
}
}
}
// 处理指数部分 (e/E)
if (tolower(current_char(lexer)) == 'e') {
if (isdigit(peek_char(lexer, 1)) ||
((peek_char(lexer, 1) == '+' || peek_char(lexer, 1) == '-') && isdigit(peek_char(lexer, 2)))) {
type = TOKEN_FLOAT_LITERAL;
advance_char(lexer); // 跳过 'e' 或 'E'
if (current_char(lexer) == '+' || current_char(lexer) == '-') {
advance_char(lexer); // 跳过 '+' 或 '-'
}
while (isdigit(current_char(lexer))) {
advance_char(lexer);
}
}
}
int length = lexer->current_pos - start_pos;
const char* lexeme_start = lexer->source_code + start_pos;
Token token = create_token(lexer, type, lexeme_start, length);
token.column = start_col; // 修正列号
// 转换字面量值
if (type == TOKEN_INTEGER_LITERAL) {
token.value.int_val = strtoll(token.lexeme, NULL, 0); // 自动处理十进制、八进制、十六进制
} else { // TOKEN_FLOAT_LITERAL
token.value.float_val = strtod(token.lexeme, NULL);
}
return token;
}
// 识别字符串字面量
static Token parse_string(Lexer* lexer) {
int start_pos = lexer->current_pos;
int start_col = lexer->column; // 记录词素起始列
advance_char(lexer); // 跳过开头的双引号 '"'
while (current_char(lexer) != '"' && current_char(lexer) != '\0') {
// 处理转义字符
if (current_char(lexer) == '\\') {
advance_char(lexer); // 跳过 '\'
// 这里可以添加更复杂的转义字符处理,例如 \n, \t, \xNN 等
// 简化起见,我们只跳过下一个字符
if (current_char(lexer) == '\0') {
lexer_error(lexer, "字符串字面量中非法的转义序列或文件意外结束");
}
}
advance_char(lexer);
}
if (current_char(lexer) == '\0') {
lexer_error(lexer, "未终止的字符串字面量");
}
advance_char(lexer); // 跳过末尾的双引号 '"'
int length = lexer->current_pos - start_pos;
const char* lexeme_start = lexer->source_code + start_pos;
Token token = create_token(lexer, TOKEN_STRING_LITERAL, lexeme_start, length);
token.column = start_col; // 修正列号
return token;
}
// 识别字符字面量
static Token parse_char_literal(Lexer* lexer) {
int start_pos = lexer->current_pos;
int start_col = lexer->column; // 记录词素起始列
advance_char(lexer); // 跳过开头的单引号 '''
char char_val;
if (current_char(lexer) == '\\') { // 处理转义字符
advance_char(lexer); // 跳过 '\'
switch (current_char(lexer)) {
case 'n': char_val = '\n'; break;
case 't': char_val = '\t'; break;
case 'r': char_val = '\r'; break;
case 'b': char_val = '\b'; break;
case 'f': char_val = '\f'; break;
case 'a': char_val = '\a'; break;
case 'v': char_val = '\v'; break;
case '\\': char_val = '\\'; break;
case '\'': char_val = '\''; break;
case '"': char_val = '"'; break;
case '?': char_val = '?'; break;
// TODO: 处理八进制和十六进制转义字符
default:
lexer_error(lexer, "字符字面量中非法的转义序列");
char_val = '\0'; // 默认值
break;
}
} else if (current_char(lexer) == '\0') {
lexer_error(lexer, "未终止的字符字面量或文件意外结束");
char_val = '\0'; // 默认值
} else {
char_val = current_char(lexer);
}
advance_char(lexer); // 跳过字符本身
if (current_char(lexer) != '\'') {
lexer_error(lexer, "未终止的字符字面量");
}
advance_char(lexer); // 跳过末尾的单引号 '''
int length = lexer->current_pos - start_pos;
const char* lexeme_start = lexer->source_code + start_pos;
Token token = create_token(lexer, TOKEN_CHAR_LITERAL, lexeme_start, length);
token.column = start_col; // 修正列号
token.value.char_val = char_val;
return token;
}
// 跳过单行注释 //
static void skip_single_line_comment(Lexer* lexer) {
while (current_char(lexer) != '\n' && current_char(lexer) != '\0') {
advance_char(lexer);
}
// 不跳过换行符,让 advance_char 在下次调用时处理
}
// 跳过多行注释 /* ... */
static void skip_multi_line_comment(Lexer* lexer) {
advance_char(lexer); // 跳过 '*'
while (!(current_char(lexer) == '*' && peek_char(lexer, 1) == '/') && current_char(lexer) != '\0') {
advance_char(lexer);
}
if (current_char(lexer) == '\0') {
lexer_error(lexer, "未终止的多行注释");
}
advance_char(lexer); // 跳过 '*'
advance_char(lexer); // 跳过 '/'
}
// 获取下一个令牌的核心函数
Token get_next_token(Lexer* lexer) {
// 1. 跳过所有空白字符和注释
while (1) {
skip_whitespace(lexer);
// 处理注释
if (current_char(lexer) == '/') {
if (peek_char(lexer, 1) == '/') { // 单行注释 //
advance_char(lexer); // 跳过第一个 '/'
advance_char(lexer); // 跳过第二个 '/'
skip_single_line_comment(lexer);
continue; // 继续循环,跳过更多空白或注释
} else if (peek_char(lexer, 1) == '*') { // 多行注释 /* ... */
advance_char(lexer); // 跳过第一个 '/'
advance_char(lexer); // 跳过 '*'
skip_multi_line_comment(lexer);
continue; // 继续循环
}
}
break; // 没有空白或注释了,退出循环
}
// 2. 检查文件是否结束
if (lexer->current_pos >= lexer->source_size) {
return create_token(lexer, TOKEN_EOF, "", 0); // 返回EOF令牌
}
// 3. 记录当前词素的起始位置信息
int token_start_pos = lexer->current_pos;
int token_start_line = lexer->line;
int token_start_column = lexer->column;
char c = current_char(lexer);
// 4. 根据当前字符识别令牌类型
// 优先级:长匹配优先(例如,先检查 `==` 再检查 `=`)
// 标识符和关键字
if (isalpha(c) || c == '_') {
return parse_identifier_or_keyword(lexer);
}
// 数字字面量
if (isdigit(c)) {
return parse_number(lexer);
}
// 字符串字面量
if (c == '"') {
return parse_string(lexer);
}
// 字符字面量
if (c == '\'') {
return parse_char_literal(lexer);
}
// 运算符和分隔符
advance_char(lexer); // 默认先前进一个字符,对于单字符令牌是正确的
// 对于多字符令牌,会在后续逻辑中进一步处理
switch (c) {
case '(': return create_token(lexer, TOKEN_LPAREN, lexer->source_code + token_start_pos, 1);
case ')': return create_token(lexer, TOKEN_RPAREN, lexer->source_code + token_start_pos, 1);
case '{': return create_token(lexer, TOKEN_LBRACE, lexer->source_code + token_start_pos, 1);
case '}': return create_token(lexer, TOKEN_RBRACE, lexer->source_code + token_start_pos, 1);
case '[': return create_token(lexer, TOKEN_LBRACKET, lexer->source_code + token_start_pos, 1);
case ']': return create_token(lexer, TOKEN_RBRACKET, lexer->source_code + token_start_pos, 1);
case ';': return create_token(lexer, TOKEN_SEMICOLON, lexer->source_code + token_start_pos, 1);
case ',': return create_token(lexer, TOKEN_OP_COMMA, lexer->source_code + token_start_pos, 1);
case ':': return create_token(lexer, TOKEN_OP_COLON, lexer->source_code + token_start_pos, 1);
case '?': return create_token(lexer, TOKEN_OP_QUESTION, lexer->source_code + token_start_pos, 1);
case '#': return create_token(lexer, TOKEN_POUND, lexer->source_code + token_start_pos, 1);
case '~': return create_token(lexer, TOKEN_OP_BIT_NOT, lexer->source_code + token_start_pos, 1);
case '+':
if (current_char(lexer) == '+') { advance_char(lexer); return create_token(lexer, TOKEN_OP_INC, lexer->source_code + token_start_pos, 2); }
if (current_char(lexer) == '=') { advance_char(lexer); return create_token(lexer, TOKEN_OP_ASSIGN_PLUS, lexer->source_code + token_start_pos, 2); }
return create_token(lexer, TOKEN_OP_PLUS, lexer->source_code + token_start_pos, 1);
case '-':
if (current_char(lexer) == '-') { advance_char(lexer); return create_token(lexer, TOKEN_OP_DEC, lexer->source_code + token_start_pos, 2); }
if (current_char(lexer) == '>') { advance_char(lexer); return create_token(lexer, TOKEN_OP_ARROW, lexer->source_code + token_start_pos, 2); }
if (current_char(lexer) == '=') { advance_char(lexer); return create_token(lexer, TOKEN_OP_ASSIGN_MINUS, lexer->source_code + token_start_pos, 2); }
return create_token(lexer, TOKEN_OP_MINUS, lexer->source_code + token_start_pos, 1);
case '*':
if (current_char(lexer) == '=') { advance_char(lexer); return create_token(lexer, TOKEN_OP_ASSIGN_MULTIPLY, lexer->source_code + token_start_pos, 2); }
return create_token(lexer, TOKEN_OP_MULTIPLY, lexer->source_code + token_start_pos, 1);
// '/' 的情况已在注释处理中处理,这里只处理作为除号和复合赋值
case '/':
if (current_char(lexer) == '=') { advance_char(lexer); return create_token(lexer, TOKEN_OP_ASSIGN_DIVIDE, lexer->source_code + token_start_pos, 2); }
return create_token(lexer, TOKEN_OP_DIVIDE, lexer->source_code + token_start_pos, 1);
case '%':
if (current_char(lexer) == '=') { advance_char(lexer); return create_token(lexer, TOKEN_OP_ASSIGN_MODULO, lexer->source_code + token_start_pos, 2); }
return create_token(lexer, TOKEN_OP_MODULO, lexer->source_code + token_start_pos, 1);
case '=':
if (current_char(lexer) == '=') { advance_char(lexer); return create_token(lexer, TOKEN_OP_EQ, lexer->source_code + token_start_pos, 2); }
return create_token(lexer, TOKEN_OP_ASSIGN, lexer->source_code + token_start_pos, 1);
case '!':
if (current_char(lexer) == '=') { advance_char(lexer); return create_token(lexer, TOKEN_OP_NE, lexer->source_code + token_start_pos, 2); }
return create_token(lexer, TOKEN_OP_NOT, lexer->source_code + token_start_pos, 1);
case '<':
if (current_char(lexer) == '=') { advance_char(lexer); return create_token(lexer, TOKEN_OP_LE, lexer->source_code + token_start_pos, 2); }
if (current_char(lexer) == '<') {
advance_char(lexer);
if (current_char(lexer) == '=') { advance_char(lexer); return create_token(lexer, TOKEN_OP_ASSIGN_LSHIFT, lexer->source_code + token_start_pos, 3); }
return create_token(lexer, TOKEN_OP_LSHIFT, lexer->source_code + token_start_pos, 2);
}
return create_token(lexer, TOKEN_OP_LT, lexer->source_code + token_start_pos, 1);
case '>':
if (current_char(lexer) == '=') { advance_char(lexer); return create_token(lexer, TOKEN_OP_GE, lexer->source_code + token_start_pos, 2); }
if (current_char(lexer) == '>') {
advance_char(lexer);
if (current_char(lexer) == '=') { advance_char(lexer); return create_token(lexer, TOKEN_OP_ASSIGN_RSHIFT, lexer->source_code + token_start_pos, 3); }
return create_token(lexer, TOKEN_OP_RSHIFT, lexer->source_code + token_start_pos, 2);
}
return create_token(lexer, TOKEN_OP_GT, lexer->source_code + token_start_pos, 1);
case '&':
if (current_char(lexer) == '&') { advance_char(lexer); return create_token(lexer, TOKEN_OP_AND, lexer->source_code + token_start_pos, 2); }
if (current_char(lexer) == '=') { advance_char(lexer); return create_token(lexer, TOKEN_OP_ASSIGN_BIT_AND, lexer->source_code + token_start_pos, 2); }
return create_token(lexer, TOKEN_OP_BIT_AND, lexer->source_code + token_start_pos, 1);
case '|':
if (current_char(lexer) == '|') { advance_char(lexer); return create_token(lexer, TOKEN_OP_OR, lexer->source_code + token_start_pos, 2); }
if (current_char(lexer) == '=') { advance_char(lexer); return create_token(lexer, TOKEN_OP_ASSIGN_BIT_OR, lexer->source_code + token_start_pos, 2); }
return create_token(lexer, TOKEN_OP_BIT_OR, lexer->source_code + token_start_pos, 1);
case '^':
if (current_char(lexer) == '=') { advance_char(lexer); return create_token(lexer, TOKEN_OP_ASSIGN_BIT_XOR, lexer->source_code + token_start_pos, 2); }
return create_token(lexer, TOKEN_OP_BIT_XOR, lexer->source_code + token_start_pos, 1);
case '.':
// 处理 ... (ellipsis)
if (peek_char(lexer, 0) == '.' && peek_char(lexer, 1) == '.') {
advance_char(lexer); // 跳过第二个 '.'
advance_char(lexer); // 跳过第三个 '.'
return create_token(lexer, TOKEN_ELLIPSIS, lexer->source_code + token_start_pos, 3);
}
return create_token(lexer, TOKEN_OP_DOT, lexer->source_code + token_start_pos, 1);
default:
// 如果遇到无法识别的字符,报告错误
lexer_error(lexer, "无法识别的字符");
// 返回一个未知令牌,但程序通常会在此处退出
return create_token(lexer, TOKEN_UNKNOWN, lexer->source_code + token_start_pos, 1);
}
}
测试驱动:让我们的词法分析器动起来!
光说不练假把式!我们需要一个main.c
文件来测试我们的词法分析器。
// main.c
#include "lexer.h"
#include "token.h"
#include <stdio.h>
#include <stdlib.h>
// 这是一个简单的测试文件,用于验证词法分析器功能
const char* TEST_CODE =
"// 这是一个单行注释\n"
"/* 这是一个\n"
" * 多行注释\n"
" */\n"
"int main() {\n"
" int x = 10;\n"
" float y = 3.14e-2;\n"
" char c = 'A';\n"
" char newline_char = '\\n';\n"
" char tab_char = '\\t';\n"
" char backslash_char = '\\\\';\n"
" char single_quote_char = '\\'';\n"
" char double_quote_char = '\"';\n"
" char question_mark_char = '?';\n"
" char null_char = '\\0';\n" // C99
" char alert_char = '\\a';\n"
" char backspace_char = '\\b';\n"
" char formfeed_char = '\\f';\n"
" char vertical_tab_char = '\\v';\n"
" const char* str = \"Hello, \\\"World!\\n\";\n"
" if (x >= 5 && y < 10.0) {\n"
" return x + y * 2;\n"
" }\n"
" x++;\n"
" y--;\n"
" x += 5;\n"
" y /= 2.0;\n"
" int a = 10, b = 20;\n"
" if (a == b) { /* do something */ }\n"
" else if (a != b && (a > b || a < b)) { /* another branch */ }\n"
" else { /* final branch */ }\n"
" for (int i = 0; i < 10; i++) {\n"
" printf(\"Loop %d\\n\", i);\n"
" }\n"
" while (x > 0) { x--; }\n"
" do { y++; } while (y < 100.0);\n"
" int bit_ops = (5 & 3) | (1 << 2) ^ (~0);\n"
" int assign_ops = 10;\n"
" assign_ops >>= 1;\n"
" assign_ops &= 0xF;\n"
" assign_ops |= 0x10;\n"
" assign_ops ^= 0x20;\n"
" struct MyStruct { int member; };\n"
" struct MyStruct s;\n"
" s.member = 1;\n"
" int* ptr = &x;\n"
" ptr->member = 2; // 这是一个语法错误,但词法分析器会识别 -> 运算符\n"
" void func(int, ...);\n" // 测试省略号
" #define MACRO 1\n" // 测试 #
" sizeof(int);\n" // 测试 sizeof
" goto end_label;\n" // 测试 goto
"end_label: ;\n"
" return 0;\n"
"}\n"
;
int main(int argc, char* argv[]) {
// 将测试代码写入一个临时文件,以便lexer可以读取
const char* temp_filepath = "test_code.c";
FILE* temp_file = fopen(temp_filepath, "w");
if (!temp_file) {
fprintf(stderr, "错误: 无法创建临时文件 '%s'\n", temp_filepath);
return EXIT_FAILURE;
}
fprintf(temp_file, "%s", TEST_CODE);
fclose(temp_file);
Lexer* lexer = init_lexer(temp_filepath);
if (!lexer) {
return EXIT_FAILURE;
}
printf("--- 词法分析开始 ---\n");
Token token;
do {
token = get_next_token(lexer);
printf("令牌: %-20s 词素: \"%s\" (行: %d, 列: %d)",
token_type_to_string(token.type),
token.lexeme,
token.line,
token.column);
// 打印字面量的值
if (token.type == TOKEN_INTEGER_LITERAL) {
printf(", 值: %lld (int)", token.value.int_val);
} else if (token.type == TOKEN_FLOAT_LITERAL) {
printf(", 值: %f (float)", token.value.float_val);
} else if (token.type == TOKEN_CHAR_LITERAL) {
printf(", 值: '%c' (char)", token.value.char_val);
}
printf("\n");
// 释放令牌的词素内存,因为每次get_next_token都会分配新内存
free_token_lexeme(&token);
} while (token.type != TOKEN_EOF);
printf("--- 词法分析结束 ---\n");
free_lexer(lexer); // 释放词法分析器资源
// 可选:删除临时文件
remove(temp_filepath);
return EXIT_SUCCESS;
}
编译和运行
将上述三个文件(token.h
, token.c
, lexer.h
, lexer.c
, main.c
)放在同一个目录下。 你可以使用GCC编译器进行编译:
gcc -o my_compiler main.c lexer.c token.c -Wall -Wextra
然后运行:
./my_compiler
你将看到源代码被逐行分解成一个个令牌,并打印出它们的类型、词素以及位置信息。
词法分析的逻辑分析与挑战
-
状态机思想: 词法分析器本质上是一个有限状态自动机(Finite State Automaton, FSA)。它根据当前字符和当前状态,决定下一个状态以及生成什么令牌。例如,当遇到
/
时,它会进入一个“可能注释”的状态,然后检查下一个字符是/
还是*
来决定是单行注释还是多行注释。 -
最长匹配原则: 当多个词素模式可以匹配同一段字符时,词法分析器通常会选择最长的那个。例如,如果源代码是
==
,它应该被识别为TOKEN_OP_EQ
,而不是两个独立的TOKEN_OP_ASSIGN
。我们的get_next_token
函数通过先检查双字符甚至三字符的运算符(如>>=
,...
)来隐式实现这一点。 -
关键字与标识符的区分: 标识符和关键字的构成规则相似(字母、数字、下划线开头)。区分它们的方法是,在识别出一个潜在的标识符后,去预定义的关键字列表中查找。如果找到了,就是关键字;否则,就是标识符。我们在这里使用了简单的数组遍历查找,实际编译器会用哈希表或Trie树来加速查找。
-
字面量的处理:
-
整数: 需要处理十进制、八进制(
0
开头)、十六进制(0x
或0X
开头)以及长整型后缀(L
,LL
)。我们的strtoll
函数可以自动处理基数,但后缀需要额外解析。 -
浮点数: 需要处理小数点和指数部分(
e
或E
)。 -
字符串/字符: 最复杂的是转义字符(
\n
,\t
,\\
,\"
等)。我们的实现中只处理了部分常见的转义字符,更完整的C语言编译器需要处理所有C标准定义的转义序列,包括八进制和十六进制表示。
-
-
错误处理: 词法分析器需要能够检测并报告词法错误,例如未终止的字符串、无法识别的字符等。我们的
lexer_error
函数提供了一个简单的错误报告机制,包括打印错误行上下文。 -
性能考量: 对于大型源代码文件,词法分析的效率至关重要。优化包括:
-
一次性将整个文件读入内存(我们已经这样做了)。
-
使用更高效的数据结构(如哈希表)来存储和查找关键字。
-
避免不必要的内存分配和复制。
-
总结与展望
恭喜你!你已经完成了手撸编译器的第一步——一个功能相对完善的C语言词法分析器!你现在应该对C语言源代码是如何被分解成一个个有意义的“砖块”有了清晰的认识。你亲手写下的每一行代码,都在将抽象的理论变为具体的实践。
这只是万里长征的第一步。在下一章中,我们将进入编译器的第二个核心阶段:语法分析(Syntax Analysis)。我们将学习如何利用这些“砖块”(令牌)来构建C语言代码的结构化表示——抽象语法树(AST)。这将是一个更加激动人心的过程,因为我们将开始真正理解C语言的“骨架”!
准备好迎接更大的挑战了吗?继续保持这份激情,我们下一章不见不散!点赞、收藏、关注,让我们一起把这“手撸编译器”的肝爆之旅进行到底!
------------------------------------------------------------------------------------------------------------------------更新于2025.6.15晚21:37
第二章:构建代码骨架——抽象语法树与递归下降解析)
朋友们,欢迎回到我们的“手撸编译器,肝爆4万行代码”的史诗级旅程!在第一章中,我们成功地打造了一个词法分析器(Lexer),它像一个高效的“阅读器”,将C语言的源代码分解成一个个独立的、有意义的“令牌”(Token)。现在,我们手里握着一串串整齐的令牌流,比如 int
, main
, (
, )
, {
, int
, x
, =
, 10
, ;
, return
, 0
, ;
, }
。
但问题来了:这些令牌仅仅是“单词”,它们之间有什么关系?哪些令牌构成一个变量声明?哪些构成一个函数调用?哪些又构成一个完整的语句或表达式?光有“单词”是远远不够的,我们需要理解它们的“语法结构”,把它们组织成有意义的“句子”!
这就是我们第二章的主角——**语法分析(Syntax Analysis)**登场的时候了!
语法分析:从“单词”到“句子”的蜕变
语法分析器(Parser),就像一位严谨的“语法学家”,它的任务就是:
-
验证语法合法性: 接收词法分析器生成的令牌流,并根据C语言的语法规则来检查这个序列是否符合语法规范。例如,
int x = 10;
是合法的,而x 10 = int;
显然是非法的。 -
构建抽象语法树(AST): 如果令牌流符合语法规则,语法分析器就会构建一个抽象语法树(Abstract Syntax Tree, AST)。AST是源代码的层次化、抽象表示,它移除了源代码中不重要的细节(如括号、分号等),只保留了代码的本质结构和语义信息。
简单来说,语法分析器就是把我们第一章生成的“砖块”(令牌),按照C语言的“建筑图纸”(语法规则),搭建成一个有结构、有层次的“毛坯房”(AST)。这个“毛坯房”就是我们后续语义分析、中间代码生成和代码优化的基础。
抽象语法树(AST):代码的“骨架”与“灵魂”
AST是编译器的核心数据结构之一。它不仅仅是简单地将令牌排列起来,而是以树形结构清晰地表达了代码的逻辑和运算优先级。
例如,对于表达式 x + y * 2
:
-
词法分析器会生成:
IDENTIFIER(x)
,OP_PLUS
,IDENTIFIER(y)
,OP_MULTIPLY
,INTEGER_LITERAL(2)
。 -
而AST会表示为:
+ (加法) ├── IDENTIFIER (x) └── * (乘法) ├── IDENTIFIER (y) └── INTEGER_LITERAL (2)
你看,AST天然地体现了乘法优先于加法的运算规则,这在扁平的令牌流中是无法直接看出的。
AST节点结构的设计
在C语言中,我们可以用enum
定义AST节点的类型,用struct
和union
来定义不同类型节点的具体数据。
// ast.h
#ifndef AST_H
#define AST_H
#include "token.h" // AST节点可能会包含Token信息
// 定义抽象语法树节点的类型
typedef enum {
// 顶层节点
AST_PROGRAM, // 整个程序,包含多个函数定义和全局声明
// 声明节点
AST_VAR_DECL, // 变量声明 (e.g., int x; int y = 10;)
AST_FUNC_DECL, // 函数声明 (e.g., int func(int a);) - 暂时不实现,先只支持定义
AST_FUNC_DEF, // 函数定义 (e.g., int main() { ... })
AST_PARAM_DECL, // 函数参数声明 (e.g., int a)
// 语句节点
AST_COMPOUND_STMT, // 复合语句/代码块 (e.g., { ... })
AST_EXPR_STMT, // 表达式语句 (e.g., x = 5; func();)
AST_RETURN_STMT, // return 语句 (e.g., return 0; return x + y;)
AST_IF_STMT, // if 语句 (e.g., if (...) { ... } else { ... })
AST_WHILE_STMT, // while 循环 (e.g., while (...) { ... })
AST_FOR_STMT, // for 循环 (e.g., for (...;...;...) { ... }) - 暂时不实现,先只支持while
// 表达式节点
AST_BINARY_EXPR, // 二元表达式 (e.g., a + b, x == y)
AST_UNARY_EXPR, // 一元表达式 (e.g., -x, !flag, ++i)
AST_ASSIGN_EXPR, // 赋值表达式 (e.g., a = b, x += 5)
AST_CALL_EXPR, // 函数调用表达式 (e.g., func(1, 2))
AST_IDENTIFIER_EXPR, // 标识符表达式 (e.g., x, myVar)
AST_INTEGER_LITERAL_EXPR, // 整数常量表达式 (e.g., 10, 0xFF)
AST_FLOAT_LITERAL_EXPR, // 浮点数常量表达式 (e.g., 3.14, 1.0e-5)
AST_STRING_LITERAL_EXPR, // 字符串常量表达式 (e.g., "hello")
AST_CHAR_LITERAL_EXPR, // 字符常量表达式 (e.g., 'a')
} ASTNodeType;
// 前向声明,因为节点之间会相互引用
struct ASTNode;
// 表达式节点通用结构
typedef struct {
struct ASTNode* left; // 左操作数
struct ASTNode* right; // 右操作数
TokenType op; // 运算符类型 (来自token.h)
} BinaryExpr;
typedef struct {
struct ASTNode* operand; // 操作数
TokenType op; // 运算符类型 (来自token.h)
} UnaryExpr;
typedef struct {
struct ASTNode* target; // 赋值目标 (通常是标识符表达式)
struct ASTNode* value; // 赋值的值
TokenType op; // 赋值运算符 (e.g., TOKEN_OP_ASSIGN, TOKEN_OP_ASSIGN_PLUS)
} AssignExpr;
typedef struct {
struct ASTNode* callee; // 被调用的函数名 (标识符表达式)
struct ASTNode** args; // 参数列表 (ASTNode* 数组)
int num_args; // 参数数量
} CallExpr;
// 标识符节点
typedef struct {
char* name; // 标识符的名称 (e.g., "x", "main")
} IdentifierExpr;
// 字面量节点
typedef struct {
long long value; // 整数值
} IntegerLiteralExpr;
typedef struct {
double value; // 浮点数值
} FloatLiteralExpr;
typedef struct {
char* value; // 字符串值 (lexeme的副本)
} StringLiteralExpr;
typedef struct {
char value; // 字符值
} CharLiteralExpr;
// 变量声明节点
typedef struct {
struct ASTNode* type_specifier; // 类型说明符 (e.g., int, char)
struct ASTNode* identifier; // 变量名 (标识符表达式)
struct ASTNode* initializer; // 初始化表达式 (可选,e.g., = 10)
} VarDecl;
// 函数参数声明节点
typedef struct {
struct ASTNode* type_specifier; // 参数类型 (e.g., int, char)
struct ASTNode* identifier; // 参数名 (标识符表达式)
} ParamDecl;
// 复合语句/代码块节点
typedef struct {
struct ASTNode** statements; // 语句列表 (ASTNode* 数组)
int num_statements; // 语句数量
} CompoundStmt;
// 表达式语句节点
typedef struct {
struct ASTNode* expression; // 表达式 (可选,如只有分号的空语句)
} ExprStmt;
// return 语句节点
typedef struct {
struct ASTNode* expression; // 返回表达式 (可选,如 return;)
} ReturnStmt;
// if 语句节点
typedef struct {
struct ASTNode* condition; // 条件表达式
struct ASTNode* then_branch; // if 分支的语句
struct ASTNode* else_branch; // else 分支的语句 (可选)
} IfStmt;
// while 循环语句节点
typedef struct {
struct ASTNode* condition; // 循环条件表达式
struct ASTNode* body; // 循环体语句
} WhileStmt;
// for 循环语句节点 (暂时不实现,但结构预留)
typedef struct {
struct ASTNode* init; // 初始化语句 (可以是声明或表达式语句)
struct ASTNode* condition; // 循环条件表达式
struct ASTNode* increment; // 步进表达式 (可以是表达式语句)
struct ASTNode* body; // 循环体语句
} ForStmt;
// 函数定义节点
typedef struct {
struct ASTNode* return_type; // 返回类型 (标识符表达式,如 "int", "void")
struct ASTNode* identifier; // 函数名 (标识符表达式)
struct ASTNode** params; // 参数列表 (ParamDecl* 数组)
int num_params; // 参数数量
struct ASTNode* body; // 函数体 (复合语句)
} FuncDef;
// 顶层程序节点
typedef struct {
struct ASTNode** declarations; // 声明和定义列表 (ASTNode* 数组)
int num_declarations; // 数量
} Program;
// 通用AST节点结构体
// 这是一个递归结构,每个节点可以包含指向其他AST节点的指针
typedef struct ASTNode {
ASTNodeType type; // 节点类型
Token token; // 关联的令牌 (用于错误报告和调试)
// 使用联合体存储不同节点类型特有的数据
union {
BinaryExpr binary_expr;
UnaryExpr unary_expr;
AssignExpr assign_expr;
CallExpr call_expr;
IdentifierExpr identifier_expr;
IntegerLiteralExpr integer_literal_expr;
FloatLiteralExpr float_literal_expr;
StringLiteralExpr string_literal_expr;
CharLiteralExpr char_literal_expr;
VarDecl var_decl;
ParamDecl param_decl;
CompoundStmt compound_stmt;
ExprStmt expr_stmt;
ReturnStmt return_stmt;
IfStmt if_stmt;
WhileStmt while_stmt;
ForStmt for_stmt; // 暂时不用
FuncDef func_def;
Program program;
} data;
} ASTNode;
// AST节点创建函数
ASTNode* ast_new_node(ASTNodeType type, Token token);
ASTNode* ast_new_binary_expr(TokenType op, ASTNode* left, ASTNode* right, Token op_token);
ASTNode* ast_new_unary_expr(TokenType op, ASTNode* operand, Token op_token);
ASTNode* ast_new_assign_expr(TokenType op, ASTNode* target, ASTNode* value, Token op_token);
ASTNode* ast_new_call_expr(ASTNode* callee, ASTNode** args, int num_args, Token call_token);
ASTNode* ast_new_identifier_expr(char* name, Token token);
ASTNode* ast_new_integer_literal_expr(long long value, Token token);
ASTNode* ast_new_float_literal_expr(double value, Token token);
ASTNode* ast_new_string_literal_expr(char* value, Token token);
ASTNode* ast_new_char_literal_expr(char value, Token token);
ASTNode* ast_new_var_decl(ASTNode* type_specifier, ASTNode* identifier, ASTNode* initializer, Token token);
ASTNode* ast_new_param_decl(ASTNode* type_specifier, ASTNode* identifier, Token token);
ASTNode* ast_new_compound_stmt(ASTNode** statements, int num_statements, Token token);
ASTNode* ast_new_expr_stmt(ASTNode* expression, Token token);
ASTNode* ast_new_return_stmt(ASTNode* expression, Token token);
ASTNode* ast_new_if_stmt(ASTNode* condition, ASTNode* then_branch, ASTNode* else_branch, Token token);
ASTNode* ast_new_while_stmt(ASTNode* condition, ASTNode* body, Token token);
ASTNode* ast_new_func_def(ASTNode* return_type, ASTNode* identifier, ASTNode** params, int num_params, ASTNode* body, Token token);
ASTNode* ast_new_program(ASTNode** declarations, int num_declarations, Token token);
// AST节点释放函数
void ast_free_node(ASTNode* node);
void ast_free_program(ASTNode* program_node);
// 辅助函数:打印AST (用于调试)
void ast_print(ASTNode* node, int indent);
#endif // AST_H
ast.c
(AST节点创建和释放的实现):
// ast.c
#include "ast.h"
#include <stdlib.h> // for malloc, free
#include <string.h> // for strdup
#include <stdio.h> // for printf
// 辅助函数:复制令牌的lexeme,因为令牌本身可能很快被释放
static char* copy_lexeme(const char* lexeme) {
if (!lexeme) return NULL;
char* copy = strdup(lexeme);
if (!copy) {
fprintf(stderr, "错误: 内存分配失败 (复制词素)\n");
exit(EXIT_FAILURE);
}
return copy;
}
// 通用AST节点创建函数
// 为所有类型的AST节点分配内存并初始化公共部分
ASTNode* ast_new_node(ASTNodeType type, Token token) {
ASTNode* node = (ASTNode*)malloc(sizeof(ASTNode));
if (!node) {
fprintf(stderr, "错误: 内存分配失败 (ASTNode)\n");
exit(EXIT_FAILURE);
}
node->type = type;
// 复制令牌信息,特别是lexeme,因为原始token可能会在lexer中被free
node->token.type = token.type;
node->token.lexeme = copy_lexeme(token.lexeme); // 复制lexeme
node->token.line = token.line;
node->token.column = token.column;
// 复制字面量值,如果存在
node->token.value = token.value;
return node;
}
// 创建二元表达式节点
ASTNode* ast_new_binary_expr(TokenType op, ASTNode* left, ASTNode* right, Token op_token) {
ASTNode* node = ast_new_node(AST_BINARY_EXPR, op_token);
node->data.binary_expr.op = op;
node->data.binary_expr.left = left;
node->data.binary_expr.right = right;
return node;
}
// 创建一元表达式节点
ASTNode* ast_new_unary_expr(TokenType op, ASTNode* operand, Token op_token) {
ASTNode* node = ast_new_node(AST_UNARY_EXPR, op_token);
node->data.unary_expr.op = op;
node->data.unary_expr.operand = operand;
return node;
}
// 创建赋值表达式节点
ASTNode* ast_new_assign_expr(TokenType op, ASTNode* target, ASTNode* value, Token op_token) {
ASTNode* node = ast_new_node(AST_ASSIGN_EXPR, op_token);
node->data.assign_expr.op = op;
node->data.assign_expr.target = target;
node->data.assign_expr.value = value;
return node;
}
// 创建函数调用表达式节点
ASTNode* ast_new_call_expr(ASTNode* callee, ASTNode** args, int num_args, Token call_token) {
ASTNode* node = ast_new_node(AST_CALL_EXPR, call_token);
node->data.call_expr.callee = callee;
node->data.call_expr.args = args; // 直接使用传入的数组,需要确保其生命周期正确
node->data.call_expr.num_args = num_args;
return node;
}
// 创建标识符表达式节点
ASTNode* ast_new_identifier_expr(char* name, Token token) {
ASTNode* node = ast_new_node(AST_IDENTIFIER_EXPR, token);
node->data.identifier_expr.name = copy_lexeme(name); // 复制标识符名称
return node;
}
// 创建整数常量表达式节点
ASTNode* ast_new_integer_literal_expr(long long value, Token token) {
ASTNode* node = ast_new_node(AST_INTEGER_LITERAL_EXPR, token);
node->data.integer_literal_expr.value = value;
return node;
}
// 创建浮点数常量表达式节点
ASTNode* ast_new_float_literal_expr(double value, Token token) {
ASTNode* node = ast_new_node(AST_FLOAT_LITERAL_EXPR, token);
node->data.float_literal_expr.value = value;
return node;
}
// 创建字符串常量表达式节点
ASTNode* ast_new_string_literal_expr(char* value, Token token) {
ASTNode* node = ast_new_node(AST_STRING_LITERAL_EXPR, token);
node->data.string_literal_expr.value = copy_lexeme(value); // 复制字符串值
return node;
}
// 创建字符常量表达式节点
ASTNode* ast_new_char_literal_expr(char value, Token token) {
ASTNode* node = ast_new_node(AST_CHAR_LITERAL_EXPR, token);
node->data.char_literal_expr.value = value;
return node;
}
// 创建变量声明节点
ASTNode* ast_new_var_decl(ASTNode* type_specifier, ASTNode* identifier, ASTNode* initializer, Token token) {
ASTNode* node = ast_new_node(AST_VAR_DECL, token);
node->data.var_decl.type_specifier = type_specifier;
node->data.var_decl.identifier = identifier;
node->data.var_decl.initializer = initializer;
return node;
}
// 创建参数声明节点
ASTNode* ast_new_param_decl(ASTNode* type_specifier, ASTNode* identifier, Token token) {
ASTNode* node = ast_new_node(AST_PARAM_DECL, token);
node->data.param_decl.type_specifier = type_specifier;
node->data.param_decl.identifier = identifier;
return node;
}
// 创建复合语句/代码块节点
ASTNode* ast_new_compound_stmt(ASTNode** statements, int num_statements, Token token) {
ASTNode* node = ast_new_node(AST_COMPOUND_STMT, token);
node->data.compound_stmt.statements = statements; // 直接使用传入的数组
node->data.compound_stmt.num_statements = num_statements;
return node;
}
// 创建表达式语句节点
ASTNode* ast_new_expr_stmt(ASTNode* expression, Token token) {
ASTNode* node = ast_new_node(AST_EXPR_STMT, token);
node->data.expr_stmt.expression = expression;
return node;
}
// 创建 return 语句节点
ASTNode* ast_new_return_stmt(ASTNode* expression, Token token) {
ASTNode* node = ast_new_node(AST_RETURN_STMT, token);
node->data.return_stmt.expression = expression;
return node;
}
// 创建 if 语句节点
ASTNode* ast_new_if_stmt(ASTNode* condition, ASTNode* then_branch, ASTNode* else_branch, Token token) {
ASTNode* node = ast_new_node(AST_IF_STMT, token);
node->data.if_stmt.condition = condition;
node->data.if_stmt.then_branch = then_branch;
node->data.if_stmt.else_branch = else_branch;
return node;
}
// 创建 while 循环语句节点
ASTNode* ast_new_while_stmt(ASTNode* condition, ASTNode* body, Token token) {
ASTNode* node = ast_new_node(AST_WHILE_STMT, token);
node->data.while_stmt.condition = condition;
node->data.while_stmt.body = body;
return node;
}
// 创建函数定义节点
ASTNode* ast_new_func_def(ASTNode* return_type, ASTNode* identifier, ASTNode** params, int num_params, ASTNode* body, Token token) {
ASTNode* node = ast_new_node(AST_FUNC_DEF, token);
node->data.func_def.return_type = return_type;
node->data.func_def.identifier = identifier;
node->data.func_def.params = params; // 直接使用传入的数组
node->data.func_def.num_params = num_params;
node->data.func_def.body = body;
return node;
}
// 创建程序根节点
ASTNode* ast_new_program(ASTNode** declarations, int num_declarations, Token token) {
ASTNode* node = ast_new_node(AST_PROGRAM, token);
node->data.program.declarations = declarations; // 直接使用传入的数组
node->data.program.num_declarations = num_declarations;
return node;
}
// 递归释放AST节点及其所有子节点占用的内存
void ast_free_node(ASTNode* node) {
if (!node) return;
// 递归释放子节点
switch (node->type) {
case AST_PROGRAM:
for (int i = 0; i < node->data.program.num_declarations; ++i) {
ast_free_node(node->data.program.declarations[i]);
}
free(node->data.program.declarations); // 释放存储子节点指针的数组
break;
case AST_FUNC_DEF:
ast_free_node(node->data.func_def.return_type);
ast_free_node(node->data.func_def.identifier);
for (int i = 0; i < node->data.func_def.num_params; ++i) {
ast_free_node(node->data.func_def.params[i]);
}
free(node->data.func_def.params);
ast_free_node(node->data.func_def.body);
break;
case AST_VAR_DECL:
ast_free_node(node->data.var_decl.type_specifier);
ast_free_node(node->data.var_decl.identifier);
ast_free_node(node->data.var_decl.initializer);
break;
case AST_PARAM_DECL:
ast_free_node(node->data.param_decl.type_specifier);
ast_free_node(node->data.param_decl.identifier);
break;
case AST_COMPOUND_STMT:
for (int i = 0; i < node->data.compound_stmt.num_statements; ++i) {
ast_free_node(node->data.compound_stmt.statements[i]);
}
free(node->data.compound_stmt.statements);
break;
case AST_EXPR_STMT:
ast_free_node(node->data.expr_stmt.expression);
break;
case AST_RETURN_STMT:
ast_free_node(node->data.return_stmt.expression);
break;
case AST_IF_STMT:
ast_free_node(node->data.if_stmt.condition);
ast_free_node(node->data.if_stmt.then_branch);
ast_free_node(node->data.if_stmt.else_branch);
break;
case AST_WHILE_STMT:
ast_free_node(node->data.while_stmt.condition);
ast_free_node(node->data.while_stmt.body);
break;
case AST_FOR_STMT: // 暂时不实现,但如果实现了需要释放
ast_free_node(node->data.for_stmt.init);
ast_free_node(node->data.for_stmt.condition);
ast_free_node(node->data.for_stmt.increment);
ast_free_node(node->data.for_stmt.body);
break;
case AST_BINARY_EXPR:
ast_free_node(node->data.binary_expr.left);
ast_free_node(node->data.binary_expr.right);
break;
case AST_UNARY_EXPR:
ast_free_node(node->data.unary_expr.operand);
break;
case AST_ASSIGN_EXPR:
ast_free_node(node->data.assign_expr.target);
ast_free_node(node->data.assign_expr.value);
break;
case AST_CALL_EXPR:
ast_free_node(node->data.call_expr.callee);
for (int i = 0; i < node->data.call_expr.num_args; ++i) {
ast_free_node(node->data.call_expr.args[i]);
}
free(node->data.call_expr.args);
break;
case AST_IDENTIFIER_EXPR:
free(node->data.identifier_expr.name); // 释放标识符名称
break;
case AST_STRING_LITERAL_EXPR:
free(node->data.string_literal_expr.value); // 释放字符串值
break;
case AST_INTEGER_LITERAL_EXPR:
case AST_FLOAT_LITERAL_EXPR:
case AST_CHAR_LITERAL_EXPR:
case TOKEN_KEYWORD_INT: // 类型说明符节点,其lexeme在ast_new_node中复制,需释放
case TOKEN_KEYWORD_VOID:
case TOKEN_KEYWORD_CHAR:
case TOKEN_KEYWORD_FLOAT:
case TOKEN_KEYWORD_DOUBLE: // 暂时只支持 int, char, void, float
case TOKEN_KEYWORD_SHORT:
case TOKEN_KEYWORD_LONG:
case TOKEN_KEYWORD_SIGNED:
case TOKEN_KEYWORD_UNSIGNED:
case TOKEN_KEYWORD_STRUCT:
case TOKEN_KEYWORD_UNION:
case TOKEN_KEYWORD_ENUM:
case TOKEN_KEYWORD_TYPEDEF:
case TOKEN_KEYWORD_CONST:
case TOKEN_KEYWORD_VOLATILE:
case TOKEN_KEYWORD_EXTERN:
case TOKEN_KEYWORD_STATIC:
case TOKEN_KEYWORD_REGISTER:
case TOKEN_KEYWORD_AUTO:
case TOKEN_KEYWORD_SIZEOF:
case TOKEN_KEYWORD_BREAK:
case TOKEN_KEYWORD_CONTINUE:
case TOKEN_KEYWORD_SWITCH:
case TOKEN_KEYWORD_CASE:
case TOKEN_KEYWORD_DEFAULT:
case TOKEN_KEYWORD_DO:
case TOKEN_KEYWORD_GOTO:
case TOKEN_KEYWORD_IF:
case TOKEN_KEYWORD_ELSE:
case TOKEN_KEYWORD_WHILE:
case TOKEN_KEYWORD_FOR:
case TOKEN_KEYWORD_RETURN:
case TOKEN_EOF:
case TOKEN_UNKNOWN:
case TOKEN_OP_ASSIGN:
case TOKEN_OP_PLUS:
case TOKEN_OP_MINUS:
case TOKEN_OP_MULTIPLY:
case TOKEN_OP_DIVIDE:
case TOKEN_OP_MODULO:
case TOKEN_OP_EQ:
case TOKEN_OP_NE:
case TOKEN_OP_LT:
case TOKEN_OP_LE:
case TOKEN_OP_GT:
case TOKEN_OP_GE:
case TOKEN_OP_AND:
case TOKEN_OP_OR:
case TOKEN_OP_NOT:
case TOKEN_OP_BIT_AND:
case TOKEN_OP_BIT_OR:
case TOKEN_OP_BIT_XOR:
case TOKEN_OP_BIT_NOT:
case TOKEN_OP_LSHIFT:
case TOKEN_OP_RSHIFT:
case TOKEN_OP_INC:
case TOKEN_OP_DEC:
case TOKEN_OP_ARROW:
case TOKEN_OP_DOT:
case TOKEN_OP_COMMA:
case TOKEN_OP_COLON:
case TOKEN_OP_QUESTION:
case TOKEN_OP_ASSIGN_PLUS:
case TOKEN_OP_ASSIGN_MINUS:
case TOKEN_OP_ASSIGN_MULTIPLY:
case TOKEN_OP_ASSIGN_DIVIDE:
case TOKEN_OP_ASSIGN_MODULO:
case TOKEN_OP_ASSIGN_LSHIFT:
case TOKEN_OP_ASSIGN_RSHIFT:
case TOKEN_OP_ASSIGN_BIT_AND:
case TOKEN_OP_ASSIGN_BIT_OR:
case TOKEN_OP_ASSIGN_BIT_XOR:
case TOKEN_LPAREN:
case TOKEN_RPAREN:
case TOKEN_LBRACE:
case TOKEN_RBRACE:
case TOKEN_LBRACKET:
case TOKEN_RBRACKET:
case TOKEN_SEMICOLON:
case TOKEN_POUND:
case TOKEN_ELLIPSIS:
// 这些节点类型没有子节点或额外的动态分配内存,除了token.lexeme
break;
}
// 释放节点自身的token.lexeme
if (node->token.lexeme) {
free(node->token.lexeme);
node->token.lexeme = NULL;
}
// 最后释放节点本身的内存
free(node);
}
// 释放整个AST
void ast_free_program(ASTNode* program_node) {
ast_free_node(program_node);
}
// 辅助函数:将ASTNodeType枚举值转换为可读的字符串
static const char* ast_node_type_to_string(ASTNodeType type) {
switch (type) {
case AST_PROGRAM: return "PROGRAM";
case AST_VAR_DECL: return "VAR_DECL";
case AST_FUNC_DECL: return "FUNC_DECL";
case AST_FUNC_DEF: return "FUNC_DEF";
case AST_PARAM_DECL: return "PARAM_DECL";
case AST_COMPOUND_STMT: return "COMPOUND_STMT";
case AST_EXPR_STMT: return "EXPR_STMT";
case AST_RETURN_STMT: return "RETURN_STMT";
case AST_IF_STMT: return "IF_STMT";
case AST_WHILE_STMT: return "WHILE_STMT";
case AST_FOR_STMT: return "FOR_STMT";
case AST_BINARY_EXPR: return "BINARY_EXPR";
case AST_UNARY_EXPR: return "UNARY_EXPR";
case AST_ASSIGN_EXPR: return "ASSIGN_EXPR";
case AST_CALL_EXPR: return "CALL_EXPR";
case AST_IDENTIFIER_EXPR: return "IDENTIFIER_EXPR";
case AST_INTEGER_LITERAL_EXPR: return "INTEGER_LITERAL_EXPR";
case AST_FLOAT_LITERAL_EXPR: return "FLOAT_LITERAL_EXPR";
case AST_STRING_LITERAL_EXPR: return "STRING_LITERAL_EXPR";
case AST_CHAR_LITERAL_EXPR: return "CHAR_LITERAL_EXPR";
default: return "UNKNOWN_AST_TYPE";
}
}
// 辅助函数:打印AST,用于调试和可视化
void ast_print(ASTNode* node, int indent) {
if (!node) return;
// 打印缩进
for (int i = 0; i < indent; ++i) {
printf(" ");
}
// 打印节点类型和关联的词素
printf("[%s] (Token: %s, Lexeme: \"%s\", Line: %d, Col: %d)",
ast_node_type_to_string(node->type),
token_type_to_string(node->token.type),
node->token.lexeme ? node->token.lexeme : "N/A",
node->token.line,
node->token.column);
// 打印特定节点的数据
switch (node->type) {
case AST_PROGRAM:
printf("\n");
for (int i = 0; i < node->data.program.num_declarations; ++i) {
ast_print(node->data.program.declarations[i], indent + 1);
}
break;
case AST_FUNC_DEF:
printf(" Function: %s\n", node->data.func_def.identifier->data.identifier_expr.name);
for (int i = 0; i < indent + 1; ++i) printf(" "); printf("Return Type:\n");
ast_print(node->data.func_def.return_type, indent + 2);
for (int i = 0; i < indent + 1; ++i) printf(" "); printf("Parameters (%d):\n", node->data.func_def.num_params);
for (int i = 0; i < node->data.func_def.num_params; ++i) {
ast_print(node->data.func_def.params[i], indent + 2);
}
for (int i = 0; i < indent + 1; ++i) printf(" "); printf("Body:\n");
ast_print(node->data.func_def.body, indent + 2);
break;
case AST_VAR_DECL:
printf(" Var: %s\n", node->data.var_decl.identifier->data.identifier_expr.name);
for (int i = 0; i < indent + 1; ++i) printf(" "); printf("Type:\n");
ast_print(node->data.var_decl.type_specifier, indent + 2);
if (node->data.var_decl.initializer) {
for (int i = 0; i < indent + 1; ++i) printf(" "); printf("Initializer:\n");
ast_print(node->data.var_decl.initializer, indent + 2);
}
break;
case AST_PARAM_DECL:
printf(" Param: %s\n", node->data.param_decl.identifier->data.identifier_expr.name);
for (int i = 0; i < indent + 1; ++i) printf(" "); printf("Type:\n");
ast_print(node->data.param_decl.type_specifier, indent + 2);
break;
case AST_COMPOUND_STMT:
printf(" (%d statements)\n", node->data.compound_stmt.num_statements);
for (int i = 0; i < node->data.compound_stmt.num_statements; ++i) {
ast_print(node->data.compound_stmt.statements[i], indent + 1);
}
break;
case AST_EXPR_STMT:
printf("\n");
if (node->data.expr_stmt.expression) {
ast_print(node->data.expr_stmt.expression, indent + 1);
}
break;
case AST_RETURN_STMT:
printf("\n");
if (node->data.return_stmt.expression) {
ast_print(node->data.return_stmt.expression, indent + 1);
}
break;
case AST_IF_STMT:
printf("\n");
for (int i = 0; i < indent + 1; ++i) printf(" "); printf("Condition:\n");
ast_print(node->data.if_stmt.condition, indent + 2);
for (int i = 0; i < indent + 1; ++i) printf(" "); printf("Then Branch:\n");
ast_print(node->data.if_stmt.then_branch, indent + 2);
if (node->data.if_stmt.else_branch) {
for (int i = 0; i < indent + 1; ++i) printf(" "); printf("Else Branch:\n");
ast_print(node->data.if_stmt.else_branch, indent + 2);
}
break;
case AST_WHILE_STMT:
printf("\n");
for (int i = 0; i < indent + 1; ++i) printf(" "); printf("Condition:\n");
ast_print(node->data.while_stmt.condition, indent + 2);
for (int i = 0; i < indent + 1; ++i) printf(" "); printf("Body:\n");
ast_print(node->data.while_stmt.body, indent + 2);
break;
case AST_BINARY_EXPR:
printf(" Op: %s\n", token_type_to_string(node->data.binary_expr.op));
for (int i = 0; i < indent + 1; ++i) printf(" "); printf("Left:\n");
ast_print(node->data.binary_expr.left, indent + 2);
for (int i = 0; i < indent + 1; ++i) printf(" "); printf("Right:\n");
ast_print(node->data.binary_expr.right, indent + 2);
break;
case AST_UNARY_EXPR:
printf(" Op: %s\n", token_type_to_string(node->data.unary_expr.op));
for (int i = 0; i < indent + 1; ++i) printf(" "); printf("Operand:\n");
ast_print(node->data.unary_expr.operand, indent + 2);
break;
case AST_ASSIGN_EXPR:
printf(" Op: %s\n", token_type_to_string(node->data.assign_expr.op));
for (int i = 0; i < indent + 1; ++i) printf(" "); printf("Target:\n");
ast_print(node->data.assign_expr.target, indent + 2);
for (int i = 0; i < indent + 1; ++i) printf(" "); printf("Value:\n");
ast_print(node->data.assign_expr.value, indent + 2);
break;
case AST_CALL_EXPR:
printf(" Callee: %s\n", node->data.call_expr.callee->data.identifier_expr.name);
for (int i = 0; i < indent + 1; ++i) printf(" "); printf("Arguments (%d):\n", node->data.call_expr.num_args);
for (int i = 0; i < node->data.call_expr.num_args; ++i) {
ast_print(node->data.call_expr.args[i], indent + 2);
}
break;
case AST_IDENTIFIER_EXPR:
printf(" Name: \"%s\"\n", node->data.identifier_expr.name);
break;
case AST_INTEGER_LITERAL_EXPR:
printf(" Value: %lld\n", node->data.integer_literal_expr.value);
break;
case AST_FLOAT_LITERAL_EXPR:
printf(" Value: %f\n", node->data.float_literal_expr.value);
break;
case AST_STRING_LITERAL_EXPR:
printf(" Value: \"%s\"\n", node->data.string_literal_expr.value);
break;
case AST_CHAR_LITERAL_EXPR:
printf(" Value: '%c'\n", node->data.char_literal_expr.value);
break;
// 对于直接由关键字或符号生成的AST节点(如类型说明符),直接打印其词素
case TOKEN_KEYWORD_INT:
case TOKEN_KEYWORD_VOID:
case TOKEN_KEYWORD_CHAR:
case TOKEN_KEYWORD_FLOAT:
case TOKEN_KEYWORD_DOUBLE: // 暂时只支持 int, char, void, float
case TOKEN_KEYWORD_SHORT:
case TOKEN_KEYWORD_LONG:
case TOKEN_KEYWORD_SIGNED:
case TOKEN_KEYWORD_UNSIGNED:
case TOKEN_KEYWORD_STRUCT:
case TOKEN_KEYWORD_UNION:
case TOKEN_KEYWORD_ENUM:
case TOKEN_KEYWORD_TYPEDEF:
case TOKEN_KEYWORD_CONST:
case TOKEN_KEYWORD_VOLATILE:
case TOKEN_KEYWORD_EXTERN:
case TOKEN_KEYWORD_STATIC:
case TOKEN_KEYWORD_REGISTER:
case TOKEN_KEYWORD_AUTO:
case TOKEN_KEYWORD_SIZEOF:
case TOKEN_KEYWORD_BREAK:
case TOKEN_KEYWORD_CONTINUE:
case TOKEN_KEYWORD_SWITCH:
case TOKEN_KEYWORD_CASE:
case TOKEN_KEYWORD_DEFAULT:
case TOKEN_KEYWORD_DO:
case TOKEN_KEYWORD_GOTO:
case TOKEN_KEYWORD_IF:
case TOKEN_KEYWORD_ELSE:
case TOKEN_KEYWORD_WHILE:
case TOKEN_KEYWORD_FOR:
case TOKEN_KEYWORD_RETURN:
case TOKEN_EOF:
case TOKEN_UNKNOWN:
case TOKEN_OP_ASSIGN:
case TOKEN_OP_PLUS:
case TOKEN_OP_MINUS:
case TOKEN_OP_MULTIPLY:
case TOKEN_OP_DIVIDE:
case TOKEN_OP_MODULO:
case TOKEN_OP_EQ:
case TOKEN_OP_NE:
case TOKEN_OP_LT:
case TOKEN_OP_LE:
case TOKEN_OP_GT:
case TOKEN_OP_GE:
case TOKEN_OP_AND:
case TOKEN_OP_OR:
case TOKEN_OP_NOT:
case TOKEN_OP_BIT_AND:
case TOKEN_OP_BIT_OR:
case TOKEN_OP_BIT_XOR:
case TOKEN_OP_BIT_NOT:
case TOKEN_OP_LSHIFT:
case TOKEN_OP_RSHIFT:
case TOKEN_OP_INC:
case TOKEN_OP_DEC:
case TOKEN_OP_ARROW:
case TOKEN_OP_DOT:
case TOKEN_OP_COMMA:
case TOKEN_OP_COLON:
case TOKEN_OP_QUESTION:
case TOKEN_OP_ASSIGN_PLUS:
case TOKEN_OP_ASSIGN_MINUS:
case TOKEN_OP_ASSIGN_MULTIPLY:
case TOKEN_OP_ASSIGN_DIVIDE:
case TOKEN_OP_ASSIGN_MODULO:
case TOKEN_OP_ASSIGN_LSHIFT:
case TOKEN_OP_ASSIGN_RSHIFT:
case TOKEN_OP_ASSIGN_BIT_AND:
case TOKEN_OP_ASSIGN_BIT_OR:
case TOKEN_OP_ASSIGN_BIT_XOR:
case TOKEN_LPAREN:
case TOKEN_RPAREN:
case TOKEN_LBRACE:
case TOKEN_RBRACE:
case TOKEN_LBRACKET:
case TOKEN_RBRACKET:
case TOKEN_SEMICOLON:
case TOKEN_POUND:
case TOKEN_ELLIPSIS:
printf("\n"); // 这些是叶子节点,不需要进一步递归
break;
}
}
递归下降解析:像读故事一样解析代码
在众多语法分析方法中,**递归下降解析(Recursive Descent Parsing)**是最直观、最容易手动实现的一种。它的核心思想是:每一个语法规则都对应一个解析函数。当一个函数需要匹配某个非终结符(例如“表达式”或“语句”)时,它就调用对应的解析函数。
简化C语言语法规则(BNF表示)
为了让我们的编译器能够工作,我们需要定义它能理解的C语言子集。这里我们采用巴科斯-诺尔范式(Backus-Naur Form, BNF)来表示简化后的语法规则。
<program> ::= { <declaration> | <function_definition> }* <EOF>
<declaration> ::= <type_specifier> <IDENTIFIER> [ '=' <expression> ] ';'
<function_definition> ::= <type_specifier> <IDENTIFIER> '(' [ <parameter_list> ] ')' <compound_statement>
<parameter_list> ::= <parameter_declaration> { ',' <parameter_declaration> }*
<parameter_declaration> ::= <type_specifier> <IDENTIFIER>
<type_specifier> ::= 'int' | 'char' | 'float' | 'void'
<statement> ::= <expression_statement>
| <compound_statement>
| <return_statement>
| <if_statement>
| <while_statement>
| <for_statement> ; // for 循环暂时不实现,但规则预留
<compound_statement> ::= '{' { <declaration> | <statement> }* '}'
<expression_statement> ::= [ <expression> ] ';'
<return_statement> ::= 'return' [ <expression> ] ';'
<if_statement> ::= 'if' '(' <expression> ')' <statement> [ 'else' <statement> ]
<while_statement> ::= 'while' '(' <expression> ')' <statement>
// 表达式的优先级从低到高,每个规则对应一个解析函数
<expression> ::= <assignment_expression>
<assignment_expression> ::= <conditional_expression> [ <assignment_operator> <assignment_expression> ]
<conditional_expression> ::= <logical_or_expression> [ '?' <expression> ':' <conditional_expression> ]
<logical_or_expression> ::= <logical_and_expression> { '||' <logical_and_expression> }*
<logical_and_expression> ::= <equality_expression> { '&&' <equality_expression> }*
<equality_expression> ::= <relational_expression> { ( '==' | '!=' ) <relational_expression> }*
<relational_expression> ::= <additive_expression> { ( '<' | '<=' | '>' | '>=' ) <additive_expression> }*
<additive_expression> ::= <multiplicative_expression> { ( '+' | '-' ) <multiplicative_expression> }*
<multiplicative_expression> ::= <unary_expression> { ( '*' | '/' | '%' ) <unary_expression> }*
<unary_expression> ::= ( '+' | '-' | '!' | '~' | '++' | '--' | '&' | '*' )? <primary_expression>
<primary_expression> ::= <IDENTIFIER>
| <INTEGER_LITERAL>
| <FLOAT_LITERAL>
| <STRING_LITERAL>
| <CHAR_LITERAL>
| '(' <expression> ')'
| <function_call>
<function_call> ::= <IDENTIFIER> '(' [ <argument_list> ] ')'
<argument_list> ::= <expression> { ',' <expression> }*
// 运算符
<assignment_operator> ::= '=' | '+=' | '-=' | '*=' | '/=' | '%=' | '<<=' | '>>=' | '&=' | '|=' | '^='
递归下降解析器(Parser)的核心实现
我们的解析器需要一个Lexer
实例来获取令牌,并维护当前正在处理的令牌。
// parser.h
#ifndef PARSER_H
#define PARSER_H
#include "lexer.h" // 需要词法分析器来获取令牌
#include "ast.h" // 需要AST节点定义来构建语法树
// 定义解析器结构体
typedef struct {
Lexer* lexer; // 词法分析器实例
Token current_token; // 当前正在处理的令牌
Token peek_token; // 预读的下一个令牌 (用于向前看)
} Parser;
// 函数声明
Parser* init_parser(Lexer* lexer); // 初始化解析器
void free_parser(Parser* parser); // 释放解析器资源
// 核心解析函数:解析整个程序
ASTNode* parse_program(Parser* parser);
// 辅助函数:报告语法错误
void parser_error(Parser* parser, const char* message);
#endif // PARSER_H
现在,激动人心的时刻到了!parser.c
将是我们整个编译器的“大脑”,它将根据C语言的语法规则,一步步地构建出抽象语法树。
// parser.c
#include "parser.h"
#include <stdlib.h> // for malloc, free, realloc, exit
#include <string.h> // for strcmp
#include <stdarg.h> // for va_list, va_start, va_end
// --- 辅助函数和错误处理 ---
// 报告语法错误并退出程序
// 使用可变参数,可以像printf一样格式化错误信息
void parser_error(Parser* parser, const char* format, ...) {
va_list args;
va_start(args, format);
fprintf(stderr, "语法错误 (%d:%d): ", parser->current_token.line, parser->current_token.column);
vfprintf(stderr, format, args);
fprintf(stderr, " (当前令牌: %s, 词素: \"%s\")\n",
token_type_to_string(parser->current_token.type),
parser->current_token.lexeme ? parser->current_token.lexeme : "N/A");
va_end(args);
// 释放所有资源并退出
free_parser(parser);
exit(EXIT_FAILURE);
}
// 获取下一个令牌并更新current_token和peek_token
// 这是解析器与词法分析器交互的核心
static void advance(Parser* parser) {
// 释放旧的current_token的lexeme内存,因为它是动态分配的
if (parser->current_token.lexeme) {
free(parser->current_token.lexeme);
parser->current_token.lexeme = NULL;
}
// 将peek_token提升为current_token
parser->current_token = parser->peek_token;
// 从词法分析器获取新的peek_token
parser->peek_token = get_next_token(parser->lexer);
}
// 检查当前令牌的类型是否符合预期
static int check(Parser* parser, TokenType type) {
return parser->current_token.type == type;
}
// 检查下一个令牌的类型是否符合预期 (向前看一个令牌)
static int check_peek(Parser* parser, TokenType type) {
return parser->peek_token.type == type;
}
// 匹配并消费期望的令牌类型
// 如果当前令牌类型不匹配,则报告错误
static Token match(Parser* parser, TokenType type) {
if (check(parser, type)) {
Token matched_token = parser->current_token; // 复制当前令牌
advance(parser); // 消费当前令牌
return matched_token;
}
parser_error(parser, "期望令牌 '%s', 但得到 '%s'",
token_type_to_string(type), token_type_to_string(parser->current_token.type));
// 错误发生后通常会退出,这里返回一个无效令牌以满足函数签名,实际不会执行到
Token error_token = {TOKEN_UNKNOWN, strdup(""), 0, 0, {0}};
return error_token;
}
// 匹配并消费期望的多个令牌类型中的一个
static Token match_any(Parser* parser, TokenType types[], int count) {
for (int i = 0; i < count; ++i) {
if (check(parser, types[i])) {
Token matched_token = parser->current_token;
advance(parser);
return matched_token;
}
}
// 构造错误消息
char expected_types[256] = "";
for (int i = 0; i < count; ++i) {
strcat(expected_types, token_type_to_string(types[i]));
if (i < count - 1) {
strcat(expected_types, ", ");
}
}
parser_error(parser, "期望令牌之一: %s, 但得到 '%s'",
expected_types, token_type_to_string(parser->current_token.type));
Token error_token = {TOKEN_UNKNOWN, strdup(""), 0, 0, {0}};
return error_token;
}
// --- 递归下降解析函数 (对应BNF规则) ---
// 声明前向引用,因为函数之间可能相互递归调用
static ASTNode* parse_declaration(Parser* parser);
static ASTNode* parse_statement(Parser* parser);
static ASTNode* parse_expression(Parser* parser);
static ASTNode* parse_type_specifier(Parser* parser);
static ASTNode* parse_compound_statement(Parser* parser);
// <primary_expression> ::= IDENTIFIER
// | INTEGER_LITERAL
// | FLOAT_LITERAL
// | STRING_LITERAL
// | CHAR_LITERAL
// | '(' <expression> ')'
// | <function_call>
static ASTNode* parse_primary_expression(Parser* parser) {
Token token = parser->current_token; // 记录当前令牌,用于创建AST节点
ASTNode* node = NULL;
switch (token.type) {
case TOKEN_IDENTIFIER:
advance(parser); // 消费标识符令牌
// 检查是否是函数调用
if (check(parser, TOKEN_LPAREN)) {
// 这是函数调用
ASTNode* callee = ast_new_identifier_expr(token.lexeme, token); // 函数名
Token call_token = token; // 记录函数调用开始的令牌
advance(parser); // 消费 '('
ASTNode** args = NULL;
int num_args = 0;
if (!check(parser, TOKEN_RPAREN)) { // 如果不是空参数列表
do {
// 动态数组扩展
args = (ASTNode**)realloc(args, (num_args + 1) * sizeof(ASTNode*));
if (!args) parser_error(parser, "内存分配失败 (函数参数)");
args[num_args++] = parse_expression(parser); // 解析每个参数表达式
} while (check(parser, TOKEN_OP_COMMA) && (advance(parser), 1)); // 消费 ','
}
match(parser, TOKEN_RPAREN); // 期望 ')'
node = ast_new_call_expr(callee, args, num_args, call_token);
} else {
// 只是一个标识符引用
node = ast_new_identifier_expr(token.lexeme, token);
}
break;
case TOKEN_INTEGER_LITERAL:
node = ast_new_integer_literal_expr(token.value.int_val, token);
advance(parser);
break;
case TOKEN_FLOAT_LITERAL:
node = ast_new_float_literal_expr(token.value.float_val, token);
advance(parser);
break;
case TOKEN_STRING_LITERAL:
node = ast_new_string_literal_expr(token.lexeme, token); // 字符串字面量的值就是lexeme
advance(parser);
break;
case TOKEN_CHAR_LITERAL:
node = ast_new_char_literal_expr(token.value.char_val, token);
advance(parser);
break;
case TOKEN_LPAREN:
advance(parser); // 消费 '('
node = parse_expression(parser); // 递归解析括号内的表达式
match(parser, TOKEN_RPAREN); // 期望 ')'
break;
default:
parser_error(parser, "期望主表达式 (标识符、常量或括号表达式)");
break;
}
return node;
}
// <unary_expression> ::= ( '+' | '-' | '!' | '~' | '++' | '--' | '&' | '*' )? <primary_expression>
static ASTNode* parse_unary_expression(Parser* parser) {
// 检查是否有一元运算符
TokenType op_type = TOKEN_UNKNOWN;
Token op_token = parser->current_token; // 记录运算符的令牌
// 支持的前缀一元运算符
TokenType unary_ops[] = {
TOKEN_OP_PLUS, TOKEN_OP_MINUS, TOKEN_OP_NOT, TOKEN_OP_BIT_NOT,
TOKEN_OP_INC, TOKEN_OP_DEC, TOKEN_OP_BIT_AND, TOKEN_OP_MULTIPLY // & (取地址), * (解引用)
};
int is_unary_op = 0;
for (int i = 0; i < sizeof(unary_ops) / sizeof(unary_ops[0]); ++i) {
if (check(parser, unary_ops[i])) {
op_type = unary_ops[i];
is_unary_op = 1;
advance(parser); // 消费一元运算符
break;
}
}
ASTNode* operand = parse_primary_expression(parser); // 解析操作数
if (is_unary_op) {
return ast_new_unary_expr(op_type, operand, op_token);
}
return operand; // 如果没有一元运算符,直接返回主表达式
}
// <multiplicative_expression> ::= <unary_expression> { ( '*' | '/' | '%' ) <unary_expression> }*
static ASTNode* parse_multiplicative_expression(Parser* parser) {
ASTNode* node = parse_unary_expression(parser); // 首先解析一个一元表达式
// 循环处理乘法、除法、取模运算符
while (check(parser, TOKEN_OP_MULTIPLY) || check(parser, TOKEN_OP_DIVIDE) || check(parser, TOKEN_OP_MODULO)) {
Token op_token = parser->current_token; // 记录运算符令牌
TokenType op_type = op_token.type;
advance(parser); // 消费运算符
ASTNode* right = parse_unary_expression(parser); // 解析右操作数
node = ast_new_binary_expr(op_type, node, right, op_token); // 创建二元表达式节点
}
return node;
}
// <additive_expression> ::= <multiplicative_expression> { ( '+' | '-' ) <multiplicative_expression> }*
static ASTNode* parse_additive_expression(Parser* parser) {
ASTNode* node = parse_multiplicative_expression(parser); // 首先解析一个乘法表达式
// 循环处理加法、减法运算符
while (check(parser, TOKEN_OP_PLUS) || check(parser, TOKEN_OP_MINUS)) {
Token op_token = parser->current_token; // 记录运算符令牌
TokenType op_type = op_token.type;
advance(parser); // 消费运算符
ASTNode* right = parse_multiplicative_expression(parser); // 解析右操作数
node = ast_new_binary_expr(op_type, node, right, op_token); // 创建二元表达式节点
}
return node;
}
// <relational_expression> ::= <additive_expression> { ( '<' | '<=' | '>' | '>=' ) <additive_expression> }*
static ASTNode* parse_relational_expression(Parser* parser) {
ASTNode* node = parse_additive_expression(parser);
TokenType relational_ops[] = {TOKEN_OP_LT, TOKEN_OP_LE, TOKEN_OP_GT, TOKEN_OP_GE};
while (match_any(parser, relational_ops, sizeof(relational_ops)/sizeof(TokenType)).type != TOKEN_UNKNOWN) {
Token op_token = parser->current_token; // current_token 已经是下一个令牌了,需要修正
// 这里的op_token应该是在match_any之前记录的,或者match_any返回的令牌
// 为了简化,我们假设match_any返回的令牌就是我们需要的
// 实际上,match_any已经消费了令牌,所以它返回的令牌就是正确的
TokenType op_type = op_token.type; // 记录操作符类型
// 由于match_any已经advance了,这里需要回退一个位置来获取正确的op_token
// 更好的做法是让match_any返回匹配的token,然后使用那个token
// 这里为了避免复杂的回退逻辑,我们直接使用current_token,它在match_any之后
// 实际上,正确的op_token应该在match_any内部被捕获
// 临时修正:在match_any内部返回的token就是正确的op_token
// 这里就直接用parser->current_token,它是match_any之后的新token,其type是正确的
// 但lexeme和位置信息是下一个词素的,这不对。
// 修正:match_any返回的token就是我们需要的op_token
// 重新设计match_any的调用,使其返回匹配的Token
// 这里简化为直接判断current_token.type
Token current_op_token = parser->current_token; // 记录当前运算符令牌
TokenType current_op_type = current_op_token.type;
advance(parser); // 消费运算符
ASTNode* right = parse_additive_expression(parser);
node = ast_new_binary_expr(current_op_type, node, right, current_op_token);
}
return node;
}
// <equality_expression> ::= <relational_expression> { ( '==' | '!=' ) <relational_expression> }*
static ASTNode* parse_equality_expression(Parser* parser) {
ASTNode* node = parse_relational_expression(parser);
while (check(parser, TOKEN_OP_EQ) || check(parser, TOKEN_OP_NE)) {
Token op_token = parser->current_token;
TokenType op_type = op_token.type;
advance(parser);
ASTNode* right = parse_relational_expression(parser);
node = ast_new_binary_expr(op_type, node, right, op_token);
}
return node;
}
// <logical_and_expression> ::= <equality_expression> { '&&' <equality_expression> }*
static ASTNode* parse_logical_and_expression(Parser* parser) {
ASTNode* node = parse_equality_expression(parser);
while (check(parser, TOKEN_OP_AND)) {
Token op_token = parser->current_token;
TokenType op_type = op_token.type;
advance(parser);
ASTNode* right = parse_equality_expression(parser);
node = ast_new_binary_expr(op_type, node, right, op_token);
}
return node;
}
// <logical_or_expression> ::= <logical_and_expression> { '||' <logical_and_expression> }*
static ASTNode* parse_logical_or_expression(Parser* parser) {
ASTNode* node = parse_logical_and_expression(parser);
while (check(parser, TOKEN_OP_OR)) {
Token op_token = parser->current_token;
TokenType op_type = op_token.type;
advance(parser);
ASTNode* right = parse_logical_and_expression(parser);
node = ast_new_binary_expr(op_type, node, right, op_token);
}
return node;
}
// <conditional_expression> ::= <logical_or_expression> [ '?' <expression> ':' <conditional_expression> ]
// 注意:三元运算符是右结合的,这里简单实现,不完全符合右结合,但能处理单层
static ASTNode* parse_conditional_expression(Parser* parser) {
ASTNode* condition = parse_logical_or_expression(parser);
if (check(parser, TOKEN_OP_QUESTION)) {
Token question_token = parser->current_token;
advance(parser); // 消费 '?'
ASTNode* then_expr = parse_expression(parser); // 解析 then 部分
match(parser, TOKEN_OP_COLON); // 期望 ':'
ASTNode* else_expr = parse_conditional_expression(parser); // 递归解析 else 部分 (右结合)
// 三元运算符可以看作一种特殊的二元表达式或自定义节点
// 这里为了简化,暂时不创建专门的三元运算符AST节点,
// 而是将其视为一个特殊的二元表达式,或者在语义分析阶段处理
// 实际上,应该创建一个 AST_CONDITIONAL_EXPR 节点
// 暂时返回一个UNKNOWN节点,表示未完全实现
parser_error(parser, "三元运算符 (?:) 暂未完全支持AST表示");
return NULL; // 应该返回一个AST节点
}
return condition;
}
// <assignment_expression> ::= <conditional_expression> [ <assignment_operator> <assignment_expression> ]
static ASTNode* parse_assignment_expression(Parser* parser) {
ASTNode* left = parse_conditional_expression(parser);
// 检查是否是赋值运算符
TokenType assign_ops[] = {
TOKEN_OP_ASSIGN, TOKEN_OP_ASSIGN_PLUS, TOKEN_OP_ASSIGN_MINUS,
TOKEN_OP_ASSIGN_MULTIPLY, TOKEN_OP_ASSIGN_DIVIDE, TOKEN_OP_ASSIGN_MODULO,
TOKEN_OP_ASSIGN_LSHIFT, TOKEN_OP_ASSIGN_RSHIFT, TOKEN_OP_ASSIGN_BIT_AND,
TOKEN_OP_ASSIGN_BIT_OR, TOKEN_OP_ASSIGN_BIT_XOR
};
for (int i = 0; i < sizeof(assign_ops) / sizeof(assign_ops[0]); ++i) {
if (check(parser, assign_ops[i])) {
// 确保赋值左侧是可赋值的(标识符或解引用等)
if (left->type != AST_IDENTIFIER_EXPR &&
!(left->type == AST_UNARY_EXPR && left->data.unary_expr.op == TOKEN_OP_MULTIPLY)) { // 简单检查解引用
parser_error(parser, "赋值运算符左侧必须是可修改的左值");
}
Token op_token = parser->current_token;
TokenType op_type = op_token.type;
advance(parser); // 消费赋值运算符
ASTNode* right = parse_assignment_expression(parser); // 赋值表达式是右结合的,所以递归调用自身
return ast_new_assign_expr(op_type, left, right, op_token);
}
}
return left; // 如果没有赋值运算符,返回左侧的条件表达式
}
// <expression> ::= <assignment_expression>
static ASTNode* parse_expression(Parser* parser) {
return parse_assignment_expression(parser);
}
// <expression_statement> ::= [ <expression> ] ';'
static ASTNode* parse_expression_statement(Parser* parser) {
ASTNode* expr = NULL;
Token stmt_token = parser->current_token; // 记录语句开始的令牌
// 如果不是分号,则解析一个表达式
if (!check(parser, TOKEN_SEMICOLON)) {
expr = parse_expression(parser);
}
match(parser, TOKEN_SEMICOLON); // 期望分号
return ast_new_expr_stmt(expr, stmt_token);
}
// <return_statement> ::= 'return' [ <expression> ] ';'
static ASTNode* parse_return_statement(Parser* parser) {
Token return_token = match(parser, TOKEN_KEYWORD_RETURN); // 期望 'return'
ASTNode* expr = NULL;
if (!check(parser, TOKEN_SEMICOLON)) { // 如果不是直接分号,则有返回表达式
expr = parse_expression(parser);
}
match(parser, TOKEN_SEMICOLON); // 期望分号
return ast_new_return_stmt(expr, return_token);
}
// <if_statement> ::= 'if' '(' <expression> ')' <statement> [ 'else' <statement> ]
static ASTNode* parse_if_statement(Parser* parser) {
Token if_token = match(parser, TOKEN_KEYWORD_IF); // 期望 'if'
match(parser, TOKEN_LPAREN); // 期望 '('
ASTNode* condition = parse_expression(parser); // 解析条件表达式
match(parser, TOKEN_RPAREN); // 期望 ')'
ASTNode* then_branch = parse_statement(parser); // 解析 if 分支语句
ASTNode* else_branch = NULL;
if (check(parser, TOKEN_KEYWORD_ELSE)) { // 如果有 'else' 关键字
advance(parser); // 消费 'else'
else_branch = parse_statement(parser); // 解析 else 分支语句
}
return ast_new_if_stmt(condition, then_branch, else_branch, if_token);
}
// <while_statement> ::= 'while' '(' <expression> ')' <statement>
static ASTNode* parse_while_statement(Parser* parser) {
Token while_token = match(parser, TOKEN_KEYWORD_WHILE); // 期望 'while'
match(parser, TOKEN_LPAREN); // 期望 '('
ASTNode* condition = parse_expression(parser); // 解析条件表达式
match(parser, TOKEN_RPAREN); // 期望 ')'
ASTNode* body = parse_statement(parser); // 解析循环体语句
return ast_new_while_stmt(condition, body, while_token);
}
// <statement> ::= <expression_statement>
// | <compound_statement>
// | <return_statement>
// | <if_statement>
// | <while_statement>
// | <for_statement> ; // for 循环暂时不实现
static ASTNode* parse_statement(Parser* parser) {
if (check(parser, TOKEN_LBRACE)) { // 如果是 '{',则是复合语句
return parse_compound_statement(parser);
} else if (check(parser, TOKEN_KEYWORD_RETURN)) { // 如果是 'return'
return parse_return_statement(parser);
} else if (check(parser, TOKEN_KEYWORD_IF)) { // 如果是 'if'
return parse_if_statement(parser);
} else if (check(parser, TOKEN_KEYWORD_WHILE)) { // 如果是 'while'
return parse_while_statement(parser);
}
// 否则,认为是表达式语句 (包括空语句 ';')
return parse_expression_statement(parser);
}
// <compound_statement> ::= '{' { <declaration> | <statement> }* '}'
static ASTNode* parse_compound_statement(Parser* parser) {
Token brace_token = match(parser, TOKEN_LBRACE); // 期望 '{'
ASTNode** statements = NULL;
int num_statements = 0;
// 循环解析内部的声明或语句,直到遇到 '}' 或文件结束
while (!check(parser, TOKEN_RBRACE) && !check(parser, TOKEN_EOF)) {
// 检查是声明还是语句
if (check(parser, TOKEN_KEYWORD_INT) || check(parser, TOKEN_KEYWORD_CHAR) ||
check(parser, TOKEN_KEYWORD_VOID) || check(parser, TOKEN_KEYWORD_FLOAT)) { // 简单的类型检查
// 如果下一个令牌是类型关键字,尝试解析为声明
// 但需要向前看,防止是函数调用等
// 这里简化:如果当前是类型关键字,并且下一个是标识符,则认为是声明
if (check_peek(parser, TOKEN_IDENTIFIER)) {
statements = (ASTNode**)realloc(statements, (num_statements + 1) * sizeof(ASTNode*));
if (!statements) parser_error(parser, "内存分配失败 (复合语句内部声明)");
statements[num_statements++] = parse_declaration(parser);
} else {
// 如果类型关键字后面不是标识符,那它可能是一个表达式的一部分,回退到解析语句
statements = (ASTNode**)realloc(statements, (num_statements + 1) * sizeof(ASTNode*));
if (!statements) parser_error(parser, "内存分配失败 (复合语句内部语句)");
statements[num_statements++] = parse_statement(parser);
}
} else {
// 否则,解析为语句
statements = (ASTNode**)realloc(statements, (num_statements + 1) * sizeof(ASTNode*));
if (!statements) parser_error(parser, "内存分配失败 (复合语句内部语句)");
statements[num_statements++] = parse_statement(parser);
}
}
match(parser, TOKEN_RBRACE); // 期望 '}'
return ast_new_compound_stmt(statements, num_statements, brace_token);
}
// <type_specifier> ::= 'int' | 'char' | 'float' | 'void'
static ASTNode* parse_type_specifier(Parser* parser) {
Token type_token = parser->current_token;
if (check(parser, TOKEN_KEYWORD_INT) ||
check(parser, TOKEN_KEYWORD_CHAR) ||
check(parser, TOKEN_KEYWORD_VOID) ||
check(parser, TOKEN_KEYWORD_FLOAT)) {
advance(parser); // 消费类型关键字
// 这里直接将类型关键字令牌作为AST节点返回,其lexeme就是类型名
// 在语义分析阶段,我们会将其映射到具体的类型对象
return ast_new_node(type_token.type, type_token); // 使用关键字的TokenType作为ASTNodeType
}
parser_error(parser, "期望类型说明符 (int, char, float, void)");
return NULL; // 错误发生,返回NULL
}
// <parameter_declaration> ::= <type_specifier> <IDENTIFIER>
static ASTNode* parse_parameter_declaration(Parser* parser) {
Token param_token = parser->current_token; // 记录参数声明开始的令牌
ASTNode* type = parse_type_specifier(parser); // 解析参数类型
Token id_token = match(parser, TOKEN_IDENTIFIER); // 期望标识符作为参数名
ASTNode* identifier = ast_new_identifier_expr(id_token.lexeme, id_token);
return ast_new_param_decl(type, identifier, param_token);
}
// <parameter_list> ::= <parameter_declaration> { ',' <parameter_declaration> }*
static ASTNode** parse_parameter_list(Parser* parser, int* num_params) {
ASTNode** params = NULL;
*num_params = 0;
if (!check(parser, TOKEN_RPAREN)) { // 如果不是空参数列表
do {
params = (ASTNode**)realloc(params, (*num_params + 1) * sizeof(ASTNode*));
if (!params) parser_error(parser, "内存分配失败 (函数参数列表)");
params[(*num_params)++] = parse_parameter_declaration(parser);
} while (check(parser, TOKEN_OP_COMMA) && (advance(parser), 1)); // 消费 ','
}
return params;
}
// <function_definition> ::= <type_specifier> <IDENTIFIER> '(' [ <parameter_list> ] ')' <compound_statement>
static ASTNode* parse_function_definition(Parser* parser) {
Token func_def_token = parser->current_token; // 记录函数定义开始的令牌
ASTNode* return_type = parse_type_specifier(parser); // 解析返回类型
Token id_token = match(parser, TOKEN_IDENTIFIER); // 期望标识符作为函数名
ASTNode* identifier = ast_new_identifier_expr(id_token.lexeme, id_token);
match(parser, TOKEN_LPAREN); // 期望 '('
ASTNode** params = NULL;
int num_params = 0;
params = parse_parameter_list(parser, &num_params); // 解析参数列表
match(parser, TOKEN_RPAREN); // 期望 ')'
ASTNode* body = parse_compound_statement(parser); // 解析函数体 (复合语句)
return ast_new_func_def(return_type, identifier, params, num_params, body, func_def_token);
}
// <declaration> ::= <type_specifier> <IDENTIFIER> [ '=' <expression> ] ';'
static ASTNode* parse_declaration(Parser* parser) {
Token decl_token = parser->current_token; // 记录声明开始的令牌
ASTNode* type_specifier = parse_type_specifier(parser); // 解析类型说明符
Token id_token = match(parser, TOKEN_IDENTIFIER); // 期望标识符作为变量名
ASTNode* identifier = ast_new_identifier_expr(id_token.lexeme, id_token);
ASTNode* initializer = NULL;
if (check(parser, TOKEN_OP_ASSIGN)) { // 如果有初始化表达式
advance(parser); // 消费 '='
initializer = parse_expression(parser); // 解析初始化表达式
}
match(parser, TOKEN_SEMICOLON); // 期望分号
return ast_new_var_decl(type_specifier, identifier, initializer, decl_token);
}
// <program> ::= { <declaration> | <function_definition> }* <EOF>
ASTNode* parse_program(Parser* parser) {
Token program_token = parser->current_token; // 记录程序开始的令牌
ASTNode** declarations = NULL;
int num_declarations = 0;
// 预先获取第一个令牌,填充 current_token 和 peek_token
advance(parser);
// 循环解析顶层声明和函数定义,直到文件结束
while (!check(parser, TOKEN_EOF)) {
// 判断是函数定义还是变量声明
// 简单判断:如果类型关键字后面是标识符,再后面是 '(',则认为是函数定义
// 否则认为是变量声明
if ((check(parser, TOKEN_KEYWORD_INT) || check(parser, TOKEN_KEYWORD_CHAR) ||
check(parser, TOKEN_KEYWORD_VOID) || check(parser, TOKEN_KEYWORD_FLOAT)) &&
check_peek(parser, TOKEN_IDENTIFIER)) {
// 进一步向前看,判断是函数定义还是变量声明
// 找到标识符后的第一个非空白令牌
int temp_pos = parser->lexer->current_pos;
int temp_line = parser->lexer->line;
int temp_col = parser->lexer->column;
Token temp_token;
// 模拟词法分析器前进,找到标识符后的令牌
// 注意:这里只是为了向前看,不改变实际的parser状态
// 更好的做法是Lexer提供一个peek_token_at_offset的功能
// 这里为了简化,直接在parser中模拟
// 找到标识符
int id_start_pos = parser->lexer->current_pos;
while(isalnum(parser->lexer->source_code[id_start_pos]) || parser->lexer->source_code[id_start_pos] == '_') {
id_start_pos++;
}
// 跳过标识符后的空白
while(isspace(parser->lexer->source_code[id_start_pos])) {
id_start_pos++;
}
if (parser->lexer->source_code[id_start_pos] == '(') {
// 看起来像函数定义
declarations = (ASTNode**)realloc(declarations, (num_declarations + 1) * sizeof(ASTNode*));
if (!declarations) parser_error(parser, "内存分配失败 (程序顶层声明)");
declarations[num_declarations++] = parse_function_definition(parser);
} else {
// 否则认为是变量声明
declarations = (ASTNode**)realloc(declarations, (num_declarations + 1) * sizeof(ASTNode*));
if (!declarations) parser_error(parser, "内存分配失败 (程序顶层声明)");
declarations[num_declarations++] = parse_declaration(parser);
}
// 恢复lexer状态 (如果模拟peek改变了它)
parser->lexer->current_pos = temp_pos;
parser->lexer->line = temp_line;
parser->lexer->column = temp_col;
} else {
parser_error(parser, "期望顶层声明或函数定义");
}
}
// 匹配文件结束符
match(parser, TOKEN_EOF);
return ast_new_program(declarations, num_declarations, program_token);
}
// --- 解析器初始化与清理 ---
Parser* init_parser(Lexer* lexer) {
Parser* parser = (Parser*)malloc(sizeof(Parser));
if (!parser) {
fprintf(stderr, "错误: 内存分配失败 (Parser)\n");
return NULL;
}
parser->lexer = lexer;
// 预先读取两个令牌,填充 current_token 和 peek_token
parser->current_token = get_next_token(parser->lexer);
parser->peek_token = get_next_token(parser->lexer);
return parser;
}
void free_parser(Parser* parser) {
if (parser) {
// 释放 current_token 和 peek_token 的 lexeme
// 注意:get_next_token每次都会分配新的lexeme,所以这里需要释放
if (parser->current_token.lexeme) free(parser->current_token.lexeme);
if (parser->peek_token.lexeme) free(parser->peek_token.lexeme);
// 释放词法分析器
free_lexer(parser->lexer);
// 释放解析器本身
free(parser);
}
}
测试驱动:让我们的语法分析器动起来!
现在,我们需要修改main.c
来集成我们的解析器,并打印生成的AST。
// main.c
#include "lexer.h"
#include "parser.h"
#include "ast.h"
#include "token.h" // 确保包含token.h以便使用token_type_to_string
#include <stdio.h>
#include <stdlib.h>
// 这是一个包含各种C语言语法元素的测试代码字符串
// 用于全面测试词法分析器和语法分析器的功能
const char* TEST_CODE =
"// 这是一个单行注释\n"
"/* 这是一个\n"
" * 多行注释\n"
" */\n"
"int global_var = 100;\n" // 全局变量声明
"char another_global = 'Z';\n" // 另一个全局变量
"\n"
"float add(float a, float b) {\n" // 函数定义
" return a + b;\n"
"}\n"
"\n"
"int main() {\n" // main 函数的定义
" int x = 10;\n" // 整数变量声明和初始化
" float y = 3.14e-2;\n" // 浮点数变量声明和初始化,包含科学计数法
" char c = 'A';\n" // 字符变量声明和初始化
" const char* str = \"Hello, \\\"World!\\n\";\n" // 字符串字面量
" \n"
" if (x >= 5 && y < 10.0) {\n" // if 条件语句,包含比较运算符和逻辑运算符
" int z = x + y * 2;\n" // 局部变量声明和表达式
" return z;\n" // return 语句
" } else if (x == 10) {\n" // else if 分支
" x++;\n" // 自增运算符
" } else {\n" // else 分支
" y--;\n" // 自减运算符
" }\n"
" \n"
" while (x > 0) {\n" // while 循环
" x--;\n"
" if (x == 5) {\n"
" // break; // 暂不支持 break/continue
" }\n"
" }\n"
" \n"
" int result = add(x, y);\n" // 函数调用
" \n"
" result += 5;\n" // 复合赋值运算符
" global_var = result;\n" // 访问全局变量
" \n"
" return 0;\n"
"}\n"
;
int main(int argc, char* argv[]) {
// 将 TEST_CODE 字符串内容写入一个临时文件
const char* temp_filepath = "test_code.c";
FILE* temp_file = fopen(temp_filepath, "w");
if (!temp_file) {
fprintf(stderr, "错误: 无法创建临时文件 '%s'\n", temp_filepath);
return EXIT_FAILURE;
}
fprintf(temp_file, "%s", TEST_CODE);
fclose(temp_file);
// 1. 初始化词法分析器
Lexer* lexer = init_lexer(temp_filepath);
if (!lexer) {
return EXIT_FAILURE;
}
printf("--- 词法分析器初始化成功 ---\n");
// 2. 初始化语法分析器
Parser* parser = init_parser(lexer);
if (!parser) {
free_lexer(lexer);
return EXIT_FAILURE;
}
printf("--- 语法分析器初始化成功 ---\n");
// 3. 开始解析程序,构建AST
printf("--- 开始语法分析,构建AST ---\n");
ASTNode* program_ast = parse_program(parser);
if (program_ast) {
printf("--- AST 构建成功!打印AST结构:---\n");
ast_print(program_ast, 0); // 打印AST,缩进0
printf("--- AST 打印完成 ---\n");
// 4. 释放AST内存
ast_free_program(program_ast);
printf("--- AST 内存已释放 ---\n");
} else {
fprintf(stderr, "错误: AST 构建失败。\n");
}
// 5. 释放解析器和词法分析器资源
free_parser(parser);
printf("--- 解析器和词法分析器资源已释放 ---\n");
// 可选:删除临时文件
remove(temp_filepath);
return EXIT_SUCCESS;
}
编译和运行
将上述所有文件(token.h
, token.c
, lexer.h
, lexer.c
, ast.h
, ast.c
, parser.h
, parser.c
, main.c
)放在同一个目录下。 你可以使用GCC编译器进行编译:
gcc -o my_compiler main.c lexer.c token.c ast.c parser.c -Wall -Wextra
然后运行:
./my_compiler
你将看到程序首先进行词法分析,然后是语法分析,并最终打印出构建好的抽象语法树的结构。通过观察AST的输出,你可以清晰地看到源代码的层次结构,以及各种语句和表达式是如何被解析和组织起来的。
语法分析的逻辑分析与挑战
-
递归下降的优雅: 递归下降解析的优点是其直观性。每个非终结符(如
<expression>
、<statement>
)都对应一个函数,函数内部的逻辑直接反映了语法规则。这使得解析器的代码结构清晰,易于理解和维护。 -
向前看(Lookahead): 我们的解析器使用了“向前看”一个令牌(
peek_token
)的能力。这对于区分某些语法结构至关重要。例如,在parse_program
中,我们需要判断当前是变量声明还是函数定义,这需要查看类型关键字之后的标识符,再看其后的令牌是(
还是;
。 -
左递归问题: 递归下降解析器不能直接处理左递归的语法规则(例如
expr ::= expr + term
)。我们的语法规则设计避免了直接左递归,而是通过左因子分解(如additive_expression ::= multiplicative_expression { ( '+' | '-' ) multiplicative_expression }*
)将其转换为右递归或迭代形式。 -
运算符优先级和结合性: C语言的运算符优先级和结合性是语法分析的难点之一。我们通过将表达式规则分解为多个层次(如
primary_expression
->unary_expression
->multiplicative_expression
->additive_expression
等),并按照优先级从高到低进行解析,自然地处理了优先级。结合性则通过循环处理相同优先级的运算符来实现(例如,a + b + c
会被解析为(a + b) + c
,这是左结合)。 -
错误恢复: 我们的
parser_error
函数在遇到错误时会直接退出。在实际的编译器中,错误恢复是一个复杂的问题,目标是在发现错误后,能够跳过错误部分,继续解析剩余的代码,以便发现更多的错误。常见的策略包括恐慌模式(panic mode)恢复,即在遇到错误时,跳过令牌直到找到一个同步令牌(如分号、大括号等)。 -
内存管理: AST节点是动态分配的,并且它们之间存在复杂的父子关系。正确地分配和释放内存至关重要,以避免内存泄漏。我们提供了
ast_new_node
系列函数来创建节点,以及ast_free_node
和ast_free_program
来递归释放整个AST占用的内存。这是一个容易出错的地方,需要非常小心。 -
语法子集的限制: 目前的解析器只支持C语言的一个非常小的子集。例如,我们还没有支持数组、指针的复杂声明、结构体成员访问(除了
->
和.
的词法识别)、break
/continue
、switch
、enum
、typedef
等。这些将在后续章节逐步添加。
总结与展望
朋友们,再次恭喜你!你已经完成了手撸编译器的第二步——一个功能初具规模的C语言语法分析器!你亲手构建了抽象语法树,现在你不仅能识别C语言的“单词”,更能理解它们的“句子”和“段落”结构了!这种从无到有的创造感,是不是让你对计算机科学的理解又提升了一个维度?
AST是我们编译器的“骨架”,它承载了源代码的全部结构信息。有了它,我们就可以在后续阶段进行更高级别的分析和转换。
这只是我们“肝爆”之旅的第二站。在下一章中,我们将进入编译器的第三个核心阶段:语义分析(Semantic Analysis)。我们将为这棵“骨架”注入“灵魂”——进行类型检查、作用域分析、符号表构建等,确保代码不仅仅是语法正确,更是逻辑正确!
准备好迎接更深层次的挑战了吗?继续保持这份激情,我们下一章不见不散!点赞、收藏、关注,让我们一起把这“手撸编译器”的肝爆之旅进行到底!
--------------------------------------------------------------------------------------------------------更新于2025.6.19下午4点28
第三章:为代码注入灵魂——语义分析与符号表构建)
朋友们,欢迎回到我们“手撸编译器,肝爆4万行代码”的史诗级旅程!在第二章中,我们亲手打造了语法分析器(Parser),将C语言的源代码成功地构建成了一棵结构清晰的抽象语法树(AST)。这棵树,就像是代码的“骨架”,它告诉我们代码的结构是符合C语言语法的。
但是,语法正确并不意味着代码就是“对”的!举个例子:
int main() {
int x = "hello"; // 语法上没问题,但语义上是错的!
myUndeclaredVar = 10; // 语法上像赋值,但 myUndeclaredVar 未声明
int func(int a, int b);
func(1); // 语法上是函数调用,但参数数量不对
return "world"; // 语法上是return语句,但返回值类型不对
}
这些代码,从语法角度看,可能都是合法的令牌序列和结构。但是,它们都存在严重的语义错误!编译器必须能够识别这些错误,否则生成的机器码将是无效的,甚至可能导致程序崩溃。
这就是我们第三章的主角——**语义分析(Semantic Analysis)**登场的时候了!
语义分析的使命:为代码注入“灵魂”
语义分析器,就像一位经验丰富的“代码审查员”,它的任务是:
-
类型检查(Type Checking): 确保所有操作符的操作数类型是兼容的,函数调用的参数类型与形参类型匹配,赋值语句左右两边的类型兼容等。这是防止运行时类型错误的关键防线。
-
作用域分析(Scope Analysis): 确定每个标识符(变量、函数、参数等)的定义位置,并确保在引用它时,它在当前作用域内是可见的。
-
符号表管理(Symbol Table Management): 构建并维护一个符号表,用于存储程序中所有标识符的信息(如名称、类型、作用域、存储位置等)。这是语义分析的核心数据结构。
-
其他语义检查: 例如,检查变量是否在使用前声明、函数调用参数数量是否正确、
return
语句的返回值类型是否与函数返回类型一致、break
/continue
是否在循环/switch
内部等。 -
收集额外信息: 在AST节点上添加语义信息,如表达式的最终类型、标识符所引用的符号表条目等,这些信息将为后续的代码生成阶段提供便利。
简单来说,语义分析器就是检查我们“毛坯房”的“装修图纸”是否合理,水电线路是否符合规范,确保房子不仅能“盖起来”,还能“住进去”!
符号表:代码的“户口本”与“地图”
符号表是编译器在语义分析阶段最重要的“工具”之一。你可以把它想象成一个巨大的“户口本”和“地图”的结合体:
-
“户口本”: 记录了程序中所有“居民”(标识符)的详细信息,包括他们的“姓名”(名称)、“身份”(变量、函数、参数)、“血型”(类型)、“住址”(内存地址或偏移量)等等。
-
“地图”: 帮助编译器在复杂的代码结构中,快速找到某个标识符的“住址”,并确定其“可见范围”(作用域)。
作用域与符号表的层级结构
C语言具有块作用域(Block Scope)特性。这意味着变量的可见性受限于其声明所在的 {}
代码块。当进入一个新的代码块时,会创建一个新的作用域;当退出代码块时,该作用域内的符号将不再可见。
为了支持这种特性,符号表通常被组织成一个栈式结构或链表结构:
-
全局作用域: 程序的顶层作用域,所有全局变量和函数都在这里声明,它们在整个程序中都可见。
-
函数作用域: 每个函数都有自己的局部作用域,用于其参数和局部变量。
-
块作用域:
if
、while
、for
语句的代码块,以及函数体内部的任意{}
都可以创建新的嵌套作用域。
当查找一个标识符时,编译器会从当前最内层的作用域开始查找,如果找不到,就向上层作用域查找,直到全局作用域。如果最终都找不到,就报告“未声明标识符”错误。
符号表条目(Symbol Entry)的设计
每个符号表条目需要包含标识符的关键信息:
// symbol_table.h (部分代码,完整版在后面)
#ifndef SYMBOL_TABLE_H
#define SYMBOL_TABLE_H
#include "type.h" // 符号需要类型信息
// 符号的种类
typedef enum {
SYM_VAR, // 普通变量 (包括全局变量和局部变量)
SYM_FUNC, // 函数
SYM_PARAM // 函数参数
} SymbolKind;
// 符号表中的一个条目
typedef struct Symbol {
char* name; // 符号的名称 (如 "x", "main", "add")
SymbolKind kind; // 符号的种类 (变量、函数、参数)
Type* type; // 符号的类型 (指向Type结构体的指针)
int offset; // 对于局部变量和参数,表示其在栈帧中的偏移量
// 对于全局变量,表示其在数据段中的偏移量 (简化为0或一个虚拟地址)
int is_global; // 是否是全局符号
// 其他信息,如是否是常量,是否已初始化等
} Symbol;
// 定义一个简单的哈希表结构来存储符号
// 为了简化,这里先用一个简单的数组+链表来模拟哈希桶
// 实际生产级编译器会用更复杂的哈希函数和冲突解决策略
#define SYMBOL_TABLE_BUCKET_SIZE 128 // 哈希桶数量
typedef struct SymbolBucket {
Symbol* symbol;
struct SymbolBucket* next;
} SymbolBucket;
// 作用域结构体
typedef struct Scope {
SymbolBucket* buckets[SYMBOL_TABLE_BUCKET_SIZE]; // 哈希桶数组
struct Scope* parent; // 指向父作用域的指针,形成作用域链
int next_local_offset; // 当前作用域内局部变量的下一个可用栈帧偏移量
} Scope;
// 符号表管理器
typedef struct SymbolTable {
Scope* current_scope; // 指向当前活动作用域的指针
// 全局变量的存储管理(简化)
int global_var_count; // 全局变量数量
} SymbolTable;
// 函数声明
SymbolTable* init_symbol_table();
void free_symbol_table(SymbolTable* sym_table);
void enter_scope(SymbolTable* sym_table); // 进入新作用域
void exit_scope(SymbolTable* sym_table); // 退出当前作用域
Symbol* add_symbol(SymbolTable* sym_table, const char* name, SymbolKind kind, Type* type, int is_global);
Symbol* lookup_symbol(SymbolTable* sym_table, const char* name); // 在当前作用域链中查找符号
Symbol* lookup_symbol_in_current_scope(SymbolTable* sym_table, const char* name); // 只在当前作用域查找
#endif // SYMBOL_TABLE_H
类型系统:代码的“血脉”与“基因”
类型是C语言的“基因”,它定义了数据如何被解释和操作。语义分析的核心任务之一就是确保类型兼容性。
类型结构的设计
我们将C语言的基本类型抽象成一个Type
结构体。由于C语言有复杂的类型系统(指针、数组、结构体、函数类型等),这里我们先实现一个简化版,只包含基本类型和函数类型。
// type.h (部分代码,完整版在后面)
#ifndef TYPE_H
#define TYPE_H
// 类型的种类
typedef enum {
TYPE_UNKNOWN, // 未知类型,用于错误或未解析
TYPE_VOID, // void 类型
TYPE_INT, // int 类型
TYPE_CHAR, // char 类型
TYPE_FLOAT, // float 类型
// TODO: 添加更多基本类型,如 double, short, long, signed, unsigned
// TODO: 添加复合类型,如 TYPE_POINTER, TYPE_ARRAY, TYPE_STRUCT, TYPE_ENUM
TYPE_FUNCTION // 函数类型
} TypeKind;
// 类型结构体
typedef struct Type {
TypeKind kind; // 类型的种类
// 对于函数类型:
struct Type* return_type; // 函数的返回类型
struct Type** param_types; // 参数类型列表 (Type* 数组)
int num_params; // 参数数量
int is_var_arg; // 是否是可变参数函数 (如 printf)
// 对于基本类型,可以存储其大小和对齐信息
int size; // 类型占用的字节数
int align; // 类型的对齐要求
// 对于指针/数组类型,可以有 base_type
// struct Type* base_type; // 指向基类型 (例如 int* 的 base_type 是 int)
// int array_size; // 如果是数组,数组大小
} Type;
// 全局静态类型实例 (单例模式,避免重复创建和内存管理复杂性)
extern Type* type_void;
extern Type* type_int;
extern Type* type_char;
extern Type* type_float;
// extern Type* type_double; // TODO
// 函数声明
void init_types(); // 初始化全局静态类型实例
void free_types(); // 释放全局静态类型实例(如果它们是动态分配的)
// 创建函数类型
Type* create_function_type(Type* return_type, Type** param_types, int num_params, int is_var_arg);
// 类型兼容性检查和类型转换辅助函数
int is_assignable(Type* dest_type, Type* src_type); // 检查 src_type 是否可以赋值给 dest_type
Type* get_common_type(Type* type1, Type* type2); // 获取两个类型的公共类型 (用于二元运算)
// 辅助函数:将TypeKind转换为可读字符串
const char* type_kind_to_string(TypeKind kind);
#endif // TYPE_H
语义分析器(Semantic Analyzer)的核心实现
语义分析器将深度优先遍历AST。在遍历过程中,它会利用符号表来管理作用域和标识符信息,并利用类型系统来执行类型检查。
// semantic_analyzer.h (部分代码,完整版在后面)
#ifndef SEMANTIC_ANALYZER_H
#define SEMANTIC_ANALYZER_H
#include "ast.h"
#include "symbol_table.h"
#include "type.h"
// 语义分析器结构体
typedef struct {
SymbolTable* sym_table; // 符号表实例
int error_count; // 记录语义错误数量
// 其他可能的状态,如当前函数返回类型等
Type* current_function_return_type; // 当前正在分析的函数的返回类型
} SemanticAnalyzer;
// 函数声明
SemanticAnalyzer* init_semantic_analyzer();
void free_semantic_analyzer(SemanticAnalyzer* analyzer);
void semantic_error(SemanticAnalyzer* analyzer, Token token, const char* format, ...);
// 核心分析函数:遍历AST并执行语义检查
void analyze_program(ASTNode* program_node, SemanticAnalyzer* analyzer);
void analyze_function_definition(ASTNode* func_def_node, SemanticAnalyzer* analyzer);
void analyze_statement(ASTNode* stmt_node, SemanticAnalyzer* analyzer);
Type* analyze_expression(ASTNode* expr_node, SemanticAnalyzer* analyzer); // 表达式分析会返回其类型
#endif // SEMANTIC_ANALYZER_H
完整的代码实现
type.h
// type.h
#ifndef TYPE_H
#define TYPE_H
#include <stdlib.h> // For size_t, NULL
// 类型的种类
// 定义C语言中我们支持的各种类型
typedef enum {
TYPE_UNKNOWN, // 未知类型,用于错误或未解析状态
TYPE_VOID, // void 类型,通常用于函数无返回值
TYPE_INT, // int 类型,整数
TYPE_CHAR, // char 类型,字符
TYPE_FLOAT, // float 类型,单精度浮点数
// TODO: 未来可以扩展更多基本类型,如 double, short, long, signed, unsigned
// TODO: 未来可以扩展复合类型,如 TYPE_POINTER, TYPE_ARRAY, TYPE_STRUCT, TYPE_ENUM
TYPE_FUNCTION // 函数类型,特殊类型,用于描述函数的签名
} TypeKind;
// 类型结构体
// 描述一个C语言类型的所有必要信息
typedef struct Type {
TypeKind kind; // 类型的种类
// 对于函数类型 (TYPE_FUNCTION) 特有:
struct Type* return_type; // 函数的返回类型
struct Type** param_types; // 参数类型列表,是一个 Type* 数组
int num_params; // 参数的数量
int is_var_arg; // 是否是可变参数函数 (例如 printf),1表示是,0表示否
// 对于所有类型,可以存储其大小和对齐信息
// 这些信息在后续代码生成阶段非常重要
int size; // 类型占用的字节数 (例如 int 是 4 字节)
int align; // 类型的对齐要求 (例如 int 通常是 4 字节对齐)
// TODO: 对于指针/数组类型,可以有 base_type
// struct Type* base_type; // 指向基类型 (例如 int* 的 base_type 是 int)
// int array_size; // 如果是数组,数组的大小
} Type;
// 全局静态类型实例 (单例模式)
// 这样可以避免在程序中重复创建相同的基本类型对象,节省内存并简化管理
// 它们在程序启动时初始化,并在程序结束时释放
extern Type* type_void;
extern Type* type_int;
extern Type* type_char;
extern Type* type_float;
// extern Type* type_double; // TODO: 未来扩展
// 函数声明
/**
* @brief 初始化全局静态类型实例。
* 应该在程序启动时调用一次。
*/
void init_types();
/**
* @brief 释放全局静态类型实例占用的内存。
* 应该在程序结束时调用一次。
*/
void free_types();
/**
* @brief 创建一个函数类型。
* @param return_type 函数的返回类型。
* @param param_types 参数类型列表数组。
* @param num_params 参数数量。
* @param is_var_arg 是否是可变参数函数。
* @return 新创建的函数Type指针。
*/
Type* create_function_type(Type* return_type, Type** param_types, int num_params, int is_var_arg);
/**
* @brief 检查源类型是否可以赋值给目标类型。
* @param dest_type 目标类型。
* @param src_type 源类型。
* @return 如果可赋值返回1,否则返回0。
*/
int is_assignable(Type* dest_type, Type* src_type);
/**
* @brief 获取两个类型的公共类型 (用于二元运算的类型提升)。
* @param type1 类型1。
* @param type2 类型2。
* @return 两个类型的公共类型,如果无法找到公共类型则返回 TYPE_UNKNOWN。
*/
Type* get_common_type(Type* type1, Type* type2);
/**
* @brief 将 TypeKind 枚举值转换为可读的字符串。
* @param kind TypeKind 枚举值。
* @return 对应的字符串表示。
*/
const char* type_kind_to_string(TypeKind kind);
#endif // TYPE_H
type.c
// type.c
#include "type.h"
#include <stdio.h> // For fprintf, printf
#include <stdlib.h> // For malloc, free, exit
#include <string.h> // For memcpy
// 全局静态类型实例的定义
// 这些是编译器的内置类型,通常作为单例存在,无需多次创建
Type _type_void = { TYPE_VOID, NULL, 0, 0, 0, 0 };
Type _type_int = { TYPE_INT, NULL, 0, 0, 4, 4 }; // int 通常是 4 字节,4 字节对齐
Type _type_char = { TYPE_CHAR, NULL, 0, 0, 1, 1 }; // char 是 1 字节,1 字节对齐
Type _type_float = { TYPE_FLOAT, NULL, 0, 0, 4, 4 }; // float 通常是 4 字节,4 字节对齐
// Type _type_double = { TYPE_DOUBLE, NULL, 0, 0, 8, 8 }; // TODO: 未来扩展
// 外部可见的指针,指向这些静态实例
Type* type_void = &_type_void;
Type* type_int = &_type_int;
Type* type_char = &_type_char;
Type* type_float = &_type_float;
// Type* type_double = &_type_double; // TODO: 未来扩展
// 初始化全局静态类型实例 (如果它们需要动态初始化)
// 对于当前这种静态定义,这个函数可以为空或做一些校验
void init_types() {
// 确保类型大小和对齐是合理的
// 实际编译器会根据目标平台架构来设置这些值
type_int->size = 4; type_int->align = 4;
type_char->size = 1; type_char->align = 1;
type_float->size = 4; type_float->align = 4;
// type_double->size = 8; type_double->align = 8; // TODO
fprintf(stderr, "类型系统初始化完成。\n");
}
// 释放全局静态类型实例占用的内存
// 对于当前这种静态定义,它们不需要被free,因为它们在程序数据段
// 但如果未来有动态分配的全局类型(如复合类型),则需要在这里释放
void free_types() {
// 目前没有动态分配的全局类型,所以这里为空
// 如果 create_function_type 返回的 Type* 需要统一管理,则这里需要一个列表来遍历释放
fprintf(stderr, "类型系统资源已清理。\n");
}
// 创建一个函数类型
// 函数类型是动态创建的,因为每个函数的签名都可能不同
Type* create_function_type(Type* return_type, Type** param_types, int num_params, int is_var_arg) {
Type* func_type = (Type*)malloc(sizeof(Type));
if (!func_type) {
fprintf(stderr, "错误: 内存分配失败 (函数类型)\n");
exit(EXIT_FAILURE);
}
func_type->kind = TYPE_FUNCTION;
func_type->return_type = return_type;
func_type->num_params = num_params;
func_type->is_var_arg = is_var_arg;
func_type->size = 0; // 函数类型本身没有大小
func_type->align = 0; // 函数类型本身没有对齐要求
// 复制参数类型数组
if (num_params > 0) {
func_type->param_types = (Type**)malloc(num_params * sizeof(Type*));
if (!func_type->param_types) {
fprintf(stderr, "错误: 内存分配失败 (函数参数类型数组)\n");
free(func_type);
exit(EXIT_FAILURE);
}
memcpy(func_type->param_types, param_types, num_params * sizeof(Type*));
} else {
func_type->param_types = NULL;
}
return func_type;
}
// 释放函数类型内存
// 注意:这个函数只释放函数类型本身和其参数类型数组,不释放参数类型指向的Type*
// 因为参数类型通常是全局静态类型或者在其他地方管理
void free_function_type(Type* func_type) {
if (func_type && func_type->kind == TYPE_FUNCTION) {
if (func_type->param_types) {
free(func_type->param_types);
}
free(func_type);
}
}
// 类型兼容性检查:检查 src_type 是否可以赋值给 dest_type
// 这是一个简化的检查,实际C语言的赋值兼容性规则非常复杂
int is_assignable(Type* dest_type, Type* src_type) {
if (!dest_type || !src_type) return 0; // 无效类型
if (dest_type->kind == TYPE_UNKNOWN || src_type->kind == TYPE_UNKNOWN) return 0;
// 相同类型总是兼容
if (dest_type->kind == src_type->kind) return 1;
// 整数类型和字符类型可以相互赋值
if ((dest_type->kind == TYPE_INT && src_type->kind == TYPE_CHAR) ||
(dest_type->kind == TYPE_CHAR && src_type->kind == TYPE_INT)) {
return 1;
}
// 浮点数类型和整数/字符类型可以相互赋值 (会发生隐式转换)
if ((dest_type->kind == TYPE_FLOAT && (src_type->kind == TYPE_INT || src_type->kind == TYPE_CHAR)) ||
(src_type->kind == TYPE_FLOAT && (dest_type->kind == TYPE_INT || dest_type->kind == TYPE_CHAR))) {
return 1;
}
// TODO: 指针、数组、结构体、联合体等的兼容性
// TODO: void* 可以赋值给任何指针类型,任何指针类型可以赋值给 void*
return 0; // 默认不兼容
}
// 获取两个类型的公共类型 (用于二元运算的类型提升)
// 这是一个简化的类型提升规则,实际C语言的算术转换规则非常复杂
Type* get_common_type(Type* type1, Type* type2) {
if (!type1 || !type2) return type_unknown; // 无效类型返回未知
if (type1->kind == TYPE_UNKNOWN || type2->kind == TYPE_UNKNOWN) return type_unknown;
// 如果类型相同,公共类型就是它本身
if (type1->kind == type2->kind) return type1;
// 浮点数优先级最高
if (type1->kind == TYPE_FLOAT || type2->kind == TYPE_FLOAT) {
return type_float;
}
// 整数和字符的公共类型是整数
if ((type1->kind == TYPE_INT && type2->kind == TYPE_CHAR) ||
(type1->kind == TYPE_CHAR && type2->kind == TYPE_INT)) {
return type_int;
}
// TODO: 更多复杂的类型提升规则
return type_unknown; // 无法找到公共类型
}
// 将 TypeKind 枚举值转换为可读的字符串
const char* type_kind_to_string(TypeKind kind) {
switch (kind) {
case TYPE_UNKNOWN: return "UNKNOWN";
case TYPE_VOID: return "VOID";
case TYPE_INT: return "INT";
case TYPE_CHAR: return "CHAR";
case TYPE_FLOAT: return "FLOAT";
case TYPE_FUNCTION: return "FUNCTION";
default: return "UNKNOWN_TYPE_KIND";
}
}
symbol_table.h
// symbol_table.h
#ifndef SYMBOL_TABLE_H
#define SYMBOL_TABLE_H
#include "type.h" // 符号需要类型信息
#include <stdlib.h> // For size_t, NULL
// 符号的种类
// 标识符在程序中扮演的角色
typedef enum {
SYM_VAR, // 普通变量 (包括全局变量和局部变量)
SYM_FUNC, // 函数 (包括函数声明和定义)
SYM_PARAM // 函数参数
// TODO: 扩展其他符号种类,如 SYM_TYPE (typedef), SYM_STRUCT (结构体名), SYM_ENUM_CONST (枚举常量)
} SymbolKind;
// 符号表中的一个条目
// 存储一个标识符的所有相关信息
typedef struct Symbol {
char* name; // 符号的名称字符串 (动态分配的副本)
SymbolKind kind; // 符号的种类
Type* type; // 符号的类型 (指向Type结构体的指针)
int offset; // 对于局部变量和参数,表示其在栈帧中的偏移量
// 对于全局变量,表示其在数据段中的偏移量 (简化为0或一个虚拟地址)
int is_global; // 标记是否是全局符号 (1为全局,0为局部/参数)
// TODO: 其他信息,如是否是常量 (const), 是否已初始化, 是否是数组/指针等
} Symbol;
// 哈希表桶结构体
// 用于解决哈希冲突,将具有相同哈希值的符号通过链表连接
typedef struct SymbolBucket {
Symbol* symbol; // 指向实际符号的指针
struct SymbolBucket* next; // 指向链表中下一个桶的指针
} SymbolBucket;
// 符号表哈希桶数量
// 一个合适的素数可以减少冲突,提高查找效率
#define SYMBOL_TABLE_BUCKET_SIZE 127 // 选择一个素数
// 作用域结构体
// 每个作用域包含一个哈希表来存储其内部的符号,并指向其父作用域
typedef struct Scope {
SymbolBucket* buckets[SYMBOL_TABLE_BUCKET_SIZE]; // 哈希桶数组,存储当前作用域的符号
struct Scope* parent; // 指向父作用域的指针,用于实现作用域链 (嵌套作用域)
int next_local_offset; // 当前作用域内局部变量的下一个可用栈帧偏移量
// 用于在代码生成阶段分配局部变量空间
} Scope;
// 符号表管理器
// 维护当前活动的作用域,并管理全局符号
typedef struct SymbolTable {
Scope* current_scope; // 指向当前活动作用域的指针 (栈顶)
// 全局变量的存储管理(简化):
// 实际编译器会有专门的全局数据段布局,这里简化为只记录数量
int global_var_offset; // 全局变量的下一个可用偏移量
} SymbolTable;
// 函数声明
/**
* @brief 初始化符号表管理器。
* 创建全局作用域并设置为当前作用域。
* @return 成功返回指向 SymbolTable 结构体的指针,失败返回NULL。
*/
SymbolTable* init_symbol_table();
/**
* @brief 释放符号表管理器及其所有作用域和符号占用的内存。
* @param sym_table 指向要释放的 SymbolTable 结构体的指针。
*/
void free_symbol_table(SymbolTable* sym_table);
/**
* @brief 进入一个新的作用域。
* 创建一个新的作用域并将其推入作用域栈,成为当前作用域。
* @param sym_table 指向 SymbolTable 结构体的指针。
*/
void enter_scope(SymbolTable* sym_table);
/**
* @brief 退出当前作用域。
* 弹出当前作用域,将其父作用域设置为新的当前作用域。释放当前作用域的内存。
* @param sym_table 指向 SymbolTable 结构体的指针。
*/
void exit_scope(SymbolTable* sym_table);
/**
* @brief 将一个新符号添加到当前作用域。
* @param sym_table 指向 SymbolTable 结构体的指针。
* @param name 符号的名称。
* @param kind 符号的种类。
* @param type 符号的类型。
* @param is_global 是否是全局符号 (1为全局,0为局部/参数)。
* @return 成功返回指向新添加符号的指针,如果符号已存在则返回NULL。
*/
Symbol* add_symbol(SymbolTable* sym_table, const char* name, SymbolKind kind, Type* type, int is_global);
/**
* @brief 在当前作用域链中查找符号。
* 从当前作用域开始,向上层作用域查找,直到全局作用域。
* @param sym_table 指向 SymbolTable 结构体的指针。
* @param name 要查找的符号名称。
* @return 如果找到返回指向符号的指针,否则返回NULL。
*/
Symbol* lookup_symbol(SymbolTable* sym_table, const char* name);
/**
* @brief 只在当前作用域中查找符号。
* 不向上层作用域查找。
* @param sym_table 指向 SymbolTable 结构体的指针。
* @param name 要查找的符号名称。
* @return 如果找到返回指向符号的指针,否则返回NULL。
*/
Symbol* lookup_symbol_in_current_scope(SymbolTable* sym_table, const char* name);
#endif // SYMBOL_TABLE_H
symbol_table.c
// symbol_table.c
#include "symbol_table.h"
#include <stdio.h> // For fprintf
#include <stdlib.h> // For malloc, free, exit
#include <string.h> // For strdup, strcmp
// --- 辅助函数 ---
// 简单的哈希函数 (DJB2)
// 将字符串转换为一个哈希值,用于确定符号在哈希表中的桶位置
static unsigned int hash_string(const char* str) {
unsigned int hash = 5381;
int c;
while ((c = *str++)) {
hash = ((hash << 5) + hash) + c; // hash * 33 + c
}
return hash % SYMBOL_TABLE_BUCKET_SIZE; // 取模以适应哈希桶大小
}
// 创建一个新的符号表条目
static Symbol* create_symbol(const char* name, SymbolKind kind, Type* type, int is_global) {
Symbol* sym = (Symbol*)malloc(sizeof(Symbol));
if (!sym) {
fprintf(stderr, "错误: 内存分配失败 (Symbol)\n");
exit(EXIT_FAILURE);
}
sym->name = strdup(name); // 复制符号名称
if (!sym->name) {
fprintf(stderr, "错误: 内存分配失败 (Symbol name)\n");
free(sym);
exit(EXIT_FAILURE);
}
sym->kind = kind;
sym->type = type;
sym->is_global = is_global;
sym->offset = 0; // 初始偏移量为0,后续由语义分析器分配
return sym;
}
// 释放一个符号表条目及其名称字符串
static void free_symbol(Symbol* sym) {
if (sym) {
if (sym->name) {
free(sym->name);
}
// 注意:sym->type 不在这里释放,因为Type*通常是全局静态的或在其他地方管理
free(sym);
}
}
// 释放一个哈希桶链表中的所有符号和桶节点
static void free_bucket_list(SymbolBucket* head) {
SymbolBucket* current = head;
while (current) {
SymbolBucket* next = current->next;
free_symbol(current->symbol); // 释放符号
free(current); // 释放桶节点
current = next;
}
}
// 创建一个新的作用域
static Scope* create_scope(Scope* parent) {
Scope* scope = (Scope*)malloc(sizeof(Scope));
if (!scope) {
fprintf(stderr, "错误: 内存分配失败 (Scope)\n");
exit(EXIT_FAILURE);
}
// 初始化所有哈希桶为NULL
for (int i = 0; i < SYMBOL_TABLE_BUCKET_SIZE; ++i) {
scope->buckets[i] = NULL;
}
scope->parent = parent;
scope->next_local_offset = 0; // 局部变量从0偏移开始分配
return scope;
}
// 释放一个作用域及其内部的所有符号
static void free_scope(Scope* scope) {
if (scope) {
for (int i = 0; i < SYMBOL_TABLE_BUCKET_SIZE; ++i) {
free_bucket_list(scope->buckets[i]); // 释放每个哈希桶链表
}
free(scope);
}
}
// --- 符号表管理器公共接口实现 ---
// 初始化符号表管理器
SymbolTable* init_symbol_table() {
SymbolTable* sym_table = (SymbolTable*)malloc(sizeof(SymbolTable));
if (!sym_table) {
fprintf(stderr, "错误: 内存分配失败 (SymbolTable)\n");
exit(EXIT_FAILURE);
}
sym_table->current_scope = NULL;
sym_table->global_var_offset = 0; // 全局变量从0偏移开始
enter_scope(sym_table); // 创建并进入全局作用域
fprintf(stderr, "符号表初始化完成,进入全局作用域。\n");
return sym_table;
}
// 释放符号表管理器及其所有作用域和符号
void free_symbol_table(SymbolTable* sym_table) {
if (sym_table) {
// 依次退出并释放所有作用域,直到只剩下全局作用域(或NULL)
while (sym_table->current_scope) {
exit_scope(sym_table);
}
free(sym_table);
fprintf(stderr, "符号表资源已清理。\n");
}
}
// 进入新作用域
void enter_scope(SymbolTable* sym_table) {
Scope* new_scope = create_scope(sym_table->current_scope);
sym_table->current_scope = new_scope;
fprintf(stderr, "进入新作用域 (层级: %d)\n", (sym_table->current_scope->parent ? sym_table->current_scope->parent->next_local_offset : 0) / 4 + 1); // 粗略层级
}
// 退出当前作用域
void exit_scope(SymbolTable* sym_table) {
if (!sym_table->current_scope) {
fprintf(stderr, "警告: 尝试退出空作用域栈。\n");
return;
}
Scope* old_scope = sym_table->current_scope;
sym_table->current_scope = old_scope->parent; // 将父作用域设为当前作用域
free_scope(old_scope); // 释放旧作用域及其内部符号
fprintf(stderr, "退出作用域。\n");
}
// 将一个新符号添加到当前作用域
Symbol* add_symbol(SymbolTable* sym_table, const char* name, SymbolKind kind, Type* type, int is_global) {
// 首先检查当前作用域是否已存在同名符号,防止重复定义
if (lookup_symbol_in_current_scope(sym_table, name)) {
// 符号已存在,返回NULL表示添加失败
return NULL;
}
Symbol* new_sym = create_symbol(name, kind, type, is_global);
// 分配偏移量
if (is_global) {
new_sym->offset = sym_table->global_var_offset;
sym_table->global_var_offset += type->size; // 根据类型大小增加偏移
} else {
// 局部变量和参数的偏移量在栈帧中是负数(相对于栈基址)
// 我们这里简化为正数,在代码生成时再处理负偏移
new_sym->offset = sym_table->current_scope->next_local_offset;
sym_table->current_scope->next_local_offset += type->size; // 根据类型大小增加偏移
}
// 将新符号添加到当前作用域的哈希表中
unsigned int hash_val = hash_string(name);
SymbolBucket* new_bucket = (SymbolBucket*)malloc(sizeof(SymbolBucket));
if (!new_bucket) {
fprintf(stderr, "错误: 内存分配失败 (SymbolBucket)\n");
free_symbol(new_sym);
exit(EXIT_FAILURE);
}
new_bucket->symbol = new_sym;
// 将新桶插入到链表头部 (简单冲突解决)
new_bucket->next = sym_table->current_scope->buckets[hash_val];
sym_table->current_scope->buckets[hash_val] = new_bucket;
fprintf(stderr, " 添加符号: '%s' (类型: %s, 种类: %d, 偏移: %d, 全局: %d)\n",
name, type_kind_to_string(type->kind), kind, new_sym->offset, is_global);
return new_sym;
}
// 在当前作用域链中查找符号
Symbol* lookup_symbol(SymbolTable* sym_table, const char* name) {
Scope* current = sym_table->current_scope;
while (current) { // 沿着作用域链向上查找
unsigned int hash_val = hash_string(name);
SymbolBucket* bucket = current->buckets[hash_val];
while (bucket) { // 在当前作用域的哈希桶链表中查找
if (strcmp(bucket->symbol->name, name) == 0) {
return bucket->symbol; // 找到符号
}
bucket = bucket->next;
}
current = current->parent; // 在当前作用域未找到,继续向上层作用域查找
}
return NULL; // 未找到符号
}
// 只在当前作用域中查找符号
Symbol* lookup_symbol_in_current_scope(SymbolTable* sym_table, const char* name) {
if (!sym_table->current_scope) return NULL;
unsigned int hash_val = hash_string(name);
SymbolBucket* bucket = sym_table->current_scope->buckets[hash_val];
while (bucket) {
if (strcmp(bucket->symbol->name, name) == 0) {
return bucket->symbol;
}
bucket = bucket->next;
}
return NULL; // 未在当前作用域找到
}
ast.h
(修改,添加 resolved_type
和 symbol_entry
)
// ast.h (修改)
#ifndef AST_H
#define AST_H
#include "token.h" // AST节点可能会包含Token信息
#include "type.h" // AST节点现在需要Type信息
// 定义抽象语法树节点的类型
typedef enum {
// 顶层节点
AST_PROGRAM, // 整个程序,包含多个函数定义和全局声明
// 声明节点
AST_VAR_DECL, // 变量声明 (e.g., int x; int y = 10;)
AST_FUNC_DECL, // 函数声明 (e.g., int func(int a);) - 暂时不实现,先只支持定义
AST_FUNC_DEF, // 函数定义 (e.g., int main() { ... })
AST_PARAM_DECL, // 函数参数声明 (e.g., int a)
// 语句节点
AST_COMPOUND_STMT, // 复合语句/代码块 (e.g., { ... })
AST_EXPR_STMT, // 表达式语句 (e.g., x = 5; func();)
AST_RETURN_STMT, // return 语句 (e.g., return 0; return x + y;)
AST_IF_STMT, // if 语句 (e.g., if (...) { ... } else { ... })
AST_WHILE_STMT, // while 循环 (e.g., while (...) { ... })
AST_FOR_STMT, // for 循环 (e.g., for (...;...;...) { ... }) - 暂时不实现,先只支持while
// 表达式节点
AST_BINARY_EXPR, // 二元表达式 (e.g., a + b, x == y)
AST_UNARY_EXPR, // 一元表达式 (e.g., -x, !flag, ++i)
AST_ASSIGN_EXPR, // 赋值表达式 (e.g., a = b, x += 5)
AST_CALL_EXPR, // 函数调用表达式 (e.g., func(1, 2))
AST_IDENTIFIER_EXPR, // 标识符表达式 (e.g., x, myVar)
AST_INTEGER_LITERAL_EXPR, // 整数常量表达式 (e.g., 10, 0xFF)
AST_FLOAT_LITERAL_EXPR, // 浮点数常量表达式 (e.g., 3.14, 1.0e-5)
AST_STRING_LITERAL_EXPR, // 字符串常量表达式 (e.g., "hello")
AST_CHAR_LITERAL_EXPR, // 字符常量表达式 (e.g., 'a')
// 类型节点 (直接使用TokenType作为ASTNodeType的补充)
AST_TYPE_INT,
AST_TYPE_CHAR,
AST_TYPE_FLOAT,
AST_TYPE_VOID,
} ASTNodeType;
// 前向声明,因为节点之间会相互引用
struct ASTNode;
struct Symbol; // 前向声明 Symbol 结构体
// 表达式节点通用结构
typedef struct {
struct ASTNode* left; // 左操作数
struct ASTNode* right; // 右操作数
TokenType op; // 运算符类型 (来自token.h)
} BinaryExpr;
typedef struct {
struct ASTNode* operand; // 操作数
TokenType op; // 运算符类型 (来自token.h)
} UnaryExpr;
typedef struct {
struct ASTNode* target; // 赋值目标 (通常是标识符表达式)
struct ASTNode* value; // 赋值的值
TokenType op; // 赋值运算符 (e.g., TOKEN_OP_ASSIGN, TOKEN_OP_ASSIGN_PLUS)
} AssignExpr;
typedef struct {
struct ASTNode* callee; // 被调用的函数名 (标识符表达式)
struct ASTNode** args; // 参数列表 (ASTNode* 数组)
int num_args; // 参数数量
} CallExpr;
// 标识符节点
typedef struct {
char* name; // 标识符的名称 (e.g., "x", "main")
struct Symbol* symbol_entry; // 指向符号表中对应条目的指针 (语义分析后填充)
} IdentifierExpr;
// 字面量节点
typedef struct {
long long value; // 整数值
} IntegerLiteralExpr;
typedef struct {
double value; // 浮点数值
} FloatLiteralExpr;
typedef struct {
char* value; // 字符串值 (lexeme的副本)
} StringLiteralExpr;
typedef struct {
char value; // 字符值
} CharLiteralExpr;
// 变量声明节点
typedef struct {
struct ASTNode* type_specifier; // 类型说明符 (e.g., AST_TYPE_INT)
struct ASTNode* identifier; // 变量名 (标识符表达式)
struct ASTNode* initializer; // 初始化表达式 (可选,e.g., = 10)
} VarDecl;
// 函数参数声明节点
typedef struct {
struct ASTNode* type_specifier; // 参数类型 (e.g., AST_TYPE_INT)
struct ASTNode* identifier; // 参数名 (标识符表达式)
} ParamDecl;
// 复合语句/代码块节点
typedef struct {
struct ASTNode** statements; // 语句列表 (ASTNode* 数组)
int num_statements; // 语句数量
} CompoundStmt;
// 表达式语句节点
typedef struct {
struct ASTNode* expression; // 表达式 (可选,如只有分号的空语句)
} ExprStmt;
// return 语句节点
typedef struct {
struct ASTNode* expression; // 返回表达式 (可选,如 return;)
} ReturnStmt;
// if 语句节点
typedef struct {
struct ASTNode* condition; // 条件表达式
struct ASTNode* then_branch; // if 分支的语句
struct ASTNode* else_branch; // else 分支的语句 (可选)
} IfStmt;
// while 循环语句节点
typedef struct {
struct ASTNode* condition; // 循环条件表达式
struct ASTNode* body; // 循环体语句
} WhileStmt;
// for 循环语句节点 (暂时不实现,但结构预留)
typedef struct {
struct ASTNode* init; // 初始化语句 (可以是声明或表达式语句)
struct ASTNode* condition; // 循环条件表达式
struct ASTNode* increment; // 步进表达式 (可以是表达式语句)
struct ASTNode* body; // 循环体语句
} ForStmt;
// 函数定义节点
typedef struct {
struct ASTNode* return_type; // 返回类型 (AST_TYPE_INT等)
struct ASTNode* identifier; // 函数名 (标识符表达式)
struct ASTNode** params; // 参数列表 (ParamDecl* 数组)
int num_params; // 参数数量
struct ASTNode* body; // 函数体 (复合语句)
struct Symbol* symbol_entry; // 指向符号表中对应函数条目的指针 (语义分析后填充)
} FuncDef;
// 顶层程序节点
typedef struct {
struct ASTNode** declarations; // 声明和定义列表 (ASTNode* 数组)
int num_declarations; // 数量
} Program;
// 通用AST节点结构体
// 这是一个递归结构,每个节点可以包含指向其他AST节点的指针
typedef struct ASTNode {
ASTNodeType type; // 节点类型
Token token; // 关联的令牌 (用于错误报告和调试)
Type* resolved_type; // 语义分析后填充:表达式的最终类型,或声明的类型
// 使用联合体存储不同节点类型特有的数据
union {
BinaryExpr binary_expr;
UnaryExpr unary_expr;
AssignExpr assign_expr;
CallExpr call_expr;
IdentifierExpr identifier_expr;
IntegerLiteralExpr integer_literal_expr;
FloatLiteralExpr float_literal_expr;
StringLiteralExpr string_literal_expr;
CharLiteralExpr char_literal_expr;
VarDecl var_decl;
ParamDecl param_decl;
CompoundStmt compound_stmt;
ExprStmt expr_stmt;
ReturnStmt return_stmt;
IfStmt if_stmt;
WhileStmt while_stmt;
ForStmt for_stmt; // 暂时不用
FuncDef func_def;
Program program;
} data;
} ASTNode;
// AST节点创建函数
ASTNode* ast_new_node(ASTNodeType type, Token token);
ASTNode* ast_new_binary_expr(TokenType op, ASTNode* left, ASTNode* right, Token op_token);
ASTNode* ast_new_unary_expr(TokenType op, ASTNode* operand, Token op_token);
ASTNode* ast_new_assign_expr(TokenType op, ASTNode* target, ASTNode* value, Token op_token);
ASTNode* ast_new_call_expr(ASTNode* callee, ASTNode** args, int num_args, Token call_token);
ASTNode* ast_new_identifier_expr(char* name, Token token);
ASTNode* ast_new_integer_literal_expr(long long value, Token token);
ASTNode* ast_new_float_literal_expr(double value, Token token);
ASTNode* ast_new_string_literal_expr(char* value, Token token);
ASTNode* ast_new_char_literal_expr(char value, Token token);
ASTNode* ast_new_var_decl(ASTNode* type_specifier, ASTNode* identifier, ASTNode* initializer, Token token);
ASTNode* ast_new_param_decl(ASTNode* type_specifier, ASTNode* identifier, Token token);
ASTNode* ast_new_compound_stmt(ASTNode** statements, int num_statements, Token token);
ASTNode* ast_new_expr_stmt(ASTNode* expression, Token token);
ASTNode* ast_new_return_stmt(ASTNode* expression, Token token);
ASTNode* ast_new_if_stmt(ASTNode* condition, ASTNode* then_branch, ASTNode* else_branch, Token token);
ASTNode* ast_new_while_stmt(ASTNode* condition, ASTNode* body, Token token);
ASTNode* ast_new_func_def(ASTNode* return_type, ASTNode* identifier, ASTNode** params, int num_params, ASTNode* body, Token token);
ASTNode* ast_new_program(ASTNode** declarations, int num_declarations, Token token);
// AST节点释放函数
void ast_free_node(ASTNode* node);
void ast_free_program(ASTNode* program_node);
// 辅助函数:打印AST (用于调试)
void ast_print(ASTNode* node, int indent);
#endif // AST_H
ast.c
(修改)
// ast.c (修改)
#include "ast.h"
#include "symbol_table.h" // 需要Symbol结构体定义
#include <stdlib.h> // for malloc, free
#include <string.h> // for strdup, memset
#include <stdio.h> // for fprintf
// 辅助函数:复制令牌的lexeme字符串
static char* copy_lexeme(const char* lexeme) {
if (!lexeme) return NULL;
char* copy = strdup(lexeme);
if (!copy) {
fprintf(stderr, "错误: 内存分配失败 (复制词素)\n");
exit(EXIT_FAILURE);
}
return copy;
}
// --- AST节点创建函数的实现 ---
// 通用AST节点创建函数
// 初始化 resolved_type 和 symbol_entry 为 NULL
ASTNode* ast_new_node(ASTNodeType type, Token token) {
ASTNode* node = (ASTNode*)malloc(sizeof(ASTNode));
if (!node) {
fprintf(stderr, "错误: 内存分配失败 (ASTNode)\n");
exit(EXIT_FAILURE);
}
node->type = type;
node->token.type = token.type;
node->token.lexeme = copy_lexeme(token.lexeme);
node->token.line = token.line;
node->token.column = token.column;
node->token.value = token.value;
node->resolved_type = NULL; // 语义分析前,类型未知
memset(&node->data, 0, sizeof(node->data)); // 初始化联合体中的所有指针为NULL
// 特殊处理标识符节点,初始化 symbol_entry
if (type == AST_IDENTIFIER_EXPR) {
node->data.identifier_expr.symbol_entry = NULL;
} else if (type == AST_FUNC_DEF) {
node->data.func_def.symbol_entry = NULL;
}
return node;
}
// 其他 ast_new_* 函数保持不变,因为它们内部会调用 ast_new_node
// 创建二元表达式节点
ASTNode* ast_new_binary_expr(TokenType op, ASTNode* left, ASTNode* right, Token op_token) {
ASTNode* node = ast_new_node(AST_BINARY_EXPR, op_token);
node->data.binary_expr.op = op;
node->data.binary_expr.left = left;
node->data.binary_expr.right = right;
return node;
}
// 创建一元表达式节点
ASTNode* ast_new_unary_expr(TokenType op, ASTNode* operand, Token op_token) {
ASTNode* node = ast_new_node(AST_UNARY_EXPR, op_token);
node->data.unary_expr.op = op;
node->data.unary_expr.operand = operand;
return node;
}
// 创建赋值表达式节点
ASTNode* ast_new_assign_expr(TokenType op, ASTNode* target, ASTNode* value, Token op_token) {
ASTNode* node = ast_new_node(AST_ASSIGN_EXPR, op_token);
node->data.assign_expr.op = op;
node->data.assign_expr.target = target;
node->data.assign_expr.value = value;
return node;
}
// 创建函数调用表达式节点
ASTNode* ast_new_call_expr(ASTNode* callee, ASTNode** args, int num_args, Token call_token) {
ASTNode* node = ast_new_node(AST_CALL_EXPR, call_token);
node->data.call_expr.callee = callee;
node->data.call_expr.args = args;
node->data.call_expr.num_args = num_args;
return node;
}
// 创建标识符表达式节点
ASTNode* ast_new_identifier_expr(char* name, Token token) {
ASTNode* node = ast_new_node(AST_IDENTIFIER_EXPR, token);
node->data.identifier_expr.name = copy_lexeme(name);
node->data.identifier_expr.symbol_entry = NULL; // 初始为NULL
return node;
}
// 创建整数常量表达式节点
ASTNode* ast_new_integer_literal_expr(long long value, Token token) {
ASTNode* node = ast_new_node(AST_INTEGER_LITERAL_EXPR, token);
node->data.integer_literal_expr.value = value;
return node;
}
// 创建浮点数常量表达式节点
ASTNode* ast_new_float_literal_expr(double value, Token token) {
ASTNode* node = ast_new_node(AST_FLOAT_LITERAL_EXPR, token);
node->data.float_literal_expr.value = value;
return node;
}
// 创建字符串常量表达式节点
ASTNode* ast_new_string_literal_expr(char* value, Token token) {
ASTNode* node = ast_new_node(AST_STRING_LITERAL_EXPR, token);
node->data.string_literal_expr.value = copy_lexeme(value);
return node;
}
// 创建字符常量表达式节点
ASTNode* ast_new_char_literal_expr(char value, Token token) {
ASTNode* node = ast_new_node(AST_CHAR_LITERAL_EXPR, token);
node->data.char_literal_expr.value = value;
return node;
}
// 创建变量声明节点
ASTNode* ast_new_var_decl(ASTNode* type_specifier, ASTNode* identifier, ASTNode* initializer, Token token) {
ASTNode* node = ast_new_node(AST_VAR_DECL, token);
node->data.var_decl.type_specifier = type_specifier;
node->data.var_decl.identifier = identifier;
node->data.var_decl.initializer = initializer;
return node;
}
// 创建参数声明节点
ASTNode* ast_new_param_decl(ASTNode* type_specifier, ASTNode* identifier, Token token) {
ASTNode* node = ast_new_node(AST_PARAM_DECL, token);
node->data.param_decl.type_specifier = type_specifier;
node->data.param_decl.identifier = identifier;
return node;
}
// 创建复合语句/代码块节点
ASTNode* ast_new_compound_stmt(ASTNode** statements, int num_statements, Token token) {
ASTNode* node = ast_new_node(AST_COMPOUND_STMT, token);
node->data.compound_stmt.statements = statements;
node->data.compound_stmt.num_statements = num_statements;
return node;
}
// 创建表达式语句节点
ASTNode* ast_new_expr_stmt(ASTNode* expression, Token token) {
ASTNode* node = ast_new_node(AST_EXPR_STMT, token);
node->data.expr_stmt.expression = expression;
return node;
}
// 创建 return 语句节点
ASTNode* ast_new_return_stmt(ASTNode* expression, Token token) {
ASTNode* node = ast_new_node(AST_RETURN_STMT, token);
node->data.return_stmt.expression = expression;
return node;
}
// 创建 if 语句节点
ASTNode* ast_new_if_stmt(ASTNode* condition, ASTNode* then_branch, ASTNode* else_branch, Token token) {
ASTNode* node = ast_new_node(AST_IF_STMT, token);
node->data.if_stmt.condition = condition;
node->data.if_stmt.then_branch = then_branch;
node->data.if_stmt.else_branch = else_branch;
return node;
}
// 创建 while 循环语句节点
ASTNode* ast_new_while_stmt(ASTNode* condition, ASTNode* body, Token token) {
ASTNode* node = ast_new_node(AST_WHILE_STMT, token);
node->data.while_stmt.condition = condition;
node->data.while_stmt.body = body;
return node;
}
// 创建函数定义节点
ASTNode* ast_new_func_def(ASTNode* return_type, ASTNode* identifier, ASTNode** params, int num_params, ASTNode* body, Token token) {
ASTNode* node = ast_new_node(AST_FUNC_DEF, token);
node->data.func_def.return_type = return_type;
node->data.func_def.identifier = identifier;
node->data.func_def.params = params;
node->data.func_def.num_params = num_params;
node->data.func_def.body = body;
node->data.func_def.symbol_entry = NULL; // 初始为NULL
return node;
}
// 创建程序根节点
ASTNode* ast_new_program(ASTNode** declarations, int num_declarations, Token token) {
ASTNode* node = ast_new_node(AST_PROGRAM, token);
node->data.program.declarations = declarations;
node->data.program.num_declarations = num_declarations;
return node;
}
// --- AST节点释放函数的实现 ---
// 递归释放AST节点及其所有子节点占用的内存
void ast_free_node(ASTNode* node) {
if (!node) return;
// 递归释放子节点
switch (node->type) {
case AST_PROGRAM:
for (int i = 0; i < node->data.program.num_declarations; ++i) {
ast_free_node(node->data.program.declarations[i]);
}
free(node->data.program.declarations);
break;
case AST_FUNC_DEF:
ast_free_node(node->data.func_def.return_type);
ast_free_node(node->data.func_def.identifier);
for (int i = 0; i < node->data.func_def.num_params; ++i) {
ast_free_node(node->data.func_def.params[i]);
}
free(node->data.func_def.params);
ast_free_node(node->data.func_def.body);
// symbol_entry 指向符号表的条目,不在这里释放
break;
case AST_VAR_DECL:
ast_free_node(node->data.var_decl.type_specifier);
ast_free_node(node->data.var_decl.identifier);
if (node->data.var_decl.initializer) {
ast_free_node(node->data.var_decl.initializer);
}
break;
case AST_PARAM_DECL:
ast_free_node(node->data.param_decl.type_specifier);
ast_free_node(node->data.param_decl.identifier);
break;
case AST_COMPOUND_STMT:
for (int i = 0; i < node->data.compound_stmt.num_statements; ++i) {
ast_free_node(node->data.compound_stmt.statements[i]);
}
free(node->data.compound_stmt.statements);
break;
case AST_EXPR_STMT:
if (node->data.expr_stmt.expression) {
ast_free_node(node->data.expr_stmt.expression);
}
break;
case AST_RETURN_STMT:
if (node->data.return_stmt.expression) {
ast_free_node(node->data.return_stmt.expression);
}
break;
case AST_IF_STMT:
ast_free_node(node->data.if_stmt.condition);
ast_free_node(node->data.if_stmt.then_branch);
if (node->data.if_stmt.else_branch) {
ast_free_node(node->data.if_stmt.else_branch);
}
break;
case AST_WHILE_STMT:
ast_free_node(node->data.while_stmt.condition);
ast_free_node(node->data.while_stmt.body);
break;
case AST_FOR_STMT:
// TODO: 如果实现了for循环,需要在这里释放其子节点
break;
case AST_BINARY_EXPR:
ast_free_node(node->data.binary_expr.left);
ast_free_node(node->data.binary_expr.right);
break;
case AST_UNARY_EXPR:
ast_free_node(node->data.unary_expr.operand);
break;
case AST_ASSIGN_EXPR:
ast_free_node(node->data.assign_expr.target);
ast_free_node(node->data.assign_expr.value);
break;
case AST_CALL_EXPR:
ast_free_node(node->data.call_expr.callee);
for (int i = 0; i < node->data.call_expr.num_args; ++i) {
ast_free_node(node->data.call_expr.args[i]);
}
free(node->data.call_expr.args);
break;
case AST_IDENTIFIER_EXPR:
free(node->data.identifier_expr.name);
// symbol_entry 指向符号表的条目,不在这里释放
break;
case AST_STRING_LITERAL_EXPR:
free(node->data.string_literal_expr.value);
break;
case AST_INTEGER_LITERAL_EXPR:
case AST_FLOAT_LITERAL_EXPR:
case AST_CHAR_LITERAL_EXPR:
case AST_TYPE_INT:
case AST_TYPE_CHAR:
case AST_TYPE_FLOAT:
case AST_TYPE_VOID:
// 这些是叶子节点,没有子节点,resolved_type通常是静态的,不在这里释放
break;
default:
// 对于其他未显式处理的ASTNodeType,确保其token.lexeme被释放
break;
}
// 释放节点自身的token.lexeme
if (node->token.lexeme) {
free(node->token.lexeme);
node->token.lexeme = NULL;
}
// resolved_type 通常指向静态类型,不在这里释放
free(node);
}
// 释放整个AST (从程序根节点开始递归释放)
void ast_free_program(ASTNode* program_node) {
ast_free_node(program_node);
}
// 辅助函数:将ASTNodeType枚举值转换为可读的字符串
static const char* ast_node_type_to_string(ASTNodeType type) {
switch (type) {
case AST_PROGRAM: return "PROGRAM";
case AST_VAR_DECL: return "VAR_DECL";
case AST_FUNC_DECL: return "FUNC_DECL";
case AST_FUNC_DEF: return "FUNC_DEF";
case AST_PARAM_DECL: return "PARAM_DECL";
case AST_COMPOUND_STMT: return "COMPOUND_STMT";
case AST_EXPR_STMT: return "EXPR_STMT";
case AST_RETURN_STMT: return "RETURN_STMT";
case AST_IF_STMT: return "IF_STMT";
case AST_WHILE_STMT: return "WHILE_STMT";
case AST_FOR_STMT: return "FOR_STMT";
case AST_BINARY_EXPR: return "BINARY_EXPR";
case AST_UNARY_EXPR: return "UNARY_EXPR";
case AST_ASSIGN_EXPR: return "ASSIGN_EXPR";
case AST_CALL_EXPR: return "CALL_EXPR";
case AST_IDENTIFIER_EXPR: return "IDENTIFIER_EXPR";
case AST_INTEGER_LITERAL_EXPR: return "INTEGER_LITERAL_EXPR";
case AST_FLOAT_LITERAL_EXPR: return "FLOAT_LITERAL_EXPR";
case AST_STRING_LITERAL_EXPR: return "STRING_LITERAL_EXPR";
case AST_CHAR_LITERAL_EXPR: return "CHAR_LITERAL_EXPR";
case AST_TYPE_INT: return "TYPE_INT";
case AST_TYPE_CHAR: return "TYPE_CHAR";
case AST_TYPE_FLOAT: return "TYPE_FLOAT";
case AST_TYPE_VOID: return "TYPE_VOID";
default: return "UNKNOWN_AST_TYPE";
}
}
// 辅助函数:打印AST,用于调试和可视化
void ast_print(ASTNode* node, int indent) {
if (!node) return;
for (int i = 0; i < indent; ++i) {
printf(" ");
}
printf("[%s] (Token: %s, Lexeme: \"%s\", Line: %d, Col: %d)",
ast_node_type_to_string(node->type),
token_type_to_string(node->token.type),
node->token.lexeme ? node->token.lexeme : "N/A",
node->token.line,
node->token.column);
// 打印语义分析后添加的信息
if (node->resolved_type) {
printf(" [Resolved Type: %s]", type_kind_to_string(node->resolved_type->kind));
if (node->resolved_type->kind == TYPE_FUNCTION) {
printf(" (Ret: %s, Params: %d)",
type_kind_to_string(node->resolved_type->return_type->kind),
node->resolved_type->num_params);
}
}
if (node->type == AST_IDENTIFIER_EXPR && node->data.identifier_expr.symbol_entry) {
printf(" [Symbol: %s, Offset: %d]",
node->data.identifier_expr.symbol_entry->name,
node->data.identifier_expr.symbol_entry->offset);
}
if (node->type == AST_FUNC_DEF && node->data.func_def.symbol_entry) {
printf(" [Symbol: %s]", node->data.func_def.symbol_entry->name);
}
printf("\n");
switch (node->type) {
case AST_PROGRAM:
for (int i = 0; i < node->data.program.num_declarations; ++i) {
ast_print(node->data.program.declarations[i], indent + 1);
}
break;
case AST_FUNC_DEF:
for (int i = 0; i < indent + 1; ++i) printf(" "); printf("Return Type:\n");
ast_print(node->data.func_def.return_type, indent + 2);
for (int i = 0; i < indent + 1; ++i) printf(" "); printf("Parameters (%d):\n", node->data.func_def.num_params);
for (int i = 0; i < node->data.func_def.num_params; ++i) {
ast_print(node->data.func_def.params[i], indent + 2);
}
for (int i = 0; i < indent + 1; ++i) printf(" "); printf("Body:\n");
ast_print(node->data.func_def.body, indent + 2);
break;
case AST_VAR_DECL:
for (int i = 0; i < indent + 1; ++i) printf(" "); printf("Type:\n");
ast_print(node->data.var_decl.type_specifier, indent + 2);
for (int i = 0; i < indent + 1; ++i) printf(" "); printf("Identifier:\n");
ast_print(node->data.var_decl.identifier, indent + 2);
if (node->data.var_decl.initializer) {
for (int i = 0; i < indent + 1; ++i) printf(" "); printf("Initializer:\n");
ast_print(node->data.var_decl.initializer, indent + 2);
}
break;
case AST_PARAM_DECL:
for (int i = 0; i < indent + 1; ++i) printf(" "); printf("Type:\n");
ast_print(node->data.param_decl.type_specifier, indent + 2);
for (int i = 0; i < indent + 1; ++i) printf(" "); printf("Identifier:\n");
ast_print(node->data.param_decl.identifier, indent + 2);
break;
case AST_COMPOUND_STMT:
for (int i = 0; i < node->data.compound_stmt.num_statements; ++i) {
ast_print(node->data.compound_stmt.statements[i], indent + 1);
}
break;
case AST_EXPR_STMT:
if (node->data.expr_stmt.expression) {
ast_print(node->data.expr_stmt.expression, indent + 1);
}
break;
case AST_RETURN_STMT:
if (node->data.return_stmt.expression) {
ast_print(node->data.return_stmt.expression, indent + 1);
}
break;
case AST_IF_STMT:
for (int i = 0; i < indent + 1; ++i) printf(" "); printf("Condition:\n");
ast_print(node->data.if_stmt.condition, indent + 2);
for (int i = 0; i < indent + 1; ++i) printf(" "); printf("Then Branch:\n");
ast_print(node->data.if_stmt.then_branch, indent + 2);
if (node->data.if_stmt.else_branch) {
for (int i = 0; i < indent + 1; ++i) printf(" "); printf("Else Branch:\n");
ast_print(node->data.if_stmt.else_branch, indent + 2);
}
break;
case AST_WHILE_STMT:
for (int i = 0; i < indent + 1; ++i) printf(" "); printf("Condition:\n");
ast_print(node->data.while_stmt.condition, indent + 2);
for (int i = 0; i < indent + 1; ++i) printf(" "); printf("Body:\n");
ast_print(node->data.while_stmt.body, indent + 2);
break;
case AST_BINARY_EXPR:
for (int i = 0; i < indent + 1; ++i) printf(" "); printf("Left:\n");
ast_print(node->data.binary_expr.left, indent + 2);
for (int i = 0; i < indent + 1; ++i) printf(" "); printf("Right:\n");
ast_print(node->data.binary_expr.right, indent + 2);
break;
case AST_UNARY_EXPR:
for (int i = 0; i < indent + 1; ++i) printf(" "); printf("Operand:\n");
ast_print(node->data.unary_expr.operand, indent + 2);
break;
case AST_ASSIGN_EXPR:
for (int i = 0; i < indent + 1; ++i) printf(" "); printf("Target:\n");
ast_print(node->data.assign_expr.target, indent + 2);
for (int i = 0; i < indent + 1; ++i) printf(" "); printf("Value:\n");
ast_print(node->data.assign_expr.value, indent + 2);
break;
case AST_CALL_EXPR:
for (int i = 0; i < indent + 1; ++i) printf(" "); printf("Callee:\n");
ast_print(node->data.call_expr.callee, indent + 2);
for (int i = 0; i < indent + 1; ++i) printf(" "); printf("Arguments (%d):\n", node->data.call_expr.num_args);
for (int i = 0; i < node->data.call_expr.num_args; ++i) {
ast_print(node->data.call_expr.args[i], indent + 2);
}
break;
case AST_IDENTIFIER_EXPR:
// 名称已在主打印中显示
break;
case AST_INTEGER_LITERAL_EXPR:
// 值已在主打印中显示
break;
case AST_FLOAT_LITERAL_EXPR:
// 值已在主打印中显示
break;
case AST_STRING_LITERAL_EXPR:
// 值已在主打印中显示
break;
case AST_CHAR_LITERAL_EXPR:
// 值已在主打印中显示
break;
case AST_TYPE_INT:
case AST_TYPE_CHAR:
case AST_TYPE_FLOAT:
case AST_TYPE_VOID:
// 这些是叶子节点,其信息已在主打印中显示
break;
default:
// 其他未处理的ASTNodeType
break;
}
}
semantic_analyzer.h
// semantic_analyzer.h
#ifndef SEMANTIC_ANALYZER_H
#define SEMANTIC_ANALYZER_H
#include "ast.h" // 需要AST节点定义
#include "symbol_table.h"// 需要符号表
#include "type.h" // 需要类型系统
#include <stdarg.h> // For va_list, va_start, va_end
// 语义分析器结构体
// 包含了语义分析过程中所需的所有状态和工具
typedef struct {
SymbolTable* sym_table; // 符号表实例,用于管理作用域和符号
int error_count; // 记录在语义分析过程中发现的错误数量
// 当前正在分析的函数的返回类型
// 用于检查函数体内的 return 语句的类型是否匹配
Type* current_function_return_type;
// TODO: 其他可能的状态,如当前循环/switch的嵌套深度 (用于break/continue检查)
} SemanticAnalyzer;
// 函数声明
/**
* @brief 初始化语义分析器。
* 创建并初始化符号表和错误计数器。
* @return 成功返回指向 SemanticAnalyzer 结构体的指针,失败返回NULL。
*/
SemanticAnalyzer* init_semantic_analyzer();
/**
* @brief 释放语义分析器占用的所有资源。
* 包括符号表和 SemanticAnalyzer 结构体本身的内存。
* @param analyzer 指向要释放的 SemanticAnalyzer 结构体的指针。
*/
void free_semantic_analyzer(SemanticAnalyzer* analyzer);
/**
* @brief 报告语义错误。
* 打印详细的错误信息,包括行号、列号,并增加错误计数。
* 使用可变参数,可以像printf一样格式化错误信息。
* @param analyzer 指向 SemanticAnalyzer 结构体的指针。
* @param token 发生错误的令牌,用于定位错误位置。
* @param format 错误消息的格式字符串。
* @param ... 格式字符串的参数。
*/
void semantic_error(SemanticAnalyzer* analyzer, Token token, const char* format, ...);
// 核心分析函数:递归遍历AST并执行语义检查
/**
* @brief 分析整个程序AST。
* 这是语义分析的入口点,它会遍历AST中的所有顶层声明和函数定义。
* @param program_node 程序根AST节点。
* @param analyzer 语义分析器实例。
*/
void analyze_program(ASTNode* program_node, SemanticAnalyzer* analyzer);
/**
* @brief 分析函数定义AST节点。
* 处理函数参数、函数体内的局部变量和语句。
* @param func_def_node 函数定义AST节点。
* @param analyzer 语义分析器实例。
*/
void analyze_function_definition(ASTNode* func_def_node, SemanticAnalyzer* analyzer);
/**
* @brief 分析语句AST节点。
* 根据语句类型分派到不同的处理函数。
* @param stmt_node 语句AST节点。
* @param analyzer 语义分析器实例。
*/
void analyze_statement(ASTNode* stmt_node, SemanticAnalyzer* analyzer);
/**
* @brief 分析表达式AST节点。
* 递归分析表达式,执行类型检查,并返回表达式的解析类型。
* @param expr_node 表达式AST节点。
* @param analyzer 语义分析器实例。
* @return 表达式的解析类型 (Type* 指针)。如果发生错误,返回 TYPE_UNKNOWN。
*/
Type* analyze_expression(ASTNode* expr_node, SemanticAnalyzer* analyzer);
#endif // SEMANTIC_ANALYZER_H
semantic_analyzer.c
// semantic_analyzer.c
#include "semantic_analyzer.h"
#include <stdio.h> // For fprintf, vfprintf
#include <stdlib.h> // For exit, malloc, free
#include <string.h> // For strcmp
#include <stdarg.h> // For va_list, va_start, va_end
// --- 辅助函数和错误处理 ---
// 报告语义错误
// 打印详细的错误信息,包括行号、列号,并增加错误计数
void semantic_error(SemanticAnalyzer* analyzer, Token token, const char* format, ...) {
analyzer->error_count++; // 增加错误计数
va_list args;
va_start(args, format); // 初始化可变参数列表
// 打印错误信息,包括令牌的位置信息
fprintf(stderr, "语义错误 (%d:%d): ", token.line, token.column);
vfprintf(stderr, format, args); // 打印格式化的错误消息
fprintf(stderr, " (相关词素: \"%s\")\n", token.lexeme ? token.lexeme : "N/A");
va_end(args); // 清理可变参数列表
// 语义错误通常不立即退出,而是继续分析以发现更多错误
// 只有当错误数量达到一定阈值或在主函数中判断错误计数时才退出
}
// 获取AST节点表示的类型 (例如 AST_TYPE_INT -> type_int)
static Type* get_ast_node_type(ASTNode* type_node) {
if (!type_node) return type_unknown;
switch (type_node->type) {
case AST_TYPE_INT: return type_int;
case AST_TYPE_CHAR: return type_char;
case AST_TYPE_FLOAT: return type_float;
case AST_TYPE_VOID: return type_void;
default: return type_unknown;
}
}
// --- 核心语义分析逻辑 ---
// 分析变量声明
static void analyze_var_declaration(ASTNode* var_decl_node, SemanticAnalyzer* analyzer, int is_global) {
// 1. 获取变量类型
Type* var_type = get_ast_node_type(var_decl_node->data.var_decl.type_specifier);
if (var_type == type_unknown) {
semantic_error(analyzer, var_decl_node->token, "无法解析变量 '%s' 的类型。", var_decl_node->data.var_decl.identifier->data.identifier_expr.name);
return;
}
// void 类型不能声明变量
if (var_type->kind == TYPE_VOID) {
semantic_error(analyzer, var_decl_node->token, "不能声明类型为 'void' 的变量 '%s'。", var_decl_node->data.var_decl.identifier->data.identifier_expr.name);
return;
}
// 2. 获取变量名
char* var_name = var_decl_node->data.var_decl.identifier->data.identifier_expr.name;
// 3. 将变量添加到符号表
Symbol* sym = add_symbol(analyzer->sym_table, var_name, SYM_VAR, var_type, is_global);
if (!sym) {
semantic_error(analyzer, var_decl_node->token, "变量 '%s' 重复定义。", var_name);
return;
}
// 将符号表条目关联到AST节点,方便后续代码生成
var_decl_node->data.var_decl.identifier->data.identifier_expr.symbol_entry = sym;
var_decl_node->resolved_type = var_type; // 变量声明节点本身也有类型
// 4. 分析初始化表达式 (如果存在)
if (var_decl_node->data.var_decl.initializer) {
Type* initializer_type = analyze_expression(var_decl_node->data.var_decl.initializer, analyzer);
// 检查初始化表达式的类型与变量类型是否兼容
if (!is_assignable(var_type, initializer_type)) {
semantic_error(analyzer, var_decl_node->data.var_decl.initializer->token,
"初始化表达式类型 '%s' 与变量类型 '%s' 不兼容。",
type_kind_to_string(initializer_type->kind), type_kind_to_string(var_type->kind));
}
}
}
// 分析函数调用表达式
static Type* analyze_call_expression(ASTNode* call_expr_node, SemanticAnalyzer* analyzer) {
// 1. 分析被调用的函数(通常是标识符)
ASTNode* callee_node = call_expr_node->data.call_expr.callee;
if (callee_node->type != AST_IDENTIFIER_EXPR) {
semantic_error(analyzer, callee_node->token, "函数调用必须使用标识符。");
call_expr_node->resolved_type = type_unknown;
return type_unknown;
}
// 2. 在符号表中查找函数
Symbol* func_sym = lookup_symbol(analyzer->sym_table, callee_node->data.identifier_expr.name);
if (!func_sym) {
semantic_error(analyzer, callee_node->token, "未声明的函数 '%s'。", callee_node->data.identifier_expr.name);
call_expr_node->resolved_type = type_unknown;
return type_unknown;
}
if (func_sym->kind != SYM_FUNC) {
semantic_error(analyzer, callee_node->token, "'%s' 不是一个函数。", callee_node->data.identifier_expr.name);
call_expr_node->resolved_type = type_unknown;
return type_unknown;
}
// 将符号表条目关联到AST节点
callee_node->data.identifier_expr.symbol_entry = func_sym;
callee_node->resolved_type = func_sym->type; // 被调函数节点的类型是函数类型
Type* func_type = func_sym->type;
if (func_type->kind != TYPE_FUNCTION) {
semantic_error(analyzer, callee_node->token, "'%s' 的类型不是函数。", callee_node->data.identifier_expr.name);
call_expr_node->resolved_type = type_unknown;
return type_unknown;
}
// 3. 检查参数数量和类型
if (call_expr_node->data.call_expr.num_args != func_type->num_params) {
// 允许可变参数函数调用时参数数量不匹配
if (!func_type->is_var_arg) {
semantic_error(analyzer, call_expr_node->token,
"函数 '%s' 期望 %d 个参数,但提供了 %d 个。",
func_sym->name, func_type->num_params, call_expr_node->data.call_expr.num_args);
}
}
for (int i = 0; i < call_expr_node->data.call_expr.num_args; ++i) {
Type* arg_type = analyze_expression(call_expr_node->data.call_expr.args[i], analyzer);
if (i < func_type->num_params) { // 只检查固定参数
if (!is_assignable(func_type->param_types[i], arg_type)) {
semantic_error(analyzer, call_expr_node->data.call_expr.args[i]->token,
"函数 '%s' 的参数 %d 类型不兼容:期望 '%s',得到 '%s'。",
func_sym->name, i + 1,
type_kind_to_string(func_type->param_types[i]->kind),
type_kind_to_string(arg_type->kind));
}
}
// TODO: 对于可变参数,需要进行默认参数提升 (integer promotion, float to double)
}
// 函数调用的结果类型是函数的返回类型
call_expr_node->resolved_type = func_type->return_type;
return func_type->return_type;
}
// 分析表达式 (递归函数)
Type* analyze_expression(ASTNode* expr_node, SemanticAnalyzer* analyzer) {
if (!expr_node) return type_unknown; // 空表达式
Type* result_type = type_unknown; // 默认结果类型
switch (expr_node->type) {
case AST_INTEGER_LITERAL_EXPR:
result_type = type_int;
break;
case AST_FLOAT_LITERAL_EXPR:
result_type = type_float;
break;
case AST_STRING_LITERAL_EXPR:
// 字符串字面量在C中是 char[] 类型,这里简化为 char* 或特殊类型
// 暂时将其视为 char 类型,后续需要更复杂的指针/数组类型支持
result_type = type_char; // 简化处理,实际应是 char*
break;
case AST_CHAR_LITERAL_EXPR:
result_type = type_char;
break;
case AST_IDENTIFIER_EXPR: {
Symbol* sym = lookup_symbol(analyzer->sym_table, expr_node->data.identifier_expr.name);
if (!sym) {
semantic_error(analyzer, expr_node->token, "未声明的标识符 '%s'。", expr_node->data.identifier_expr.name);
result_type = type_unknown;
} else {
expr_node->data.identifier_expr.symbol_entry = sym; // 关联符号表条目
result_type = sym->type;
}
break;
}
case AST_BINARY_EXPR: {
Type* left_type = analyze_expression(expr_node->data.binary_expr.left, analyzer);
Type* right_type = analyze_expression(expr_node->data.binary_expr.right, analyzer);
if (left_type == type_unknown || right_type == type_unknown) {
result_type = type_unknown; // 子表达式有错误,当前表达式也未知
break;
}
// 检查二元运算符的类型兼容性并推导结果类型
switch (expr_node->data.binary_expr.op) {
case TOKEN_OP_PLUS:
case TOKEN_OP_MINUS:
case TOKEN_OP_MULTIPLY:
case TOKEN_OP_DIVIDE:
case TOKEN_OP_MODULO: {
// 算术运算符,要求操作数是数值类型
if ((left_type->kind != TYPE_INT && left_type->kind != TYPE_CHAR && left_type->kind != TYPE_FLOAT) ||
(right_type->kind != TYPE_INT && right_type->kind != TYPE_CHAR && right_type->kind != TYPE_FLOAT)) {
semantic_error(analyzer, expr_node->token, "二元算术运算符 '%s' 的操作数必须是数值类型。", token_type_to_string(expr_node->data.binary_expr.op));
result_type = type_unknown;
} else {
result_type = get_common_type(left_type, right_type); // 类型提升
}
break;
}
case TOKEN_OP_EQ:
case TOKEN_OP_NE:
case TOKEN_OP_LT:
case TOKEN_OP_LE:
case TOKEN_OP_GT:
case TOKEN_OP_GE: {
// 关系运算符,要求操作数可比较,结果为int (布尔值)
if (!get_common_type(left_type, right_type)) { // 检查是否有公共类型
semantic_error(analyzer, expr_node->token, "二元关系运算符 '%s' 的操作数类型 '%s' 和 '%s' 不兼容。",
token_type_to_string(expr_node->data.binary_expr.op),
type_kind_to_string(left_type->kind), type_kind_to_string(right_type->kind));
result_type = type_unknown;
} else {
result_type = type_int; // 关系运算结果是 int (0或1)
}
break;
}
case TOKEN_OP_AND:
case TOKEN_OP_OR: {
// 逻辑运算符,要求操作数可转换为布尔值 (非0即真)
// C语言中任何数值类型都可以作为布尔值
result_type = type_int; // 逻辑运算结果是 int (0或1)
break;
}
case TOKEN_OP_LSHIFT:
case TOKEN_OP_RSHIFT:
case TOKEN_OP_BIT_AND:
case TOKEN_OP_BIT_OR:
case TOKEN_OP_BIT_XOR: {
// 位运算符,要求操作数是整数类型
if (left_type->kind != TYPE_INT || right_type->kind != TYPE_INT) {
semantic_error(analyzer, expr_node->token, "二元位运算符 '%s' 的操作数必须是整数类型。", token_type_to_string(expr_node->data.binary_expr.op));
result_type = type_unknown;
} else {
result_type = type_int; // 位运算结果是 int
}
break;
}
default:
semantic_error(analyzer, expr_node->token, "不支持的二元运算符 '%s'。", token_type_to_string(expr_node->data.binary_expr.op));
result_type = type_unknown;
break;
}
break;
}
case AST_UNARY_EXPR: {
Type* operand_type = analyze_expression(expr_node->data.unary_expr.operand, analyzer);
if (operand_type == type_unknown) {
result_type = type_unknown;
break;
}
switch (expr_node->data.unary_expr.op) {
case TOKEN_OP_PLUS:
case TOKEN_OP_MINUS: { // 一元加减
if (operand_type->kind != TYPE_INT && operand_type->kind != TYPE_FLOAT && operand_type->kind != TYPE_CHAR) {
semantic_error(analyzer, expr_node->token, "一元运算符 '%s' 的操作数必须是数值类型。", token_type_to_string(expr_node->data.unary_expr.op));
result_type = type_unknown;
} else {
result_type = operand_type; // 结果类型不变
}
break;
}
case TOKEN_OP_NOT: { // 逻辑非
result_type = type_int; // 结果为 int (0或1)
break;
}
case TOKEN_OP_BIT_NOT: { // 位非
if (operand_type->kind != TYPE_INT) {
semantic_error(analyzer, expr_node->token, "位非运算符 '~' 的操作数必须是整数类型。");
result_type = type_unknown;
} else {
result_type = type_int;
}
break;
}
case TOKEN_OP_INC:
case TOKEN_OP_DEC: { // 自增自减,要求操作数是左值且是数值类型
// TODO: 检查是否是左值 (L-value)
if (operand_type->kind != TYPE_INT && operand_type->kind != TYPE_FLOAT && operand_type->kind != TYPE_CHAR) {
semantic_error(analyzer, expr_node->token, "自增/自减运算符的操作数必须是可修改的数值类型。");
result_type = type_unknown;
} else {
result_type = operand_type;
}
break;
}
case TOKEN_OP_BIT_AND: { // 取地址 &
// TODO: 任何左值都可以取地址,结果是指针类型
semantic_error(analyzer, expr_node->token, "取地址运算符 '&' 暂未完全支持类型推导。");
result_type = type_unknown; // 暂时返回未知
break;
}
case TOKEN_OP_MULTIPLY: { // 解引用 *
// TODO: 操作数必须是指针类型,结果是基类型
semantic_error(analyzer, expr_node->token, "解引用运算符 '*' 暂未完全支持类型推导。");
result_type = type_unknown; // 暂时返回未知
break;
}
default:
semantic_error(analyzer, expr_node->token, "不支持的一元运算符 '%s'。", token_type_to_string(expr_node->data.unary_expr.op));
result_type = type_unknown;
break;
}
break;
}
case AST_ASSIGN_EXPR: {
Type* target_type = analyze_expression(expr_node->data.assign_expr.target, analyzer);
Type* value_type = analyze_expression(expr_node->data.assign_expr.value, analyzer);
if (target_type == type_unknown || value_type == type_unknown) {
result_type = type_unknown;
break;
}
// 检查赋值目标是否是左值 (这里简化为标识符表达式)
if (expr_node->data.assign_expr.target->type != AST_IDENTIFIER_EXPR) {
semantic_error(analyzer, expr_node->data.assign_expr.target->token, "赋值目标必须是左值。");
result_type = type_unknown;
break;
}
// 检查赋值类型兼容性
if (!is_assignable(target_type, value_type)) {
semantic_error(analyzer, expr_node->token,
"赋值操作符 '%s' 类型不兼容:无法将 '%s' 赋值给 '%s'。",
token_type_to_string(expr_node->data.assign_expr.op),
type_kind_to_string(value_type->kind), type_kind_to_string(target_type->kind));
result_type = type_unknown;
} else {
result_type = target_type; // 赋值表达式的结果是赋值目标的类型
}
break;
}
case AST_CALL_EXPR:
result_type = analyze_call_expression(expr_node, analyzer);
break;
default:
semantic_error(analyzer, expr_node->token, "未知或不支持的表达式类型用于语义分析。");
result_type = type_unknown;
break;
}
expr_node->resolved_type = result_type; // 将解析出的类型存储回AST节点
return result_type;
}
// 分析语句 (递归函数)
void analyze_statement(ASTNode* stmt_node, SemanticAnalyzer* analyzer) {
if (!stmt_node) return;
switch (stmt_node->type) {
case AST_COMPOUND_STMT: {
enter_scope(analyzer->sym_table); // 进入新的块作用域
for (int i = 0; i < stmt_node->data.compound_stmt.num_statements; ++i) {
ASTNode* child_node = stmt_node->data.compound_stmt.statements[i];
if (child_node->type == AST_VAR_DECL) {
analyze_var_declaration(child_node, analyzer, 0); // 局部变量声明
} else {
analyze_statement(child_node, analyzer); // 递归分析子语句
}
}
exit_scope(analyzer->sym_table); // 退出当前块作用域
break;
}
case AST_EXPR_STMT:
if (stmt_node->data.expr_stmt.expression) {
analyze_expression(stmt_node->data.expr_stmt.expression, analyzer);
}
break;
case AST_RETURN_STMT: {
Type* return_expr_type = type_void; // 默认返回void
if (stmt_node->data.return_stmt.expression) {
return_expr_type = analyze_expression(stmt_node->data.return_stmt.expression, analyzer);
}
// 检查返回表达式的类型是否与当前函数返回类型兼容
if (analyzer->current_function_return_type == NULL) {
semantic_error(analyzer, stmt_node->token, "return 语句出现在函数体之外。");
} else if (!is_assignable(analyzer->current_function_return_type, return_expr_type)) {
semantic_error(analyzer, stmt_node->token,
"return 语句返回类型 '%s' 与函数期望返回类型 '%s' 不兼容。",
type_kind_to_string(return_expr_type->kind),
type_kind_to_string(analyzer->current_function_return_type->kind));
}
break;
}
case AST_IF_STMT: {
Type* condition_type = analyze_expression(stmt_node->data.if_stmt.condition, analyzer);
// 条件表达式通常期望为数值类型 (非0即真)
if (condition_type->kind == TYPE_UNKNOWN || condition_type->kind == TYPE_VOID) {
semantic_error(analyzer, stmt_node->data.if_stmt.condition->token, "if 语句条件表达式类型无效。");
}
analyze_statement(stmt_node->data.if_stmt.then_branch, analyzer); // 分析then分支
if (stmt_node->data.if_stmt.else_branch) {
analyze_statement(stmt_node->data.if_stmt.else_branch, analyzer); // 分析else分支
}
break;
}
case AST_WHILE_STMT: {
Type* condition_type = analyze_expression(stmt_node->data.while_stmt.condition, analyzer);
// 条件表达式通常期望为数值类型
if (condition_type->kind == TYPE_UNKNOWN || condition_type->kind == TYPE_VOID) {
semantic_error(analyzer, stmt_node->data.while_stmt.condition->token, "while 语句条件表达式类型无效。");
}
analyze_statement(stmt_node->data.while_stmt.body, analyzer); // 分析循环体
break;
}
// TODO: AST_FOR_STMT, AST_BREAK_STMT, AST_CONTINUE_STMT, AST_SWITCH_STMT 等
default:
semantic_error(analyzer, stmt_node->token, "未知或不支持的语句类型用于语义分析。");
break;
}
}
// 分析函数定义
void analyze_function_definition(ASTNode* func_def_node, SemanticAnalyzer* analyzer) {
// 1. 获取函数返回类型
Type* return_type = get_ast_node_type(func_def_node->data.func_def.return_type);
if (return_type == type_unknown) {
semantic_error(analyzer, func_def_node->token, "无法解析函数 '%s' 的返回类型。", func_def_node->data.func_def.identifier->data.identifier_expr.name);
return;
}
// 2. 获取函数名
char* func_name = func_def_node->data.func_def.identifier->data.identifier_expr.name;
// 3. 收集参数类型
Type** param_types = NULL;
if (func_def_node->data.func_def.num_params > 0) {
param_types = (Type**)malloc(func_def_node->data.func_def.num_params * sizeof(Type*));
if (!param_types) {
semantic_error(analyzer, func_def_node->token, "内存分配失败 (函数参数类型数组)。");
return;
}
for (int i = 0; i < func_def_node->data.func_def.num_params; ++i) {
ASTNode* param_decl = func_def_node->data.func_def.params[i];
param_types[i] = get_ast_node_type(param_decl->data.param_decl.type_specifier);
if (param_types[i] == type_unknown) {
semantic_error(analyzer, param_decl->token, "无法解析参数 '%s' 的类型。", param_decl->data.param_decl.identifier->data.identifier_expr.name);
// 此时 param_types[i] 仍然是 type_unknown,但可以继续
}
if (param_types[i]->kind == TYPE_VOID && func_def_node->data.func_def.num_params > 1) {
semantic_error(analyzer, param_decl->token, "void 类型参数只能单独出现。");
}
}
}
// 4. 创建函数类型
Type* func_type = create_function_type(return_type, param_types, func_def_node->data.func_def.num_params, 0); // 暂不支持可变参数
func_def_node->resolved_type = func_type; // 函数定义节点本身也有类型
// 5. 将函数添加到符号表 (全局作用域)
// 检查函数是否重复定义
Symbol* existing_sym = lookup_symbol_in_current_scope(analyzer->sym_table, func_name);
if (existing_sym) {
semantic_error(analyzer, func_def_node->token, "函数 '%s' 重复定义。", func_name);
// TODO: 检查函数签名是否一致 (重载/重定义)
// 如果是重复定义,并且签名不一致,则报错
// 如果是重复定义,但签名一致(例如先声明后定义),则更新符号表条目
// 这里简化为直接报错
free_function_type(func_type); // 释放新创建的函数类型
if (param_types) free(param_types);
return;
}
Symbol* func_sym = add_symbol(analyzer->sym_table, func_name, SYM_FUNC, func_type, 1); // 函数是全局符号
if (!func_sym) { // 理论上不会发生,因为前面已经检查了重复定义
semantic_error(analyzer, func_def_node->token, "添加函数 '%s' 到符号表失败。", func_name);
free_function_type(func_type);
if (param_types) free(param_types);
return;
}
func_def_node->data.func_def.symbol_entry = func_sym; // 关联符号表条目
// 6. 进入函数作用域,添加参数到符号表
enter_scope(analyzer->sym_table);
analyzer->current_function_return_type = return_type; // 设置当前函数的返回类型
for (int i = 0; i < func_def_node->data.func_def.num_params; ++i) {
ASTNode* param_decl = func_def_node->data.func_def.params[i];
char* param_name = param_decl->data.param_decl.identifier->data.identifier_expr.name;
Type* param_type = get_ast_node_type(param_decl->data.param_decl.type_specifier);
Symbol* param_sym = add_symbol(analyzer->sym_table, param_name, SYM_PARAM, param_type, 0); // 参数是局部符号
if (!param_sym) {
semantic_error(analyzer, param_decl->token, "参数 '%s' 重复定义。", param_name);
}
param_decl->data.param_decl.identifier->data.identifier_expr.symbol_entry = param_sym; // 关联符号表条目
param_decl->resolved_type = param_type; // 参数声明节点本身也有类型
}
// 7. 分析函数体
analyze_statement(func_def_node->data.func_def.body, analyzer);
// 8. 退出函数作用域
exit_scope(analyzer->sym_table);
analyzer->current_function_return_type = NULL; // 清除当前函数的返回类型
if (param_types) free(param_types); // 释放临时参数类型数组
// func_type 在符号表中被引用,不在这里释放
}
// 分析整个程序AST
void analyze_program(ASTNode* program_node, SemanticAnalyzer* analyzer) {
if (program_node->type != AST_PROGRAM) {
semantic_error(analyzer, program_node->token, "AST根节点不是程序类型。");
return;
}
// 遍历所有顶层声明和定义
for (int i = 0; i < program_node->data.program.num_declarations; ++i) {
ASTNode* decl_node = program_node->data.program.declarations[i];
if (decl_node->type == AST_VAR_DECL) {
analyze_var_declaration(decl_node, analyzer, 1); // 全局变量声明
} else if (decl_node->type == AST_FUNC_DEF) {
analyze_function_definition(decl_node, analyzer); // 函数定义
} else {
semantic_error(analyzer, decl_node->token, "顶层只能是变量声明或函数定义。");
}
}
}
// --- 语义分析器初始化与清理 ---
SemanticAnalyzer* init_semantic_analyzer() {
SemanticAnalyzer* analyzer = (SemanticAnalyzer*)malloc(sizeof(SemanticAnalyzer));
if (!analyzer) {
fprintf(stderr, "错误: 内存分配失败 (SemanticAnalyzer)\n");
return NULL;
}
analyzer->error_count = 0;
analyzer->current_function_return_type = NULL;
init_types(); // 初始化类型系统
analyzer->sym_table = init_symbol_table(); // 初始化符号表
if (!analyzer->sym_table) {
free(analyzer);
return NULL;
}
fprintf(stderr, "语义分析器初始化成功。\n");
return analyzer;
}
void free_semantic_analyzer(SemanticAnalyzer* analyzer) {
if (analyzer) {
if (analyzer->sym_table) {
free_symbol_table(analyzer->sym_table); // 释放符号表
}
free_types(); // 释放类型系统资源
free(analyzer);
fprintf(stderr, "语义分析器资源已清理。\n");
}
}
main.c
(修改)
// main.c (修改)
#include "lexer.h"
#include "parser.h"
#include "ast.h"
#include "token.h"
#include "semantic_analyzer.h" // 包含语义分析器头文件
#include "type.h" // 包含类型系统头文件
#include "symbol_table.h" // 包含符号表头文件
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
// 这是一个包含各种C语言语法元素的测试代码字符串
// 用于全面测试词法分析器、语法分析器和语义分析器的功能
const char* TEST_CODE =
"// 这是一个单行注释\n"
"/* 这是一个\n"
" * 多行注释\n"
" */\n"
"int global_var = 100;\n" // 全局变量声明
"char another_global = 'Z';\n" // 另一个全局变量
"\n"
"float add(float a, float b) {\n" // 函数定义:浮点数加法
" return a + b;\n"
"}\n"
"\n"
"int subtract(int x, int y) {\n" // 新增函数:整数减法
" return x - y;\n"
"}\n"
"\n"
"int main() {\n" // main 函数的定义
" int x = 10;\n" // 整数变量声明和初始化
" float y = 3.14e-2;\n" // 浮点数变量声明和初始化,包含科学计数法
" char c = 'A';\n" // 字符变量声明和初始化
" const char* str_literal = \"Hello, \\\"World!\\n\";\n" // 字符串字面量
" \n"
" if (x >= 5 && y < 10.0) {\n" // if 条件语句,包含比较运算符和逻辑运算符
" int z = x + (int)y * 2; // 强制类型转换 (暂不支持,但会尝试处理表达式) \n" // 局部变量声明和表达式
" return z;\n" // return 语句
" } else if (x == 10) {\n" // else if 分支 (else后面跟if语句)
" x++;\n" // 自增运算符
" } else {\n" // else 分支
" y--;\n" // 自减运算符
" }\n"
" \n"
" while (x > 0) {\n" // while 循环
" x--;\n" // 循环体内的表达式语句
" if (x == 5) {\n"
" // break; // 暂不支持 break/continue,但语法上可以识别
" }\n"
" }\n"
" \n"
" float sum_result = add(x, y);\n" // 函数调用,并将结果赋值给变量
" int diff_result = subtract(20, 5);\n" // 调用新函数
" \n"
" sum_result += 5.0f;\n" // 复合赋值运算符
" global_var = (int)sum_result;\n" // 访问并修改全局变量,隐式转换为int
" \n"
" // 语义错误测试用例\n"
" // int undeclared_var = undeclared_func(1, 2);\n" // 未声明的函数
" // int bad_assign = \"string\";\n" // 类型不兼容的赋值
" // int x = 20; // 重复定义变量 x (在main函数内)
" // return \"string\"; // return 类型不匹配\n"
" // int main() { /* 重复定义 main 函数 */ }\n"
" // void bad_var; // void 类型变量\n"
" \n"
" return 0;\n" // main 函数返回
"}\n"
;
int main(int argc, char* argv[]) {
// 1. 将 TEST_CODE 字符串内容写入一个临时文件
const char* temp_filepath = "test_code.c";
FILE* temp_file = fopen(temp_filepath, "w");
if (!temp_file) {
fprintf(stderr, "错误: 无法创建临时文件 '%s'\n", temp_filepath);
return EXIT_FAILURE;
}
fprintf(temp_file, "%s", TEST_CODE);
fclose(temp_file);
// 2. 初始化词法分析器
Lexer* lexer = init_lexer(temp_filepath);
if (!lexer) {
return EXIT_FAILURE;
}
printf("--- 词法分析器初始化成功 ---\n");
// 3. 初始化语法分析器
Parser* parser = init_parser(lexer);
if (!parser) {
free_lexer(lexer);
return EXIT_FAILURE;
}
printf("--- 语法分析器初始化成功 ---\n");
// 4. 开始解析程序,构建抽象语法树 (AST)
printf("--- 开始语法分析,构建AST ---\n");
ASTNode* program_ast = parse_program(parser);
// 5. 检查AST是否成功构建并打印
if (program_ast) {
printf("--- AST 构建成功!---\n");
// 6. 初始化语义分析器
SemanticAnalyzer* analyzer = init_semantic_analyzer();
if (!analyzer) {
ast_free_program(program_ast);
free_parser(parser);
return EXIT_FAILURE;
}
printf("--- 语义分析开始!---\n");
analyze_program(program_ast, analyzer); // 执行语义分析
if (analyzer->error_count > 0) {
fprintf(stderr, "语义分析完成,发现 %d 个语义错误。\n", analyzer->error_count);
} else {
printf("语义分析完成,未发现语义错误!代码是语义正确的!\n");
}
// 打印带有语义信息的AST
printf("--- 打印带有语义信息的AST结构:---\n");
ast_print(program_ast, 0);
printf("--- AST 打印完成 ---\n");
// 7. 释放AST内存
ast_free_program(program_ast);
printf("--- AST 内存已释放 ---\n");
// 8. 释放语义分析器资源
free_semantic_analyzer(analyzer);
printf("--- 语义分析器资源已释放 ---\n");
} else {
fprintf(stderr, "错误: AST 构建失败。\n");
}
// 9. 释放解析器和词法分析器资源
free_parser(parser);
printf("--- 解析器和词法分析器资源已释放 ---\n");
// 可选:删除之前创建的临时文件
remove(temp_filepath);
// 如果有语义错误,返回失败状态
if (analyzer->error_count > 0) {
return EXIT_FAILURE;
}
return EXIT_SUCCESS;
}
编译和运行
将上述所有文件(token.h
, token.c
, lexer.h
, lexer.c
, ast.h
, ast.c
, type.h
, type.c
, symbol_table.h
, symbol_table.c
, semantic_analyzer.h
, semantic_analyzer.c
, main.c
)放在同一个目录下。
你可以使用GCC编译器进行编译:
gcc -o my_compiler main.c lexer.c token.c ast.c parser.c type.c symbol_table.c semantic_analyzer.c -Wall -Wextra
然后运行:
./my_compiler
你将看到程序依次进行词法分析、语法分析,然后是语义分析。在语义分析阶段,它会构建符号表,进行类型检查,并报告任何发现的语义错误。最终,它会打印出带有丰富语义信息(如解析出的类型、符号表条目)的AST。
语义分析的逻辑分析与挑战
-
符号表与作用域管理:
-
我们实现了基于哈希表的符号表,并支持栈式的作用域管理(
enter_scope
和exit_scope
)。每次进入一个{}
代码块,我们都会推入一个新的作用域;退出时则弹出。 -
add_symbol
负责将新声明的标识符添加到当前作用域,并检查重定义。 -
lookup_symbol
则会沿着作用域链向上查找,遵循C语言的可见性规则。 -
我们还为变量分配了简单的偏移量,这在后续的代码生成阶段将用于确定变量的内存位置。
-
-
类型系统:
-
我们定义了
Type
结构体来抽象C语言的类型,并支持int
,char
,float
,void
以及function
类型。 -
init_types
初始化了这些基本类型的全局单例,避免了重复创建。 -
create_function_type
用于动态创建函数类型,因为每个函数的参数和返回类型都可能不同。 -
is_assignable
和get_common_type
实现了简化的类型兼容性检查和类型提升规则。在实际编译器中,这些规则会复杂得多,需要考虑隐式转换、算术转换规则等。
-
-
AST遍历与语义检查:
-
语义分析器通过递归遍历AST来执行检查。每个
analyze_*
函数都对应AST中的一个节点类型。 -
变量声明 (
analyze_var_declaration
): 获取变量类型和名称,添加到符号表。如果重复定义,则报告错误。检查初始化表达式的类型是否与变量类型兼容。 -
标识符引用 (
AST_IDENTIFIER_EXPR
在analyze_expression
中处理): 在符号表中查找标识符。如果未声明,则报告错误。如果找到,则将符号表条目关联到AST节点,并设置表达式的resolved_type
。 -
表达式 (
analyze_expression
): 递归分析子表达式以获取它们的类型。然后根据操作符的类型,检查操作数类型是否兼容,并推导出整个表达式的结果类型。例如,int + float
的结果是float
。 -
函数定义 (
analyze_function_definition
): 在全局作用域添加函数符号。然后进入新的函数作用域,将参数添加到符号表。设置current_function_return_type
,用于检查return
语句。最后分析函数体。 -
函数调用 (
analyze_call_expression
): 查找被调函数符号,检查其是否确实是函数。检查参数数量是否匹配,并逐个检查参数类型是否兼容。 -
return
语句 (AST_RETURN_STMT
在analyze_statement
中处理): 检查返回表达式的类型是否与当前函数的返回类型兼容。
-
-
错误报告: 我们的
semantic_error
函数会记录错误数量,并打印详细的错误信息(包括行号、列号和相关词素),但不会立即终止程序。这使得编译器可以发现代码中的所有语义错误,而不是在第一个错误处就停止。 -
挑战与未来扩展:
-
更复杂的类型: 目前只支持基本类型和函数类型。C语言还有指针、数组、结构体、联合体、枚举、
typedef
等复杂类型。实现这些需要更精细的Type
结构体和类型系统逻辑。 -
左值/右值: 赋值操作符和自增/自减操作符要求其操作数是左值(L-value)。我们的实现中只做了简单的检查,未来需要更严格的左值判断。
-
常量表达式求值: 编译器在编译期可以对常量表达式进行求值,这可以优化代码。
-
控制流分析: 检查是否所有路径都有
return
语句(对于非void
函数),检查不可达代码等。 -
类型转换: C语言的隐式类型转换规则非常复杂,需要细致地实现。
-
错误恢复: 尽管我们记录了错误数量,但程序在发现错误后仍然会继续分析。更高级的错误恢复机制可以尝试修复AST,以便后续阶段能够继续处理。
-
总结
目前,咱们已经完成了手撸编译器的第三步——一个功能强大的C语言语义分析器!你亲手为AST“骨架”注入了“灵魂”,让它能够理解代码的真正含义,并识别出那些隐藏的逻辑错误。符号表和类型系统,这两个看似抽象的概念,现在已经活生生地呈现在你的代码中了!这种从“语法正确”到“语义正确”的飞跃,是不是让你对编译原理有了更深层次的理解?
现在,我们的AST已经包含了丰富的语义信息,它不仅仅是一个语法结构,更是一个语义上完整且正确的程序表示。有了这些信息,我们就可以开始将高级C语言代码,一步步地转化为更接近机器的指令了!
这只是我们“肝爆”之旅的第三站。在下一章中,我们将进入编译器的第四个核心阶段:中间代码生成(Intermediate Code Generation)。我们将把AST转换成一种更简单、更通用的中间表示形式,为最终生成机器码做好准备!
这里 我也斗胆推荐几个教材:
-
必读教材:
-
《Compilers: Principles, Techniques, and Tools》(龙书) - 解析算法
-
《Advanced Compiler Design and Implementation》 - 语义优化
-
《Types and Programming Languages》 - 类型系统理论
-
-
工业级参考:
--------------------------------------------------------------------------------------------------------------------------------------------更新于2025.6.21晚 8:37分
因为csdn技术的不知道什么规则限制,不允许我在一个编辑器里放超过20w的内容,456的章节内容放在下一篇,让我们不见不散,点赞、收藏、关注走起来!