表达式语义分析与转换的深度剖析
立即解锁
发布时间: 2025-08-20 00:35:29 阅读量: 1 订阅数: 2 

### 表达式语义分析与转换的深度剖析
#### 1. 表达式语义概述
表达式在编程中不仅要满足语法规则,还要符合语义规则。语义分析主要解决三个难度相近的子问题:隐式转换、类型检查和求值顺序。
- **隐式转换**:源程序中未显式出现,但编译器为遵循标准语义规则而添加的转换。例如,在 `a + b` 中,若 `a` 为 `int` 类型,`b` 为 `float` 类型,编译器会隐式将 `a` 的值转换为 `float` 类型。
- **类型检查**:确认运算符操作数的类型是否合法,确定结果类型,并计算所需的特定类型运算符。例如,对 `a + b` 进行类型检查时,会验证 `a` 和 `b` 的类型是否为合法的算术类型组合,根据它们的类型确定结果类型,并确定所需的加法类型。
- **求值顺序**:编译器生成的树必须遵循标准的求值顺序规则。许多运算符的求值顺序未明确规定,如 `a [ i ++ ] = i` 中,`i` 的递增操作在赋值前后的顺序未指定;但也有部分运算符的求值顺序是明确的,如 `f() && g()` 中,必须先调用 `f`,若 `f` 返回 0,则不调用 `g`。
#### 2. 转换函数
转换函数主要有两种类型:一种接受一个或多个类型并返回结果类型,另一种接受树和可能的类型并返回经过适当转换的树。
##### 2.1 积分提升函数 `promote`
`promote(Type ty)` 实现积分提升,将积分类型 `ty` 扩展为 `int`、`unsigned` 或 `long`。积分提升会保留值和符号,但不保留无符号性。例如,`unsigned char` 会被提升为 `int`,而不是 `unsigned int`。
```c
// 假设的 promote 函数示例
Type promote(Type ty) {
// 实现积分提升逻辑
if (ty 为小积分类型且 int 能表示其所有值) {
return inttype;
} else {
return unsignedtype;
}
}
```
##### 2.2 常用算术转换函数 `binary`
`binary` 函数接受两个算术类型,并返回任何二元算术运算符的结果类型。
```c
Type binary(xty, yty) Type xty, yty; {
if (isdouble(xty) || isdouble(yty)) {
return doubletype;
}
if (xty == floattype || yty == floattype) {
return floattype;
}
if (isunsigned(xty) || isunsigned(yty)) {
return unsignedtype;
}
return inttype;
}
```
##### 2.3 指针转换函数 `pointer`
`pointer` 函数接受一个树并返回可能经过转换的树。数组和函数类型在表达式中会衰减为指针。
```c
Tree pointer(p) Tree p; {
if (isarray(p->type)) {
p = retype(p, atop(p->type));
} else if (isfunc(p->type)) {
p = retype(p, ptr(p->type));
}
return p;
}
```
#### 3. 一元和后缀运算符
每个运算符的代码都实现了标准规定的约束和语义。例如,一元 `-` 运算符要求操作数具有算术类型,结果是操作数的负值,并对操作数进行积分提升。
```c
// 一元 - 运算符代码示例
p = pointer(p);
if (isarith(p->type)) {
p = cast(p, promote(p->type));
if (isunsigned(p->type)) {
warning("unsigned operand of unary -\n");
p = simplify(NEG, inttype, cast(p, inttype), NULL);
p = cast(p, unsignedtype);
} else {
p = simplify(NEG, p->type, p, NULL);
}
} else {
typeerror(SUB, p, NULL);
}
```
一元 `&` 运算符要求操作数是函数指示符或左值,且该对象不是位域,也未使用 `register` 存储类说明符声明。一元 `*` 运算符是一元 `&` 的逆运算,将类型为 `(POINTER T)` 的操作数包装在 `INDIR` 树中,以表示类型为 `T` 的右值。
类型转换指定显式转换,部分转换(如指针到指针的转换)不生成代码,仅指定表达式的类型;其他转换(如 `int` 到 `float` 的转换)会在运行时生成实现转换的代码。
#### 4. 函数调用
函数调用的解析相对容易,但分析较为复杂。语义分析需要处理对新式和旧式函数的调用,包括转换、参数检查、参数求值顺序、按值传递和返回结构等问题。
```c
// call 函数示例
Tree call(f, fty, src) Tree f; Type fty; Coordinate src; {
int n = 0;
Tree args = NULL, r = NULL;
Type *proto, rty = unqual(freturn(fty));
Symbol t3 = NULL;
if (fty->u.f.oldstyle) {
proto = NULL;
} else {
proto = fty->u.f.proto;
}
if (hascall(f)) {
r = f;
}
if (isstruct(rty)) {
// 初始化结构体函数相关操作
}
// 解析参数
if (t != ')') {
for (; ;) {
// 解析一个参数
Tree q = pointer(exprl(0));
if ((still in a new-style prototype?)) {
// 新式参数处理
} else {
// 旧式参数处理
}
if (!IR->wants_argb && isstruct(q->type)) {
// 直接传递结构体
}
if (q->type->size == 0) {
q->type = inttype;
}
if (hascall(q)) {
r = r ? tree(RIGHT, voidtype, r, q) : q;
}
args = tree(ARG + widen(q->type), q->type, q, args);
n++;
if (Aflag >= 2 && n == 32) {
warning("more than 31 arguments in a call to %s\n", funcname(f));
}
if (t != ',') {
break;
}
t = gettok();
}
}
expect(')');
if ((still in a new-style prototype?)) {
error("insufficient number of arguments to %s\n", funcname(f));
}
if (r) {
args = tree(RIGHT, voidtype, r, args);
}
if (events.calls) {
// 插入事件钩子
}
return calltree(f, rty, args, t3);
}
```
#### 5. 二元运算符
二元运算符的语义函数接受通用运算符和两个操作数的树,并返回二元表达式的树。不同组的运算符具有相似的语义,通过 `optree` 进行间接调用。
```c
// 加法运算符示例
static Tree addtree(op, l, r) int op; Tree l, r; {
Type ty = inttype;
if (isarith(l->type) && isarith(r->type)) {
ty = binary(l->type, r->type);
l = cast(l, ty);
r = cast(r, ty);
} else if (isptr(l->type) && isint(r->type)) {
return addtree(ADD, r, l);
} else if (isptr(r->type) && isint(l->type) && !isfunc(r->type->type)) {
// 构建 ADD+P 树
int n;
ty = unqual(r->type);
n = ty->type->size;
if (n == 0) {
error("unknown size for type '%t'\n", ty->type);
}
l = cast(l, promote(l->type));
if (n > 1) {
l = multree(MUL, consttree(n, inttype), l);
}
return simplify(ADD+P, ty, l, r);
} else {
typeerror(op, l, r);
}
return simplify(op, ty, l, r);
}
```
#### 6. 赋值运算符
赋值表达式、函数参数、返回语句或初始化的合法性取决于将右值赋值给左值所表示位置的合法性。`assign` 函数用于执行赋值的类型检查,返回合法赋值的目标类型,否则返回 `NULL`。
```c
// assign
```
0
0
复制全文
相关推荐










