Yacc
yacc的文法由一个使用BNF文法(Backus-Naur form 的变量描述。文法规则最初由 John Backus 和 Peter Naur 发明,并且用于描述Algol60 语言。 BNF 能够用于表达上下文无关语言。现代程序语言中的大多数结构可以用BNF 文法来表达。例如,数值相乘和相加的文法是:
E > E + E
E > E * E
E > id
像 E (表达式)这样出现在左边的结构叫非终结符(nonterminal)。像 id(标识符)这样的结构叫终结符(terminal,由lex 返回的标记),它们只出现在右边。
yacc的输入文件格式与lex类似,给出一个例子,计算加减乘除的简单计算器,yacc和lex结合使用。
Lex的输入文件:
%{
/****************************************************************************
calclexer.l
ParserWizard generated Lex file.
Date: 2008 年10 月13 日
****************************************************************************/
#include <stdlib.h>
void yyerror(char *);
#include "calcparser.h"
extern YYSTYPE yylval;
%}
/
// declarations section
// place any declarations here
%%
/
// rules section
// place your Lex rules here
[0-9]+ {
yylval = atoi(yytext);
return INTEGER;
}
[-+*//n] return *yytext;
[ /t] ; /* skip whitespace */
. yyerror("invalid character");
%%
/
// programs section
int yywrap(void) {
return 1;
}
Yacc的输入文件:
%{
/****************************************************************************
calcparser.y
ParserWizard generated YACC file.
Date: 2008 年10 月13 日
****************************************************************************/
#include "calclexer.h"
int yylex(void);
void yyerror(char *);
%}
/
// declarations section
// attribute type
%include {
#ifndef YYSTYPE
#define YYSTYPE int
#endif
}
// place any declarations here
%token INTEGER
%left '+' '-'
%left '*' '/'
%%
/
// rules section
// place your YACC rules here (there must be at least one)
program:
program expr '/n' { printf("%d/n", $2); }
|
;
expr:
INTEGER { $$ = $1; }
| expr '+' expr { $$ = $1 + $3; }
| expr '-' expr { $$ = $1 - $3; }
| expr '*' expr { $$ = $1 * $3; }
| expr '/' expr { $$ = $1 / $3; }
;
%%
/
// programs section
void yyerror(char *s)
{
fprintf(stderr, "%s/n", s);
}
int main(void)
{
yyparse();
return 0;
}
需要注意以下几点:
l 在计算+-的时候,文法本身是有移近-规约冲突(shift-reduce conflict)的,例如 10-2-2 ,到底是先计算10-2,还是先计算2-2。默认是移近,就会先计算2-2,结果是不正确的。可以通过%left和%right指定结合顺序来解决冲突,即【%left '+' '-' 】,最后列出的定义拥有最高的优先权。因此乘法和除法拥有比加法和减法更高的优先权。
l yylval是在yacc的输出文件中定义的,在lex中使用时需要先用extern将其引入【extern YYSTYPE yylval;】
l yacc 在内部维护着两个堆栈;一个分析栈和一个内容栈。分析栈中保存着终结符和非终结符,并且代表当前剖析状态。内容栈是一个YYSTYPE 元素的数组,对于分析栈中的每一个元素都保存着一个对应的值。例如,当yylex 返回一个INTEGER 标记时,yacc 把这个标记移入分析栈。同时,相应的yylval 值将会被移入内容栈中。分析栈和内容栈的内容总是同步的,因此从栈中找到对应于一个标记的值是很容易实现的。