前言
凡是lex识别正则表达式的地方,yacc都可以识别完整的语法,lex将输入流分成块(标记),然后yacc去的这些块并将他们逻辑性的归组到一起
语法
yacc采用你指定的语法,并为这些语法中的表达式创建分析程序,语法是是一系列规则,用来识别输入符号
statement -> NAME = expression
expression -> NUMBER + NUMBER | NUMBER - NUMBER
->左侧缩写为LHS(left -hand side),右侧缩写为RHS(right-hand side),LHS必须为非终结符,输入中的和被词法分析程序返回的为终结符或标记
使用树状结构来表示所分析的句子,例如输入“fred = 12 + 13”
递归规则
规则可以直接或者间接的引用本身
expression -> NUMBER
| expression + NUMBER
| expression - NUMBER
现在这个规则可以匹配"fred = 12 + 13 - 11 +7"这样的句子了
移进/规约分析
yacc的语法程序总是在寻找哪一个规则和当前能看到的标记匹配,每次读取一个标记,如果没有匹配到规则,就压入内部堆栈,并且换到一个反映刚刚读取的标记的新的状态,这个动作叫做移进,如果匹配到了一条规则,就会把右侧符号弹出堆栈,将左侧符号压入堆栈,并且切换到一种反映新符号的新状态,这个动作叫做规约,通过不断移进规约,语法程序就可以分析输入了
yacc不能分析的语法
- 歧义语法:同样的输入,符合多个分析树
- 需要向前(输入流的后面)看多个标记才能确定是否匹配到规则
phrase -> cart_animal AND CART
| work_animal AND PLOW
cart_animal -> HORSE | GOAT
work_animal -> HORSE | OX
对于上面的文法,并没有歧义,输入如果是“HORSE AND CART”,当看到HORSE的时候,并不知道是cart_animal 还是work_animal,因为要知道后面AND 和后面的CART的时候,才可以确定是cart_animal,所以这个不行,改成下面的就可以,因为只向前看了一个标记,这个yacc是支持的
phrase -> cart_animal CART
| work_animal PLOW
cart_animal -> HORSE | GOAT
work_animal -> HORSE | OX
当输入“HORSE CART”,读取HORSE的时候并不知道是cart_animal 还是work_animal,但是下一个是CART,所以规约成cart_animal CART,再规约到phrase
加减法程序
cal.y
%{
#include <stdio.h>
%}
%token PRINT NUMBER
%%
statement:
PRINT expression {
printf("\ntest yacc PRINT expression\n");
printf("result is %d", $2);
}
|expression {
printf("\ntest yacc expression\n");
printf("%d is the result", $1);
}
;
expression:
expression '+' NUMBER {
printf("\ntest yacc expression + NUMBER\n");
$$ = $1 + $3;
}
|NUMBER {
printf("\ntest yacc NUMBER\n");
$$ = $1;
}
cal.l
%{
#include "y.tab.h"
extern int yylval;
%}
%%
print {printf("\ntest lex print\n"); return PRINT;}
[0-9]+ {printf("\ntest lex NUMBER\n");yylval = atoi(yytext); return NUMBER;}
[ \t] ;
\n return 0;
. {printf("\ntest lex %s", yytext); return yytext[0];}
%%
mini2:cal nosources$ yacc -d cal.y && lex cal.l && cc -o cal y.tab.c lex.yy.c -ly -ll
mini2:cal nosources$ ./cal
print 2+3
test lex print
test lex NUMBER
test yacc NUMBER
test lex +
test lex NUMBER
test yacc expression + NUMBER
test yacc PRINT expression
result is 5mini2:cal nosources$
可见,“print 2+3” 这条语句,先接受到print,然后接受2,并且识别成NUMBER,然后接受+,然后接受3,识别成NUMBER,这时候,可以规约expression + NUMBER,之后又可以规约 PRINT expression,最终得到“result is 5”