深入解析DoctorWkt/acwj项目:编译器开发中的运算符扩展与优化

深入解析DoctorWkt/acwj项目:编译器开发中的运算符扩展与优化

acwj A Compiler Writing Journey acwj 项目地址: https://gitcode.com/gh_mirrors/ac/acwj

前言

在编译器开发过程中,逐步扩展语言特性并优化现有功能是一个持续演进的过程。本文将详细分析DoctorWkt/acwj项目中关于运算符扩展的实现细节,包括问题发现、解决方案设计以及具体实现方法。

问题背景

当尝试使用编译器编译自身源代码时,开发者遇到了几个关键挑战:

  1. 编译器需要能够正确解析和识别自己的源代码
  2. 编译器需要能够从源代码生成正确的工作代码

这一过程暴露了编译器实现中的多个问题,包括:

  • 指针未初始化为NULL导致的段错误
  • 扫描器无法处理以负号开头的整数字面量
  • 结构体中联合体(union)的解析问题
  • 全局变量声明中表达式的解析缺失
  • 常用复合赋值运算符(如+=、-=等)的缺失

关键问题分析与解决

负数字面量解析问题

原始扫描器无法正确处理"-1"这样的负数字面量。解决方案是在扫描器中添加特殊处理:

case '-':
  if ((c = next()) == '-') {
    t->token = T_DEC;
  } else if (c == '>') {
    t->token = T_ARROW;
  } else if (isdigit(c)) {          // 负整数字面量
    t->intvalue = -scanint(c);
    t->token = T_INTLIT;
  } else {
    putback(c);
    t->token = T_MINUS;
  }

技术细节

  • 通过检查'-'后的字符是否为数字来识别负数字面量
  • 使用scanint()扫描整数值后取负
  • 注意处理空格情况,确保"1 - 1"不被误认为"1 -1"

结构体与联合体的优化

原始实现使用无名联合体节省内存,但编译器自身无法解析这种结构。解决方案改为使用#define实现字段共享:

struct ASTnode {
  int op;
  ...
#define a_intvalue a_size       // A_INTLIT使用a_size作为整数值存储
  int a_size;                   // A_SCALE使用此字段存储缩放大小
};

设计考量

  • 保持内存效率的同时提高可解析性
  • 使用不同前缀避免命名空间污染
  • 需要同步修改多个源文件中的字段引用

复合赋值运算符的实现

新增四个复合赋值运算符:+=、-=、*=和/=,需要完整实现从词法分析到代码生成的整个流程。

1. 词法分析扩展

添加新的token类型:

  • T_ASPLUS, T_ASMINUS, T_ASSTAR, T_ASSLASH

2. 语法分析与AST构建

关键点

  • 确保token与AST操作一一对应
  • 设置正确的运算符优先级
  • 处理右结合性
// 运算符优先级表更新
static int OpPrec[] = {
  0, 10, 10,                    // T_EOF, T_ASSIGN, T_ASPLUS,
  10, 10, 10,                   // T_ASMINUS, T_ASSTAR, T_ASSLASH,
  ...
};

// 右结合性判断
static int rightassoc(int tokentype) {
  if (tokentype >= T_ASSIGN && tokentype <= T_ASSLASH)
    return (1);
  return (0);
}

3. 代码生成策略

采用AST重写等效策略,将复合赋值转换为基本运算加赋值:

case A_ASPLUS:
  leftreg= cgadd(leftreg, rightreg);  // 执行加法
  n->right= n->left;                 // 准备赋值操作
  // 继续执行标准赋值逻辑
  break;

优势

  • 重用现有赋值代码生成逻辑
  • 保持生成的汇编代码效率
  • 实现简洁明了

测试验证

提供测试用例验证运算符正确性:

int main() {
  x= 3; y= 15; y += x; printf("%d\n", y);  // 18
  x= 3; y= 15; y -= x; printf("%d\n", y);  // 12
  x= 3; y= 15; y *= x; printf("%d\n", y);  // 45
  x= 3; y= 15; y /= x; printf("%d\n", y);  // 5
  return(0);
}

经验总结与展望

本次实现的关键收获:

  1. 运算符扩展需要全面考虑词法、语法和代码生成各环节
  2. AST操作设计应考虑代码重用可能性
  3. 右结合性对复合赋值运算符的正确解析至关重要

未来优化方向:

  • 实现常量折叠优化
  • 支持更多复合赋值运算符(如%=、&=等)
  • 改进负数字面量的扫描逻辑

通过这种渐进式的功能扩展和问题修复,编译器正逐步增强其能力和健壮性,为最终实现自举(self-hosting)奠定坚实基础。

acwj A Compiler Writing Journey acwj 项目地址: https://gitcode.com/gh_mirrors/ac/acwj

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

雷芯琴

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值