Bison生成文件分析

Bison功能很强大,可以加参数-v可以生成可阅读的.output文件,还可以生成dot转换图

本文以lex yacc 创建一个桌面计算器 为例子研究bison生成代码

所有介绍都以bison生成为准,通过-v生成*.output文件,通过设置#define YYDEBUG 1以及yydebug=1进行调制

文法

0 ACCEPT : OVER

1 OVER : E '\n'

2 E : E + T

3 E : T

4 T : T * F

5 T : F

6 F : ( E )

7 F : NUM

状态图

    NUM + * ( ) $ \n E F T OUT def
0 ACC->.OVER s1     s2       4 6 5 3  
1 F->NUM.                       r7
2 F->(.E) s1     s2       7 6 5    
3 ACC->OVER.           acc            
4 OUT->e.'\n'
E->E.+T
  s10         s9          
5 E->T.
T->T.*F
    s11                 r3
6 T->F.                       r5
7 E->E.+T
F->(E.)
  s10     s12              
8 ACC->OVER end.                       acc
9 OVER->E'\n'.                       r1
10 E->E+.T s1     s2         6 13    
11 T->T*.F s1     s2         14      
12 F->(E).                       r6
13 E->E+T.
T->T.*F
    s11                 r2
14 T->T*F.                       r4

3+2*5规约过程

  符号 输入 动作
0 0 $ 3+2*5\n$ shift1
1 0 1 $3 +2*5\n$ r7
2 0 6 $F +2*5\n$ r5
3 0 5 $T +2*5\n$ read->r3
4 0 4 $E +2*5\n$ shift10
5 0 4 10 $E+ 2*5\n$ shift1
6 0 4 10 1 $E+2 *5\n$ r7
7 0 4 10 6 $E+F *5\n$ r5
8 0 4 10 13 $E+T *5\n$ shift11
9 0 4 10 13 11 $E+T* 5\n$ shift1
10 0 4 10 13 11 1 $E+T*5 \n$ r7
11 0 4 10 13 11 14 $E+T*F \n$ r4
12 0 4 10 13 $E+T \n$ r2
13 0 4 $E \n$ shift9
14 0 4 9 $E\n $ r1
15 0 3 $OVER $ over

源代码分析

yyprhs[] {  }             产生式右端文法串在yyrhs中的开始位置

yyrhs[] { } 以-1隔开的产生式右端串

yyrline[] { 0, 8, 8, 9 }      .y文件中产生式定义所在的行号,调试信息用

yyname[] { {0:$end}, {1:error}, {2:$undefined}, {3:NUM}, {4:'\n'}, {5:+}, {6:*}, {7:(}, {8:)}, {9:acc}, {10:OUT}, {11:E}, {12:T}, {13:F}, '\0' }

yytoknum[] { 0, 256, 257, 258, 10, 43, 42, 40, 41 } 功能与yytranslate相同,token转化为标示符

yy1[] {}  产生式左端符号的编号,阅读Bison生成的.output文件开头即可明白

yyr2[] { }       产生式右端长度,阅读Bison生成的.output文件开头即可明白,规约的时候从栈中弹出多少个状态用到

yydefact[] { }    对于每个状态的缺省规约表达式,对应于状态图中def这一列+1

yydefgoto[] { }                 对于每个非终结符号,状态0情况下GOTO状态

yypact[] { }    如果某个状态只有默认动作,则为设置默认操作值,这儿是-6,  没有向前看token的情况下执行判断

yypgoto[] { }

yytable[] { }

yycheck[] { }

yystos[] { }

如果yypact值不为默认值,则yypact、yytoken、yytable、yycheck来确定状态转移,应该是用了某种压缩算法

规约以后状态转移根据yypgoto,判断采用yytable还是yydefgoto计算新的state



参考资源

以下是网上找到的资源,太不容易了

bison的实现中用到的表格:

yyrhs 和yyprhs一起表示产生式右端文法串,是一个用-1隔开的索引串大数组,串的内容是文法符号的编号 yyr1 产生式左端文法符号的编号 yyr2 产生式右端长度 yyrline 产生式的定义行 yyprhs 和yyrhs一起表示产生式右端文法串,内容为右端在yyrhs中的起始位置 yytname 文法符号的名称,必须用YYDEBUG条件编译 yytranslate 把flex返回的token编号翻译成bison的文法编号 yytoknum flex返回的token编号翻译成bison的token编号 yytable DFA状态转移表 yycheck 和yycheck等长的数组 yypgoto 非终结符号上的goto下个状态 yypact Index in YYTABLE of the portion describing STATE-NUM. yydefact 缺省动作,长度为DFA的状态个数 yydefgoto 缺省goto,长度为非终结符号

实 际上BISON就是给我们造表,至于怎么用这个表是我们的事,这些好比是个瓤子,在这个瓤子外头套个毛衣,一个翻译程序就出来了。当然一般来说都是直接用 嵌在BISON里头的毛衣,这样就得到一个LALR(1)分析程序,BISON程序多半支持一个``-S''参数可以用来切换毛衣。

想 了想,明白为什么昨天搞的那个parse tree那么庞大了,因为那个语法是从c99手册扒出来的一点都没有改造过。c99为了严谨用的是优先级级联的无二义性的文法(所以c99里头用不着说明 什么优先级了),这个文法的毛病就是实现太不方便,太多的单一产生式(右端只有一个文法符号的产生式),所以parse tree打印出来就成了一个个很大的锯齿。反观gcc的语法文件就很不一样,引入优先级说明之后,文法变的很简单,所以分析树也得到了简化。

http://www.docin.com/p-483435351.html

介绍了gcc语法分析

yyprhs[] { 0, 0, 3, 7 }               产生式右端文法串在yyrhs中的开始位置,表示从第3、7项开始

yyrhs[] { 6, 0, -1, 6, 4, 3, -1, 3, -1 } 以-1隔开的产生式右端串

yydefact[] { 0, 3, 0, 1, 0, 2 }    对于每个状态的缺省规约表达式,0表示默认出错

yydefgoto[] { -1, 2 }                 对于每个非终结符号的缺省GOTO

yypact[] { -2, -3, 0, -3, -1, -3 }    -3:默认  没有向前看token的情况下执行判断

yypgoto[] { -3, -3 }

yytable[] { 3, 1, 5, 0, 4 }

yycheck[] { 0, 3, 3, -1, 4 }

yystos[] { 0, 3, 6, 0, 4, 3 }

你可能感兴趣的:(编译器编译原理)