C编译器剖析_1.2_分析器的构造方法

来源:互联网 发布:21天学通java哪一版好 编辑:程序博客网 时间:2024/05/14 05:25

1.2 分析器的构造方法

在1.1节中,我们讨论了语言、集合、文法和递归这几者之间的关系,在1.1节的末尾

给出了图1-2。为了阅读方便,这里把该图再列出如下

         Program  ----->  CompoundStatement

         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 ;

         Expression   ----->  AdditiveExpression

         AdditiveExpression  ----->  MultiplicativeExpression

         AdditiveExpression   ----->  AdditiveExpression + MultiplicativeExpression

         AdditiveExpression   ----->  AdditiveExpression - MultiplicativeExpression

         MultiplicativeExpression  ----->  PrimaryExpression

         MultiplicativeExpression  ----->  MultiplicativeExpression * PrimaryExpression

         MultiplicativeExpression  ----->  MultiplicativeExpression / PrimaryExpression

         PrimaryExpression  ----->  id | num | (Expression)

         Declaration   ----->    int Declarator

         Declarator   ----->    * Declarator | PostfixDeclarator 

         PostfixDeclarator ---> DirectDeclarator | PostfixDeclarator [num]| PostfixDeclarator (void)

         DirectDeclarator  ----->  id  |  (Declarator)

                                                        图1-2       文法Program

        这个看似复杂的文法,其实很多产生式与1.1节的产生式(1-2)及产生式(1-3)本质上是相同的。

                                    T  ----->  a | a + T                                                                                                 (1-2)

            T  ----->  a | T +a                                                                                                 (1-3)

         例如,以下几个从图1-2取出的产生式,如果把AdditveExpression视为T,把MultiplicativeExpression视为a后,就和产生式(1-3)是一样的。如前文所述,产生式(1-3)是一个左递归的产生式。虽然由(1-2)和(1-3)生成的语言是等同的,但是左递归的产生式隐含了其运算符是左结合的,而右递归的产生式隐含了其运算符是右结合的。按习惯,加减乘除这四则运算符都是左结合的。所以我们选取形如(1-3)的产生式来表达加法运算。与T所表达的集合类似,AdditiveExpression由若干个MultiplicativeExpression相加构成。换言之,要进行加法运算,需要先得到MultiplicativeExpression。同理,MultiplicativeExpression由若干个PrimaryExpression相乘构成,这意味着,要进行乘法运算,得先得到PrimaryExpression。而PrimaryExpression则由标志符id、数num和加括号的表达式构成。按这样的层次结构,以下产生式实际上隐含了加减乘除运算符之间的优先级。加减视为同一优先级,按结合性进行从左到右的结合;乘除视为同一优先级。用与此类似的方法,我们还可以引入更多的运算符,如&和|等。

         AdditiveExpression  ----->  MultiplicativeExpression

         AdditiveExpression   ----->  AdditiveExpression + MultiplicativeExpression

         MultiplicativeExpression  ----->  PrimaryExpression

         MultiplicativeExpression  ----->  MultiplicativeExpression * PrimaryExpression

         PrimaryExpression  ----->  id | num | (Expression)

         让我们再来分析图1-2中与语句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 

         Statement告诉我们语句由if语句、while语句、复合语句及表达式语句构成。而if语句则以if开头,后面紧跟左括号,然后是表达式expression,接着是右括号,之后又是一个语句Statement。这里,我们再次在产生式看到递归定义。而StatementList对应的产生式实际上形如前面的产生式(1-3)。StatementListopt表示是在复合语句CompoundStatement 中StatementList是Optional的,即可有可无。

         至于声明Declaration,与之相关的产生式有这么几条:

         Declaration   ----->    int Declarator

         Declarator   ----->    * Declarator | PostfixDeclarator 

         PostfixDeclarator ---> DirectDeclarator | PostfixDeclarator [num]| PostfixDeclarator (void)

         DirectDeclarator  ----->  id  |  (Declarator)

         PostfixDeclarator再次与产生式(1-3)神似。其产生式实际上告诉我们PostfixDeclarator由DirectDeclarator 后面跟上任意多个[num]或者(void)构成。而Declarator对应的产生式实际上表达了若干个*后面再跟一个PostfixDeclarator. 通过[num]的后缀,我们实际上声明了一个大小为num的数组,而通过(void)的后缀,我们实际上声明了一个无参的函数。通过以上分析,而在声明中使用*,实际上是声明了一个指针。我们再回头看图1-2,这个世界应突然变得清晰了一些。

         例如,对于字符串int aa[30][50] , 我们可以按前文画分析树的方法,由Declaration出发作推导,最终生成int a[30][50]。其中30,50皆为数num,而a为标志符id。还可以生成int (*cc)[3][5]。其中,aa是数组,而cc是指向数组的指针。仔细观察,我们发现上述文法中并没有关于数num是如何构成的产生式。因为这些实际相当于句子中的单词,我们习惯上对num和id的识别列入“词法分析”范畴。在分析器眼中,只有一个个单词,至于如果由基本的英文字母和阿拉伯数字组成单词,我们只在词法分析中进行处理。

         至此,我们慢慢进入了我们的主题“C编译器剖析”。图1-2实际上是一个微缩版的C语言文法。其中有常见的语句、表达式和声明。当然,为了简单,我们只引入了一个基本类型int,个别细节与标准C语言的文法也不尽相同。但管中窥豹,我们已可见一斑。如果理解了上述文法,由文法进行分析器的构造则是件“依样画葫芦”的事情。基本的原则很简单:

     (1) 对于文法中的非终结符,因为其代表的是一个子集,我们要识别判断某个字符串是否属于这个子集,则会构造一个与非终结符同名的函数来处理。

     (2) 对于文法中的终结符,例如上述的id、num、+、-、*、/、(和)等,我们则直接进行终结符的匹配。


     分析器中各函数的编写,稍后我们会举例进行分析。


0 0
原创粉丝点击