210 likes | 411 Views
使用Flex. 怎样使用 LEX?. LEX 的输入为包含正则表达式及其处理动作的文件 (*.l) 输出为包含词法分析函数 yylex() 的源代码文件 (lexyy.c 或 lex.yy.c) 。用户可将它与其它程序链接即可运行。 LEX 分析采用 DFA 转换表算法。. 怎样写 LEX 的输入文件 ?. 首先要弄清两个基本问题 : 正规式在 LEX 中怎样书写 输入文件的格式和约定. 正则表达式. flex词法分析器的模式使用了正则表达式语言,本质上是扩展的POSIX正则表达式 正则表达式使用元语言来描述想匹配的模式
E N D
怎样使用LEX? • LEX的输入为包含正则表达式及其处理动作的文件(*.l) • 输出为包含词法分析函数yylex()的源代码文件(lexyy.c或lex.yy.c)。用户可将它与其它程序链接即可运行。 • LEX分析采用DFA转换表算法。
怎样写LEX的输入文件? • 首先要弄清两个基本问题: • 正规式在LEX中怎样书写 • 输入文件的格式和约定
正则表达式 • flex词法分析器的模式使用了正则表达式语言,本质上是扩展的POSIX正则表达式 • 正则表达式使用元语言来描述想匹配的模式 • 元语言使用标准的文本字符,一部分代表自身而另一部分代表模式(有特殊意义) • 未说明有特殊意义的文本字符代表其自身,如:字母和数字
正则表达式中特殊意义的字符 • 字符 含义 • . 匹配除 \n外的任意字符 • [ ] 字符类。匹配括号内的任意字符。 • 如果第一个字符是 ^ 那么它表示否定模式。 • 例: [abC] 匹配 a, b, 和 C中的任何一个。 • - 用来指定范围 • 例如:[A-Z] 指从 A 到 Z 之间的任意一个字符。
正则表达式中特殊意义的字符 • 字符 含义 • {-}新版本才有,匹配前一个字符类减去后一个字符类,如:[a-z]{-}[jv] • ^ 如果是正则表达式的第一个字符就匹配行首,也被用于方括号中表示补集 • $ 如果是正则表达式的最后一个字符则匹配一行的结尾。 • { } 表示重复(如果它们括起来的是数字)或 • 定义的表达式 (如果它们括起来的是一个名字) 。 • 例: A{1,3} 表示 A 可能出现1次~3次0{3}匹配000, {digit} 名字为digit的正则表达式
正则表达式中特殊意义的字符 * 匹配0个或者多个上述的模式。 + 匹配1个或者多个上述模式。 ? 匹配0个或1个上述模式。 \ 用来转义元字符。 用来覆盖字符在此表中定义的特殊意义,表示字符本意,如:\n表示换行,\\表示字符\本身,\*表示星号本身 | 表达式间的逻辑或,匹配其中之一,如:性别可表示为male|female
正则表达式中特殊意义的字符 字符 含义 “<一些符号>” 字符的字面含义。用于元字符。 / 尾部上下文(向前匹配)。 如果在匹配的模版中的“/”后跟有后续表达式, 只匹配模版中“/”前面的部分。 例:输入 A01,那么在模版 A0/1 中的 A0 是匹配的。 但不会匹配串A0或者A02。 ( a) 用于把一系列正则表达式组成一个新表达式,匹配a本身。 % 专用于Lex源程序的段间分割符
正则表达式举例 正则表达式 含义 joke[rs]匹配 jokes 或 joker。 A{1,2}shis? 匹配 AAshis, Ashis, AAshi, Ashi。 (A[b-e])+ 匹配以A开头、奇数位为A、偶数位为b~e之间的任意字符且长度为大于0的偶数的串。
程序设计语言正则表达式举例 Lex 中的标记声明类似 C 中的变量名。每个标记都有一个相关的表达式。 标记 相关表达式 含义 数字(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用户希望接受整个输入串,而不产生任何 输出,就必须提供与所有可能词形相匹配的规则. Lex 动作 当上述的正规式被匹配的时候,Lex将运行相应的动作.
Lex 动作 ——滤掉某些输入 使用一个 C的空语句 ; 例: 滤掉三种空格的字符(空格,tab和回车换行) [ \t\n] ; 省略动作的另一个简单的方法是使用动作字符|, 表示这条规则的动作与下一条规则相同. 上例也可写为:“”| "\t"| "\n" ;
Lex动作 ——输出正规式真正匹配的内容 例:正规式[a-z]+ [a-z]+ printf("% s",yytext); 或 [a-z]+ ECHO; 注:Lex 将匹配的字符串存在外部字符数组yytext中
Lex动作 ——计数 Lex中使用计数器yyleng来表示被匹配的字符个数. 例:对输入串中的单词的个数和字符的个数进行计数, 用语句 [a-zA-Z]+ {words++; chars+=yyleng;} 匹配的串的最后一个字符可用以下方式访问 yytext[yyleng-1]
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) C代码的插入 • 任何写在定义部分% {和% }之间的文本将被直接复制到外置于任意过程的输出程序之中。 • 辅助过程中的任何文本都将被直接复制到Lex 代码末尾的输出程序中。 • 将任何跟在行为部分(在第1个% %之后)的正则表达式之后(中间至少有一个空格)的代码插入到识别过程yylex的恰当位置,并在与对应的正则表达式匹配时执行它。代表一个行为的C代码可以既是一个C语句,也可以是一个由任何说明及由位于花括号中的语句组成的复杂的C语句。
(3) 内部名字 列出了在本章中所提到过的Lex内部名字。 表 一些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)