词法分析

Posted 松子茶

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了词法分析相关的知识,希望对你有一定的参考价值。

本章概述

执行词法分析的程序称为词法分析器。本章中,了解词法分析器的功能和输出形式,熟练掌握词法分析器设计的原理和方法,能够以转换图为工具使用某种语言的编写并调试一个扫描器。在正确理解正规表达式与有限自动机的有关概念、理论的基础上,了解词法分析的自动产生原理。词法分析器的功能和设计方法,正规表达式与有限自动机的等价性,有限自动机的确定化和最小化。

对于词法分析器的要求

词法分析器的功能和输出形式

词法分析器的功能是输入源程序,输出单词符号。单词符号是一个程序语言的基本语法符号。程序语言的单词符号一般可分为下列五种:
1. 关键字 是由程序语言定义的具有固定意义的标识符。有时称这些标识符为保留字或基本字。例如,Pascal中的begin, end, if, while都是保留字。这些字通常不用作一般标识符。
2. 标识符 用来表示各种名字,如变量名、数组名、过程名等等。
3. 常数 常数的类型一般有整型、实型、布尔型、文字型等等,例如,100,3.14159,TRUE,‘Sample’。
4. 运算符 如+、-、*、/等等。
5. 界符 如逗号、分号、括号、//等等。
一个程序语言的关键字、运算符和界符都是确定的,一般只有几十个或上百个。而对于标识符或常数的使用通常都不加什么限制。
词法分析器所输出的单词符号常常表示成如下的二元式:
(单词种别,单词符号的属性值)
单词种别通常用整数编码。一个语言的单词符号如何分种,分成几种,怎样编码,是一个技术性的问题。它主要取决于处理上的方便。标识符一般统归为一种。常数则宜按类型(整、实、布尔等)分种。关键字可将其全体视为一种,也可以一字一种。采用一字一种的分法实际处理起来较为方便。运算符可采用一符一种的分法,但也可以把具有一定共性的运算符视为一种。至于界符一般用一符一种的分法。
如果一个种别只含一个单词符号,那么,对于这个单词符号,种别编码就完全代表它自身了。若一个种别含有多个单词符号,那么,对于它的每个单词符号,除了给出种别编码之外,还应给出有关单词符号的属性信息。
单词符号的属性是指单词符号的特性或特征。属性值则是反应特性或特征的值。例如,对于某个标识符,常将存放它的有关信息的符号表项的指针作为其属性值;对于某个常数,则将存放它的常数表项的指针作为其属性值。
在本书中,我们假定关键字、运算符和界符都是一符一种。对于它们,词法分析器只给出其种别编码,不给出它自身的值。标识符单列一种。常数按类型分种。

词法分析器作为一个独立子程序

词法分析器的功能是输入源程序,输出单词符号。单词符号是一个程序语言的基本语法符号。程序语言的单词符号一般可分为下列五种:
1. 关键字 是由程序语言定义的具有固定意义的标识符。有时称这些标识符为保留字或基本字。例如,Pascal中的begin, end, if, while都是保留字。这些字通常不用作一般标识符。
2. 标识符 用来表示各种名字,如变量名、数组名、过程名等等。
3. 常数 常数的类型一般有整型、实型、布尔型、文字型等等,例如,100,3.14159,TRUE,‘Sample’。
4. 运算符 如+、-、*、/等等。
5. 界符 如逗号、分号、括号、//等等。
一个程序语言的关键字、运算符和界符都是确定的,一般只有几十个或上百个。而对于标识符或常数的使用通常都不加什么限制。
词法分析器所输出的单词符号常常表示成如下的二元式:
(单词种别,单词符号的属性值)
单词种别通常用整数编码。一个语言的单词符号如何分种,分成几种,怎样编码,是一个技术性的问题。它主要取决于处理上的方便。标识符一般统归为一种。常数则宜按类型(整、实、布尔等)分种。关键字可将其全体视为一种,也可以一字一种。采用一字一种的分法实际处理起来较为方便。运算符可采用一符一种的分法,但也可以把具有一定共性的运算符视为一种。至于界符一般用一符一种的分法。
如果一个种别只含一个单词符号,那么,对于这个单词符号,种别编码就完全代表它自身了。若一个种别含有多个单词符号,那么,对于它的每个单词符号,除了给出种别编码之外,还应给出有关单词符号的属性信息。
单词符号的属性是指单词符号的特性或特征。属性值则是反应特性或特征的值。例如,对于某个标识符,常将存放它的有关信息的符号表项的指针作为其属性值;对于某个常数,则将存放它的常数表项的指针作为其属性值。
在本书中,我们假定关键字、运算符和界符都是一符一种。对于它们,词法分析器只给出其种别编码,不给出它自身的值。标识符单列一种。常数按类型分种。

词法分析器的设计

输入和预处理

词法分析器工作的第一步是输入源程序文本。输入串一般是放在一个缓冲区中,这个缓冲区称输入缓冲区。词法分析的工作(单词符号的识别)可以直接在这个缓冲区中进行。但在许多情况下,把输入串预处理一下,对单词符号的识别工作将是比较方便的。
对于许多程序语言来说,空白符、跳格符、回车符和换行符等编辑性字符除了出现在文字常数中之外,在别处的任何出现都没有意义,而注解部分几乎允许出现在程序中的任何地方。它们不是程序的必要组成部分;它们存在的意义仅仅在于改善程序的易读性和易理解性。对于它们,预处理时可以将其剔掉。
有些语言把空白符(一个或相继数个)用作单词符号之间的间隔,即用作界符。在这种情况下,预处理时可把相继的若干个空白结合成一个。
我们可以设想构造一个预处理子程序,它能够完成上面所述的任务。每当词法分析器调用它时,它就处理出一串确定长度(如120个字符)的输入字符,并将其装进词法分析器所指定的缓冲区中(称为扫描缓冲区)。这样,分析器就可以在此缓冲区中直接进行单词符号的识别,而不必照管其它繁琐事务。
分析器对扫描缓冲区进行扫描时一般用两个指示器,一个指向当前正在识别的单词的开始位置(指向新单词的首字符),另一个用于向前搜索以寻找单词的终点。
假定每半区可容120个字符,而这两个半区又是互补使用的。如果搜索指示器从单词起点出发搜索到半区的边缘但尚未到达单词的终点,那么就应调用预处理程序,令其把后续的120个输入字符装进另半区。我们认定,在搜索指示器对另半区进行扫描的期间内,现行单词的终点必定能够到达。这意味着对标识符和常数的长度实际上必须加以限制(例如,不得多于120个字符),否则,即使缓冲区再大也无济于事。

单词符号的识别:超前搜索

下面我们来介绍单词符号识别的一个简单方法——超前搜索。
关键字的识别
象FORTRAN这样的语言,关键字不加特殊保护(只要不引起矛盾,用户可以用它们作为普通标识符),关键字和用户自定义的标识符或标号之间没有特殊的界符作间隔。这就使得关键字的识别甚为麻烦。请看下面的例子:
1 DO99K=1,10
2 IF(5.EQ.M) I=10
3 DO99K=1.10
4 IF(5)=55
这四个语句都是正确的FORTRAN语句。语句1和2分别是DO和IF语句,它们都是以基本字开头的。语句3和4是赋值句,它们都是以用户自定义标识符开头的。
为了从1、2中识别出关键字DO和IF,我们必须要能够区别1、3和区别2、4。语句1、3的区别在于等号之后的第一个界符:一个为逗点,另一个为句末符。语句2、4的主要区别在于右括号后的第一个字符:一个为字母,另一个为等号。这就是说,为了识别 1、2 中的关键字,我们必须超前扫描许多个字符,超前到能够肯定词性的地方为止。对于1、3来说,尽管都以‘D’和‘O’两字母开头,但不能一见‘DO’就认定是DO语句。我们必须超前扫描,跳过所有的字母和数字,看看是否有等号。如果有,再向前搜索。若下一个界符是逗号,则可以肯定DO应为关键字。否则,DO不构成关键字,它只是用户标识符的头两个字母。所以,为了区别1和3,我们必须超前扫描到等号后的第一个界符处。对于语句2、4来说,必须超前扫描到与IF后的左括号相对应的那个右括号之后的第一个字符为止。若此字符是字母,则得逻辑IF。若此字符是数字,则得算术IF。否则,就应认为是用户自定义标识符IF。
标识符的识别
多数语言的标识符是字母开头的“字母/数字”串,而且在程序中标识符的出现都后跟着算符或界符。因此标识符的识别大多没有困难。
常数的识别
多数语言算术常数的表示大体相似,对它们的识别也是很直接的。但对于某些语言的常数的识别也需用超前搜索的方法。例如,对于上述FORTRAN片断的第2句中的5.EQ.M,只有当超前扫描到字母Q时才能断定5的词性。因为5.E08和5.EQ.M的头三个字符完全一样。
逻辑(或布尔)常数和用引号括起来的字符串常数都很容易识别。但对FORTRAN的文字常数(例如3HABC)的识别却有点麻烦。对3HABC的识别单单依靠形式规则是不够的,而且需要了解“3H”的含义。也就是说,对于这种单词的识别有赖于理解词头的词义。所以,FORTRAN文字常数的识别通常需要特殊处理:当分析器读到尾跟H的无符号整型常数时,必须首先将这个常数的值翻译出来,然后把后续的n个(n为该整型常数的值)字符取出来,作为“字符串常数”输出。
算符和界符的识别
词法分析器应将那些由多个字符复合成的算符和界符(如C++和Java中的++、--、>=等)拼合成一个单词符号。因为这些字符串是不可分的整体,若分划开来,便失去了原来的意义。在这里同样需要超前搜索。

状态转换图

使用状态转换图是设计词法分析程序的一种好途径。转换图是一张有限方向图。在状态转换图中,结点代表状态,用圆圈表示。状态之间用箭弧连结。箭弧上的标记(字符)代表在射出结点(即箭弧始结点)状态下可能出现的输入字符或字符类。一张转换图只包含有限个状态(即有限个结点),其中有一个被认为是初态,而且实际上至少要有一个终态(用双圈表示)。一个状态转换图可用于识别(或接受)一定的字符串。
一个非常重要的事实是,大多数程序语言的单词符号都可以用转换图予以识别。
注意,一个程序语言的所有单词符号的识别也可以用若干张状态转换图予以描述。虽然用一张图就可以了,但用若干张图有时会有助于概念的清晰化。

状态转换图的实现

转换图容易用程序实现。最简单的办法是让每个状态结点对应一小段程序。下面我们将引进一组全局变量和过程,将它们作为实现转换图的基本成分。这些变量和过程是:
1. ch 字符变量,存放最新读进的源程序字符。
2. strToken 字符数组,存放构成单词符号的字符串。
3. GetChar 子程序过程,将下一输入字符读到ch中,搜索指示器前移一字符位置。
4. GetBC 子程序过程,检查ch中的字符是否为空白。若是,则调用GetChar直至ch中进入一个非空白字符。
5. Concat 子程序过程,将ch中的字符连接到strToken之后。例如,假定strToken原来的值为“AB”,而ch中存放着‘C’,经调用Concat后,strToken的值就变为“ABC”。
6. IsLetter和IsDigit 布尔函数过程,它们分别判断ch中的字符是否为字母和数字。
7. Reserve 整型函数过程,对strToken中的字符串查找保留字表,若它是一个保留字则返回它的编码,否则返回0值(假定0不是保留字的编码)。
8. Retract 子程序过程,将搜索指示器回调一个字符位置,将ch置为空白字符。
9. InsertId 整型函数过程,将strToken中的标识符插入符号表,返回符号表指针。
10. InsertConst 整型函数过程,将strToken中的常数插入常数表,返回常数表指针。
这些函数和子程序过程都不难编制。使用它们能够方便地构造状态转换图的对应程序。一般来说,可让每个状态结点对应一程序段。
对于不含回路的分叉结点来说,可让它对应一个switch语句或一组if…then…else语句。
当程序执行到达“错误处理”时,意味着现行状态i和当前所面临的输入串不匹配。如果后面还有状态图,出现在这个地方的代码应为:将搜索指示器回退一个位置,并令下一个状态图开始工作。如果后面没有其它状态图,则出现在上述位置的代码应进行真正的出错处理,报告源程序含有非法符号,并进行善后处理。
对于含回路的状态结点来说,可让它对应一个由while语句和if语句构成的程序段。
终态结点一般对应一个形如return (code, value)的语句。其中,code为单词种别编码;value或是单词符号的属性值,或无定义。这个return意味着从分析器返回到调用者,一般指返回到语法分析器。凡带星号*的终态结点意味着多读进了一个不属于现行单词符号的字符,这个字符应予退回,也就是说,必须把搜索指示器回调一字符位置。这项工作由Retract过程来完成。
对于某些状态,若需要将ch的内容送进strToken,则可以调用Concat。

正规表达式与有限自动机

正规式与正规集

对于字母表å,我们感兴趣的是它的一些特殊字集,即所谓正规集。我们将使用正规式这个概念来表示正规集。下面是正规式和正规集的递归定义:
1. e和f都是S上的正规式,它们所表示的正规集分别为e和f;
2. 任何aÎS,a是S上的一个正规式,它所表示的正规集为a;
3. 假定U和V都是S上的正规式,它们所表示的正规集分别记为L(U)和L(V),那么,(U|V)、(U·V)和(U)也都是正规式,它们所表示的正规集分别为L(U) ∪L(V)、L(U)L(V)(连接积)和(L(U))(闭包)。
仅由有限次使用上述三步骤而得到的表达式才是S上的正规式。仅由这些正规式所表示的字集才是S上的正规集。
正规式的运算符“|”读为“或”,“·”读为“连接”,“”读为“闭包”(即,任意有限次的自重复连接)。在不致混淆时,括号可以省去,但规定算符的优先顺序为:先“”,次“·”,最后“|”。连接符“·”一般可省略不写。
若两个正规式所表示的正规集相同,则认为二者等价。两个等价的正规式U和V记为U=V。例如,b(ab)=(ba)*b, (a|b)=(a*b*)*。
令U、V和W均为正规式,显而易见,下列关系普遍成立:
1. U|V=V|U(交换律)
2. U|(V|W)=(U|V)|W(结合律)
3. U(VW)=(UV)W(结合律)
4. U(V|W)=UV|UW(分配律)
(V|W)U=VU|WU
5. eU=Ue=U

确定有限自动机(DFA)

一个确定的有限自动机(DFA) M是一个五元式

M=(Q,,f,S,Z)

其中

  1. Q是一个有限集,它的每个元素称为一个状态;
  2. 是一个有穷字母表,它的每个元素称为一个输入字符;
    3. f是一个从Q× 至Q的单值部分映射。 f(qi,a)=qj,(qi,qjQ,a 意味着:当现行状态为 qi 、输入字符为a时,将转换到下一状态 qj 。我们称 qj qi 的一个后继状态;
  3. SQ ,是唯一的初态。
  4. ZQ ,是一个终态集(可空)。

显然,一个DFA可用一个矩阵表示,该矩阵的行表示状态,列表示输入字符,矩阵元素表示d(s, a)的值。这个矩阵称为状态转换矩阵。例如,有DFA

M=(0,1,2,3,a,b,d,0,3)

其中d为:
f(0,a)=1 f(0,b)=2
f(1,a)=3 f(1,b)=2
f(2,a)=1 f(2,b)=3
f(3,a)=3 f(3,b)=3
它所对应的状态转换矩阵如表3.2所示。
表3.2 状态转换矩阵

状态ab
012
132
213
333

一个DFA也可表示成一张(确定的)状态转换图。假定DFA M含有m个状态和n个输入字符,那么,这个图含有m个状态结点,每个结点顶多有n条箭弧射出和别的结点相连接,每条箭弧用S中的一个不同输入字符作标记,整张图含有唯一的一个初态结点和若干个(可以是0个)终态结点。
对于 中的任何字a,若存在一条从初态结点到某一终态结点的通路,且这条通路上所有弧的标记符连接成的字等于a,则称a可为DFA M所识别(读出或接受)。若M的初态结点同时又是终态结点,则空字e可为M所识别(或接受)。DFA M所能识别的字的全体记为L(M)。
如果一个DFA M的输入字母表为S,则我们也称M是S上的一个DFA。可以证明:S上的一个字集VÍS*是正规的,当且仅当存在S上的DFA M,使得V=L(M)。
DFA的确定性表现在映射d: S×S→S是一个单值函数。也就是说,对任何状态sÎS和输入符号aÎS,d(s,a)唯一地确定了下一状态。从转换图的角度来看,假定字母表S含有n个输入字符,那么,任何一个状态结最多只有n条弧射出,而且每条弧以一个不同的输入字符标记。如果也允许d是一个多值函数,我们就得到非确定自动机的概念。

非确定有限自动机(NFA)

一个非确定有限自动机(NFA)M是一个五元式

M=(Q,,f,S,Z)

其中

  1. Q,,Z 意义同DFA;
  2. f是一个从S× 到S的子集的映照,即 f: Q×2Q
  3. SQ ,是一个非空初态集;
  4. ZQ ,是一个终态集(可空)。

显然,一个含有m个状态和n个输入字符的NFA可表示成如下的状态转换图:该图含有m个状态结点,每个结点可射出若干条箭弧与别的结点相连接,每条弧用S*中的一个字(不一定要不同的字而且可以是空字e)作标记(称为输入字),整张图至少含有一个初态结点以及若干个(可以是0个)终态结点。某些结点既可以是初态结点也可以是终态结点。
对于 中的任何一个字a,若存在一条从某一初态结点到某一终态结点的通路,且这条通路上所有弧的标记字依序连接成的字(忽略那些标记为e的弧)等于a,则称a可为NFA M所识别(读出或接受)。若M的某些结点既是初态结点又是终态结点,或者存在一条从某个初态结点到某个终态结点的e通路,那么,空字e可为M所接受。
显然,DFA是NFA的特例。但是,对于每个NFA M存在一个DFA M”,使L(M)=L(M”)。把NFA确定化为DFA的方法称为子集法。

正规文法与有限自动机的等价性

对于正规文法G和有限自动机M,如果L(G)=L(M),则称G和M是等价的。关于正规文法和有限自动机的等价性,有以下结论:
1. 对每一个右线性正规文法G或左线性正规文法G,都存在一个有限自动机(FA) M,使得L(M)=L(G)。
2. 对每一个FA M,都存在一个右线性正规文法GR和左线性正规文法GL,使得L(M)=L(GR)=L(GL)。

正规式与有限自动机的等价性

下面我们将证明:
1. 对任何FA M,都存在一个正规式r,使得L(r)=L(M)。
2. 对任何正规式r,都存在一个FA M,使得L(M)=L(r)。
上述结论加上3.3.3和3.3.4所证明的结论,说明正规文法、正规式、确定有限自动机和非确定有限自动机在接收语言的能力上是互相等价的。

确定有限自动机的化简

一个确定有限自动机M的化简是指:寻找一个状态数比M少的DFA M¢,使得L(M)=L(M¢)。
假定s和t是M的两个不同状态,我们称s和t是等价的:如果从状态s出发能读出某个字w而停于终态,那么同样,从t出发也能读出同样的字w而停于终态;反之,若从t出发能读出某个字w而停于终态,则从s出发也能读出同样的w而停于终态。如果DFA M的两个状态s和t不等价,则称这两个状态是可区别的。例如,终态与非终态是可区别的。因为,终态能读出空字e,非终态则不能读出空字e。又例如,图3.8中的状态1与2是可区别的,因为,状态1读出a而停于终态,状态2读出a后不到达终态。
一个DFA M的状态最少化过程旨在将M的状态集分割成一些不相交的子集,使得任何不同的两子集中的状态都是可区别的,而同一子集中的任何两个状态都是等价的。最后,在每个子集中选出一个代表,同时消去其它等价状态。
对M的状态集S进行分划的步骤是:首先,把S的终态与非终态分开,分成两个子集,形成基本分划P。显然,属于这两个不同子集的状态是可区别的。假定到某个时候P已含m个子集,记P=I(1), I(2), …, I(m),并且属于不同子集的状态是可区别的。检查P中的每个I(i)看能否进一步分划。对于某个I(i),令I(i)=q1, q2, …, qk,若存在一个输入字符a使得(关于Ia的定义见3.3.3)不全包含在现行P的某一子集I(j)中,就将I(i)一分为二。例如,假定状态s1和s2经a弧分别达到状态t1和t2,而t1和t2属于现行P的两个不同子集,那就将I(i)分成两半,使得一半含有s1:
I(i1) = s | s ÎI(i) 且s经a弧到达t1所在子集中的某状态
另一半含有s2:
I(i2) = I(i) - I(i1)
由于t1和t2是可区别的,即存在一个字w,t1将读出w而停于终态,而t2或读不出w或虽然可读出w但不到达终态;或情形恰好相反。因而字aw将状态s1和s2区别开来。也就是说,I(i1)中的状态与I(i2)中的状态是可区别的。至此我们将I(i)分成两半,形成了新的分划。
一般地,若落入现行P中N个不同子集,则应将I(i)划分为N个不相交的组,使得每个组J的Ja都落入P的同一子集,这样形成新的分划。重复上述过程,直至分划中所含的子集数不再增长为止。至此,P中的每个子集已不可再分。也就是说,每个子集中的状态是互相等价的,而不同子集中的状态则是可互相区别的。
经上述过程之后,得到一个最后分划P。对于这个P中的每一个子集,我们选取子集中的一个状态代表其它状态。例如,假定I=q1, …, qk是这样一个子集,我们即可挑选q1代表这个子集。在原来的自动机中,凡导入到q2, …, qk的弧都改成导入到q1。然后,将q2, …, qk从原来的状态集S中删除。若I中含有原来的初态,则q1是新初态;若I中含有原来的终态,则q1是新终态。可以证明,经如此化简之后得到的DFA M¢和原来的M是等价的,也就是L(M)=L(M¢)。若从M¢中删除所有无用状态(即从初态结开始永远到达不了的那些状态),则M¢便是最简的(包含最少状态)。

词法分析器的自动产生

语言LEX的一般描述

一个LEX源程序主要包括两部分。一部分是正规定义式,另一部分是识别规则。
如果å是一个字母表,å上的正规定义式是下述形式的定义序列:
d1 → r1
d2 → r2

di → ri
其中di表示不同的名字,每个ri是å∪d1,…, di-1上的符号所构成的正规式。ri中不能含有di, di+1, …, dn,这样,对任何ri,可以构成一个S上的正规表达式,只要反复地将式中出现的名字代之以相应的正规式即可。注意,如果允许ri中出现某些dj, j³i,那么这种替代过程将有可能不终止。
LEX源程序中的识别规则是一串如下形式的LEX语句:
P1 A1
P2 A2
… …
Pm Am
其中,每个Pi是一个正规式,称为词形。Pi中除了出现S中的字符外,还可以出现正规定义式左部所定义的任何简名di。即,Pi是S∪d1,…,dn上的一个正规式。由于每个di最终都可化为纯粹S上的正规式,因此,每个Pi也同样如此。每个Ai是一小段程序代码,它指出了,在识别出词形为Pi的单词之后,词法分析器应采取的动作。这些识别规则完全决定了词法分析器L的功能。分析器L只能识别具有词形P1,…,Pm的单词符号。
关于描述动作Ai的LEX语言成分可以有种种不同的选择。下面,在讨论L的作用时,将对Ai的有关组成成分予以必要的说明和解释。
首先,我们考察由LEX所产生的目标程序L(词法分析器)是如何进行工作的:L逐一扫描输入串的每个字符,寻找一个最长的子串匹配某个Pi,将该子串截下来放在一个叫做TOKEN的缓冲区中(事实上,这个TOKEN也可以只包含一对指示器,它们分别指出这个子串在原输入缓冲区中的始末位置)。然后,L就调用动作子程序Ai,当Ai工作完后,L就把所得的单词符号(由种别编码和属性值两部分构成)交给语法分析程序。当L重新被调用时就从输入串中继上次截出的位置之后识别下一个单词符号。
可能存在这样的情形,对于现行输入串找不到任何词形Pi与之相匹配。在这种情形下,L应报告输入串含有错误(如非法字符),并进行善后处理。但也可能存在一个最长子串,可以匹配若干个不同的Pi。在这种二义的情形下,以LEX程序中出现在最前面的那个Pi为准。换句话说,愈处于前面的Pi,匹配优先权就愈高(在服从最长匹配的前提下)。
每个词形Pi相应的动作Ai的基本组成成分是“返回Pi的种别编码和属性值”。这可用一个LEX过程表示成return(code, value)。如果Pi是“标识符”,则value为符号表入口指针;若Pi是“整型常数”,则value为常数表入口指针;若Pi既不是标识符也不是某种常数,那么,value便无定义。

超前搜索

在某些语言中,要识别一个单词符号必须超前看若干字符。例如,在标准FORTRAN中,空白字符除了出现在文字常数中有意义之外,在别的任何地方出现都是没有意义的。一个众所周知的例子是:
DO 501 I=1.25
在碰到小数点之前,我们弄不清楚这是DO语句还是一个对标识符DO501I进行赋值的赋值语句。为此,我们引进另一个正规式运算符‘/’,用它来指出一个单词符号的截取点。于是,关于基本字DO的识别规则就可写为:
DO/(letter|digit)= (letter|digit),动作
这意味着要求词法分析器L向前扫描到逗点,识别出具有如下词形的输入子串:
DO/(letter|digit)= (letter|digit)
在寻找到这种匹配之后,就按识别规则中斜线所指处将输入串截断,取出其前一部分子串(即DO)作为词法分析器L的输出,而将后一部分子串归还给输入串。斜线‘/’应被当成是正规式的一个“算符”,可称它为“截断”算符。

以上是关于词法分析的主要内容,如果未能解决你的问题,请参考以下文章

词法分析

简单词法编译器

词法分析程序

软件构造——制作词法分析器

词法分析096

词法分析