深入解析DoctorWkt/acwj项目中的全局变量初始化实现

深入解析DoctorWkt/acwj项目中的全局变量初始化实现

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

前言

在编译器开发领域,变量初始化是一个基础但至关重要的功能。本文将深入探讨DoctorWkt/acwj项目中全局变量初始化的实现细节,帮助读者理解编译器如何处理不同类型的全局变量初始化。

全局变量初始化概述

在C语言中,全局变量可以有多种初始化形式,包括:

int x = 2;                      // 标量初始化
char *str = "Hello world";      // 字符串指针初始化
int a[10];                      // 数组声明
char b[] = {'q','w','e','r'};   // 数组初始化

编译器需要能够正确解析这些语法,并生成相应的汇编代码来初始化这些全局变量。

标量变量初始化实现

字面量解析

编译器首先需要能够解析不同类型的字面量值。项目中实现了一个关键函数parse_literal()

int parse_literal(int type) {
  // 处理字符串字面量
  if ((type == pointer_to(P_CHAR)) && (Token.token == T_STRLIT))
    return(genglobstr(Text));

  // 处理整数字面量
  if (Token.token == T_INTLIT) {
    switch(type) {
      case P_CHAR: 
        if (Token.intvalue < 0 || Token.intvalue > 255)
          fatal("字符类型字面值超出范围");
      case P_INT:
      case P_LONG: break;
      default: fatal("类型不匹配:整数字面值与变量");
    }
  } else
    fatal("期望整数字面值");
  return(Token.intvalue);
}

这个函数会根据变量类型验证字面量的有效性,并返回相应的值或字符串标签。

符号表结构变更

为了支持初始化功能,项目的符号表结构进行了重要修改:

struct symtable {
  ...
  int size;        // 符号总大小(字节)
  int nelems;      // 函数:参数数量;数组:元素数量
  int *initlist;   // 初始化值列表
  ...
};

这些字段使得编译器能够跟踪变量的尺寸、元素数量以及初始值。

数组初始化实现

数组初始化比标量更复杂,需要处理三种情况:

  1. 固定大小数组(可能部分初始化)
  2. 未指定大小的数组(根据初始化列表确定大小)
  3. 混合情况

数组声明解析

数组声明的解析逻辑如下:

static struct symtable *array_declaration(...) {
  int nelems = -1;  // 初始设为-1表示未指定大小
  
  // 解析数组大小(如果有)
  if (Token.token == T_INTLIT) {
    if (Token.intvalue <= 0)
      fatald("非法数组大小", Token.intvalue);
    nelems = Token.intvalue;
    scan(&Token);
  }
  
  // 处理初始化列表
  if (Token.token == T_ASSIGN) {
    match(T_LBRACE, "{");
    
    // 动态调整初始化列表大小
    if (nelems != -1)
      maxelems = nelems;
    else
      maxelems = TABLE_INCREMENT;
    initlist = (int *)malloc(maxelems * sizeof(int));
    
    // 解析初始化值
    while (1) {
      if (nelems != -1 && i == maxelems)
        fatal("初始化列表值过多");
      initlist[i++] = parse_literal(type);
      
      // 动态扩展列表
      if (nelems == -1 && i == maxelems) {
        maxelems += TABLE_INCREMENT;
        initlist = (int *)realloc(initlist, maxelems * sizeof(int));
      }
      
      if (Token.token == T_RBRACE) break;
      comma();
    }
    
    // 处理未初始化的元素
    for (j=i; j < sym->nelems; j++) initlist[j]=0;
    sym->initlist = initlist;
  }
  
  // 设置数组最终大小
  sym->nelems = nelems;
  sym->size = sym->nelems * typesize(type, ctype);
}

汇编代码生成

初始化后的全局变量需要生成相应的汇编代码。关键函数cgglobsym()负责此功能:

void cgglobsym(struct symtable *node) {
  // 确定元素大小和类型
  if (node->stype == S_ARRAY) {
    size = typesize(value_at(node->type), node->ctype);
    type = value_at(node->type);
  } else {
    size = node->size;
    type = node->type;
  }
  
  // 为每个元素生成初始化代码
  for (i=0; i < node->nelems; i++) {
    initvalue = (node->initlist != NULL) ? node->initlist[i] : 0;
    
    switch (size) {
      case 1: fprintf(Outfile, "\t.byte\t%d\n", initvalue); break;
      case 4: fprintf(Outfile, "\t.long\t%d\n", initvalue); break;
      case 8: 
        if (type == pointer_to(P_CHAR))
          fprintf(Outfile, "\t.quad\tL%d\n", initvalue);
        else
          fprintf(Outfile, "\t.quad\t%d\n", initvalue);
        break;
      default: // 处理大尺寸类型
    }
  }
}

实际应用示例

以下是一些初始化示例及其生成的汇编代码:

  1. 标量初始化:
int x = 5;

生成:

.globl x
x:
    .long 5
  1. 字符串指针初始化:
char *y = "Hello";

生成:

L1:
    .byte 72
    .byte 101
    .byte 108
    .byte 108
    .byte 111
    .byte 0
.globl y
y:
    .quad L1
  1. 数组初始化:
int arr[4] = {1, 4, 17};

生成:

.globl arr
arr:
    .long 1
    .long 4
    .long 17
    .long 0

总结与展望

本文详细分析了DoctorWkt/acwj项目中全局变量初始化的实现机制。通过修改符号表结构、完善字面量解析逻辑以及增强数组处理能力,编译器现在能够正确处理各种全局变量初始化场景。

这种实现不仅考虑了语法正确性,还关注了内存布局和汇编代码生成的效率。动态调整初始化列表大小的设计特别值得注意,它优雅地处理了未指定大小数组的初始化问题。

在后续开发中,可以进一步扩展此功能以支持更复杂的初始化场景,如结构体和联合体的初始化,以及静态局部变量的初始化处理。这些扩展将基于本文介绍的基础架构,进一步丰富编译器的功能集。

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

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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

万宁谨Magnus

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

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

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

打赏作者

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

抵扣说明:

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

余额充值