词法分析器采取的操作
当词法分析器与说明文件规则部分中的一个扩展正则表达式匹配时,它执行与扩展正则表达式相对应的操作。没有足够的规则匹配输入流中的所有字符串,词法分析器则将输入复制到标准输出。因此,不要创建仅将输入复制到输出的规则。缺省的输出能够帮助在规则中查找间隔。
当使用 lex 命令处理由 yacc 命令产生的解析器的输入时,请提供与所有输入字符串匹配的规则。那些规则必须生成 yacc 命令能够解释的输出。
空操作
要忽略与扩展正则表达式关联的输入,请使用 ;(C 语言空语句)作为操作。下面的示例忽略了三个间隔字符(空白、制表符和换行):
[ \t\n] ;
与下一个操作相同
要避免反复写相同的操作,请使用 |(管道符号)。此字符指示此规则的操作与下一条规则的操作相同。例如,先前忽略空白、制表符和换行字符的示例也可写成:
" " |
"\t" |
"\n" ;
\n 和 \t 两边的引号并不需要。
打印匹配字符串
要确定哪个文本与说明文件的规则部分的表达式匹配,您可以包扩 C 语言 printf 子例程调用作为该表达式的一个操作。当词法分析器在输入流中找到匹配,程序将匹配字符串放入外部字符(char)和宽字符(wchar_t)数组中,分别称为 yytext 和 yywtext。例如,您能使用下面的规则打印匹配字符串:
[a-z]+ printf("%s",yytext);
C 语言 printf 子例程接受格式参数和要打印的数据。在此示例中,printf 子例程的参数具有下面的含义:
%s
|
在打印之前将数据转换为类型字符串的符号 |
%S
|
在打印之前将数据转换为宽字符串(wchar_t)的符号 |
yytext
|
包含要打印的数据的数组的名称 |
yywtext
|
包含要打印的多字节类型(wchar_t)数据的数组名称 |
lex 命令定义 ECHO;作为要打印 yytext 的内容的特殊操作。例如,下面的两条规则是等价的:
[a-z]+ ECHO;
[a-z]+ printf("%s",yytext);
您可以在 lex 说明文件的定义部分使用 %array 或者 %pointer 如下更改 yytext 的说明:
%array
|
将 yytext 定义为以 null 结束的字符数组。这是缺省操作。 |
%pointer
|
将 yytext 定义为指向以 null 结束的字符串的指针。 |
查找匹配字符串的长度
要查找词法分析器与特定的扩展正则表达式所匹配的字符数,请使用 yyleng 或者 yywleng 外部变量。
yyleng
|
跟踪匹配的字节数。 |
yywleng
|
跟踪匹配字符串中的宽字符数。多字节字符的长度大于 1。 |
要对输入的字数和字中的字符数进行计数,请使用下面的操作:
[a-zA-Z]+ {words++;chars += yyleng;}
此操作总计匹配的字中的字符数,并将该数字赋予 chars。
下面的表达式在匹配字符串中查找最后一个字符:
yytext[yyleng-1]
匹配字符串中的字符串
lex 命令对输入流进行分区,并不搜索每个表达式的所有可能的匹配字符串。每个字符仅计算一次。要覆盖此选项并搜索可能重叠或者互相包含的项,请使用 REJECT 操作。例如,要对 she 和 he 的所有实例(包括包含在 she 中的 he)计数,请使用下面的操作:
she {s++; REJECT;}
he {h++}
\n |. ;
在对 she 的出现次数进行计数后,lex 命令拒绝输入字符串,然后对 he 的出现次数进行计数。因为 he 并不包括 she,所以 REJECT 操作不必在 he 上。
将结果添加到 yytext 数组
典型情况下,来自输入流的下一个字符串覆盖 yytext 数组中的当前项。如果您使用 yymore 子例程,来自输入流的下一个字符串将被添加到 yytext 数组的当前项的尾部。
例如,下面的词法分析器搜索字符串:
%s instring
%%
<INITIAL>\" { /* start of string */
BEGIN instring;
yymore();
}
<instring>\" { /* end of string */
printf("matched %s\n", yytext);
BEGIN INITIAL;
}
<instring>. {
yymore();
}
<instring>\n {
printf("Error, new line in string\n");
BEGIN INITIAL;
}
尽管通过匹配多个规则,字符串可能被识别,但是反复调用 yymore 子例程可以确保 yytext 数组包含整个字符串。
将字符返回到输入流
要将字符返回给输入流,请使用下面的调用:
yyless(n)
其中 n 是当前字符串中要保持的字符数。字符串中超过此数目的字符被返回到输入流。yyless 子例程提供的先行函数类型与 /(斜杠)运算符所使用的相同,但是它允许更多对其用法的控制。
不止一次使用 yyless 子例程处理文本。例如,当语法分析 C 语言程序时,诸如 x=-a 之类的表达式很难理解。它表示 x等于-a,还是 x -= a 的旧的表述形式(意味着将 x减去值a)?要将此表达式作为 x等于-a 处理,但是要打印警告消息则请使用如下的规则:
=-[a-zA-Z] {
printf("Operator (=-) ambiguous\n");
yyless(yyleng-1);
... action for = ...
}
输入/输出子例程
lex 程序允许程序使用下述输入/输出(I/O)子例程:
input()
|
返回下一个输入字符 |
output(c)
|
将字符 c 写到输出 |
unput(c)
|
将字符 c 推回输入流,稍后再通过 input 子例程读出 |
winput()
|
返回下一个多字节输入字符 |
woutput(C)
|
将多字节字符 C 写回输出流 |
wunput(C)
|
将多字节字符 C 推回输入流,以通过 winput 子例程读出 |
lex 程序提供这些子例程作为宏定义。子例程的代码在 lex.yy.c 文件中。您能覆盖它们并提供其他版本。
定义 winput、wunput 和 woutput 宏以使用 yywinput、yywunput 和 yywoutput 子例程。考虑到兼容性,yy 子例程随后使用 input、unput 和 output 子例程来读、写和替换完全多字节字符中需要数目的字节。
这些子例程定义外部文件和内部字符之间的关系。如果您更改子例程,请以相同的方式将它们全部更改。这些子例程应该遵循这些规则:
- 所有的子例程必须使用相同的字符集。
-
input 子例程必须返回 0 值以指示文件的末尾。
- 不要更改 unput 子例程和 input 子例程的关系,否则先行函数会不起作用。
lex.yy.c 文件允许词法分析器最多备份 200 个字符。
要读包含 NULL 的文件,请创建不同版本的 input 子例程。在 input 子例程的正常版本中,(从空字符)返回的值 0 表明这是文件的末尾,且将终止输入。
字符集
lex 命令生成的词法分析器通过 input、output 和 unput 子例程处理字符 I/O。因此,要在 yytext 子例程中返回值,lex 命令使用这些子例程使用的字符说明。但是,在内部 lex 命令使用小整数代表每一个字符。当使用标准库时,此整数是计算机用来表示字符的位模式的值。正常情况下,字母 a 用与字符常量 a 相同的格式表示。如果您使用不同的 I/O 子例程更改此解释,请将转换表放到说明文件的定义部分。转换表在包含下述条目的行开始和结束:
%T
转换表包含指示与每个字符关联的值的其他行。例如:
%T
{integer} {character string}
{integer} {character string}
{integer} {character string}
%T
文件末尾处理
当词法分析器到达文件末尾时,它调用 yywrap 库子例程,此调用返回值 1,指示词法分析器应该继续在输入末尾正常结束。
但是,如果词法分析器从多个源接收到输入,请更改 yywrap 子例程。新的函数必须获取新的输入并将值 0 返回给词法分析器。返回值 0 指示程序应该继续处理。
您也可以包含代码,以在词法分析器在新版本的 yywrap 子例程中终止时,打印摘要报告和表。yywrap 子例程是强制 yylex 子例程识别输入末尾的唯一途径。