用途
生成一个与输入流的简单语法分析相匹配的 C 或 C++ 语言程序。
语法
lex [ -C ] [ -t ] [ -v| -n ] [ File... ]
描述
lex 命令读取 File 或标准输入,生成 C 语言程序并将它写到一个名为 lex.yy.c 的文件中。这个文件,lex.yy.c ,是一个兼容的 C 语言的程序。一个 C++ 编译器也能够编译 lex 命令的输出。-C 标志将输出文件重命名为 lex.yy.C 供 C++ 编译器使用。
由 lex 命令生成的 C++ 程序可使用 STDIO 或 IOSTREAMS。如果在 C++ 编译中,cpp 定义 _CPP_IOSTREAMS 是真,程序为所有 I/O 使用 IOSTREAMS。否则,使用 STDIO。
lex 命令使用包含在 File 中的规则和操作来生成一个程序,lex.yy.c,这个程序可用 cc 命令编译。这个编译过的 lex.yy.c 然后能接受输入,将输入分成为由在 File 文件中的规则定义的逻辑片,并运行包含在 File 文件中的操作的程序片断。
这个生成的程序是一个称为 yylex 的 C 语言函数。lex 命令将 yylex 函数存储在一个名为 lex.yy.c 的文件中。可单独用 yylex 函数来识别简单的一个单词的输入,或能用它和其他 C 语言程序一起来执行更困难的输入分析函数。例如,您能用 lex 命令来生成一个程序。这个程序能在将输入流发送到一个由 yacc 命令生成的解析器程序之前简化输入流。
yylex 函数用称为有限自动机的程序结构来分析输入流。这个结构在一个时间允许程序仅在一个状态(或条件)下退出。允许有有限个数目的状态。在 File 中的规则确定程序怎样从一个状态移动到另一个状态。
如果不指定一个 File,lex 命令读取标准输入。它将多个文件作为一个单个的文件对待。
注: 由于 lex 命令为中间和输出文件使用固定的名称,您可仅有一个由 lex 在给定目录中生成的程序。
lex 规范文件
输入文件文件包含三部分:定义、规则和用户子例程。每部分必须用仅含定界符 %%(双百分号)的行和其他部分分开。格式是:
下面描述了各自的用途和格式。
定义
如果想在您的规则中应用变量,必须在这个部分定义它们。变量组成左边的列,它们的定义组成右边的列。例如,如果想定义 D 作为数字,应该这样写:
D [0-9]
您可用一个在 {} (大括号)内围住变量名的规则部分定义的变量。
{D}
在以空格开头或由 %{, %} 定界符行中括住的定义部分中的行被复制到 lex.yy.c 文件。能用这个构造声明 C 语言变量用在 lex 操作或包含头文件,例如:
%{
#include <math.h>
int count;
%}
这些行也可出现在规则部分的开头部分,仅在第一个 %% 定界符之后,但它们不应当用在规则部分的其他地方。如果这行在 File 的定义部分,lex 命令将它复制到 lex.yy.c 文件的外部声明部分。如果这行出现在规则部分,在第一个规则前,lex 命令将它复制到 lex.yy.c 文件的 yylex 子例程的本地声明部分。那些行不能在第一个规则后出现。
lex 外部的类型,yytext,能通过在定义部分指定以下之一来设置为以空结束的字符数组(缺省)或者是以空结束字符串的指针:
%array (缺省) %pointer
在定义部分,可为生成的有限状态机设置表的大小。缺省大小对小程序足够大。可能想为更复杂的程序设置更大的大小。
%an |
转变数是 n(缺省 5000) |
%en |
语法分析树节点数是 n(缺省 2000) |
%hn |
多字节字符输出槽数(缺省 0) |
%kn |
压缩字符类数(缺省 1000) |
%mn |
多字节字符类输出槽数(缺省 0) |
%nn |
状态数是 n(缺省 2500) |
%on |
输出槽数(缺省 5000,最小 257) |
%pn |
位置数是 n(缺省 5000) |
%vp |
在由 %h 和 %m 控制的散列表中的空槽百分比(缺省 20,范围 0 <= P < 100) |
%zn |
多字节字符类输出槽数(缺省 0) |
如果多字节字符出现在扩展的正则表达式字符串中,可能需要用 %o 参数复位输出数组大小(可能的数组大小在 10,000 到 20,000 的范围内)。这个复位反映相对于单字节字符数大得多的字符数。
如果多字节字符出现在一个扩展的正则表达式中,必须用 %h 和 %m 参数设置多字节散列表大小为一个比包含在 lex 文件中的多字节字符总数更大的大小。
如果没有多字节字符出现在扩展的规则表达式中,但是您想 '.' 来匹配多字节字符,必须设置 %z 大于零。类似的,对逆字符类(例如,[^abc])来匹配多字节字符,必须设置 %h 和 %m 大于零。
当用多字节字符时,lex.yy.c 文件必须用 -qmbcs 编译选项来编译。
规则
一旦定义了条件,就可写规则部分。它包含由 yylex 子例程来匹配的字符串和表达式,和当匹配时要执行的 C 命令。需要这一部分,这一部分必须由定界符 %%(双百分号)开头,不论是否有一个定义部分。lex 命令不识别没有定界符的规则。
在这个部分,左边列包含扩展正则表达式形式的模式。这些表达式可由在到 yylex 子例程的输入文件中被识别。右边的列包含一个当这个模式被识别时执行的 C 程序段,称为一个操作。
当词法分析程序发现一个扩展的正则表达式的匹配,词法分析程序执行与那个扩展正则表达式相关联的操作。
模式可包含扩展的字符。如果多字节语言环境在系统中安装,模式也可包含属于安装代码集一部分的多字节字符。
列由跳格或空格分开。例如,如果想搜索关键字为 KEY 的文件,可输入如下内容:
(KEY) printf ("found KEY");
如果在 File 文件中包含这个规则,yylex 词法分析程序匹配模式 KEY 并运行 printf 子例程。
每个模式可有一个对应操作,既,当一个模式匹配时,一个 C 命令来执行。每个语句必须以 ;(分号)结束。如果在一个操作中用多于一条的语句,必须将它们包含在 { } (大括号)中。如果有个用户子例程部分,第二个定界符 %%,必须跟着这个规则部分。如果没有一个指定操作的模式匹配,词法分析程序将在不更改输入模式的情况下将之复制到输出。
当 yylex 词法分析程序在匹配一个输入流中的一个字符串时,在它执行规则部分的任何命令前,它会将这个匹配的字符串复制到一个外部字符数组(或指向字符串的指针),yytext。类似的,外部的 int,yyleng,被设置为以字节表示的匹配字符串的长度(因此,多字节字符的大小大于 1)。
如想获得如何形成扩展正则表达式的信息,请参阅在 《AIX V6.1 通用编程概念:编写并调试程序》 中的『lex 命令中的扩展正则表达式』。
用户子例程
lex 库定义下列子例程作为能在 lex 规范文件的规则部分用的宏。
input |
从 yyin 读取字节。 |
unput |
在读取后替换一个字节。 |
output |
写一个输出字节到 yyout。 |
winput |
从 yyin 读取多字节字符。 |
wunput |
在读取后替换一个多字节字符。 |
woutput |
写一个多字节输出字符到 yyout。 |
yysetlocale |
调用 setlocale (LC_ALL、 " " ); 子例程来确定当前语言环境。 |
winput、wunput 和 woutput 宏被定义来使用在 lex.yy.c 文件中编码的 yywinput、yywunput 和 yywoutput 子例程。为了兼容性,那些 yy 子例程顺序地使用 input、unput 和 output 子例程用完全多字节字符来读取、替换和写必要的字节数。
能通过为在用户子例程部分的例程写自己的代码来覆盖那些宏。但是如果写自己的,必须如下那样在定义部分取消那些宏的定义:
%{
#undef input
#undef unput
#undef unput
#undef output
#undef winput
#undef wunput
#undef woutput
#undef yysetlocale
%}
在 lex.yy.c 中没有 main 子例程,因为 lex 库包含 main 子例程,而这个子例程调用 yylex 词法分析程序和由 yylex() 在 File 结束处调用的 yywrap 子例程。因此,如果在用户子例程部分不包含 main() 或 yywrap() 或两者都不包含,当编译 lex.yy.c 时,必须在 ll 调用 lex 库的地方输入 cclex.yy.c-ll。
由 lex 命令生成的外部名称都以 yy 开始,象在 yyin、yyout、 yylex 和 yytext 中那样。
有限状态机
有限状态机的缺省骨架在 /usr/ccs/lib/lex/ncform 中定义。用户可通过设置一个环境变量 LEXER=PATH. 使用一个个人配置的有限状态机。PATH 变量指定用户定义的有限状态机路径和文件名。lex 命令为变量检查环境,如果它被设置,那么用补充的路径。
在表达式中放置空格
一般的,空格或跳格结束一个规则,接着结束定义一个规则的表达式。然而,可在 " "(引号)内包括空格和跳格字符来在表达式中包含它们。用引号括住没有在 [ ] (括号)集合中的表达式中的所有空格。
其他特殊字符
lex 程序识别许多正常的 C 语言特殊字符。这些字符序列是:
序列 |
含义 |
\a |
提醒 |
\b |
退格 |
\f |
反馈表单 |
\n |
换行符(在表达式中不用实际的换行符。) |
\r |
返回 |
\t |
跳格 |
\v |
纵向跳格 |
\\ |
反斜杠 |
\digits |
通过由 digits 指定 1、2、3 位的八进制整数表示的带编码的字符。 |
\xdigits |
通过由 digits 指定的十六进制字符的序列表示的带编码的字符。 |
\c |
在 c 不是上面列出的字符的情况下,表示这个 c 字符未改变。 |
注: 在 lex 规则中不使用 \0 或 \x0。
当在一个表达式中用这些特殊字符,不必将它们括到引号中。除了在 《AIX V6.1 通用编程概念:编写并调试程序》 中的『lex 命令中的扩展正则表达式』中描述的特殊字符和运算符符号,所有字符总是一个文本字符。
匹配规则
当多于一个表达式可匹配当前输入,lex 命令先选择最长的匹配。当几个规则匹配相同数目的字符,lex 命令选择先出现的那个。例如,如果规则
integer keyword action...;
[a-z]+ identifier action...;
以这个顺序给出,integers 是输入单词,lex 匹配输入作为一个标识,因为 [a-z]+ 匹配八个字符然而 integer 仅匹配七个字符。然而,输入是 integer,两个规则匹配七个字符。lex 选择这个关键字规则因为它先出现。一个更短的输入,如 int,不匹配整数表达式,所以 lex 选择标识规则。
用通配符匹配一个字符串
因为 lex 先选择最长的匹配,所以不使用包含像 .* 的表达式。例如:
'.*'
可能象是一个在单引号中识别一个字符串的好方法。然而,词法分析程序读取源头,来查找一个远的单引号来完成长匹配。如果带这样规则的词法分析规则得到以下输入:
'first' quoted string here, 'second' here
它匹配:
'first' quoted string here, 'second'
为了发现更短的字符串,first 和 second,使用以下规则:
'[^'\n]*'
这个规则在 'first' 后停止。
这个类型的错误不是远到达,因为 .(句点)运算符不匹配换行字符。因此,像 .*(句号,星号)的表达式在当前行停止。不要试图用像 [.\n]+ 这样的表达式来使它失败。词法分析程序试图读取整个输入文件,并且发生一个内部缓冲区溢出。
在字符串中查找字符串
lex 程序分割输入流同时不搜索每个表达式的所有可能匹配。每个字符计算一次且仅一次。例如,计算 she 和 he 在输入文本中的出现次数,尝试以下规则:
she s++
he h++
\n |. ;
在这里最后两个规则忽略除了 he 和 she 的所有东西。然而,因为 she 包含 he,lex 不识别包含在 she 中的 he 的情况。
为覆盖这个选择,用 REJECT 操作。这个伪指令告诉 lex 转到下一个规则。lex 然后在第一个规则被执行前调整输入指针的位置到它在的地方,并执行第二个选择规则。例如,计算包含 he 的实例,用以下规则:
she {s++;REJECT;}
he {h++;REJECT;}
\n |. ;
在计算完 she 的发生次数,lex 拒绝输入流然后计算 he 的发生次数。因为在这种情况下,she 包含 he 但反之不然,可在 he 上省略 REJECT 操作。在其他情况下,确定哪个输入字符在两个类中可能较困难。
总之,无论何时 lex 的目的不是分割输入流而是检测在输入中的某些项的所有示例,REJECT 总是有用的,并且这些项的实例可交迭或互相包含。
标志
-C |
生成 lex.yy.C 文件而不是 lex.yy.c 以和 C++ 编译器一起使用。为得到 I/O 流库,使用宏 _CPP_IOSTREAMS。 |
-n |
禁止统计摘要。当为有限状态机设置自己的表的大小时,如果您不选该标志,lex 命令自动生成这个摘要。 |
-t |
写 lex.yy.c 到标准输出而不是到一个文件。 |
-v |
提供一个生成的有限状态机统计的一行摘要。 |
退出状态
此命令返回以下退出值:
示例
- 从 lexcommands 文件提取 lex 指令,并在 lex.yy.c 中放置输出,用下列命令:
lex lexcommands
- 创建一个 lex 程序,它将大写转换为小写,删除行尾空格,并用一个空格代替多个空格,在 lex 命令文件中包括下列内容:
%%
[A-Z] putchar(yytext[0]+ 'a'-'A');
[ ]+$ ;
[ ]+ putchar(' ');
文件
/usr/ccs/lib/libl.a |
包含运行时库。 |
/usr/ccs/lib/lex/ncform |
定义一个有限状态机。 |