yacc源程序的结构
声明部分
%%
翻译规则
%%
辅助函数
声明部分和辅助函数与lex相同。
翻译规则:语法规则+相应的语义动作
下面是一个完整的yacc程序代码:
%{
#include<stdio.h>
#include<ctype.h>
#include<stdlib.h>
int yylex();
void yyerror(char const*);
%}
%token DIGIT
%%
line : expr'\n' {printf("%d\n",$1);}
;
expr : expr'+'term {$$=$1+$3;}
| term
;
term : term'*'factor {$$=$1*$3;}
| factor
;
factor : '('expr')' {$$=$2;}
| DIGIT
;
%%
int main(){
return yyparse();
}
int yylex(){
int c;
c=getchar();
if(isdigit(c)){
yylval=c-'0';
return DIGIT;
}
return c;
}
void yyerror(char const*s){
fprintf(stderr,"%s\n",s);
}
代码分析:
声明部分
符号常量、变量等的声明;
指出DIGIT是token类型(单词符号类型)的词汇,供后面两部分引用;
在编译yacc源程序后,会生成相应的头文件(y.tab.h),在该文件中,将DIGIT定义为一个整数,代表单词符号的种别编码。
特别说明:语法分析需要读入单词符号,因此在yacc源程序中必须包含实现词法分析功能的yylex()函数。可手动编写ylex()函数,也可以使用词法分析器的自动产生工具lex产生yylex()函数。如果使用lex创建的传送单词符号的词法分析器,则lex的源程序需要使用这里声明的token。并且在lex源程序中包含#include"y.tab.h"头文件。
翻译规则
由文法的产生式和相关的语义动作组成。
:文法的产生式
yacc产生式中冒号表示定义为,竖线表示分割后选式,分号表示该产生式结束。
文法G(line)的产生式由终结符号和非终结符号组成。
终结符号:①用单引号括起来的单个字符;②token类型。
非终结符号:没括起来并且也没被说明成token类型的字符数字串。
文法的开始符号:第一个产生式的左部非终结符。
: 语义动作部分
yacc的语义动作:在语义动作中可以引用文法符号的属性值。
$$:左部非终结符号的属性值。
$n:右部第n个文法符号(终结符或非终结符)的属性值。
辅助函数
语法分析器,调用该函数开始语法分析。
在使用yyparse()函数执行语法分析的过程中,会调用yylex()函数得到单词符号。
:必须提供的函数。
词法分析器:必须包含
每次调用yylex()时,yylex()返回1个单词符号,该单词符号包括两部分:①单词种别编码:通过yacc源程序中声明部分%token说明;②单词属性值:通过yacc定义的全局变量yylval传递给语法分析器。
当yyparse()函数执行语法分析遇到语法错误时,会调用yyerror()函数,输出一条错误信息。
错误报告例程:必须包含
yacc编译器的实践
将上述完整代码写入后缀为.y的文件中
编译yacc
bison -y -d 文件名.y
目标文件下生成c语言的源程序和头文件。
对c语言的源程序进行编译
gcc y.tab.c
生成文件
运行该文件
a.exe
可以执行加法运算和乘法运算。