📄 fu.htm.bak
字号:
<table border=0>
<tr>
<td>
%{<br>
.<br>
.<br>
.<br>
extern int nfg;<br>
douhle dreg[ 26];<br>
INTERVAL Vreg[26];<br>
.<br>
.<br>
.<br>
%}<br>
</td>
</tr>
</table>
</center>
<br>
另外非整型函数的类型声明也包含在这部分中,请参看2.6例2。<br>
<br>
重申一遍,上述四部分括在 %{和%}之间的内容是由yacc原样照抄到y.tab.c中去,所以必须完全符合C语言文法,另外,界符%{和%}最好各自独占一行,即最好不写成<p align="center">%{ int x; %}</p>
<p>2.3.5 语法开始符定义<br>
<br>
上下文无关文法的开始符号是一个特殊的非终结符,所有的推导都从这个非终结符开始,在yacc中,语法开始符定义语句是:<br>
<br>
% start 非终结符……<br>
<br>
如果没有上面的说明,yacc自动将语法规则部分中第一条语法规则左部的非终结符作为语法开始符。<br>
<br>
2.3.6语义值类型定义<br>
<br>
yycc生成的语法分析程序yyparse用的是LR分析方法,它在作语法分析时除了有一个状态钱外,还有一个语义值钱,
存放它所分析到的非经结符和终结符的语义值,这些语义值有的是从词法分析程序传回的,有的是在语义动作中赋与的,
这些在介绍语义动作时再详细说明。如果没有对语义值的类型做定义,那么yacc认为它是整型(int)的,即所有语法符
号如果赋与了语义值,则必须是整型的,否则会出类型错,但是用户经常会希望语义值的类型比较复杂,如双精度浮点数,
字符串或树结点的指针.这时就可以用语义值类型定义进行说明。因为不同的语法符号的语义值类型可能不同,
所以语义值类型说明就是将语义值的类型定义为一个联合(Union),这个联合包括所有可能用到的类型(各自对应一个成员名),
为了使用户不必在存取语义值时每次都指出成员名,在语义值类型定义部分还要求用户说明每一个语法符号(终结符和非终结符)
的语义值是哪一个联合成员类型。下面举例说明并请参看2.6例2。<br>
<br>
<center>
<table border=0>
<tr>
<td>
% union{<br>
int ival<br>
double dval<br>
INTERVAL VVal;<br>
}<br>
%token <ival> DREG VREG<br>
%token <dval> CONST<br>
%type <dyal>dexp<br>
%type <vval>vexP<br>
...<br>
</td>
</tr>
</table>
</center>
<br>
在上述定义中,以%union开始的行定义了语义值的联合类型,共有三个成员类型分别取名为ival, dval, vval。<br>
<br>
以%token开始的行定义的是终结符(见2.3.7)所以DREG,VREG和CONST都是终结符,尖括号中的名字就是这些终结符的语义值的具体类型。如DREG和VREG这两个终结符的语义值将是整型(int)的,成员名是ival。<br>
<br>
以%tyPe开始的行是说明非终结符语义值的类型。如非终结符dexP的语义值将是双精度浮点类型,请注意,在yacc中非终结符不必特别声明,但是当说明部分有对语义值
类型的定义,而且某非终结符的语义值将被存取,就必须用上面的方法定义它的类型。<br>
<br>
2.3.7 终结符定义<br>
<br>
在yacc源程序语法规则部分出现的所有终结符(文字字符literal除外)必须在这部分定义,定义方法如下例:<br>
<br>
% token DIGIT LETTER<br>
<br>
每个终结符定义行以%token开头,注意%与token之间没有空格,一行中可以定义多个终结符,它们之间用空格分开,终结符名可以由字母,数字,下划线组成,但必须用字母于头。非终结符名的组成规则与此相同。终结符定义行可多于一个。<br>
<br>
yacc规定每个终结符都有一个唯一的编号(token number)。当我们用上面的方式定义经结符时,终结符的编号由yacc内部决定,其编号规则是从257开始依次递增,每次加1。但这个规则不适用于文字字符(literal)的终结符。例如在下面的语法规则中,’+’,’;'就是文字字符终结符:<br>
<br>
stats: stats';' stat;<br>
expr: expr'+’ expr;<br>
<br>
文字字符终结符在规则中出现时用单引号括起来。它们不需要用%token语句定义,yacc对它们的编号就采用该字符在其字符集(如ASCII)中的值。注意上面两条语法规则末尾的分号是yacc元语言的标点符号,不是文字字符终结符。<br>
<br>
yacc也允许用户自己定义终结符的编号。如果这样,那么终结符定义的格式就是:<br>
<br>
%token终结符名 整数<br>
<br>
其中“终结符名”就是要定义的终结符,“整数”就是该终结符的编号,每一个这样的行定义一个终结符。特别注意不同终结符的编号不能相同。例如<br>
<br>
<center>
<table border=0>
<tr>
<td>
%token BEGIN 100<br>
%token END 101<br>
%token IF 105<br>
%token THEN 200<br>
...<br>
</td>
</tr>
</table>
</center>
<br>
在3.6中我们说过如果用户定义了语义值的类型,那么那些具有有意义的语义值的终结符其语义值的类型要用Union中的成员名来说明,除了在3.6段中介绍的定义方法外,还可以把对终结符的定义和其语义值的类型说明分开,例如
:<br>
<br>
<center>
<table border=0>
<tr>
<td>
%token DREG VREG CONST<br>
%type <ival> DREG VREG<br>
%type <dval> CONST<br>
</td>
</tr>
</table>
</center>
<br>
2.3.8运算符优先级及结合性定义<br>
<br>
请看下面的关于表达式的文法:<br>
<br>
<center>
<table border=0>
<tr>
<td>
%token NAME<br>
expr: expr'+' expr<br>
|expr '–' expr<br>
|expr'*'expr<br>
|NAME<br>
;<br>
</td>
</tr>
</table>
</center>
<br>
这个文法有二义性,例如句子:a+b-c ,可以解释成(a+ b)一 c也可以解译成 a+(b- c),虽然这两种解释都合理但造成了二义性,如果将句子<br>
<br>
a+b*C<br>
<br>
解释为(a+b)*c就在语义上错了。<br>
<br>
yacc允许用户规定运算符的优先级和结合性,这样就可以消除上述文法的二义性。例如规定’+”-'具有相同的优先级,而且都是左结合的,这样。a+b-c就唯一地解释为( a+ b)一 c。再规定'*'的优先级大于’+”-’,则 a+ b* c就正确地解释为 a+(b*c) 了,因此上述文法的正确形式应是
:<br>
<br>
<center>
<table border=0>
<tr>
<td>
%token NAME<br>
%left '+''-’<br>
%left '*' <br>
%%<br>
expr:expr'+’ expr<br>
|expr'-’ exPr<br>
|expr'*’ expr<br>
|NAME<br>
;<br>
</td>
</tr>
</table>
</center>
<br>
在说明部分中以%left开头的行就是定义算符的结合性的行。%left表示其后的算符是遵循左结合的;%right表示右结合性,而%nonassoc则表示其后的算符没有结合性。优先级是隐含的,在说明部分中,排在前面行的算符较后面行的算符的优先级低;排在同一行的算符优先级相同,因此在上述文法中,’+’和’一’优先级相同,而它们的优先级都小于'*’,三个其符都是左结合的。<br>
<br>
在表达式中有时要用到一元运算符,而且它可能与某个二元运算符是同一个符号,例如一元运算符负号“-”就与减号’-’相同,显然一元运算符的优先级应该比相应的二元运算符的优先级高。至少应该与’*'的优先级相同,这可以用yacc的%Prec子句来定义,请看下面的文法:<br>
<br>
<center>
<table border=0>
<tr>
<td>
%token NAME<br>
%left '-''+’<br>
%left '*''/'<br>
%%<br>
expr;expr'+' expr<br>
|expr'+' expr<br>
|expr'-’expr<br>
|expr'*' expr<br>
|expr'/’ expr<br>
|'-'expr %prec'*'<br>
|NAME<br>
;<br>
</td>
</tr>
</table>
</center>
在上述文法中,为使一元’-’的优先级与’*’相同,我们使用了子句<br>
<br>
%prec’*’<br>
<br>
它说明它所在的语法规则中最右边的运算符或终结符的优先级与%Prec后面的符号的优先级相同,注意%Prec子句必须出现在某语法规则结尾处分号之前,%prec子句并不改变’-’作为二元运算符时的优先级。<br>
<br>
上面介绍的八项定义,没有必要的部分都可以省去。<br>
<br>
2.4. yacc源程序中语法规则部分的写法<br>
<br>
语法规则部分是yacc源程序的核心部分,这一部分定义了要处理的语言的语法及要采用的语义动作。下面介绍语法规则的书写格式、语义动作的写法以及yacc解决二义性和冲突的具体措施。最后介绍错误处理。<br>
<br>
2.4.1语法规则的书写格式<br>
<br>
每条语法规则包括一个左部和一个右部,左右部之间用冒号’:’来分隔,规则结尾处要用分号”;”标记,所以一条语法规则的格式如下:<br>
<br>
nonterminal : BODY;<br>
<br>
或<br>
<br>
nonterminal:BODY<br>
<br>
其中nonterminal是一个非终结符,右部的BODY是一个由终结符和非终结符组成的串、可以为空,请看几个例子:<br>
<br>
stat: WHILE bexp DO Stat<br>
;<br>
stat: IF bexp THEN stat<br>
;<br>
stat:/* empty*/<br>
;<br>
<br>
上面的第三条语法规则的右部为空,用’/*’和‘*/’括起来的部分是注解.可以把左部非终结符相同的语法规则集中在一起,规则间用短线’|’分隔,最后一条规则之后才用分号,例如:<br>
<br>
stat: WHILE bexp DO stat<br>
| IF bexp THEN stat<br>
|/* empty*/<br>
;<br>
<br>
对语法规则部分的书写有几条建议;<br>
<br>
1. 用小写字母串表示非终结符,用大写字母串表示终结符。<br>
<br>
2.将左部相同的产生式集中在一起,象上例一样。<br>
<br>
3.各条规则的右部尽量对齐,例如都从第一个tab处开始。按这样的风格写yacc源程序清晰可读性强而且易修改和检查错误。<br>
<br>
4.如果产生式(语法规则)需要递归,尽可能使用左递闭方式.例如:<br>
<br>
seq: item<br>
| seq',’ item<br>
;<br>
<br>
因为用左速归方式可以使语法分析器尽可能早地进行归约,不致使状态栈溢出。<br>
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -