表达式是C编译器里最重要的一部份,由于表达式的使用是无所不在,任何的计算都需要使用到表达式运算。这次就带你去分析一下LCC编译器处理表达式的代码。
比如在例子里:
int nTest1 = 1;
int nTest2 = 2;
赋值语句的右边是一个表达式,这个表达式可以简单的也可以复杂的。像下面语句的右边也是表达式:
nTest3 = nTest1 + nTest2;
还有很多地方都需要使用到表达式的,在LCC里处理表达式的方法是递归下降的分析方法,比如分析语句(
nTest1 = 1
)右边的表达式产生下面的调用关系:
#001 expr1
#002 expr2
#003
expr3
#004
unary
#005
primary
它是使用递归的调用关系来分析优先级,最低优先级在expr里,接着高一点的在expr1里,依次类推。primary函数是处理最基本的运算单元,比如常量字符串,常量等。由于在赋值语句右边不可能出现逗号表达式,所以直接就调用expr1来处理右边的表达式。下面就来分析基本表达式primary,它的代码如下:
#001 static Tree primary(void)
#002 {
#003
Tree p;
#004
#005
assert(t != '(');
#006
switch (t)
#007
{
#008
case ICON:
#009
case FCON:
#010
p = tree(mkop(CNST,tsym->type), tsym->type, NULL, NULL);
#011
p->u.v = tsym->u.c.v;
#012
break;
第8行是识别整型常量,比如像1,100,200,0xF3等等。
第9行是识别浮点数常量,比如像1.0f, 5.5等等。
第10行是创建一个树节点来表示这个常量。其实,这就是LCC的中间表示。
#013
case SCON:
#014
if (ischar(tsym->type->type))
#015
tsym->u.c.v.p = stringn(tsym->u.c.v.p, tsym->type->size);
#016
else
#017
tsym->u.c.v.p = memcpy(allocate((tsym->type->size/widechar->size)*sizeof (int), PERM),
#018
tsym->u.c.v.p, (tsym->type->size/widechar->size)*sizeof (int));
#019
#020
tsym = constant(tsym->type, tsym->u.c.v);
#021
if (tsym->u.c.loc == NULL)
#022
tsym->u.c.loc = genident(STATIC, tsym->type, GLOBAL);
#023
p = idtree(tsym->u.c.loc); break;
第13行是识别处理字符串常量,比如像“abc”。
第15行是处理一般字符串。
第16行是处理宽字符串。
第20行是生成字符串常量符号。然后在第23行里创建一个ID树节点来表示这个字符串。
#024
case ID:
#025
if (tsym == NULL)
#026
{
#027
Symbol p = install(token, &identifiers, level, PERM);
#028
p->src = src;
#029
if (getchr() == '(')
#030
{
#031
Symbol q = lookup(token, externals);
#032
p->type = func(inttype, NULL, 1);
#033
p->sclass = EXTERN;
#034
#035
if (Aflag >= 1)
#036
warning("missing prototype/n");
#037
if (q && !eqtype(q->type, p->type, 1))
#038
warning("implicit declaration of `%s' does not match previous declaration at
#039 %w/n", q->name, &q->src);
#040
#041
if (q == NULL)
#042
{
#043
q = install(p->name, &externals, GLOBAL, PERM);
#044
q->type = p->type;
#045
q->sclass = EXTERN;
#046
q->src = src;
#047
(*IR->defsymbol)(q);
#048
}
#049
p->u.alias = q;
#050
}
#051
else
#052
{
#053
error("undeclared identifier `%s'/n", p->name);
#054
p->sclass = AUTO;
#055
p->type = inttype;
#056
if (p->scope == GLOBAL)
#057
(*IR->defsymbol)(p);
#058
else
#059
addlocal(p);
#060
}
#061
t = gettok();
#062
if (xref)
#063
use(p, src);
#064
return idtree(p);
#065
}
上面这段代码是处理表达式中的ID没有符号表的情况,目前先把这段代码放下,以后再分析。
#066
#067
if (xref)
#068
use(tsym, src);
#069
if (tsym->sclass == ENUM)
#070
p = consttree(tsym->u.value, inttype);
#071
else
#072
{
#073
if (tsym->sclass == TYPEDEF)
#074
error("illegal use of type name `%s'/n", tsym->name);
#075
p = idtree(tsym);
#076
}
#077
break;
像上面的表达式(nTest3 = nTest1 + nTest2;),其中nTest3的识别就是在第75行里生成ID树节点。第69行是处理枚举的类型,生成常量树节点。
#078
case FIRSTARG:
#079
if (level > PARAM && cfunc && cfunc->u.f.callee[0])
#080
p = idtree(cfunc->u.f.callee[0]);
#081
else {
#082
error("illegal use of `%k'/n", FIRSTARG);
#083
p = cnsttree(inttype, 0L);
#084
}
#085
break;
上面处理__firstarg参数。
#086
default:
#087
error("illegal expression/n");
#088
p = cnsttree(inttype, 0L);
#089
}
#090
t = gettok();
#091
return p;
#092 }
第
87
行处理不能识别的基本表达式,提示出错,并创建
0
常量的树节点。
通过上面的函数分析可知,基本表达式的就是常量和变量
ID
,不管多么复杂的表达式都是有这两种类型构成的。也可以看到
C
的基本表达式的语法如下:
primary-expression:
identifier
constant
string-literal
( expression )
expression:
assignment-expression
expression , assignment-expression