两种设计方案以及各自优缺点
我们先学手工构造
词法分析器手工构造
- 关系运算符的手工构造:
上面这个图其实我有点疑惑,下面说一下,我自己的理解
C语言中的关系运算符,有<、 <=、 > 、>= 、!= 、== 、=等
我们可以看到关系运算符最多有两个字符,所以我们至少要读两个字符。①第一次读的是“<”号,那我们再读第二个运算符,如果第二 个读到的是=号,那我们返回LE,意思就是小于等于。如果第二次读到的是>(事实上我就是不明白为什么能出现<>这种组合方式,编译器不会报错吗,可能老师只是让我们好理解吧),那么返回NE,表示不相等。如果我们读到其他运算符,那我们就回滚,返回LT,称为仅小于。所以懂这种使用分支方式来考虑关系运算的设计思想就可以了。
这里就是使用伪代码实现关系运算的示例 - 标识符的手工构造
c的标识符只能以_和字母为开头,中间可以有数字,下划线,字母;所以构造方法和关系运算一样,先读第一个字符看是不是字母或者下划线,然后读第二个,依次。。。
- 第二类关键字算法构造
可以构造一张包含所有关键字的哈希表,我们先按照第二类里面所给的手工构造的分析器识别字符,然后将我们识别之后得到的结果到关键字的哈希表中查找,看是否是关键字,如果是的话按关键字返回,否则按标识符返回。
该方法的好处是:我们可以不在2给出的方法中区分关键字和标识符,方法2区分关键字和标识符还需要花费更多的代码和时间。现在我们不区分,通过构造关键字哈希表,查表来完成这一工作会更为便捷。
词法分析器生成器
原理: 我们只需要给出我们识别源语言的规则给词法分析器生成器,他就能给我们生成一个词法分析器
所以我们的任务就被分解成了①如何给出声明式的规范,也就是给出我们识别源语言的规则,这也需要去写代码②如何构造中间的词法分析器生成器,内容包括定义数据结构等
任务①:
- 首先我们要理解正则表达式(RE)这个概念,它属于申明规范中的内容(可以近似的视正则表达式为申明规范)
- 我们先以熟悉的算术表达式来类比上图的内容。给定的字符集={0,1,2,3…9,+,-,X,➗,=},我们用这些字符集构造的表达式就是算术表达式。比如2+3-4这样的。
- (以C语言为例)正则表达式的字符集={NUL,EOF,…1,2,…9,A,…Z}等,说白了C语言的正则表达式的字符集就是ASCII码的集合,而Java就是Unicode编码的集合,我们这里是抽象成了c1,c2来表示
- WCNM,什么垃圾编辑器,不小心撤销之后找不会了,我那么多字白打了
- 简而言之就是,空串是正则表达式,字符集中的单个字符是正则表达式,两个正则表达式MIN,MN的表示方法{MIN表示或,即选择一个,MN是两个表达式并起来},还是正则表达式,对一个正则表达式M,可以折腾出无限个正则表达式M*来表示。
- 总之,正则表达式就是我们用一种符号集合的方法表示出所有的符合我们要求的表达式。
- 例如,之前我们的说的是C语言的正则表达式,我们知道他的字符集是ASCII码。现在我们构造C语言中标识符的正则表达式。首先,标识符只能由字母,数字,下划线表示,所以标识符的字符集={a,b,…z,A,…,Z,0,1,…9,_},是2X26+10+1=63个字符。
- 现在构造正则表达式:我们知a是正则表达式,b是正则表达式,那么a|b还是正则表达式,那么(a|b|c|…|A|…|Z|)也是正则表达式,这里用到了上面的选择,注意括号只是为了让我们好识别它是一个字符表达式。我们令K=(a|b|c|…|A|…|Z|),发现这是标识符的第一位的正则表达式,即只能为字母与下划线,同理我们构造出非第一位的正则表达式,令S=(a|b|c|…|A|…|Z|_|0|1|…|9),相比于K多了10个,我们令M=KL,即连接,然后再将闭包,即M*,这样我们就得到了标识符的所有正则表达式(注意这里M是我缩写的,事实上它是等于两个长串的连接),怎么样,太神奇了,虽然我现在不知道有什么用。
10.最后我们可以引入语法糖,简化构造,例如我们上面说的标识符的构造用到的k,K=(a|b|c|…|A|…|Z|),我们用第一种语法糖,可以简化写为[a-],即a到下划线,简化我们正则表达式的构造。语法糖可以为我们的构造提供很大的方便,不过语法糖本身也是我们使用正则表达式来实现的。
任务②: 我们要了解有限状态自动机这个概念,它是我们描述经中间的词法分析器生成器生成的词法分析器的一个数学工具