产生一个与输入流的简单语法分析相匹配的 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 在给定目录中产生的程序。
输入文件文件包含三部分:定义、规则和用户子例程。每部分必须用仅含定界符 %%(双百分号)的行和其他部分分开。格式是:
下面描述了各自的用途和格式。
如果想在你的规则中应用变量,必须在这个部分定义它们。变量组成左边的列,它们的定义组成右边的列。例如,如果想定义 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
在定义部分,可为生成的有限状态机设置表的大小。缺省大小对小程序足够大。可能想为更复杂的程序设置更大的大小。
如果多字节字符出现在扩展的正则表达式字符串中,可能需要用 %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)。
如想获得如何形成扩展正则表达式的信息,请参阅 《lex 命令中的扩展正则表达式》AIX 5L Version 5.2 General Programming Concepts: Writing and Debugging Programs.
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 语言特殊字符。这些字符序列是:
注: 在 lex 规则中不使用 \0 或 \x0。
当在一个表达式中用这些特殊字符,不必把它们括到引号中。除了在《 lex 命令中的扩展正则表达式》AIX 5L Version 5.2 General Programming Concepts: Writing and Debugging Programs中描述的特殊字符和运算符符号,所有字符总是一个文本字符。
当多于一个表达式可匹配当前输入,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 | 提供一个生成的有限状态机统计的一行摘要。 |
这个命令返回以下退出值:
0 | 成功完成。 |
>0 | 有错误发生。 |
lex lexcommands
%% [A-Z] putchar(yytext[0]+ 'a'-'A'); [ ]+$ ; [ ]+ putchar(' ');
/usr/ccs/lib/libl.a | 包含运行时库。 |
/usr/ccs/lib/lex/ncform | 定义一个有限状态机。 |
yacc 命令。
用 lex 和 cc 命令创建一个输入语言 AIX 5L Version 5.2 General Programming Concepts: Writing and Debugging Programs。
和 yacc 程序一起使用 lex 程序AIX 5L Version 5.2 General Programming Concepts: Writing and Debugging Programs。
lex 和 yacc 程序的示例程序 AIX 5L Version 5.2 General Programming Concepts: Writing and Debugging Programs。
编程的本地语言支持概述 AIX 5L Version 5.2 National Language Support Guide and Reference。