词法分析

词法分析
词法分析

第一章词法分析

本章主要掌握下面一些内容。

1.词法分析器的作用和接口,用高级语言编写词法分析器等内容,它们与词法分析器的实现有关。(我们没有安排这方面的习题,因为大部分教材上都有这方面的例子)。

2.掌握下面涉及的一些概念,它们之间转换的技巧、方法或算法。

?非形式描述的语言?正规式(?表示两个方向的转换都要掌握)

?正规式→ NFA(非确定的有限自动机)

?非形式描述的语言? NFA

? NFA → DFA(确定的有限自动机)

? DFA →最简DFA

?非形式描述的语言? DFA(或最简DFA)

1.1.叙述正规式(00 | 11)*( (01 | 10) (00 | 11)* (01 | 10) (00 | 11)* )*描述的语言。

答案该正规式所描述的语言是,所有由偶数个0和偶数个1构成的串。另外,和该正规式等价的正规式有( 00 | 11 | ( (01 | 10) (00 | 11)* (01 | 10) ) )*。

分析叙述正规式描述的语言并没有一种统一的办法,只能是通过对正规式的具体分析去总结。

该正规式的一个重要特点是,它是两个字符一组来考虑的。正规式(00 | 11)*表示的串的长度是偶数,每两个字符一组的话,不是00就是11。再看正规式(01 | 10) (00 | 11)* (01 | 10),它表示的串由01或10开始,中间有若干组00或11,最后出现01或10。这样的串仍然由偶数个0和偶数个1构成,只不过第一组是01或10的话,那么一定还要有一组01或10才能保证它们的偶数性。显然,正规式(01 | 10) (00 | 11)* (01 | 10) (00 | 11)*表示的串也仍然是由偶数个0和偶数个1构成。这样,可以判断题目所给的正规式表示的语言的每个句子都是由偶数个0和偶数个1构成。

反过来还需要考虑,任何由偶数个0和偶数个1构成的串是否都在这个语言中。这实际上是问,每个这样的串,其结构是否都符合正规式(00 | 11)*( (01 | 10) (00 | 11)* (01 | 10) (00 | 11)* )*所做的刻划。我们可以这样叙述由偶数个0和偶数个1构成的串,从左向右,每两个字符一组地考察,它

1,由若干个(强调一下,可以是零个)00或11开始(这由正规式(00 | 11)*描述);

2.一旦出现一个01或10,那么经过若干个00或11后,一定会出现一个01或10。这第二个01或10的后面可能还有若干个00或11,一直到串的结束,或者到再次出现01或10为止。如果串没有结束的话,就是重复出现这里所描述的结构(所以这由( (01 | 10) (00 | 11)* (01 | 10) (00 | 11)* )*描述)。

因此正规式(00 | 11)*( (01 | 10) (00 | 11)* (01 | 10) (00 | 11)* )*描述的是偶数个0和偶数个1构成的串。

可能会提出一个问题,这样的串是否能用更简单的观点来看待,也就是该语言是否能用更简洁的正规式描述。这是可能的。我们写出这样的正规式,

( 00 | 11 | ( (01 | 10) (00 | 11)* (01 | 10) ) )*

它是基于这样的考虑,满足要求的最简单的串有三种形式(空串除外):

1.00

2.11

3.(01 | 10) (00 | 11)* (01 | 10)

它们任意多次的重复构成的串仍然满足要求。

1.2.写出语言“由偶数个0和奇数个1构成的所有0和1的串”的正规定义。

答案even_0_even_1→(00 | 11)*( (01 | 10) (00 | 11)* (01 | 10) (00 | 11)* )* even_0_odd_1→ 1 even_0_even_1 | 0 (00 | 11)* (01 | 10) even_0_even_1

分析有了上一题的结果,这个问题应该容易解决。首先给上一题的正规式起个名字:even_0_even_1→ (00 | 11)*( (01 | 10) (00 | 11)* (01 | 10) (00 | 11)* )*对于偶数个0和奇数个1构成的串,其第一个字符可能是0或1。

1.如果是1,那么剩下的部分一定是偶数个0和偶数个1。

2.如果是0,那么经过若干个00或11,一定会出现一个01或10,才能保证0的个数是偶数,1的个数是奇数。若串还没有结束,剩余部分一定是偶数个0和偶数个1。

这样,正确的正规定义是:

even_0_odd_1→ 1 even_0_even_1 | 0 (00 | 11)* (01 | 10) even_0_even_1

1.3.写出语言“所有相邻数字都不相同的非空数字串”的正规定义。

答案no_0-8 →9

no_0-7 →(8 | no_0-8 8 ) (no_0-8 8 )* (no_0-8 | ε) | no_0-8

no_0-6 →(7 | no_0-7 7 ) (no_0-7 7 )* (no_0-7 | ε) | no_0-7

no_0-5 →(6 | no_0-6 6 ) (no_0-6 6 )* (no_0-6 | ε) | no_0-6

no_0-4 →(5 | no_0-5 5 ) (no_0-5 5 )* (no_0-5 | ε) | no_0-5

no_0-3 →(4 | no_0-4 4 ) (no_0-4 4 )* (no_0-4 | ε) | no_0-4

no_0-2 →(3 | no_0-3 3 ) (no_0-3 3 )* (no_0-3 | ε) | no_0-3

no_0-1 →(2 | no_0-2 2 ) (no_0-2 2 )* (no_0-2 | ε) | no_0-2

no_0 →(1 | no_0-1 1 ) (no_0-1 1 )* (no_0-1 | ε) | no_0-1

answer →(0 | no_0 0 ) (no_0 0 )* (no_0 | ε) | no_0

分析刚拿到这个问题,一定不知从哪儿下手。其实和上面一样,关键是找到一种合适的看待这种句子结构的观点。我们的观点是这样,每个这样的句子由若干个0把它分成若干段,如

123031357106678035590123

可以看成

123, 0, 313571, 0, 6678, 0, 3559, 0, 123

由0隔开的每一段,如313571,它不含0,并且又可以看成是由若干个1把它分成若干段。如此下去,就能找到该语言的正规定义。

按这个思路,上面的正规定义应该逆序看。

answer →(0 | no_0 0 ) (no_0 0 )* (no_0 | ε) | no_0

表示一个句子由若干个0分成若干段,特殊情况是整个句子不含0。在这个正规定义中,所引用的no_0表示不含0的串,它的定义和这个定义的形式一样,因为串的形式是一样的,只不过没有数字0。所以有

no_0 →(1 | no_0-1 1 ) (no_0-1 1 )* (no_0-1 | ε) | no_0-1

其中no_0-1表示不含0和1的串。依此类推,最后no_0-8是表示不含0, …, 8的没有重复数字的串,它只可能是单个9。

1.4.构造一个DFA,它接受∑ = {0, 1}上0和1的个数都是偶数的字符串。

答案见图1.1。

分析 对于这样的问题,不要急于去尝试画DFA ,先把问题分析一下,这里要接受的是偶数个0和偶数个1的串,和偶数相对的是奇数,因此,对于任意一个0和1的串,不论其0和1的个数有多少,总归不是偶数个就是奇数个。因此任意一个串属于下面四种情况之一。

0:偶数个0和偶数个1;

1:偶数个0和奇数个1; 2:奇数个0和偶数个1;

3:奇数个0和奇数个1。

并且不管一个串是处于上面哪一种情况,该串再添加一个0或1后,总是处于上面另一种情况。由此分析可以知道,DFA 只需四个状态就够了,并且状态转换也很容易画出来。答案中的四个状态对应到这儿的四种情况。空串是属于偶数个0和偶数个1的情况,因此0状态是开始状态。因为我们接受偶数个0和偶数个1的串,因此它也是接受状态。

1.5.构造一个DFA ,它接受 = {0, 1}上能被5整除的二进制数。 答案 见图1.2。 分析 由上题我们知道,构造DFA 之前,首先搞请楚问题的状态空间。即想明白应该有多少个状态,状态之间的转换条件,以及针对该问题的开始状态和接受状态。对于本题目,任意一个二进制数除以5时,只有余数为0(即整除),1,2,3和4五种情况。图中的五个状态也是这样起名字的。一个二进制数的后面添上一个0意味着其值变成原来的两倍,而后面添上一个1意味着其值变成原来的两倍再加1。不管是哪一种情况,都很容易从原来的余数决定值变化后的余数。这样,我们很快可以得出所有的状态转换。例如,我们考虑状态4。任何一个余4的数,两倍后一定余3,两倍再加1后一定还是余4。所以,状态4的0转换到状态3,而1转换到本身。显然,状态0既是开始状态又是接受状态。 需要注意的是,考虑状态空间时,还要检查我们是否取的是最简情况(即状态数极小)。例如,对于本题目,假如我们从这样的观点出发,每个二进制数都可以转换成一个十进制数。十进制数的末位有0到9十种情况,其中末位为0和5是能被5整除的情况。这样我们很可能会构造十个状态的DFA ,接受状态有两个。这也是一种解,但它不是最简的DFA 。

1.6处于/* 和 */之间的串构成注解,注解中间没有*/。画出接受这种注解的DFA 的状态转换图。

答案 见图1.3。标记为others 的边是指字符集中未被别的边指定的任意其它字符。 分析 这个DFA 的状态数及含义并不难确定,见下面的五个状态说明。 状态1:注释开始状态。 状态2:进入注释体前的中间状态。

图1.1 接受偶数个0和偶数个1的的DFA

图1.2 接受能被5整除的二进制数的DFA

状态3:表明目前正在注释体中的状态。 状态4:离开注释前的中间状态。 状态5:注释结束状态,即接受状态。

在这个DFA 中,最容易忽略的是状态4到本身的’*’转换。这个边的含义是:在离开注

释前的中间状态,若下一个字符是’*’,那么把刚才读过的’*’看成是注释中的一个字符,而把这下一个字符看成可能是结束注释的第一个字符。若没有这个边,那么象 /**** This is a comment ****/ 这样的注释就被拒绝。 另外,上面的状态转换图并不完整。例如,对于状态1,没有指明遇到其它字符怎么办。要把状态转换图画完整,还需引入一个死状态6,进入这个状态就再也出不去了。因为它不是接受状态,因此进入这个状态的串肯定不被接受。完整的状态转换图见下面图1.4,其中all 表示任意字符。在能够说清问题时,通常我们省略死状态和所有到它的边。 1.7 某操作系统下合法的文件名为

device:name.extension

其中第一部分(device:)和第三部分(.extension )可缺省,若device, name 和extension 都是字母串,长度不限,但至少为1,画出识别这种文件名的DFA 。 答案 见图1.5,图中的标记d 表示任意字母。

分析 这个DFA 和一些教材上接受无符号数的DFA 有类似的地方。我们首先考虑device:和.extension 全都出现的情况。这时的DFA 比较容易构造,见图1.6。

图1.3 接受注释的DFA

图1.4 接受注释的完整DFA

图1.5 接受文件名的DFA

然后考虑缺省情况。因为.extension 可缺省,因此把状态4也作为接受状态。因为name

和device 一样,都是字母序列,因此在device:缺省时,把到状态2为止得到的字母序列看成是name ,所以从状态2画一条转换边到状态5,标记为’.’。(如果构成name 和device 的字符完全不一样,那么可以从状态1到状态4画一条边,其标记同状态3到状态4的标记一样。)由于device:和.extension 都可缺省,因此把状态2也作为接受状态。

1.8 为正规式(a | b)* a (a | b) (a | b)构造NFA 。 答案 该NFA 的状态转换图见图1.7。 分析 各种教材在介绍有限状态自动机和正规表达式的等价时,都给出了从正规表达式构造等价的NFA 的算法。不同书上的构造算法虽然不一样,但有一个共同的特点,或多或少引入了ε转换,使状态转换图变得复杂。尤其是,如果题目还要求你画出DFA ,那么状态数的增多,使得手工完成NFA 确定化为DFA 的过程变得更容易出错。因此,我们既要会用书上的算法构造NFA ,也要会手工构造更简一些的NFA ,尽量避免在NFA 中出现ε转换。这在大多数情况下是可以做到的,本题就是一个例证。

1.9 用状态转换图表示接收(a | b)* aa 的确定的有限自动机。 答案 状态转换图见图1.8。 分析 和上一题不同的是,现在是直接构造DFA 。我们仍然坚持这一点,大家既要会按教材上的算法从NFA 的确定化得到DFA ,也要会手工直接构造DFA 。我们通过本题和下一题来说明,手工直接构造DFA 也并不困难。

该正规式表示的语言是,字母表∑= {a, b}上最后两个字符都是a 的串的集合。抓住这个特点,我们首先画出构造过程中的第一步,见图1.9。它表明最简单的句子是aa 。

然后,因为在第一个a 前可以有若干个b ,因此状态0有到自身的b 转换。在最后两个字符都是a 的串的末尾添加若干个a ,能够保持串的这个性质,因此状态2有到自身的a 转换。这样我们有图1.10。

图1.6 文件名的三部分都出现的DFA

start

图1.7 接受正规式(a | b)* a (a | b) (a | b)的NFA start

图1.8 接收(a | b)* aa 的DFA

最后,在状态1和状态2碰到b 时,前面刚读过的a ,不管连续有多少个,都不可能作

为句子结尾的那两个字符a ,因此状态1和状态2的b 转换回到状态0。所有状态的a 转换和b 转换都已给出,这就得到最后结果。

1.10 用状态转换图表示接收(a | b)* a (a | b) (a | b)的确定的有限自动机。 答案 状态转换图见图1.11。 分析 该正规式表示的语言是,字母表∑= {a, b}上倒数第三个字符是a 的串的集合。根据上

题的经验,我们首先画出图1.12。因为最后两个字符任意,因此有这样的分杈,并有四个接受状态。

现在考虑这四个接受状态上的转换。

1.状态4 该状态表示最后三个字符是aaa ,若再添加一个a ,最后三个字符仍是aaa ,因此状态4的a 转换到本身。若添加的是b ,那么最后三个字符是aab ,而状态5表示最后三个字符是aab ,因此状态4的b 转换到状态5。 2.状态5 该状态表示最后三个字符是aab ,若再添加一个a ,最后三个字符成了aba ,而状态6表示最后三个字符是aba ,因此状态5的a 转换到状态6。若添加的是b ,那么最后三个字符是abb ,而状态7表示最后三个字符是abb ,因此状态5的b 转换到状态7。 3.状态6 该状态表示最后三个字符是aba ,若再添加一个a ,最后三个字符成了baa ,其由a 开始的后缀是aa ,因此状态6的a 转换到状态2(因为从状态0出发经aa 是到状态2)。若添加的是b ,那么最后三个字符是bab ,其由a 开始的后缀是ab ,因此状态6的b 转换到状态3。

stars

图1.9 构造过程中的第一步

图1.10 构造过程中的第二步

a

b

图1.11 接收(a | b)* a (a | b) (a | b)的DFA

图1.12 构造过程的第一步

4.状态7 该状态表示最后三个字符是abb,若再添加一个a,最后三个字符成了bba,其由a开始的后缀是a,因此状态7的a转换到状态1。若添加的是b,那么最后三个字符是bbb,不存在由a开始的后缀,因此状态7的b转换到状态0。

这样,所有状态的a转换和b转换都已给出,也就得到了最后结果。

1.11 将1.8得到的NFA变换成DFA。

答案所求的DFA就是1.10题的结果。

分析我们之所以选这个题目,是为了比较一下,从正规式到NFA,再把NFA确定化,这样得的结果同1.10题直接构造DFA的结果是否一样。

按照教材上的子集构造法,作为结果的DFA并不难得到。另外由于没有ε转换,构造过程相对简单了很多。

NFA的开始状态是0,因此首先从NFA的状态集合{0}开始,它是DFA的开始状态,起名叫状态0'。它的a转换和b转换所得到的NFA的状态集合见下面第一行。根据子集构造法所得的DFA的所有状态和它们的转换函数都列在下面。

状态0':{0} move ({0}, a) = {0, 1} move ({0}, b) = {0}

状态1':{0, 1} move ({0, 1}, a) = {0, 1, 2} move ({0, 1}, b) = {0, 2}

状态2':{0, 1, 2} move ({0, 1, 2}, a) = {0, 1, 2, 3} move ({0, 1, 2}, b) = {0, 2, 3}

状态3':{0, 2} move ({0, 2}, a) = {0, 1, 3} move ({0, 2}, b) = {0, 3}

状态4':{0, 1, 2, 3} move ({0, 1, 2, 3}, a) = {0, 1, 2, 3} move ({0, 1, 2, 3}, b) = {0, 2, 3} 状态5':{0, 2, 3} move ({0, 2, 3}, a) = {0, 1, 3} move ({0, 2, 3}, b) = {0, 3}

状态6':{0, 1, 3} move ({0, 1, 3}, a) = {0, 1, 2} move ({0, 1, 3}, b) = {0, 2}

状态7':{0, 3} move ({0, 3}, a) = {0, 1} move ({0, 3}, b) = {0}

状态4', 5', 6'和7'中都含原NFA的接受状态3,因此它们都是DFA的接受状态。不难看出所得的DFA和1.10题的结果是同构的,仅状态名不一样。

1.12 将图1.13的DFA极小化。

答案 最简DFA 见图1.14。 分析 本题要注意的是,在使用极小化算法前,一定要检查一下,看状态转换函数是否为全

函数,即每个状态对每个输入符号都有转换。若不是全函数,需加入死状态,然后再用极小化算法。有些教材上没有强调这一点,有的习题解上的示例甚至忽略了这一点,本题将告诉你,这一点是重要的。本题加入死状态5后的状态转换图见图1.15。 使用极小化算法,先把状态集分成非接受状态集{0, 1, 2, 3, 5}和接受状态集{4}这两个子

集。

1.集合{4}不能再分解,我们看集合{0, 1, 2, 3, 5}。 move ({0, 1, 2, 3, 5}, a) = {1, 2, 5} move ({0, 1, 2, 3, 5}, b) = {3, 4, 5}

由于b 转换的结果{3, 4, 5}不是最初划分的某个集合的子集,因此{0, 1, 2, 3, 5}需要再分,由于状态1和状态2的b 转换都到状态4。因此状态集合的进一步划分是:

{1, 2},{0, 3, 5}和{4}

2.由于 move ({1, 2}, a) = {2, 5} move ({1, 2}, b) = {4} move ({0, 3, 5}, a) = {1, 5} move ({0, 3, 5}, b) = {3, 5} 显然{1, 2}和{0, 3, 5}需要再分,分别分成: {1}和{2}以及{0, 3}和{5} 3.由于 move ({0, 3}, a) = {1} move ({0, 3}, b) = {3}

图1.13 一个DFA

b

a

图1.14 最简DFA

a, b

图1.15 加入死状态后的DFA

因此不需要再分。这样状态0和状态3合并成一个状态,我们取0为代表,再删去死状态5,就得到该题的结果。 如果不加死状态,我们来看一下极小化算法的结果。最初的划分是{0, 1, 2, 3}和{4}。

1.状态集合的进一步划分是:

{1, 2},{0, 3}和{4}

2.忽略了死状态的影响,会认为它们都不需要再分,此时得到的DFA 如图1.16。 显然,它和原来的DFA 不等价。

1.13 将习题1.10结果的DFA 极小化。

答案 化简的结果仍是习题1.10结果的DFA ,即该DFA 已是最简DFA 。这说明手工构造最简DFA 是完全可能的。

分析 我们简要说明执行过程。 初始时将状态集分成两组:{0, 1, 2, 3}和{4, 5, 6, 7}。

1.由于

move ({0, 1, 2, 3}, a) = {1, 2, 4, 6} move ({0, 1, 2, 3}, b) = {0, 3, 5, 7}

move ({4, 5, 6, 7}, a) = {1, 2, 4, 6} move ({4, 5, 6, 7}, b) = {0, 3, 5, 7} 因此进一步分成{0, 1}、{2, 3}、{4, 5}和{6, 7}四组。 2.由于 move ({0, 1}, a) = {1, 2} move ({0, 1}, b) = {0, 3} 因此{0, 1}还要进一步分,其它几组也是这样。因此最终分成了每个集合中都只有一个状态。 3.所以原来的DFA 已经是最简形式了。

1.14 若L 是正规语言,证明下面L '语言也是正规语言。L '语言的定义是 L ' = {x | x R ∈ L } x R 表示x 的逆。

答案 若我们能定义出接受语言L '的一个NFA ,那么L '是正规语言。因为L 是正规语言,那么一定存在接受L 的DFA M ,我们就基于M 来构造接受L '的NFA M ',并且我们基于M 的状态转换图来叙述。 1.将状态转换图上的所有边改变方向,边上的标记不变。 2.将原来的开始状态改成接受状态。 3.增添一个状态作为开始状态,从它有ε转换到原来的每一个接受状态,并把原来的这些接受状态都改成普通的状态。 所得到的新状态转换图就是M '的状态转换图。

分析 简单说,判断一个串是否为L '的句子,只要在原来的状态转换图上逆行就可以了。由于M 可能有多个接受状态,而不管是DFA 还是NFA 都只有唯一的开始状态,因此有上面的第3步。 注意经过上面的改造后,得到的可能不是一个DFA 的状态转换图。除了上面第3步有可能引入不确定性外,第1步也可能引入不确定性。

b

a

a

图1.16 一个不正确的结果

本题介绍的证明语言的正规性的方法在下一题表现得更加充分,叙述也比本题严格。

1.15.若L 是正规语言,证明下面21L 语言也是正规语言。2

1

L 语言的定义是

2

1

L = {x | ?y. xy ∈ L & |x| = |y| } 答案 因为L 是正规语言,那么存在一个DFA M = (S, ∑, δ, s, F),它接受语言L 。接受语言2

1

L 的DFA M ' = (S ', ∑, δ', s ', F ')可如下构造。 1.S '的每个状态是一个二元组?s 1, S 1?,其中s 1∈S ,S 1 ? S 。 2.δ' (?s 1, S 1?, a) = ?s 2, S 2?,其中 s 2 = δ (s 1, a) S 2 = { s 2 | ? s 1∈ S 1.?b ∈∑.δ (s 2, b) = s 1} 3.s ' = ?s, F ? 4.F ' = {?s 1, S 1? | s 1 ∈ S 1} 可以证明这是接受语言

21L 的DFA ,因此2

1L 是正规语言(证明比较复杂,我们在此略去)。 分析 这个题目超出的编译课程的范围。但是如果理解了这儿的解答,那就掌握了证明正规

语言的一种很重要的技巧。下面我们说明,为什么这样定义DFA M '。 长度为n 的串w ,若它是语言L 的某个句子的前缀,那么从M 的开始状态s ,经n 步转换,到达某个状态s n 。该串是否属于

2

1

L ,取决于是否存在从s n 开始到某个接受状态的路径,其长度为n 。

怎样判断是否存在这样的到某个接受状态的路径呢?我们可以在M 的状态转换图上按下面的步骤办。

1.首先找出从任意接受状态逆向任意走一步能到达的所有状态,把这个状态集合称做R 1。

2.再找出从R 1的任意状态逆向任意走一步能到达的所有状态,把这个状态集合称做R 2。

3.这样逆向操作n 步后,得到状态集合R n 。

4.若s n 在R n 中,那么串w 属于

2

1

L ,否则不是。 可以看出,上面定义的M '是在M 上同时跟踪从开始状态出发的正向状态转换和从接受状态集合出发的逆向搜索所有状态。我们再看一下M '的定义。 1.S '的每个状态之所以是一个二元组?s 1, S 1?,是因为需要用s 1来表示正向的状态转换,用S 1来表示逆向搜索。 2.再看状态转换的定义δ' (?s 1, S 1?, a) = ?s 2, S 2?,其中s 2 = δ (s 1, a)表示了M 上正向一步转换到达的状态,S 2 = { s 2 | ? s 1∈ S 1.?b ∈∑.δ (s 2, b) = s 1}表示了M 上逆向一步搜索到达的状态集合。 3.开始状态s ' = ?s, F ?表示在M 上的跟踪是从开始状态和接受状态集合这两端启动。 4.接受状态F ' = {?s 1, S 1? | s 1 ∈ S 1}表达的意思是,若在M 上从开始状态经n 转换到s 1,那么一定存在长度为n 的从s 1到某个接受状态的路径。

下面我们以1.4题的正规语言为例。不难理解,该语言的

2

1

语言应该是字母表∑={0, 1}上的所有串。我们用上面的方法来构造接受它的DFA 。该DFA 的开始状态s 0 = ?0, {0}?。

状态s0: ?0, {0}?δ (s0, 0) = ?2, {1, 2}?δ (s0, 1) = ?1, {1, 2}?

状态s1: ?2, {1, 2}?δ (s1, 0) = ?0, {0, 3}?δ (s1, 1) = ?3, {0, 3}?

状态s2: ?1, {1, 2}?δ (s2, 0) = ?3, {0, 3}?δ (s2, 1) = ?0, {0, 3}?

状态s3: ?0, {0, 3}?δ (s3, 0) = ?2, {1, 2}?δ (s3, 1) = ?1, {1, 2}?

状态s4: ?3, {0, 3}?δ (s4, 0) = ?1, {1, 2}?δ (s4, 1) = ?2, {1, 2}?

从s0到s4的这五个状态,每个状态的第一元都在第二元的集合中,因此这五个状态都是接受状态。这个DFA的图形表示见图1.17(图中用状态0, 1, 2, 3和4代替了这里对应的状态)。显然,它能接受字母表∑={0, 1}上所有的串,化简这个DFA可以得到只有一个状态的DFA。

图1.17 接受∑={0, 1}上所有串的一个DFA

1.16 保留字、关键字和标准标识符之间的区别是什么?

答案保留字是语言预先确定了含义的单词,程序员不可以对这样的单词重新声明它的含义。如PASCAL语言的type 和procedure等。词法分析器读到一个符合标识符拼写的单词时,都要和保留字进行比较,以确定该单词是标识符呢还是哪个保留字。

很多语言的关键字是保留的,因此和上面的保留字没有区别,如C语言和Java语言。但是FORTRAN语言的关键字不保留,如IF,当它作为语句的第一个单词时,很可能是关键字,但也不排除是用户定义的标识符。这给词法分析带来很大困难,因为识别单词和该单词所处的上下文有关。现在的语言都不会象FORTRAN这样来定义关键字。

标准标识符是预先确定了含义的单词,但是程序员可以重新声明它的含义。在这个声明的作用域内,程序员声明的含义起作用,而预先确定的含义消失,如PASCAL语言的integer 和true等。词法分析器对标准标识符没有什么特别的处理,由符号表管理来解决这件事。

1.17 词法分析器能查出源程序中什么样的错误,举例说明。

答案词法分析器对源程序采取非常局部的观点,因此象C语言的语句

fi (a == f (x) )…

中,词法分析器把fi当作一个普通的标识符交给编译的后续阶段,而不会把它看成是关键字if的拼写错。

PASCAL语言要求作为实型常量的小数点后面必须有数字,如果程序中出现小数点后面没有数字情况,它由词法分析器报错。

1.18 一个C语言编译器编译下面的函数时,报告parse error before ‘else’。这是因为else的前面少了一个分号。但是如果第一个注释

/* then part */

误写成

/* then part

那么该编译器发现不了遗漏分号的错误。这是为什么?

long gcd(p,q)

long p,q;

{

if (p%q == 0)

/* then part */

return q

else

/* else part */

return gcd(q, p%q);

}

答案此时编译器认为

/* then part

return q

else

/* else part */

是程序的注释,因此它不可能再发现else前面的语法错误。

分析这是注释用配对括号表示时的一个问题。注释是在词法分析时忽略的,而词法分析器对程序采取非常局部的观点。当进入第一个注释后,词法分析器忽略输入符号,一直到出现注释的右括号为止,由于第一个注释缺少右括号,所以词法分析器在读到第二个注释的右括号时,才认为第一个注释处理结束。

为克服这个问题,后来的语言一般都不用配对括号来表示注释。例如Ada语言的注释始于双连字符(--),随行的结束而终止。如果用Ada语言的注释格式,那么上面函数应写成

long gcd(p,q)

long p,q;

{

if (p%q == 0)

--then part

return q

else

-- else part

return gcd(q, p%q);

}

实验一 词法分析器的设计

实验一词法分析器的设计 (2) 1.1 词法分析器的结构和主要任务 (2) 1.1.1 输入输出接口 (2) 1.1.2 条件限制 (2) 1.2 词法分析程序的总体设计 (3) 1.3 词法分析程序的详细设计 (4) 1.4实验步骤 (5) 1.5输入数据 (15) 1.6结果输出 (15)

实验一词法分析器的设计 实验目的:掌握词法分析的概念,设计方法,熟悉高级语言中词法的定义,词法分析程序的编写。 实验要求:在8学时内实现SAMPLE语言的词法分析器,要求用VC窗口界面实现。 实验内容:分为4次实验完成。 1.1 词法分析器的结构和主要任务 1.1.1 输入输出接口 图1-1词法分析器的输入输出界面 词法分析程序的主要任务是从左到右扫描每行源程序,拼成单词,换成统一的内部表示(token)输出,送给语法分析器。具体包括: 1.组织源程序的输入; 2.按规则拼单词,并转换成二元形式; 3.滤掉空白符,跳过注释、换行符及一些无用的符号(如字符常数的引号) 4.进行行列计数,用于指出出错的行列号,并复制出错部分; 5.列表打印源程序; 6.发现并定位词法错误; 7.生成符号表。 token文件和符号表用作语法分析的输入部分。 1.1.2 条件限制 本实验可以作如下假定: (1) 假定SAMPLE语言采用自由格式书写; (2) 可以使用注解,用/*……*/或者{……}标识,但注解不能插在单词内部,注解要在一行内结束,若一行结束,没有遇到注释后面的结束标记,自动认为注释也结束; (3) 一行可以有多个语句,一个语句也可以分布在多行中,单词之间和语句之间可以插入任意空格,单词中间不能有空白符号,单词中间也不能有回车换行符,即单词不能跨行书写; (4) 关键字都是保留字。

词法分析报告程序设计与实现

实验一词法分析程序设计与实现 一、实验目的及内容 调试并完成一个词法分析程序,加深对词法分析原理的理解。 二、实验原理(状态转换图) 1、C语言子集 (1)关键字: begin if then while do end 所有关键字都是小写。 (2)运算符和界符: := + – * / < <= <> > >= = ; ( ) # (3)其他单词是标识符(ID)和整型常数(NUM),通过以下正规式定义:ID=letter(letter| digit)* NUM=digit digit * (4)空格由空白、制表符和换行符组成。空格一般用来分隔ID、NUM,运算符、界符和关键字,词法分析阶段通常被忽略。 2、各种单词符号对应的种别码

3、词法分析程序的功能 输入:所给文法的源程序字符串。 输出:二元组(syn,token或sum)构成的序列。 其中:syn为单词种别码; token为存放的单词自身字符串; sum为整型常数。 二、软件平台及工具 PC机以及VISUAL C++6.0软件。 三、实验方法、步骤(或:程序代码或操作过程)(1)程序代码: #include #include #include char prog[80],token[8]; char ch; int syn,p,m=0,n,row,sum=0; char *rwtab[6]={"begin","if","then","while","do","end"}; void scaner() { for(n=0;n<8;n++) token[n]=NULL; ch=prog[p++]; while(ch==' ') { ch=prog[p]; p++; }

词法分析小结

词法分析小结 词法分析是编译器工作的第一阶段,它的工作就是从输入(源代码)中取得token,以作为parser(语法分析)的输入,一般在词法分析阶段都会把一些无用的空白字符(whitespace,即空格、tab和换行)以及注释剔除,以降低下一步分析的复杂度,词法分析器一般会提供一个gettoken()这样的方法,parser可以在做语法分析时调用词法分析器的这个方法来得到下一个token,所以词法分析器并不是一次性遍历所有源代码,而是采取这种on-demand的方式:只在parser需要时才工作,并且每次只取一个token。 token和lexeme 首先,token不等于lexeme。token和lexeme的关系就类似于面向对象语言中“类”和“实例”(或“对象”)之间的关系,这个用中文不知该如何解释才好,比如语言中的变量a和b,它们都属于同一种token:identifier,而a的lexeme是”a”,b则是”b”,而每个关键字都是一种token。token可以附带有一个值属性,例如变量a,当调用词法分析器的gettoken()时,会返回一个identifier类型的token,这个token带有一个属性“a”,属性可以是多样的,例如表示数字的token

可以带有一个表示数字值的属性,它是整型的。 如下代码: intage=23; intcount=50; 可以依次提取出8个token:int(值为”int”),id(值为”age”),assign(值为”=”),number(值为整型数值23),int(值为”int”),id(值为”count”),assign(值为”=”),number(值为50) 正则表达式 正则表达式可以用来描述字符串模式,例如我们可以用digit+来表示number的token,其中digit表示单个数字(这里说正则表达式并不完全和实现的正则引擎所识别的正则表达式等价,这里只是为了描述问题而已)。 然而像c语言的的多行注释,用正则表达式来描述就比较麻烦,此时更倾向于直接用有穷自动机(finiteautomaton)来描述,因为用它来描述非常直观且很容易。

编译原理词法分析报告

1、实验目的 1、为初等函数运算语言构造词法分析器。 2、掌握生成词法分析器的方法,加深对词法分析原理的理解。 3、掌握设计、编制并调试词法分析程序的思想和方法 2、实验内容 一、根据下面的要求设计初等函数运算语言的词法模式,并用正则式表达出来 1、初等函数运算语言的常量为实数类型,其定义方式为实数的最一般书写方式,如: 123.321。具体要求:不支持整数部分大于。时首数字为0;不支持小数点后结尾为 0;不支持科学记数法;不支持仅为整数时有小数点;支持负数符号,不支持正数 符号。 2、初等函数运算语言的变量采用与C语言的标识符定义一样的方式:首字符为字母或 下划线;其他的为字母、数字及下划线的混合串;区分大小写;变量长度不超过32 个字符。 3、初等函数运算语言需要处理的函数仅为表一中所列举的内容。函数的格式及参数内 容也如表一所示。 4、初等函数运算语言支持四则运算,其计算的符号与C语言相同,为:+-*/。 5、初等函数运算语言的合法的分隔符包括:空格、制表符、、分行符圆括号(左、右)、 分号。其中空格、制表符、分行符可以出现在任何两个不同的单词中间;圆括号(左、右)用于表达式中,用于改变运算的优先级,以及标识函数的参数;分号用于标识一个语句的结束。 6、初等函数运算语言支持的常量还包括:PI,Eo其中,PI为圆周率,E为自然常数。 二、将正则式转化为最小DFA,给出该DFA的形式化表示和图形表示。 三、根据DFA给出状态转换表。 四、给出初等函数运算语言的记号表,即词法分析中,语言中的记号将分为多少类,每一类型的编码、类型、属性等内容是什么。 五、编写词法分析器,将输入的字符串转化成为记号流,便于后续的语法分析工作。要求词法分析器中能够识别词法错误。

编译原理 简单样本语言的词法分析器

昆明理工大学信息工程与自动化学院学生实验报告 (2012 —2013 学年第 1 学期) 课程名称:编译原理开课实验室:信自楼44 年月日 一、实验目的及内容 设计、编制、调试一个词法分析子程序-识别单词,加深对词法分析原理的理解。 二、实验原理及基本技术路线图(方框原理图或程序流程图) 对给定的程序通过词法分析器弄够识别一个个单词符号,并以二元式(单词种别码,单词符号的属性值)显示。而本程序则是通过对给定路径的文件的分析后以单词符号和文字提示显示。 三、所用仪器、材料(设备名称、型号、规格等或使用软件) W INDOWS下的VISUAL C++6.0; 四、实验方法、步骤(或:程序代码或操作过程) #include #include using namespace std;

#define MAX 22 char ch =' '; string key[15]={"begin","end","if","then","else","while","write","read", "do", "call","const","char","until","procedure","repeat"}; int Iskey(string c){ //关键字判断 int i; for(i=0;i='a'))||((c<='Z')&&(c>='A'))) return 1; else return 0; } int IsDigit(char c){ //判断是否为数字 if(c>='0'&&c<='9') return 1; else return 0; } void analyse(FILE *fpin){ string arr=""; while((ch=fgetc(fpin))!=EOF) { arr=""; if(ch==' '||ch=='\t'||ch=='\n'){} else if(IsLetter(ch)){ while(IsLetter(ch)||IsDigit(ch)) { if((ch<='Z')&&(ch>='A')) ch=ch+32; arr=arr+ch; ch=fgetc(fpin); } fseek(fpin,-1L,SEEK_CUR); if (Iskey(arr)){cout<

编译原理 实验2 词法分析器

编译原理实验2 词法分析器 一、实验目的 1. 通过设计编制调试一个具体的词法分析程序,加深对词法分析原理的理解。 2. 掌握在对程序设计语言源程序进行扫描过程中将其分解为各类单词的词法分析方法。 3. 编制一个读单词的程序,从输入的源程序中,识别出各个具有独立意义的单词,即基本保留字、标识符、常数、运算符和分隔符五大类。并依次输出各个单词的内部编码及单词符号自身值。(遇到错误时可显示“Error”,然后跳过错误部分继续显示) 二、词法分析的基础知识 1. 词法分析器的功能和输出格式 词法分析器的功能是输入源程序,输出单词符号。词法分析器的单词符号常常表示成以下的二元式(单词种别码,单词符号的属性值)。在本实验中,采用的是一类符号一种别码的方式。 标识符的BNF表示: <标识符>-> <字母><字母数字串> ) <字母数字串>-><字母><字母数字串>|<数字><字母数字串>|ε 无符号整数的BNF表示: <无符号整数>-> <数字><数字串> <数字串>-> <数字><数字串> |ε 运算符的BNF表示: <加法运算符>-> + <减法运算符>-> - <大于关系运算符>-> > <大于等于关系运算符>-> >= 2. 超前搜索 ; 词法分析时,常常会用到超前搜索方法。如当前待分析字符串为“a > i”,当前字符为“>”,此时,分析器到底是将其分析为大于关系运算符还是大于等于关系运算符呢显然,只有知道下一个字符是什么才能下结论。于是分析器读入下一个字符“+”,这时可知应将“>”解释为大于运算符。但此时,超前读了一个字符“i”,所以要回退一个字符,词法分析器才能正常运行。在分析标识符,无符号整数等时也有类似情况。 三、程序要求 1. 程序输入示例: 如源程序为C语言,输入如下一段: main() { int a, b; a = 10; b = a+20; } ; 2. 程序输出示例:

实验一词法分析实验报告

实验一词法分析 一、实验目的 通过设计编制调试一个具体的词法分析程序,加深对词法分析原理的理解。并掌握在对程序设计语言源程序进行扫描过程中将其分解为各类单词的词法分析方法。 编制一个读单词过程,从输入的源程序中,识别出各个具有独立意义的单词,即基本保留字、标识符、常数、运算符、分隔符五大类。并依次输出各个单词的内部编码及单词符号自身值。(遇到错误时可显示“Error”,然后跳过错误部分继续显示) 二、实验要求 使用一符一种的分法 关键字、运算符和分界符可以每一个均为一种 标识符和常数仍然一类一种 三、实验内容 功能描述: 1、待分析的简单语言的词法 (1)关键字: begin if then while do end (2)运算符和界符: := + –* / < <= <> > >= = ; ( ) # (3)其他单词是标识符(ID)和整型常数(NUM),通过以下正规式定义: ID=letter(letter| digit)* NUM=digit digit * (4)空格由空白、制表符和换行符组成。空格一般用来分隔ID、NUM,运算符、界符和关键字,词法分析阶段通常被忽略。 2、各种单词符号对应的种别码 图 1

程序结构描述: 图 2 四、实验结果 输入begin x:=9: if x>9 then x:=2*x+1/3; end # 后经词法分析输出如下序列:(begin 1)(x 10)(:17)(= 18)(9 11)(;26)(if 2)……如图3所示:

图3 输入private x:=9;if x>0 then x:=2*x+1/3; end#后经词法分析输出如下序列:(private 10)(x 10)(:17)(= 18)(9 11)(;26)(if 2)……如图4所示: 图4 显然,private是关键字,却被识别成了标示符,这是因为图1中没有定义private关键字的种别码,所以把private当成了标示符。 输入private x:=9;if x>0 then x:=2*x+1/3; @ end#后经词法分析输出如下序列:(private 10)(x 10)(:17)(= 18)(9 11)(;26)(if 2)……如图5所示

词法分析器实验报告

词法分析器实验报告 词法分析器设计 一、实验目的: 对C语言的一个子集设计并实现一个简单的词法分析器,掌握利用状 态转换图设计词法分析器的基本方法。利用该词法分析器完成对源程 序字符串的词法分析。输出形式是源程序的单词符号二元式的代码, 并保存到文件中。 二、实验内容: 1. 设计原理 词法分析的任务:从左至右逐个字符地对源程序进行扫描,产生一个个单词符号。 理论基础:有限自动机、正规文法、正规式 词法分析器(Lexical Analyzer) 又称扫描器(Scanner):执行词法分析的程序 2. 词法分析器的功能和输出形式 功能:输入源程序、输出单词符号 程序语言的单词符号一般分为以下五种:关键字、标识符、常数、运算符,界符 3. 输出的单词符号的表示形式: 单词种别用整数编码,关键字一字一种,标识符统归为一种,常数一种,各种符号各一种。 4. 词法分析器的结构 单词符号 5. 状态转换图实现

三、程序设计 1.总体模块设计 /*用来存储目标文件名*/ string file_name; /*提取文本文件中的信息。*/ string GetText(); /*获得一个单词符号,从位置i开始查找。并且有一个引用参数j,用来返回这个单词最后一个字符在str的位置。*/ string GetWord(string str,int i,int& j); /*这个函数用来除去字符串中连续的空格和换行 int DeleteNull(string str,int i); /*判断i当前所指的字符是否为一个分界符,是的话返回真,反之假*/ bool IsBoundary(string str,int i); /*判断i当前所指的字符是否为一个运算符,是的话返回真,反之假*/ bool IsOperation(string str,int i);

词法分析实验报告

词法分析器 一、实验目的: 通过设计编制调试一个具体的词法分析程序,加深对词法分析原理的理解。并掌握在对程序设计语言源程序进行扫描过程中将其分解为各类单词的词法分析方法。 编制一个读单词过程,从输入的源程序中,识别出各个具有独立意义的单词,即基本保留字、标识符、常数、运算符、分隔符五大类。并依次输出各个单词的内部编码及单词符号自身值。 二、实验要求 如源程序为C语言。输入如下一段:Array main() { int a,b; a = 10; b = a + 20; }# 要求输出如右图。 要求: 1、将单词分为五种 识别关键字:main、if、int、for、while、do、return、break、continue; 单词种别码为1。 标识符;单词种别码为2。 常数为无符号整形数;单词种别码为3。 运算符包括:+、-、*、/、=、>、<、>=、<=、!= ; 单词种别码为4。 分隔符包括:,、;、{、}、(、);单词种别码为5。 2、使用一符一种的分法 关键字、运算符和分界符可以每一个均为一种 标识符和常数仍然一类一种 三、实验内容 1、功能描述 改程序是一个实现词法分析的功能,能识别5种单词,其他单词报错。 2、程序结构描述 int IsKey(char *Word)关键字匹配函数,查询是否为关键字,若是,返回值为1,否则为0。 int IsAlpha(char c) 查看是否为字母,若是,返回值为1,否则为0。 int IsNum(char c) 查看是否为数字,若是,返回值为1,否则为0。 void scanner(FILE *fp) 扫描函数,扫描程序中的字符串并调用上述三种函数检查是否是字母、 数字,是否是关键字,并输出。 fseek(fp,-1,1)回退一个字符。 fgetc(fp)从数据流中区下一个字符。 fopen 文件打开函数,返回指向文件第一个字符的指针 四、实验结果 测试内容为 main() {

编译原理实验报告一 简单样本语言的词法分析器

理工大学信息工程与自动化学院学生实验报告 (2012 —2013学年第一学期) 一、实验目的及容 编译技术是理论与实践并重的课程,而其实验课要综合运用所学的多门课程的容,用来完成一个小型编译程序。从而巩固和加强对词法分析、语法分析、语义分析、代码生成和报错处理等理论的认识和理解;培养学生对完整系统的独立分析和设计的能力,进一步培养学生的独立编程能力。 调试并完成一个词法分析程序,加深对词法分析原理的理解。 二、实验原理及基本技术路线图(框原理图或程序流程图) 1、待分析的简单语言的词法 (1)关键字: begin if then while do end 所有关键字都是小写。 (2)运算符和界符: := + –* / < <= <> > >= = ; ( ) #

(3)其他单词是标识符(ID)和整型常数(NUM),通过以下正规式定义:ID=letter(letter| digit)* NUM=digit digit * (4)空格由空白、制表符和换行符组成。空格一般用来分隔ID、NUM,运算符、界符和关键字,词法分析阶段通常被忽略。 2、各种单词符号对应的种别码 3、词法分析程序的功能 输入:所给文法的源程序字符串。 输出:二元组(syn,token或sum)构成的序列。 其中:syn为单词种别码; token为存放的单词自身字符串; sum为整型常数。 二、所用仪器、材料(设备名称、型号、规格等或使用软件)

1台PC以及VISUAL C++6.0软件。 三、实验法、步骤(或:程序代码或操作过程) (1)程序代码: #include #include #include char prog[80],token[8]; char ch; int syn,p,m=0,n,row,sum=0; char *rwtab[6]={"begin","if","then","while","do","end"}; void scaner() { for(n=0;n<8;n++) token[n]=NULL; ch=prog[p++]; while(ch==' ') { ch=prog[p]; p++; } if((ch>='a'&&ch<='z')||(ch>='A'&&ch<='Z')) { m=0; while((ch>='0'&&ch<='9')||(ch>='a'&&ch<='z')||(ch>='A'&&ch<='Z')) { token[m++]=ch; ch=prog[p++]; } token[m++]='\0'; p--; syn=10; for(n=0;n<6;n++)

词法分析

实验二:词法分析 一、实验目的:编制一个读单词过程,从输入的源程序中,识别出各个具有独立意义的单词, 即基本保留字、标识符、常数、运算符、分隔符五大类。并依次输出各个单词的内部编码及 Error”,然后跳过错误部分继续显示) 二、估计实验时间:1.课余准备15小时;2.上机二次4小时;3.完成实验报告5小时。 三、实验过程和指导: (一)准备:1.阅读课本有关章节,花一周时间明确语言的语法,写出基本保留字、标识符、 常数、运算符、分隔符和程序例。2.初步编制好程序。3.准备好多组测试数据。 (二)上课上机:将源代码拷贝到机上调试,发现错误,再修改完善。第二次上机调试通过。 (三)程序要求:Array程序输入/输出示例: 如源程序为C语言。输入如下一段: main() { int a,b; a = 10; b = a + 20; } 要求输出如右图。 要求: 识别保留字:if、int、for、while、do、return、break、 其他的都识别为标识符;

常数为无符号整形数; 运算符包括:+、-、*、/、=、>、<、>=、<=、!= 分隔符包括:,、;、{、}、(、) 程序思路(仅供参考): 0.定义部分:定义常量、变量、数据结构。 1.初始化:从文件将源程序全部输入到字符缓冲区中。 2.取单词前:去掉多余空白。 3.取单词后:去掉多余空白(可选,看着办)。 4.取单词:利用实验一的成果读出单词的每一个字符,组成单词,分析类型。(关键是如何判断取单词结束?取到的单词是什么类型的单词?) 5.显示结果。 (四)练习该实验的目的和思路: 程序开始变得复杂起来,可能是大家以前编过的程序中最复杂的,但相对于以后的程序来说还是简单的。因此要认真把握这个过渡期的练习。程序规模大概为200行。本实验和以后的实验相关。通过练习,掌握对字符进行灵活处理的方法。 (五)为了能设计好程序,主意以下事情: 1.模块设计:将程序分成合理的多个模块(函数),每个模块做具体的同一事情。 2.写出(画出)设计方案:模块关系简图、流程图、全局变量、函数接口等。 3.编程时注意编程风格:空行的使用、注释的使用、缩进的使用等。 四、上交: 1.程序源代码;

东南大学编译原理词法分析器实验报告

词法分析设计 1. 实验目的 通过本实验的编程实践,了解词法分析的任务,掌握词法分析程序设计的原理和构造方法,对编译的基本概念、原理和方法有完整的和清楚的理解,并能正确地、熟练地运用。 2. 实验内容 用C++语言实现对C++语言子集的源程序进行词法分析。通过输入源程序从左到右对字符串进行扫描和分解,依次输出各个单词的内部编码及单词符号自身值;若遇到错误则显示“Error”,然后跳过错误部分继续显示;同时进行标识符登记符号表的管理。 3. 实验原理 本次实验采用NFA->DFA->DFA0的过程: 对待分析的简单的词法(关键词/id/num/运算符/空白符等)先分别建立自己的FA,然后将他们用产生式连接起来并设置一个唯一的开始符,终结符不合并。 待分析的简单的词法 (1)关键字: "asm","auto","bool","break","case","catch","char","class","

const","const_cast"等 (2)界符(查表) ";",",","(",")","[","]","{","}" (3)运算符 "*","/","%","+","-","<<","=",">>","&","^","|","++","--"," +=","-=","*=","/=","%=","&=","^=","|=" relop: (4)其他单词是标识符(ID)和整型常数(SUM),通过正规式定义。 id/keywords: digit: (5)空格有空白、制表符和换行符组成。空格一般用来分隔ID、SUM、运算符、界符和关键字,词法分析阶段通常被忽略。

简单词法分析器

简单词法分析器 1、将源文件中的单词识别出来,以用'$'为首的标识符标记识别出的单词 2、单词符号及内部表示如表: 单词符号种别编码助记符内码值 DIM 1 $DOM — IF 2 $IF — DO 3 $DO — STOP 4 $STOP — END 5 $END — 标识符 6 $ID 内部字符串 常数7 $INT 标准二进制形式= 8 $ASSIGN — + 9 $PLUS — * 10 $STAR — ** 11 $POWER — ; 12 $SEMICOLON — { 13 $LBRACE — } 14 $RBRACE — /* * 词法分析:将源文件中的单词符号一一识别 * 并将其与助记符保存到文本文件 */ #include "iostream" #include "string" using namespace std; //reserve保留字 string reserve[5] = {"DIM","IF","DO","STOP","END"}; //结构体数组,保存已识别的单词 struct table { string str; string name; }table[400]; int count = 0; //判断是否为保留字

bool Reserve(string str) { bool flag = false; for(int i=0; i>filename; if((fp = fopen(filename,"r")) == NULL) { cout<<"file not found"<

词法分析课程设计

《词法分析》设计说明书 学生姓名 学 号 5011110122 5011110133 5011110128 所属学院 信息工程学院 专 业 计算机科学与技术 班 级 计算机15-1班 信息工程学院 《编译原理及实践》结课大作 业

摘要 编译,简单的说,就是把源程序转换为可执行程序。从hellow worl说程序运行机制里面简单的说明了程序运行的过程,以及一个程序是如何一步步变成可执行文件的。在这个过程中,编译器做了很多重要的工作。对于编译的内部实现,也就是编译的原理。 这篇论文主要说的是编译器前端,词法分析器的原理,最后会给出一个词法分析器的简单实现。 编译简单的说,就是把源程序转化为另一种形式的程序,而其中关键的部分就是理解源程序所要表达的意思,才能转化为另一种源程序。 可以用一个比喻来说明问题:人A和人B想要交谈,但是他们都不知道彼此的语言,这就需要一个翻译C,同时懂得A和B的语言。有了C做中间层,A和B才能正常交流。C的作用就有点像编译器,它必须能理解源程序所要表达的意思,才能把信息传递给另一个。编译器也一样,它的输入是语言的源文件(一般可以是文本文件)对于输入的文件,首先要分离出这个输入文件的每个元素(关键字、变量、符号、、),然后根据语言的文法,分析这些元素的组合是否合法,以及这些组合所表达的意思。 程序设计语言和自然语言不一样,都是用符号来描述,每个特定的符号表示特定的意思,而且程序设计语言是上下文无关的。上下文无关就是某一个特定语句所要表达的意思和它所处的上下文没有关系,只有它自身决定。 这篇论文主要说的就是词法分析,也就是把输入的符号串整理成特定的词素。 关键词:单片机;词法分析

编译原理实验报告2-词法分析程序的设计

实验2 词法分析程序的设计 一、实验目的 掌握计算机语言的词法分析程序的开发方法。 二、实验内容 编制一个能够分析三种整数、标识符、主要运算符和主要关键字的词法分析程序。 三、实验要求 1、根据以下的正规式,编制正规文法,画出状态图; 标识符<字母>(<字母>|<数字字符>)* 十进制整数0 | ((1|2|3|4|5|6|7|8|9)(0|1|2|3|4|5|6|7|8|9)*) 八进制整数0(1|2|3|4|5|6|7)(0|1|2|3|4|5|6|7)* 十六进制整数0x(0|1|2|3|4|5|6|7|8|9|a|b|c|d|e|f)(0|1|2|3|4|5|6|7|8|9|a|b|c|d|e|f)* 运算符和界符+ - * / > < = ( ) ; 关键字if then else while do 2、根据状态图,设计词法分析函数int scan( ),完成以下功能: 1)从文本文件中读入测试源代码,根据状态转换图,分析出一个单词, 2)以二元式形式输出单词<单词种类,单词属性> 其中单词种类用整数表示: 0:标识符 1:十进制整数 2:八进制整数 3:十六进制整数 运算符和界符,关键字采用一字一符,不编码 其中单词属性表示如下: 标识符,整数由于采用一类一符,属性用单词表示 运算符和界符,关键字采用一字一符,属性为空 3、编写测试程序,反复调用函数scan( ),输出单词种别和属性。 四、实验环境 PC微机 DOS操作系统或Windows 操作系统 Turbo C 程序集成环境或Visual C++ 程序集成环境 五、实验步骤 1、根据正规式,画出状态转换图;

词法分析

词法分析器的实现 开篇 编译,简单的说,就是把源程序转换为可执行程序。从hello world 说程序运行机制里面简单的说明了程序运行的过程,以及一个程序是如何一步步变成可执行文件的。在这个过程中,编译器做了很多重要的工作。对底层该兴趣的我,自然的,也就迫切想搞清楚编译的内部实现,也就是编译的原理。 这篇文章主要说的是编译器前端,词法分析器的原理,最后会给出一个词法分析器的简单实现。 介绍 编译简单的说,就是把源程序转化为另一种形式的程序,而其中关键的部分就是理解源程序所要表达的意思,才能转化为另一种源程序。 可以用一个比喻来说明问题:人A和人B想要交谈,但是他们都不知道彼此的语言,这就需要一个翻译C,同时懂得A和B的语言。有了C做中间层,A和B才能正常交流。C的作用就有点像编译器,它必须能理解源程序所要表达的意思,才能把信息传递给另一个。 编译器也一样,它的输入是语言的源文件(一般可以是文本文件)对于输入的文件,首先要分离出这个输入文件的每个元素(关键字、变量、符号、、) 然后根据语言的文法,分析这些元素的组合是否合法,以及这些组合所表达的意思。 程序设计语言和自然语言不一样,都是用符号来描述,每个特定的符号表示特定的意思,而且程序设计语言是上下文无关的。上下文无关就是某一个特定语句所要表达的意思和它所处的上下文没有关系,只有它自身决定。 这篇博文主要说的就是词法分析,也就是把输入的符号串整理成特定的词素。 词法分析 定义: 词法分析器的功能输入源程序,按照构词规则分解成一系列单词符号。单词是语言中具有独立意义的最小单位,包括关键字、标识符、运算符、界符和常量等 (1) 关键字是由程序语言定义的具有固定意义的标识符。例如,Pascal 中的begin,en d,if,while都是保留字。这些字通常不用作一般标识符。 (2) 标识符用来表示各种名字,如变量名,数组名,过程名等等。 (3) 常数常数的类型一般有整型、实型、布尔型、文字型等。 (4) 运算符如+、-、*、/等等。 (5) 界符如逗号、分号、括号、等等。 输出:

词法分析说明

一、创建环境 1.将java拷贝到c:盘根目录下。 2.设置环境变量: 在“计算机”图标上单击右键--------属性------高级系统设置------环境变量-----系统变量 path中设置内容为: c:\java\jdk1.6.0_10\bin; classpath中设置内容为: c:\java\jdk1.6.0_10\lib\dt.jar; c:\java\jdk1.6.0_10\lib\tools.jar; c:\java\jre6\lib\rt.jar; .; c:\1000; 3.在c盘要建立1000 文件夹,将SimpleLexer文件夹复制到该文件 夹内(注意字母的大小写)。 二、词法分析 1. 运行cmd 命令 输入cmd回车

输入cd\ 回车,使输入符号到c:\> 状态 再输入cd 1000 回车。进入到c:\1000> 状态 2.编译程序 先编译Token.java文件,再编译Main.java文件,最后编译lexer.java 文件。如下图所示。 3.运行程序 test.in 是词法分析器的输入程序(即输入的源程序,此程序由学生给出)。运行后,自动在1000目录下生成result.out文件。(test.in 和result.out文件都可由记事本查看内容)

test.in 文件内容如下: result.out文件内容如下:

如果test.in中输入有误,则运行时提示:

4.编译原理课程设计内容如下:(注:每个同学从下列关键字中各选 五个,从运算符中各选四个,标识符和界符都要设计) 单词符号分为关键字,标识符,运算符,界符等: 关键字:begin end if then else while do void switch catch try case for continue break default return long short int float double char abstract Boolean

词法分析实验报告

词法分析实验报告 中北大学软件学院 实验报告 专业课程名称学号姓名 辅导教师成绩 实验日期实验时间 1实验名称 :词法分析器的设计与实现 2、实验目的 (1)掌握C语言单词符号的划分、正规式、状态转换图及词法分析器的实现。 (2)掌握词法分析程序的作用。 3、实验要求 (1)对任给的一个C语言源程序,能够滤掉空格、回车换行符、tab键及注释。 (2)识别各类单词符号,如关键字、标识符、运算符、常数、界符,结果以二元式形式输出,并构造符号表。 (3)输出有词法错误的单词及所在行号。(在此阶段只能识别有限的词法错误) 4、实验原理 根据扫描到的单词符号的第一个字符的种类,分别转到相应的程序进行处理。这些程序的功能就是识别以相应字符开头的各类单词符号。 5、实验步骤 (1)根据C语言各类单词的正规式,构造能识别各类单词的状态转换图。 (2)根据状态转换图,构造识别各类单词的词法分析器。 6、状态转换图及词法分析程序 (1)标识符(ID)和整型常数(NUM)的正规式如下:

ID = _ | letter (letter | digit)* NUM = digit digit* 其中标识符的状态转换图为: 其词法分析程序为: if(((ch>='A')&&(ch<='Z'))||((ch>='a')&&(ch<='z'))||(ch=='_')) { /*以字母开头*/ while(((ch>='A')&&(ch<='Z'))||((ch>='a')&&(ch<='z'))||(ch =='_')||((ch>='0')&&(ch<='9'))) { array[i++]=ch; ch=fgetc(fpin); } word=(char *)malloc((i+1)*sizeof(char)); memcpy(word,array,i); word[i]='\0'; is_id_key(word); if(ch!=EOF) fseek(fpin,-1L,SEEK_CUR); } 常数的状态转换图为: 其词法分析程序为: else if(ch>='0'&&ch<='9') { /*以数字开头*/ while(ch>='0'&&ch<='9')

词法分析报告设计实验报告材料(附代码)

实验一词法分析设计 实验学时:4 实验类型:综合 实验要求:必修 一、实验目的 通过本实验的编程实践,使学生了解词法分析的任务,掌握词法分析程序设计的原理和构造方法,使学生对编译的基本概念、原理和方法有完整的和清楚的理解,并能正确地、熟练地运用。 二、实验容 用VC++/VB/JAVA语言实现对C语言子集的源程序进行词法分析。通过输入源程序从左到右对字符串进行扫描和分解,依次输出各个单词的部编码及单词符号自身值;若遇到错误则显示“Error”,然后跳过错误部分继续显示;同时进行标识符登记符号表的管理。 以下是实现词法分析设计的主要工作: (1)从源程序文件中读入字符。 (2)统计行数和列数用于错误单词的定位。 (3)删除空格类字符,包括回车、制表符空格。 (4)按拼写单词,并用(码,属性)二元式表示。(属性值——token的机表示) (5)如果发现错误则报告出错 (6)根据需要是否填写标识符表供以后各阶段使用。 单词的基本分类: ◆关键字:由程序语言定义的具有固定意义的标识符。也称为保留字例如if、for、while、printf ;单词种别码为1。 ◆标识符:用以表示各种名字,如变量名、数组名、函数名; ◆常数:任何数值常数。如125, 1,0.5,3.1416; ◆运算符:+、-、*、/; ◆关系运算符:<、<=、= 、>、>=、<>; ◆分界符:;、,、(、)、[、]; 三、实验要求

1、编程时注意编程风格:空行的使用、注释的使用、缩进的使用等。 2、将标识符填写的相应符号表须提供给编译程序的以后各阶段使用。 3、根据测试数据进行测试。测试实例应包括以下三个部分: ◆全部合法的输入。 ◆各种组合的非法输入。 ◆由记号组成的句子。 4、词法分析程序设计要求输出形式: 例:输入VC++语言的实例程序: If i=0 then n++; a﹤= 3b %); 输出形式为: 单词二元序列类型位置(行,列)(单词种别,单词属性) for (1,for ) 关键字(1,1)i ( 6,i ) 标识符(1,2)= ( 4,= ) 关系运算符(1,3)0 ( 5,0 ) 常数(1,4)then ( 1,then) 关键字(1,5)n (6,n ) 标识符(1,6)++ Error Error (1,7);( 2, ; ) 分界符(1,8)a (6,a ) 标识符(2,1)

实验一(词法分析)

实验一词法分析 一.实验目的 1、学会针对DFA转换图实现相应的高级语言源程序。 2、深刻领会状态转换图的含义,逐步理解有限自动机。 3、掌握手工生成词法分析器的方法,了解词法分析器的内部工作原理。 二.实验内容 TINY计算机语言的编译程序的词法分析部分实现。 从左到右扫描每行该语言源程序的符号,拼成单词,换成统一的内部表示(token)送给语法分析程序。 为了简化程序的编写,有具体的要求如下: (1)数仅仅是整数。 (2)空白符仅仅是空格、回车符、制表符。 (3)代码是自由格式。 (4)注释应放在花括号之内,并且不允许嵌套 三.实验要求 要求实现编译器的以下功能: (1)按规则拼单词,并转换成二元式形式 (2)删除注释行 (3)删除空白符(空格、回车符、制表符) (4)列表打印源程序,按照源程序的行打印,在每行的前面加上行号,并且打印出每行包含的记号的二元形式

(5)发现并定位错误 ,词法分析进行具体的要求: (1)记号的二元式形式中种类采用枚举方法定义;其中保留字和特殊字符是每个都一个种类,标示符自己是一类,数字是一类;单词的属性就是表示的字符串值。 (2)词法分析的具体功能实现是一个函数GetToken(),每次调用都对剩余的字符串分析得到一个单词或记号识别其种类,收集该记号的符号串属性,当识别一个单词完毕,采用返回值的形式返回符号的种类,同时采用程序变量的形式提供当前识别出记号的属性值。这样配合语法分析程序的分析需要的记号及其属性,生成一个语法树。 (3)标示符和保留字的词法构成相同,为了更好的实现,把语言的保留字建立一个表格存储,这样可以把保留字的识别放在标示符之后,用识别出的标示符对比该表格,如果存在该表格中则是保留字,否则是一般标示符。 源程序: /* 注意: 测试时把测试的源程序存入当前目录中,并改名为source.txt */ #include #include #include #include #include usingnamespacestd;

词法分析的实验报告

《词法分析》 实验报告 目录 目录 0 1 实验目的 (1) 2 实验内容 (1) 2、1 TINY计算机语言描述 (1) 2、2 实验要求 (1) 3 此法分析器的程序实现 (2) 3、1 状态转换图 (2) 3、2 程序源码 (3) 3、3 实验运行效果截图 (8) 4 实验体会 (8)

1实验目的 1、学会针对DFA转换图实现相应的高级语言源程序。 2、深刻领会状态转换图的含义,逐步理解有限自动机。 3、掌握手工生成词法分析器的方法,了解词法分析器的内部工作原理。 2实验内容 2.1TINY计算机语言描述 TINY计算机语言的编译程序的词法分析部分实现。 从左到右扫描每行该语言源程序的符号,拼成单词,换成统一的内部表示(token)送给语法分析程序。 为了简化程序的编写,有具体的要求如下: 1、数仅仅就是整数。 2、空白符仅仅就是空格、回车符、制表符。 3、代码就是自由格式。 4、注释应放在花括号之内,并且不允许嵌套 TINY语言的单词 2.2实验要求 要求实现编译器的以下功能 1、按规则拼单词,并转换成二元式形式 2、删除注释行 3、删除空白符(空格、回车符、制表符)

4、列表打印源程序,按照源程序的行打印,在每行的前面加上行号,并且打印出每行包含的记号的二元形式 5、发现并定位错误 词法分析进行具体的要求 1、记号的二元式形式中种类采用枚举方法定义;其中保留字与特殊字符就是每个都一个种类,标示符自己就是一类,数字就是一类;单词的属性就就是表示的字符串值。 2、词法分析的具体功能实现就是一个函数GetToken(),每次调用都对剩余的字符串分析得到一个单词或记号识别其种类,收集该记号的符号串属性,当识别一个单词完毕,采用返回值的形式返回符号的种类,同时采用程序变量的形式提供当前识别出记号的属性值。这样配合语法分析程序的分析需要的记号及其属性,生成一个语法树。 3、标示符与保留字的词法构成相同,为了更好的实现,把语言的保留字建立一个表格存储,这样可以把保留字的识别放在标示符之后,用识别出的标示符对比该表格,如果存在该表格中则就是保留字,否则就是一般标示符。 3此法分析器的程序实现 3.1状态转换图 图1 TINY语言的确定有限自动机(DFA)

相关文档
最新文档