深入解析DoctorWkt/acwj项目:编译器开发中的空函数参数与词法分析改进
acwj A Compiler Writing Journey 项目地址: https://gitcode.com/gh_mirrors/ac/acwj
前言
在编译器开发过程中,处理函数参数和词法分析是构建可靠编译器的关键环节。本文将详细探讨如何实现void函数参数的支持,以及如何改进词法分析器以处理十六进制和八进制常量。
空函数参数的处理
问题背景
在C语言中,函数参数列表有两种方式表示无参数:
int func(void); // 明确使用void表示无参数
int func(); // 空括号也表示无参数
虽然第二种语法已经能够表示无参数,但第一种形式在C语言中更为常见和明确,因此编译器需要支持这种语法。
技术挑战
当编译器遇到左括号时,通常会进入declaration_list()
函数处理参数声明。但这个函数设计时假设每个类型后面都会跟着一个标识符,难以处理只有类型(void)而没有标识符的情况。
解决方案
我们采用"前瞻"(peek)技术来解决这个问题:
- 遇到左括号后,检查下一个token是否为void
- 如果是void,再前瞻检查再下一个token
- 如果是右括号,则表示无参数
- 否则,正常处理参数列表
这种技术避免了直接消费token导致的状态混乱,保持了词法分析的灵活性。
词法分析器的改进
前瞻机制实现
为了实现token前瞻功能,我们在词法分析器中增加了以下结构:
struct token Token; // 当前token
struct token Peektoken; // 前瞻token
扫描函数scan()
被修改为首先检查Peektoken,如果存在则返回Peektoken,否则才从输入中读取新token。
参数列表处理逻辑
参数列表处理函数param_declaration_list()
的核心逻辑变为:
while (未遇到右括号) {
if (当前token是void) {
扫描Peektoken;
if (Peektoken是右括号) {
设置参数计数为0;
跳过void token;
退出循环;
}
}
// 否则正常处理参数声明
}
这种处理方式优雅地解决了void参数列表的歧义问题。
数值常量解析增强
十六进制和八进制支持
原始编译器只支持十进制整数常量,我们扩展了scanint()
函数以支持:
- 以0x开头的十六进制数
- 以0开头的八进制数
实现原理是:
- 检测前导0
- 如果是0x则设置基数为16
- 如果是单独0则设置基数为8
- 使用相应基数进行数值转换
字符常量增强
我们还改进了字符常量的解析,支持:
- 八进制转义序列(如'\0')
- 十六进制转义序列(如'\x41')
关键函数scanch()
和新增的hexchar()
协同工作,精确解析各种形式的字符常量。
技术实现细节
基数转换算法
数值转换的核心算法保持简洁高效:
val = val * radix + digit_value;
这个通用公式适用于任何基数(10、8或16)的转换。
错误处理
增强的错误检查包括:
- 数字有效性验证(确保数字符合当前基数)
- 十六进制数字缺失检测
- 数值范围检查(特别是字符常量)
总结与展望
本次改进主要集中在词法分析器方面,虽然改动不大,但为编译器的自举(self-compiling)能力奠定了基础。这些看似微小的功能实际上是构建完整编译器不可或缺的部分。
未来工作可能包括:
- 实现sizeof运算符
- 支持static函数和变量
- 进一步的代码重构和优化
通过逐步解决这些技术挑战,我们的编译器正朝着功能完整性和健壮性稳步前进。
acwj A Compiler Writing Journey 项目地址: https://gitcode.com/gh_mirrors/ac/acwj
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考