Lex和Yacc使用学习笔记(二)

来源:互联网 发布:淘宝兔子20块 编辑:程序博客网 时间:2024/03/28 23:48

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类似,给出一个例子,计算加减乘除的简单计算器,yacclex结合使用。

 

Lex的输入文件:

%{

/****************************************************************************

calclexer.l

ParserWizard generated Lex file.

 

Date: 20081013

****************************************************************************/

#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: 20081013

****************************************************************************/

 

#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 值将会被移入内容栈中。分析栈和内容栈的内容总是同步的,因此从栈中找到对应于一个标记的值是很容易实现的。