既然我们已经有了一个可以识别标记序列的简单语法,将需要寻求一种将这些标记提供给语法分析器的方法。lex 这种工具可以接受输入,将它转换成标记,然后将这些标记传递给 yacc。下面,我们将描述 lex 要将其转换成标记的表达式:
%{ /* Again, this is C code that is inserted into the beginning of the output */ #include #include "y.tab.h" /* Include the token definitions generated by yacc */ %} /* Prevent the need for linking with -lfl */ %option noyywrap /* This next section is a set of regular expressions that describe input tokens that are passed back to yacc. The tokens are defined in y.tab.h, which is generated by yacc. */ %% .* /* ignore comments */ -[0-9]+|[0-9]+ { yylval.number=atoi(yytext); return NUMBER; } [ /t/n] /* ignore whitespace */ && { return AND; } /|/| { return OR; } == { return EQUALS; } . return yytext[0]; %% |
现在,在当前目录中已经有了分析源码,我们需要一个 Makefile 来构建它们:
all: eval y.tab.c: eval.y yacc -d ___FCKpd___1lt; lex.yy.c: eval.l lex ___FCKpd___1lt; eval: y.tab.o lex.yy.o $(CC) -o $@ $^ |
转移/压缩冲突 当 yacc 必须在继续分析一组标记或将它解析成规则之间作出选择时,会发生转移/压缩冲突。例如,如果创建了由以下内容组成的语法: expression: NUMBER | plus ; plus: expression '+' expression ;当 yacc 语法分析器看到一个数字时,它不知道是立即将该数字解析成表达式,还是等待 "X + Y"。这种情况下,yacc 会警告有转移/压缩冲突,缺省情况下,它会等待完整的 "X + Y" 表达式。对 GNU bison 指定 '-v' 命令行选项会创建一个 *.output 文件,该文件包含了已经发生的规则和冲突,通过这个文件可以知道使用 GNU bison 时究竟发生了什么情况。 只要在 plus 表达式两边加上括号,就可以很容易地解决这个多义性问题: expression: NUMBER | plus ; plus: '(' expression '+' expression ')' ; |