lex与yacc入门示例

August 29, 2014

Lex 和 Yacc 是 UNIX 两个非常重要的、功能强大的工具。如果能够熟练的熟练掌握 Lex 和 Yacc ,可以轻易的写出c编译器。本文将详细的讨论了这两种工具,包括常规表达式、声明、匹配模式、变量、Yacc 语法和解析器代码。最后,他把 Lex 和 Yacc 结合起来,完成一个示例。


1.lex

lex是一种词法分析器,可以识别文本中的词汇模式,模式可以用正则表达式表示。通过lex编译l文件(词法文件)就可以生产对应的c代码,然后编译连接c代码就可以生成词法分析器了。

一个l文件分为三个部分,每部分通过双百分号(%%)分割。如下所示:

... definitions ...
%%
... rules ...
%%
... subroutines ...

definitions 部分用于定义模式、C语言变量、以及包含c头文件等。 rules 部分用户定义模式对应的动作。 subroutines 部分用于定义c函数等。

一个简单的l文件例子,test.l。

%{
    int yylineno;
%}
%%
^(.*)\n    printf("%4d\t%s", ++yylineno, yytext);
%%
int main(int argc, char *argv[]) {
    yyin = fopen(argv[1], "r");
    yylex();
    fclose(yyin);
}

int yywrap(void) {
    return 1;
}
首先这个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]+                  return NUMBER;
heat                    return TOKHEAT;
on|off                  return STATE;
target                  return TOKTARGET;
temperature             return TOKTEMPERATURE;
\n                      /* ignore end of line */;
[ \t]+                  /* ignore whitespace */;
%%
这个l文件主要是参数y文件定义的各种token,大家可以看到它的subroutines部分为空,因为该词法分析器的结果直接输出到语法分析器,因此不需要额外的函数。**下面的y文件都依赖于该l文件**。 一个y文件(语法文件)同样包含definitions、rules、subroutines三个部分,每部分同样通过双百分号(%%)分割。各个部分的作用l文件的对应部分也基本一致。 一个简单的y文件例子,test.y。
%{
#include <stdio.h>
#include <string.h>
void yyerror(const char *str);
%}
%token NUMBER TOKHEAT STATE TOKTARGET TOKTEMPERATURE
%% 
commands: /* empty */
        | commands command
        ;

command:
        heat_switch
        |
        target_set
        ;

heat_switch:
        TOKHEAT STATE
        {
                printf("\tHeat turned on or off\n");
        }
        ;

target_set:
        TOKTARGET TOKTEMPERATURE NUMBER
        {
                printf("\tTemperature set\n");
        }
        ;
%%
void yyerror(const char *str)
{
        fprintf(stderr,"error: %s\n",str);
}
int yywrap()
{
        return 1;
}  
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文件编译连接生产语法解析器。具体步骤见图:

更多lex和yacc的资料如下:

http://epaperpress.com/lexandyacc/index.html
http://tldp.org/HOWTO/Lex-YACC-HOWTO-1.html

全面理解React,实现自己的React

通过实现一个简单的React, 来理解React的原理 Continue reading

同构渲染的常见风险

Published on October 01, 2017

React16升级避坑指南

Published on September 10, 2017