本来不打算全部拷贝过来的,但是这篇文章在网上已经删除了,我是从google的快照中找到的,第8部分的出错处理中error的使用是第一次从这里看到,郁闷了半天的问题
文章来源(http://www.duangw.net/computer/reference/scripts/yacc.html)
Yacc使用
索引:
- 概念
- Yacc文件格式
- 定义部分
- 规则部分
- 第三部分
- 递归的处理
- If-Else的冲突
- 出错处理
- Yacc源程序的风格
1.概念
yacc使用巴克斯范式(BNF)定义语法,能处理上下文无关文法(context-free)。
出现在每个产生式左边(left-hand side:lhs)的符号是非终端符号,出现在产生式右边(right-hand side:rhs)的符号有非终端符号和终端符号,但终端符号只出现在右端。
在规约过程中可能会有冲突。Yacc对此有一些缺省的处理方法,那就是使用第一个匹配的规则。
2.Yacc文件格式
Yacc文件分为三部分:
... definitions ...
%%
... rules ...
%%
... subroutines ...
3.定义部分
第一部分包括标志(token)定义和C代码(用“%{”和“%}”括起来)。
如在定义部分定义标志:
%token INTEGER
当运行yacc后,会产生头文件,里面包含该标志的预定义,如:
#ifndef YYSTYPE
#define YYSTYPE int
#endif
#define INTEGER 258
extern YYSTYPE yylval;
lex使用该头文件中的标志定义。Yacc调用lex的yylex()来获得标志(token),与标志对应的值由lex放在变量yylval中。yylval的类型由YYSTYPE决定,YYSTYPE缺省类型是int。如:
[0-9]+ {
yylval = atoi(yytext);
return INTEGER;
}
标志0-255被保留作为字符值,一般产生的token标志从258开始。如:
[-+] return *yytext; /* return operator */
返回加号或减号。注意要把减号放在前面,避免被认作是范围符号。
对于操作符,可以定义%left和%right:%left表示左相关(left-associative),%right表示右相关(right-associative)。可以定义多组%left或%right,在后面定义的组有更高的优先级。如:
%left ‘+’ ‘-‘
%left ‘*’ ‘/’
上面定义的乘法和除法比加法和减法有更高的优先级。
Yacc内部维持着两个栈:符号栈(parse stack)和值栈(value stack),这两个栈始终是同步的。
改变YYSTYPE的类型。如这样定义TTSTYPE:
%union {
int iValue; /* integer value */
char sIndex; /* symbol table index */
nodeType *nPtr; /* node pointer */
};
则生成的头文件中的内容是:
typedef union {
int iValue; /* integer value */
char sIndex; /* symbol table index */
nodeType *nPtr; /* node pointer */
} YYSTYPE;
extern YYSTYPE yylval;
可以把标志(token)绑定到YYSTYPE的某个域。如:
%token <iValue> INTEGER
%type <nPtr> expr
把expr绑定到nPtr,把INTEGER绑定到iValue。yacc处理时会做转换。如:
expr: INTEGER { $ = con($1); }
转换结果为:
yylval.nPtr = con(yyvsp[0].iValue);
其中yyvsp[0]是值栈(value stack)当前的头部。
定义一元减号符有更高的优先级的方法:
%left GE LE EQ NE '>' '<'
%left '+' '-'
%left '*'
%nonassoc UMINUS
%nonassoc的含义是没有结合性。它一般与%prec结合使用表示该操作有同样的优先级。如:
expr: '-' expr %prec UMINUS { $ = node(UMINUS, 1, $2); }
表示该操作的优先级与UMINUS相同,在上面的定义中,UMINUS的优先级高于其他操作符,所以该操作的优先级也高于其他操作符计算。
4.规则部分
规则部分很象BNF语法。
规则中目标或非终端符放在左边,后跟一个冒号(:),然后是产生式的右边,之后是对应的动作(用{}包含)。如:
%token INTEGER
%%
program: program expr '\n' { printf("%d\n", $2); }
|
;
expr: INTEGER { $ = $1; }
| expr '+' expr { $ = $1 + $3; }
| expr '-' expr { $ = $1 - $3; }
;
%%
int yyerror(char *s)
{
fprintf(stderr, "%s\n", s);
return 0;
}
int main(void)
{
yyparse();
return 0;
}
其中,$1表示右边的第一个标记的值,$2表示右边的第二个标记的值,依次类推。$$表示规约后的值。
5.第三部分
该部分是函数部分。当yacc解析出错时,会调用函数yyerror(),用户可自定义函数的实现。main函数是调用yacc解析入口函数yyparse()。
6.递归的处理
递归处理有左递归和右递归。
左递归形式:
list:
item
| list ',' item ;
右递归形式:
list: item
| item ',' list
使用右递归时,所有的项都压入堆栈里,才开始规约;而使用左递归的话,同一时刻不会有超过三个项在堆栈里。
所以,使用左递归有很大的优势。
7.If-Else的冲突
当有两个IF一个ELSE时,该ELSE和哪个IF匹配是一个问题。有两中匹配方法:与第一个匹配和与第二匹配。现代程序语言都让ELSE与最近的IF匹配,这也是yacc的缺省行为。
虽然yacc行为正确,但为避免警告,可以给IF-ELSE语句比IF语句更高的优先级:
%nonassoc IFX
%nonassoc ELSE
stmt: IF expr stmt %prec IFX
| IF expr stmt ELSE stmt
8.出错处理
当yacc解析出错时,缺省的行为是调用函数yyerror(),然后从yylex返回一个值。一个更友好的方法是忽略一段错误输入流,继续开始扫描。实现方法如下:
stmt:
';'
| expr ';'
| PRINT expr ';'
| VARIABLE '=' expr ';
| WHILE '(' expr ')' stmt
| IF '(' expr ')' stmt %prec IFX
| IF '(' expr ')' stmt ELSE stmt
| '{' stmt_list '}'
| error ';'
| error '}'
;
这里的error标志表示,当yacc发现错误时,它调用yyerror(),之后是输入流往前到‘;’或‘}’,然后继续扫描。
9.Yacc源程序的风格
建议按照如下风格来写:
- 终端符名全部用大写字母,非终端符全部用小写字母;
- 把语法规则和语义动作放在不同的行;
- 把左部相同的规则写在一起,左部只写一次,而后面所有规则都写在竖线“|”之后;
- 把分号“;”放在规则最后,独占一行;
- 用制表符来对齐规则和动作。
分享到:
相关推荐
完整介绍Lex和Yacc Windows 上的使用 及工具的按装, 及环境变量的设置, GNU Bison实际上是使用最广泛的Yacc-like分析器生成器,使用它可以生成解释器,编译器,协议实现等多种程序. 它不但与Yacc兼容还具有许多Yacc不...
* Yacc 代表 Yet Another Compiler Compiler。 * Lex 和 Yacc 是 UNIX 两个非常重要的、功能强大的工具。事实上,如果你熟练掌握 Lex和 Yacc 的话,它们的强大功能使创建 FORTRAN 和 C 的编译器很简单。
5. **LR分析**:Yacc使用的解析方法,包括LALR(1)和LR(1)解析。 6. **组合Lex与Yacc**:如何将词法分析器和语法分析器结合,形成一个完整的编译器前端。 7. **实例分析**:书中可能包含多个实际编程示例,帮助读者将...
lex yacc简单使用,lex词法分析器自动生成,yacc语法分析器自动生成
随后,Yacc使用这些词法单元来进行语法分析,构造出抽象语法树。这一过程为后续的语义分析、优化和目标代码生成奠定了基础。 #### 六、总结 通过以上分析,我们可以看出Lex和Yacc在构建编译器前端方面发挥着重要...
yacc使用巴科斯范式(BNF)来描述语法规则,用户可以定义一系列的产生式来表示语言的结构。yacc会自动生成解析函数,这些函数能够根据这些规则解析输入的符号流,处理复杂的语法结构。 lex和yacc的结合使用,使得...
Yacc使用一种称为巴科斯范式(Backus-Naur Form, BNF)的语法描述语言来定义语言的语法规则。用户通过编写.y文件,用BNF规则描述语言的结构。Yacc将这些规则解析后,生成一个C源码文件(通常命名为y.tab.c),该文件...
1. **LR解析**:yacc使用LR(1)解析算法,保证了对大部分上下文无关语言的有效解析。 2. **自动冲突解决**:在解析表中有冲突时,yacc会尝试自动解决,或者给出冲突报告供用户手动处理。 3. **用户定义的项集**:...
- `www.pudn.com.txt`:这可能是一个文档,提供了关于YACC使用方法、示例或者相关资源的链接。 - `Yacc相关`:这个目录可能包含了YACC的实例项目,比如一个`.y`文件、词法分析器(如`.l`文件)以及相关的源代码和...
3. **解析表**:YACC使用LR(Left-to-Right, Leftmost Derivation)解析方法,生成一个解析表,该表指示在遇到特定的词法单元时应执行的动作(接受、移进、归约等)。 4. **输出文件**:YACC将输入文件转化为C代码,...
2. **LR分析**:yacc使用LR(左到右,右most衍生)解析方法,这是一种高效的语法分析技术。 3. **冲突解决**:在编写yacc文件时,可能会遇到语法冲突,yacc会给出警告并尝试自动解决,但有时需要手动调整文法或添加...
yacc使用巴科斯范式(BNF)来描述语法规则,用户可以通过编写.y文件定义语法规则,yacc会自动生成相应的解析器代码。这本书将深入讲解yacc的工作原理,如何编写有效的语法规范,以及如何处理错误和回溯等问题。 在...
yacc使用巴科斯范式(Bison Grammar)定义语法规则,这些规则描述了语言的句法规则和结构。当yacc解析器遇到符合规则的输入序列时,它会调用对应的用户定义函数,执行相应的操作。 在《lex&yacc教程》中,你可能会...
在生成的C代码中,YACC使用了LR(1)解析算法,这是一种自底向上的解析方法。LR(1)解析器根据当前的输入符号和一个额外的“看前缀”信息来决定下一步的动作。在我们的计算器中,LR(1)解析器会处理用户输入的表达式,...
Yacc使用巴科斯-诺尔范式(BNF, Backus-Naur Form)来定义语言的语法结构,这是一种标准的用于描述形式语言的方法。 ####Lex与Yacc的工作原理 - **Lex的工作原理** - **输入**:用户为Lex提供一系列的正则表达式...
Yacc使用巴科斯范式(BNF,Backus-Naur Form)来定义语言的上下文无关语法。 在《LexAndYacc入门教程》中,可能涵盖了以下内容: 1. **Lex的基本概念**:介绍Lex的工作流程,如何定义模式匹配规则,以及如何处理...
- **Thomas Niemann** 的《A Compact Guide to Lex & Yacc》提供了详细的Lex与Yacc使用指南。 - 可以参考的网站: - Mortice Kern Systems (MKS): www.mks.com - GNU flex和bison: www.gnu.org - Mingw: ...
目的:通过编写简单的YACC程序熟悉YACC词法分析工具的基本用法,及词法分析器的设计思路。编写包括赋值语句、if语句以及含有加合乘运算的... 功能:程序由两个模块LEX和YACC构成,YACC使用LEX分析后的正规单词输出流
首先,lex通过识别输入文本中的模式生成标记,然后yacc使用这些标记来构建抽象语法树(AST),这有助于理解代码的结构和含义。这个过程是编译器和解释器开发的核心部分,对于理解和实现新的编程语言至关重要。 书中...
yacc使用LALR(1)解析技术,这是一种有效的自底向上的解析方法。 书中通过详尽的代码示例和清晰的解释,帮助读者深入理解这两个工具的工作原理和使用技巧。学习lex和yacc,不仅可以掌握底层解析技术,还有助于理解...