1、%正则表达式声明的格式为“命名 表达式”,比如:digit 0-9+alphabet A-Za-z+whitespace t+功能函数即为正常的C语言代码,遵守C语言代码格式即可。(二)Lex特性简介1Lex依次尝试每一个规则,尽可能地匹配最长的输入流。2如果有一些内容根本不匹配任何规则,那么Lex将把它拷贝到标准输出。二、正则表达式简介在编写处理字符串的程序或网页时,经常会有查找符合某些复杂规则的字符串的需要。正则表达式就是用于描述这些规则的工具。换句话说,正则表达式就是记录文本规则的代码。在这里,只用到了字符转义、重复、字符类、反义、贪婪与懒惰等较为基础的语法。以下就简单对涉及到的语法进行
2、介绍。(一)字符转义例如,.*在正则表达式中表示匹配所有,因此若要匹配.或*,则需要对其进行转义,即.和*。(二)重复在对数字和标识符等进行匹配时,字符位数不止一位,但遵守相同的规则,因此使用“重复”进行匹配。例如,对数字进行匹配:0-9+,“+”表示至少重复一次,即数字长度至少为1。例如,对标识符进行匹配,由于C语言标识符必须以下划线或字母开头,所以规则为:_|A-Za-z+(_|A-Za-z|0-9)*。“_|A-Za-z+”表示必须以下划线或字母开头,“(_|A-Za-z|0-9)*”表示之后可以跟字母数字下划线,但是也可以不跟,这就是“+”和“*”所表示的“重复”的不同之处。(三)字符
3、类与反义对没有预定义元字符的字符集合,需要在方括号里将其列出,例如,aeiou就是对任何一个英文元音字母的匹配,同理,0-9代表的含义与元字符d完全一致,都是对一位数字进行的匹配。有时需要查找不属于某个能简单定义的字符类的字符。比如想查找除了数字以外,其它任意字符都行的情况,这时需要用到反义:对于元字符,其反义即为其大写:D,匹配非数字的字符。更为普遍的表达方式如下:aeiou,匹配除了aeiou这几个字母以外的所有字符。(四)贪婪与懒惰与Lex对规则的处理类似,正则表达式也会在匹配整个表达式的前提下,匹配尽可能多的字符,这就是“贪婪”。有时,需要匹配尽可能少的字符,就是“懒惰”匹配,具体的例
4、子如下:a.*?b,由于.*后加了?,表示“懒惰”匹配,因此会匹配最短的,以a开始,以b结束的字符。例如字符串aabab,“贪婪”匹配的结果是aabab,而“懒惰”匹配的结果则是aab。三、词法分类识别这里,将源程序中的字符分为七类。由于字符串对于词法分析以及后续的语法分析的意义不大,因此,这里将其单独列为一类;由于头文件的声明中会出现“”“”等分隔符,尤其是“”,会与运算符中的大于小于号产生冲突,因此这里把头文件也单独列为一类。(一)保留字C语言共有32个保留字,如图:(二)标识符C语言标识符的要求是, 必须以下划线或者字母开头,之后可以跟字母、数字、下划线。(三)常数由于常数的表达方式较多
5、,这里只是代表性地选取了几种方式进行识别。(四)操作符C语言操作符包括一元操作符、二元操作符和三元操作符。(五)字符串这里的字符串是指包围在单引号或双引号内的字符串。(六)分隔符这里选取了以下经常见到的分隔符:(七)头文件头文件的格式有一下两种:四、词法识别分类规则(一)常数的识别根据上述对常数识别的需求,构造出了如下正则表达式:0-9+.0-9*|0-9+.0-9*(e|E+|-0-9+)|0x|X(0-9abcdefABCDEF+)这里,为了美观,以“|”为分隔符,对表达式进行了换行处理,每一行对应一个常数类别。第一行,表示对整数和浮点数进行匹配;第二行,先对整数或浮点数进行匹配,之后匹配
6、e或E,最后匹配一个整数,即123.456e+789的形式,即科学计数法;第三行,先匹配数字0,之后匹配字母x或X,最后匹配0-9及a-f或A-F的字符,即0x123abc的形式,即16进制表示法。(二)保留字的识别C语言中有32个保留字,且区分大小写,因此,这里构造如下正则表达式进行匹配:auto|break|case|char|const|continue|default|do|double|else|enum|extern|float|for|goto|if|int|long|register|return|short|signed|sizeof|static|struct|switch
7、|typedef|union|unsigned|void|volatile|while可见,只需简单的将各个保留字通过“|”符号进行连接,即可实现对所有保留字的识别。(三)分隔符的识别由于分隔符中存在与正则表达式中元字符相冲突的字符,如“.”,“”等,因此需要对这些产生冲突的符号进行转义,构造如下正则表达式:,|;|(|)|(四)标识符的识别根据上述的C语言标识符的要求,构造如下正则表达式:_|A-Za-z+(_|A-Za-z|0-9)*此处涉及正则表达式语法较为简单,不再赘述。(五)操作符的识别由于操作符中,存在大量运算符号与正则表达式的元字符冲突,因此,根据C语言中的三种操作符,构造以下正
8、则表达式:=|=|!=|&|!|&|=可以看到表达式中含有大量转义字符,使得原本容易识别的C语言操作符变得难以分辨,但根据“|”连接符的指示,单个单个的分析,还是很容易明白的。(六)其他字符的识别对于空白、换行、注释、头文件等的识别,规则较为简单,这里不再一一举出。五、词法识别分类顺序由于Lex对正则表达式的支持并不完全,因此存在不同表达式匹配同一字符串的现象。这里,为了减少这种现象的发生,降低出错率,对规则的优先级进行了调整,以更好的满足设计需求。这里从两方面进行说明,一是标识符和保留字的识别顺序,二是一、二、三元操作符的识别顺序。(一)标识符和保留字的识别顺序由于标识符的识别规则会同时把保
9、留字当做标识符进行匹配,从而产生错误,因此,需要调换这两条规则的匹配优先级,调整的结果如图所示:这样就能保证,Lex在进行词法分析时,优先将满足保留字匹配规则的字符串匹配出来,从而避免误判。(二)一、二、三元操作符的识别顺序由于C语言中存在多元操作符,因此在对操作符进行匹配的时候,就要考虑到将其完整匹配,而不是将一个多元操作符分为多个一元操作符进行匹配。这里,就需要对正则表达式内匹配规则的顺序进行一个调整,即对位数多的操作符优先进行匹配,具体的做法就是,将其匹配规则提前,如下所示:这样在对操作符进行匹配时,就能够正确进行匹配,不产生错误和冲突。六、测试代码为了检验该词法分析器是否能实现词法分析
10、,根据功能需求,设计了如下测试代码ori.c:对头文件、两种注释方式、四种类型的数字、一元和三元运算符、字符串、分隔符、标识符、保留字在词法上进行了相关的构造。由于未涉及语义分析,因此,为了便于做词法分析,代码加入了各种类型的语句,但只是形式,不能正常运行。七、结果分析程序运行命令:其中lex.l即为编写的Lex源代码;lex.yy.x是上一步flex对lex.l源代码进行处理,生成的文件;用gcc对lex.yy.c进行编译,生成可执行二进制文件flex;运行flex,将对ori.c文件进行词法分析。以下为程序分析结果:可以看出,该词法分析器能够正常运行,对程序的词法进行了准确率较高的识别。但
11、是,仍然存在一些问题,比如,对于负数无法进行识别,程序会将负号识别为操作符,这一错误无法通过调整优先级进行改正,需要对正则表达式进行进一步改进。由于减号和负号的适用场合较为复杂,之前尝试着使用正则表达式的“负向零宽断言”语法进行匹配,但发现Lex无法识别该语法,导致无法运行,目前暂时还未想到解决办法。另外一点是,Lex只能进行语法分析,且没有错误处理机制,要做到错误处理,只有在语义层使用Yacc进行处理。因此,若出现词法错误,Lex会直接把无法识别的字符打印出来,而不对其进行进一步的处理,也未提供相关功能。八、参考文献程序设计语言编译原理,陈火旺等著,国防工业出版社.正则表达式30分钟入门教程,Yacc 与 Lex 快速入门,Lex Manual Page,http:/plan9.bell-
copyright@ 2008-2022 冰豆网网站版权所有
经营许可证编号:鄂ICP备2022015515号-1