第 2章 词法分析要点
利用 Lex自动生成扫描程序
2.6 利用 Lex自动生成扫描程序
Lex 代表 Lexical Analyzar
使用 LEX工具可根据描述语言的词法构成规则的正规式来自动生成扫描程序。
LEX在 unix平台上使用,在 windows平台上的一种常用实现是 FLEX,它是 GNU compiler
package的一部分。
Lex 简介词法分析器的自动产生 --LEX
Lex
compiler
Lex
source
program
lex.l
lex.yy.c
C
compilerlex.yy.c a.out
a.outInputstream
sequence
of
tokens
正则表达式由符号组成,符号一般是字符和数字,但
Lex 中还有一些特殊标记。
2.6.1 正则表达式的 Lex约定 (1)
字符 含义
A-Z,0-9,a-z 构成了部分模式的字符和数字
,匹配除 \n外的任意字符
- 用来指定范围例如,A-Z 指从 A 到 Z 之间的所有字符。
字符 含义
[ ] 一个字符集合。匹配括号内的任意字符。
如果第一个字符是 ^ 那么它表示否定模式。
例,[abC] 匹配 a,b,和 C中的任何一个。
* 匹配 0个或者多个上述的模式。
+ 匹配 1个或者多个上述模式。
匹配 0个或 1个上述模式。
\n 匹配行尾
2.6.1 正则表达式的 Lex约定 (2)
字符 含义
$ 作为模式的最后一个字符匹配一行的结尾。
{ } 表示重复(如果它们括起来的是数字)或定义的表达式 (如果它们括起来的是一个名字 ) 。
例,A{1,3} 表示 A 可能出现 1次或 3次。
{digit} 名字为 digit的 正则表达式
\ 用来转义元字符。
用来覆盖字符在此表中定义的特殊意义,表示字符本意。
^ 否定。
| 表达式间的逻辑或。
2.6.1 正则表达式的 Lex约定 (3)
字符 含义
,<一些符号 >” 字符的字面含义。用于元字符。
/ 向前匹配。
如果在匹配的模版中的,/”后跟有后续表达式,
只匹配模版中,/”前面的部分。
例:输入 A01,那么在模版 A0/1 中的 A0 是匹配的。
( a) a本身。
% 专用于 Lex源程序的段间分割符
2.6.1 正则表达式的 Lex约定 (4)
正则表达式 含义
joke[rs] 匹配 jokes 或 joker。
A{1,2}shis? 匹配 AAshis,Ashis,AAshi,Ashi。
(A[b-e])+ 匹配以 A开头、奇数位为 A、偶数位为 b~ e之间的任意字符且长度为大于 0的偶数的串。
正则表达式举例标记 相关表达式 含义数字 (nat) ([0-9])+ 1个或多个数字带符号数( signedNat) (“+”|”-”)? Nat 带符号的整数字符 (chars) [A-Za-z] 1个任意字符空格 (blank) " " 一个空格字 (word) (chars)+ 1个或多个 chars
标识符 (id) [A-Za-z][A-Za-z0-9]* 以字母开头后跟任意字母或者数字程序设计语言正则表达式举例
Lex 中的标记声明类似 C 中的变量名。每个标记都有一个相关的表达式。
2.6.2 LEX输入文件的格式定义集
%%
规则部分
%%
辅助程序部分
LEX源程序由 3个部分构成,
定义部分出现在第 1个双百分号之前。
它包括两种成份:
( 1) 函数外部的任意 C代码必须插入到应在这一部分中分隔符,% {”和
,% }”之间。
( 2) 正则表达式的名字定义正则表达式的名字写在一行的第 1列,空格后跟所表示的正则表达式 。
2.6.2 LEX输入文件的格式
—— 定义部分
<规则部分 >包含着一些规则。
一条规则由带有一段 C代码的正则表达式组成,书写为:
正则表达式 { c代码 }
当匹配相对应的正则表达式时,对应的 C代码就会被执行。
<辅助程序部分 >是一些 C代码,供第二部分调用。
还可定义 main函数,以使生成的扫描程序能独立运行,
2.6.2 LEX输入文件的格式
—— 规则部分、辅助程序部分
Lex 动作当上述的正规式被匹配的时候,Lex将运行相应的动作,
注:有一个将输入串原样照抄到输出串的默认动作,
所有不匹配的串都将执行这个动作,
因此如果 Lex用户希望接受整个输入串,而不产生任何输出,就必须提供与所有可能词形相匹配的规则,
2.6.2 LEX输入文件的格式
Lex 动作
——滤掉某些输入使用一个 C的空语句 ;
例,滤掉三种空格的字符 (空格,tab和回车换行 )
[ \t\n] ;
省略动作的另一个简单的方法是使用动作字符 |,
表示这条规则的动作与下一条规则相同,
上例也可写为:,,|
"\t"|
"\n" ;
2.6.2 LEX输入文件的格式
Lex动作
——输出正规式真正匹配的内容例:正规式 [a-z]+
[a-z]+ printf("% s",yytext);
[a-z]+ ECHO;
或注,Lex 将匹配的字符串存在外部字符数组 yytext中
2.6.2 LEX输入文件的格式匹配的串的最后一个字符可用以下方式访问
yytext[yyleng-1]
Lex中使用计数器 yyleng来表示被匹配的字符个数,
例:对输入串中的单词的个数和字符的个数进行计数,
用语句 [a-zA-Z]+ {words++; chars+=yyleng;}
2.6.2 LEX输入文件的格式
Lex动作
—— 计数例 给文本添加行号的扫描程序
% {
/* 在源代码中加行号的 LEX程序 * /
#include <stdio.h>
int lineno = 1;
% }
line,*\n
%%
{line} { printf ( "%5d %s",lineno++,yytext ); }
%%
main( )
{ yylex(); return 0; }
2.6.2 LEX输入文件的格式
Lex应用举例(一)
例,只输出以 a开头和 a结尾的所有行的 Lex 输入文件:
% {
#include <stdio.h>
% }
ends_with_a,*a\n
begins_with_a a.*\n
% %
{ends_with_a} ECHO;
{begins_with_a} ECHO;
.*\n ;
% %
main( )
{ yylex( ); return 0; }
2.6.2 LEX输入文件的格式
Lex应用举例(二)
注:已匹配的串将不再与后面的正则表达式进行匹配。
例 将除 c-风格注释外所有的大写字母转变成小写字母的程序:
% {
#include <stdio.h>
#ifndef FALSE
#define FALSE 0
#endif
#ifndef TRUE
#define TRUE 1
#endif
% }
% %
2.6.2 LEX输入文件的格式
Lex应用举例(三)
[A-Z] {putchar(tolower(yytext[0]));
/* yytext[0]为找到的单大写字母 */ }
"/*" { char c ;
int done = FALSE;
ECHO ;
do
{ while ((c=input())!='*') putchar( c ) ;
putchar( c ) ;
while( ( c = input( ) ) = = ‘ * ’) putchar( c ) ;
putchar( c ) ;
if (c == '/') done = TRUE;
} while (!done);
}
% %
void main(void ){ yylex();}
2.6.2 LEX输入文件的格式
Lex应用举例(三)
(1) 二义性的解决
Lex输出总是首先将可能的最长子串与规则相匹配。
如果某个子串可与两个或更多的规则匹配,则 L e x的输出将找出列在行为部分中的第 1个规则。
如果没有规则可与任何非空子串相匹配,则缺省行为将下一个字符复制到输出中并继续下去。
Lex约定
2.6.2 LEX输入文件的格式
(2) C代码的插入
任何写在定义部分 % {和 % }之间的文本将被直接复制到外置于任意过程的输出程序之中。
辅助过程中的任何文本都将被直接复制到 Lex 代码末尾的输出程序中。
将任何跟在行为部分(在第 1个 % %之后)的正则表达式之后(中间至少有一个空格)的代码插入到识别过程
yylex的恰当位置,并在与对应的正则表达式匹配时执行它。代表一个行为的 C代码可以既是一个 C语句,也可以是一个由任何说明及由位于花括号中的语句组成的复杂的 C语句。
Lex约定
2.6.2 LEX输入文件的格式
(3) 内部名字列出了在本章中所提到过的 Lex内部名字。
表 2-3 一些 L e x内部名字
Lex 内部名字 含义 /使用
lex.yy.c或 lexyy.c Lex 输出文件名
yylex Lex 扫描例程
yytext 当前行为匹配的串
yyin Lex 输入文件(缺省,stdin)
yyout Lex 输出文件(缺省,stdout)
input Lex 缓冲的输入例程
ECHO Lex 缺省行为(将 yytext打印到 yyout
2.6.2 LEX输入文件的格式
利用 Lex自动生成扫描程序
2.6 利用 Lex自动生成扫描程序
Lex 代表 Lexical Analyzar
使用 LEX工具可根据描述语言的词法构成规则的正规式来自动生成扫描程序。
LEX在 unix平台上使用,在 windows平台上的一种常用实现是 FLEX,它是 GNU compiler
package的一部分。
Lex 简介词法分析器的自动产生 --LEX
Lex
compiler
Lex
source
program
lex.l
lex.yy.c
C
compilerlex.yy.c a.out
a.outInputstream
sequence
of
tokens
正则表达式由符号组成,符号一般是字符和数字,但
Lex 中还有一些特殊标记。
2.6.1 正则表达式的 Lex约定 (1)
字符 含义
A-Z,0-9,a-z 构成了部分模式的字符和数字
,匹配除 \n外的任意字符
- 用来指定范围例如,A-Z 指从 A 到 Z 之间的所有字符。
字符 含义
[ ] 一个字符集合。匹配括号内的任意字符。
如果第一个字符是 ^ 那么它表示否定模式。
例,[abC] 匹配 a,b,和 C中的任何一个。
* 匹配 0个或者多个上述的模式。
+ 匹配 1个或者多个上述模式。
匹配 0个或 1个上述模式。
\n 匹配行尾
2.6.1 正则表达式的 Lex约定 (2)
字符 含义
$ 作为模式的最后一个字符匹配一行的结尾。
{ } 表示重复(如果它们括起来的是数字)或定义的表达式 (如果它们括起来的是一个名字 ) 。
例,A{1,3} 表示 A 可能出现 1次或 3次。
{digit} 名字为 digit的 正则表达式
\ 用来转义元字符。
用来覆盖字符在此表中定义的特殊意义,表示字符本意。
^ 否定。
| 表达式间的逻辑或。
2.6.1 正则表达式的 Lex约定 (3)
字符 含义
,<一些符号 >” 字符的字面含义。用于元字符。
/ 向前匹配。
如果在匹配的模版中的,/”后跟有后续表达式,
只匹配模版中,/”前面的部分。
例:输入 A01,那么在模版 A0/1 中的 A0 是匹配的。
( a) a本身。
% 专用于 Lex源程序的段间分割符
2.6.1 正则表达式的 Lex约定 (4)
正则表达式 含义
joke[rs] 匹配 jokes 或 joker。
A{1,2}shis? 匹配 AAshis,Ashis,AAshi,Ashi。
(A[b-e])+ 匹配以 A开头、奇数位为 A、偶数位为 b~ e之间的任意字符且长度为大于 0的偶数的串。
正则表达式举例标记 相关表达式 含义数字 (nat) ([0-9])+ 1个或多个数字带符号数( signedNat) (“+”|”-”)? Nat 带符号的整数字符 (chars) [A-Za-z] 1个任意字符空格 (blank) " " 一个空格字 (word) (chars)+ 1个或多个 chars
标识符 (id) [A-Za-z][A-Za-z0-9]* 以字母开头后跟任意字母或者数字程序设计语言正则表达式举例
Lex 中的标记声明类似 C 中的变量名。每个标记都有一个相关的表达式。
2.6.2 LEX输入文件的格式定义集
%%
规则部分
%%
辅助程序部分
LEX源程序由 3个部分构成,
定义部分出现在第 1个双百分号之前。
它包括两种成份:
( 1) 函数外部的任意 C代码必须插入到应在这一部分中分隔符,% {”和
,% }”之间。
( 2) 正则表达式的名字定义正则表达式的名字写在一行的第 1列,空格后跟所表示的正则表达式 。
2.6.2 LEX输入文件的格式
—— 定义部分
<规则部分 >包含着一些规则。
一条规则由带有一段 C代码的正则表达式组成,书写为:
正则表达式 { c代码 }
当匹配相对应的正则表达式时,对应的 C代码就会被执行。
<辅助程序部分 >是一些 C代码,供第二部分调用。
还可定义 main函数,以使生成的扫描程序能独立运行,
2.6.2 LEX输入文件的格式
—— 规则部分、辅助程序部分
Lex 动作当上述的正规式被匹配的时候,Lex将运行相应的动作,
注:有一个将输入串原样照抄到输出串的默认动作,
所有不匹配的串都将执行这个动作,
因此如果 Lex用户希望接受整个输入串,而不产生任何输出,就必须提供与所有可能词形相匹配的规则,
2.6.2 LEX输入文件的格式
Lex 动作
——滤掉某些输入使用一个 C的空语句 ;
例,滤掉三种空格的字符 (空格,tab和回车换行 )
[ \t\n] ;
省略动作的另一个简单的方法是使用动作字符 |,
表示这条规则的动作与下一条规则相同,
上例也可写为:,,|
"\t"|
"\n" ;
2.6.2 LEX输入文件的格式
Lex动作
——输出正规式真正匹配的内容例:正规式 [a-z]+
[a-z]+ printf("% s",yytext);
[a-z]+ ECHO;
或注,Lex 将匹配的字符串存在外部字符数组 yytext中
2.6.2 LEX输入文件的格式匹配的串的最后一个字符可用以下方式访问
yytext[yyleng-1]
Lex中使用计数器 yyleng来表示被匹配的字符个数,
例:对输入串中的单词的个数和字符的个数进行计数,
用语句 [a-zA-Z]+ {words++; chars+=yyleng;}
2.6.2 LEX输入文件的格式
Lex动作
—— 计数例 给文本添加行号的扫描程序
% {
/* 在源代码中加行号的 LEX程序 * /
#include <stdio.h>
int lineno = 1;
% }
line,*\n
%%
{line} { printf ( "%5d %s",lineno++,yytext ); }
%%
main( )
{ yylex(); return 0; }
2.6.2 LEX输入文件的格式
Lex应用举例(一)
例,只输出以 a开头和 a结尾的所有行的 Lex 输入文件:
% {
#include <stdio.h>
% }
ends_with_a,*a\n
begins_with_a a.*\n
% %
{ends_with_a} ECHO;
{begins_with_a} ECHO;
.*\n ;
% %
main( )
{ yylex( ); return 0; }
2.6.2 LEX输入文件的格式
Lex应用举例(二)
注:已匹配的串将不再与后面的正则表达式进行匹配。
例 将除 c-风格注释外所有的大写字母转变成小写字母的程序:
% {
#include <stdio.h>
#ifndef FALSE
#define FALSE 0
#endif
#ifndef TRUE
#define TRUE 1
#endif
% }
% %
2.6.2 LEX输入文件的格式
Lex应用举例(三)
[A-Z] {putchar(tolower(yytext[0]));
/* yytext[0]为找到的单大写字母 */ }
"/*" { char c ;
int done = FALSE;
ECHO ;
do
{ while ((c=input())!='*') putchar( c ) ;
putchar( c ) ;
while( ( c = input( ) ) = = ‘ * ’) putchar( c ) ;
putchar( c ) ;
if (c == '/') done = TRUE;
} while (!done);
}
% %
void main(void ){ yylex();}
2.6.2 LEX输入文件的格式
Lex应用举例(三)
(1) 二义性的解决
Lex输出总是首先将可能的最长子串与规则相匹配。
如果某个子串可与两个或更多的规则匹配,则 L e x的输出将找出列在行为部分中的第 1个规则。
如果没有规则可与任何非空子串相匹配,则缺省行为将下一个字符复制到输出中并继续下去。
Lex约定
2.6.2 LEX输入文件的格式
(2) C代码的插入
任何写在定义部分 % {和 % }之间的文本将被直接复制到外置于任意过程的输出程序之中。
辅助过程中的任何文本都将被直接复制到 Lex 代码末尾的输出程序中。
将任何跟在行为部分(在第 1个 % %之后)的正则表达式之后(中间至少有一个空格)的代码插入到识别过程
yylex的恰当位置,并在与对应的正则表达式匹配时执行它。代表一个行为的 C代码可以既是一个 C语句,也可以是一个由任何说明及由位于花括号中的语句组成的复杂的 C语句。
Lex约定
2.6.2 LEX输入文件的格式
(3) 内部名字列出了在本章中所提到过的 Lex内部名字。
表 2-3 一些 L e x内部名字
Lex 内部名字 含义 /使用
lex.yy.c或 lexyy.c Lex 输出文件名
yylex Lex 扫描例程
yytext 当前行为匹配的串
yyin Lex 输入文件(缺省,stdin)
yyout Lex 输出文件(缺省,stdout)
input Lex 缓冲的输入例程
ECHO Lex 缺省行为(将 yytext打印到 yyout
2.6.2 LEX输入文件的格式