首先这个l文件在definitions 部分定义了一个int型变量yylineno用于记录行号。然后,在rules定义了一个模式动作:当遇到一行的结尾时,输出当前行号,并输出当前行内容。最后在subroutines部分定义了一个c语言main函数,读取文件,并实现yywrap并返回1表示停止解析。
----
###2.yacc
Yacc( Yet Another Compiler Compiler)。 Yacc 的 GNU 版叫做 Bison。它是一种工具,将一种编程语言的语法翻译成针对此种语言的 Yacc 语法解析器。从上文中我们可以知道lex可以参数一系列标记,如果我们想当某个标记序列出现时执行某一动作,该怎么实现呢?Yacc该出场了。通过yacc编译y文件(语法文件)就可以产生对应的c程序了;生成的c代码通过编译链接就可以生产语法分析器了。但是,语法分析的前提是词法分析,因此我们需要lex出现输入文件并生成标记。在讲解y文件(语法文件)之前我们假设以及存在如下l文件(词法文件):
%{#include <stdio.h>
#include "y.tab.h"
%}%%[0-9]+returnNUMBER;heatreturnTOKHEAT;on|offreturnSTATE;targetreturnTOKTARGET;temperaturereturnTOKTEMPERATURE;\n/* ignore end of line */;[\t]+/* ignore whitespace */;%%
%{#include <stdio.h>
#include <string.h>
voidyyerror(constchar*str);%}%tokenNUMBERTOKHEATSTATETOKTARGETTOKTEMPERATURE%%commands:/* empty */|commandscommand;command:heat_switch|target_set;heat_switch:TOKHEATSTATE{printf("\tHeat turned on or off\n");};target_set:TOKTARGETTOKTEMPERATURENUMBER{printf("\tTemperature set\n");};%%voidyyerror(constchar*str){fprintf(stderr,"error: %s\n",str);}intyywrap(){return1;}main(){yyparse();}
该y文件的definitions部分声明了一个函数,并定义了一系列标记(TOKEN)。然后在rules部分定义了四个模式序列对应(语句)的动作,其中commands是一个递归定义。最后在subroutines部分定义了一个c语言main函数,读取文件,并实现yywrap并返回1表示停止解析。这个y文件实现了以下功能
```
输入:heat on
输出:Heat turned on or off
输入:target temperature 22
输出:New temperature set!
```
###3.lex与yacc结合
也许你已经注意到了,l文件的definitions部分往往要包含#include "y.tab.h"。而y.tab.h是yacc对y文件编译后产生的c源文见。因此y文件必须限于l文件进行编译成c源文件,然后将l文件产生的c文件和y文件产生的c文件编译连接生产语法解析器。具体步骤见图: