理解了变量和函数的声明,及如何处理四则运算表达式后,接下来我们想在表达式的基础上去实现控制流语句,即常见的if语句和while语句。与语句statement相关的产生式如下所示:
Statement --> IfStatement | WhileStatement|CompoundStatement|ExpressionStatement
IfStatement -----> if ( Expression) Statement
IfStatement -----> if ( Expression) Statement else Statement
WhileStatement -----> while(Expression) Statement
CompoundStatement -----> { StatementListopt}
StatementList -----> Statement | StatementList Statement
ExpressionStatement -----> id = Expression ;
ExpressionStatement -----> Declaration ;
根据上述文法,图1.12是我们为ExpressionStatement所编写的处理函数,之前我们已经实现了非终结符表达式Expression和声明Declaration对应的处理函数。依照C语言标准,此处我们把赋值操作”id =Expression”视为赋值表达式,赋值表达式之后再跟上分号,则构成了表达式语句ExpressionStatement,图中第60至71行实现了对赋值表达式的识别。为简单起见,我们把” Declaration ;”也视为表达式语句,图中第72至77行完成了这部分功能。第72行的IS_PREFIX_OF_DECL()实际用到的是我们前文介绍的首符集的概念,只有当前token属于非终结符Declaration的首符集时,我们才去识别” Declaration ;”。这里可能存在问题是,如果TK_ID也属于Declaration的首符集时,该如何处理。就跟到陌生地方遇到叉路口一样,如果向左向右傻傻分不清楚时,不妨先右转,发现此路不通时,再退回到叉路口左转。小平同志的“摸着石头过河”也是这个道理。在后续章节分析UCC源代码时,我们会在”ucc\ucl\lex.c”看到BeginPeekToken(void)和EndPeekToken(void)这一对函数,正是用来做尝试和回溯之用的。当然也可以对文法进行改造,就如龙书中“提取左公因子”一节所做的那样。
图1.12 ExpressionStatement()
在图1.12的第61行我们还看到了CreateStmtNode()和CreateAstNode()这两个不同的函数调用,从函数名中我们可以发现两者都是为了创建一个结点Node。CreateStmtNode()创建的是astStmtNode对象,而CreateAstNode(