第二章 Flex 使用

第一节 输入文件的格式

Flex 输入文件由三部分组成:定义(Definitions),规则(Rules),用户代码(User Code)。

Definitions

%%

Rules

%%

User Code

定义的格式

定义段包含了简单名称的声明(这些声明可以简化扫描器的说明)和开始条件(这个在后面的章节中讲解)

名称的定义有如下的格式:

名称 定义

name             definition

名称由字母或下划线开头,后跟任意字符或数字和破折号。定义是跟在名称后面,第一个不是空白符开始,直到一行结束。名称的定义可以在后面相关内容中被引用,使用"{名称}",这个引用将会被扩展成"(定义)"

DIGIT           [0-9]

ID                 [a-z][a-z0-9]*

定义了"DIGIT"为一个匹配一个数字的正则表达式,"ID"定义为匹配一个以字母开头后面跟零个或多个字母或数字的字符串的正则表达式。随后,可以引用它们,如

{DIGIT}+"."{DIGIT}*

上面这个和下面这一个是一样的

([0-9])+"."([0-9])*

匹配以一个数字开头后跟一个"."再跟零个或多个数字的字符串

 

flex的输入规则段部分包含了一组以如下形式组成的规则模式动作

这里的模式不能缩进的,并且动作是在同一行上跟在模式后面。可以看下面更多关于模式和动作的描述。

最后的用户代码只是简单的拷贝到lex.yy.c。这个和扫描器组成一起,调用扫描器或是被扫描器调用。如果被省略,那么输入文件中的第2个个%%也可以被省略。

在定义和规则段,任何缩进的文本和以%{%}包裹起来的字符将被逐字的拷贝到输出文件中(%{%}被移除)。%{}%在一行的开头,不能缩进。在规则段,出现在第一个规则之前的任何缩进或是%{%}文本一般用于定义变量,这些变量是扫描程序和在声明之后的代码(由扫描程序启动)使用的本地变量。在规则段的其他缩进的和%{%}文本还是拷贝到输出文件,但它们的含义不明确并可能会产生编译错误(这个特性在POSIX兼容部分讨论,查看后面关于其他这样的特性说明)。在定义段(不是在规则段),一个没缩进的注释(如以"/*"开始)到下一个"*/"之间的文本也被逐字的拷贝到输出设备。

 

规则的格式

 

用户代码的格式

 

第二节 一个简单的例子

例子

%{#define YY_NO_UNISTD_H

    int num_lines = 0, num_chars = 0;

%}

%%

    \n      ++num_lines; ++num_chars;

.           ++num_chars;

%%

    int main(int argc, char **argv)

{

    ++argv, --argc; /* skip over program name */

    if ( argc > 0 )

        yyin = fopen( argv[0], "r" );

    else

        yyin = stdin;

    yylex();

    printf( "# of lines = %d, # of chars = %d\n",

        num_lines, num_chars );

    return 0;

}

 

输入Flex

将上述内容保存为flex1.lex文件,打开Cygwin终端。

$flex --main --nounistd --noyywrap flex1.lex

$ls

flex1.lex  lex.yy.c

 

lex.yy.c就是Flex生成的C文件。

导入到VC++

打开VS,新建一个C++控制台项目。



1.      在弹出的向导里直接点“完成”即可。

2.      将刚才生成的lex.yy.c改名为lex.yy.h

3.      lex.yy.h文件的末端将Main函数的内容拷贝到lex8.cpp中的main函数里。

4.      lex.yy.h文件的开头添加下面的语句:

#include "stdafx.h"

#include <io.h>

#include <stdio.h>

5.      lex.yy.h文件中找到isatty()函数,改为_isatty()同时filen()改成_fileno()

6.      修改lex8.cpp文件中的C函数,使之符合MS Visual C++ 推荐的用法。

比如:

fopen()改成_tfopen_s()

char*改成tchar*

 

 

7.      Lex8.cpp内容为:

 // lex8.cpp : Defines the entry point for the console application.

//

#include "stdafx.h"

#include "lex.yy.h"

 

int _tmain(int argc, _TCHAR* argv[])

{

    ++argv, --argc; /* skip over program name */

    if ( argc > 0 )

         errno_t errornum = _tfopen_s(&yyin,argv[0], _T("r"));

    else

        yyin = stdin;

    yylex();

    printf( "# of lines = %d, # of chars = %d\n",

           num_lines, num_chars );

    return 0;

}

 

8.      这是解决方案中的文件。

9.      现在生成项目,就不会有错误了。

10.   我们可以在DEBUG文件夹内找到lex8.exe

11.   现在我们可以测试一下lex8的扫描结果。


 


根据上图所显示的结果,我们的Lex程序已经正确运行。