编译原理-词法分析

本文深入探讨编程语言的词法分析过程,介绍词法分析器的工作原理,包括手动设计词法分析器、正则式、正则文法与正则表达式的相互转化,以及有穷自动机的概念。文章详细阐述了确定有穷自动机(DFA)和非确定有限自动机(NFA)的转换方法,并提供了实例说明。同时,讨论了词法分析器的实际设计和测试,涉及词法分析器的构建方式,包括手工编写和利用自动生成工具。
摘要由CSDN通过智能技术生成

词法分析

对源程序进行扫描产生单词符号,将源程序改造为单词符号串的中间程序,即输入源程序、输出单词符号。词法分析器(Lexical Analyzer)包括扫描器(Scanner)与执行词法分析的程序

单词符号是一个程序语言的基本语法符号。称作 token(记号) ,是具有独立意义的最小语法单位。将字符组合成记号与在一个英语句子中将字母构成单词并确定单词的含义很相像,此时的任务很像拼写。

程序语言的单词符号一般分为:

  • 关键字:保留字
  • 标识符:变量名、过程名等
  • 常数:数字、字符串、布尔型等
  • 运算符:±/*等
  • 界符:逗号,分号,// 等

单词符号常常表示成二元式:(单词种别,单词符号的属性值)。单词种别是语法分析需要的信息,通常用整数编码。一个语言的单词符号如何分种,分成几种,怎样编码,是一个技术性的问题。它主要取决于处理上方便。

  • 标识符一般统归为一种。
  • 常数则按类型分种。
  • 关键字可将其全体视为一种,也可以一字一种。采用一字一种的分法实际处理起来较为方便。
  • 运算符可采用一符一种的分法,但也可以把具有一定共性的运算符视为一种。
  • 界符一般用一符一种的分法。

种别通常定义为枚举类型的逻辑项。

typedef enum {
   
   IF,ELSE,PLUS,NUM,ID,……
} TokenType;

单词符号的属性值是指单词符号的特性或特征,可以是标识符在符号表的入口地址、数值的二进制值等。

如果是一符一种的分法(如关键字,运算符等),词法分析器只给出其种别编码,不给出其属性值。
如果一个种别含有多个单词符号,那么对于它的每个单词符号,除了给出种别编码,还应给出属性值,以便把同一种类的单词区别开来。标识符属性值是自身的符号串;也可是在符号表的入口地址。常数自身值是常数的二进制数值。

扫描器必须计算每一个记号的若干属性,所以将所有的属性收集到一个单独构造的数据类型中是很有用的,这种数据类型称作记号记录(token record)。

typedef struct {
   
    TokenType tokenval; 
    char* stringval;
    int numval;
} TokenRecord;

或作为一个联合

typedef struct {
   
    TokenType tokenval; 
    unon {
    char* stringval;
          int numval; 
         } attribute;
} TokenRecord;

简而言之,扫描源程序,按照(单词种别,单词符号的属性值)这样的二元形式输出单词符号串

【例】试给出程序段 if (a>1) b = 100;输出的单词符号串。
假定基本字、运算符和界符都是一符一种,标识符自身的值是字符串,常数是二进制值。
(2,)	基本字 if
(29,)	左括号 (
(10,‘a’)	标识符 a
(23,)	大于号 >
(11,‘1’的二进制)	常数 1
(30,)	右括号 )
(10,‘b’)	标识符 b
(17,)	赋值号 =
(11,‘100’的二进制)	常数 100
(26,)	分号 ;

另一种表示可以为:

【例】考虑下述 C++ 代码段:while ( i >= j ) i--;	
假定基本字、运算符和界符都是一符一种,标识符自身的值是符号表的入口地址,常数是二进制值。
经词法分析器处理后,转换为如下的单词符号序列:
	( while ,- )
    ( (	   ,- )
    ( id    ,指向i的符号表表项的指针 )
    ( >=    ,- )
    ( id    ,指向j的符号表表项的指针 )
    ( )     ,- )
    ( id    ,指向i的符号表表项的指针 )
    ( --    ,- )
    ( ;    ,- )

词法分析作为一个独立的阶段(一遍),把源程序的字符序列翻译成单词符号序列存放于文件中,待语法分析程序工作时再从文件输入这些单词符号进行分析。结构更简单、清晰和条理化。有利于集中考虑词法分析一些细节问题。
词法分析作为一个子程序,每当语法分析器需要一个单词符号时就调用这个词法分析子程序。每一次调用,词法分析器就从输入字符串中识别出一个单词符号。

image.png

通常,构造词法分析程序有两种方法:

  • 手工方式:根据识别语言单词的状态转换图,使用某种高级语言。例如:用 C 语言直接编写词法分析程序
  • 自动方式:利用词法分析程序的自动生成工具 LEX 自动生成词法分析程序。

词法分析手动设计

image.png

1.输入缓冲区、预处理

词法分析器工作的第一步是输入源程序文本到输入缓冲区。
预处理工作:是将输入的源程序中的多余的空白符、跳格符、回车符、换行符等编辑性字符以及注释部分剔除掉,并将结果存入扫描缓冲区,方便单词符号的识别。

2.扫描缓冲区

  • 为了保证单词符号不被扫描缓冲区边界打断,扫描缓冲区一般设计为如下一分为二的区域;
  • 每次输入更新其一半空间的内容,使得词法分析器在最坏情况下识别单词符号的长度是扫描缓冲区长度的一半。因此也称配对缓冲区。
  • 两个指示器
    • 起点指示器:新单词的首字符;
    • 搜索指示器:用于向前搜索以寻找单词的终点;
  • 如果搜索指示器从单词起点出发搜索到半区的边缘,但未到单词的终点,则调用预处理程序,把后续的字符串装进另半区
image.png

单词符号的识别:超前搜索:源程序中的单词符号构成没有特殊的结尾,单词符号与单词符号之间在不引起可读性理解错误时也可以不必有空格作间隔,因此有时当单词符号的所有字符都已处理后,特别是当有单词符号是另一个单词符号的前缀子串时,词法分析器不能确定当前单词识别是否已结束,需要再超前搜索若干个字符后才能确定,返回识别出的单词,如果这时有多读进的字符,则需要回退处理。
例如 C 语言中的单词符号“>”、“>=”的识别就需要超前搜索。

不使用超前搜索的几种情况

  • 规定所有基本字都是保留字;用户不能用它们作自己的标识符;基本字作为特殊的标识符来处理,使用保留字表;
  • 如果基本字、标识符和常数(或标号)之间没有确定的运算符或界符作间隔,则必须使用一个空白符作间隔

状态转换图:状态转换图可用于识别(或接受)一定的字符串。大多数程序语言的单词符号都可以用转换图予以识别。

状态转换图是一张有限方向图,结点代表状态,用圆圈表示,状态之间用箭弧连结,箭弧上的标记(字符)代表射出结状态下可能出现的输入字符或字符类,一张转换图只包含有限个状态,其中有一个为初态,至少要有一个终态

状态转换图可用于识别(或接受)一定的字符串,若存在一条从初态到某一终态的道路,且这条路上所有弧上的标记符连接成的字等于 α,则称α被该状态转换图所识别(接受)

image.png image.png

【示例】

image.png

设计时假定:

  • 基本字:所有基本字都是保留字,用户不得使用它们作为自己定义的标识符;
  • 基本字作为一类特殊的标识符来处理,不再专设对应的转换图。但需要把关键字预先安排在一个表格,此表叫关键字表。当识别出一个标识符时,就去查关键字表,以确定它是否为一关键字。
  • 若关键字、标识符和常数之间没有确定的运算符或界限府作间隔,则必须至少用一个空白符作间隔。

状态转换图实现时可以让每个状态结点对应一小段程序。

分支节点:

image.png

循环状态节点

image.png

终态节点

image.png

下面使用伪码来简单实现词法分析器

全局变量与过程
ch 字符变量,存放最新读入的源程序字符
strToken 字符数组,存放构成单词符号的字符串
GetChar 子程序过程,把下一个字符读入到 ch 中
GetBC 子程序过程,跳过空白符,直至 ch 中读入一非空白符
Concat 子程序,把ch中的字符连接到 strToken 
IsLetter和 IsDisgital 布尔函数,判断ch中字符是否为字母和数字
Reserve 整型函数,对于 strToken 中的字符串查找保留字表,若它是保留字则给出它的编码,否则回送0
Retract 子程序,把搜索指针回调一个字符位置
InsertId  整型函数,将strToken中的标识符插入符号表,返回符号表指针
InsertConst  整型函数,将strToken中的常数插入常数表,返回常数表指针
int code, value;
strToken := “ ”;	/*置strToken为空串*/
GetChar();GetBC();
if (IsLetter())
begin
	while (IsLetter() or IsDigit())
	begin
		Concat(); GetChar(); 
	end
	Retract();
	code := Reserve();
	if (code = 0)
	begin
		value := InsertId(strToken);
		return ($ID, value);
	end
	else
		return (code, -);	
end
else if (IsDigit())
begin
	while (IsDigit())
	begin
		Concat( ); GetChar( );
	end
	Retract();
	value := InsertConst(strToken);
	return($INT, value);
end
else if (ch =‘=’) return ($ASSIGN, -);
else if (ch =‘+’) return ($PLUS, -);
else if (ch =‘*’)
begin
	GetChar();
	if (ch =‘*’) return ($POWER, -);
	Retract(); return ($STAR, -);
end
else if (ch =‘,’) return ($COMMA, -);
else if (ch =‘(’) return ($LPAR, -);
else if (ch =‘)’) return ($RPAR, -);
else ProcError( );		/* 错误处理*/
curState = 初态
GetChar();
while( stateTrans[curState][ch]有定义){
   //存在后继状态,读入、拼接
   Concat();
   //转换入下一状态,读入下一字符
   curState= stateTrans[curState][ch];
   if curState是终态 then 返回strToken中的单词
   GetChar( ); 
}

正则式

正则式:定义语言单词符号的一种方式(或者叫正规式)

【例】定义标识符的正则式
字母(字母 | 数字)*

正则式和正则集的递归定义:

  • ε 和 Φ 都是 ∑ 上的正则式,它们所表示的正则集分别为 {ε} 和 Φ;
  • 任何 a∈∑, a 是 ∑ 上的一个正则式,它所表示的正则集为 {a};
  • 假定 e1 和 e2 都是 ∑ 上的正则式,它们所表示的正则集分别记为 L(e1)和 L(e2),则:
    e1|e2 是正则式,表示的正则集为 L(e1)∪L(e2)(并集)
    e1e2 是正则式,表示的正则集为 L(e1)L(e2) (连接集)
    (e1)* 是正则式,表示的正规集为 (L(e1))*(闭包)
    优先级为闭包、连接积、或。

正则集可以用正则式表示,正则式是表示正则集一种方法,一个字符串集合是正则集当且仅当它能用正则式表示

正则式表示字符串的格式,正则式 r 完全由它所匹配的串集来定义。这个集合为由正则式生成的语言(language generated by the regular expression),写作 L®,每个正则式可以看作是一个匹配模式。

【例】设∑={a,b,c},则aa*bb*cc* 是∑上的一个正则式
aa*bb*cc* 是∑上的一个正则式,它所表示的正则集是
L = {abc,aabc,abbc,abcc,aaabc,…}
  = {a^m b^n c^l | m,n,l ≥1}
【例】设程序语言字母表是键盘字符集合,则程序语言单词符号可用如下正则式定义
关键字	if | else | while | do
标识符	l (l | d)*
所整常数	dd*
关系运算符	< | <= | > | >= | <>
	其中 l 代表 a~z 中任一英文字母
    其中 d 代表 0~9 中任一数字

正则式等价

若两个正则式所表示的正则集相同,则认为二者等价。两个等价的正则式 R1 和 R2 记为 R1=R2。

【例】
	(a|b)* = (a*|b*)*
	b(ab)* = (ba)* b

正则式扩展

正则式 r 的一个或多个重复,写作 r+
. 表示与任意字符匹配。
用方括号和一个连字符表示字符的范围,如[0-9],[a-z],[a-zA-Z]。这种表示法还可以用作表示单个的解,如a|b|c 可写成[abc]

不在给定集合中的任意字符用 ”~”,如正规式 ~a 表示字母表中非 a 字符
可选的子表达式r?表示由 r 匹配的串是可选的(0个或1个)。如natural = [0-9]+,signedNatural = (+|-)? Natural

正则文法与正则式

正则文法与正则式都是描述正规集的工具。

对任意一个正则文法,存在定义同一语言的正则式;反之,对每个正则式存在一个生成同一语言的正则文法。

正则文法到正则式的转换

将正则文法中的每个非终结符表示成关于它的正则式方程,获得一个联立方程组。依照求解规则:

若 x = αx |β(或x = αx+β),则解为x = α*β

若 x = xα | β(或x = xα+β),则解为x = βα*

以及正规式的分配率、交换率和结合率求关于文法开始符号的正规式方程组的解。
这个解是关于文法开始符号 S 的一个正则式。上述两个规则较为重要,将递归的x消为α的闭包

【例1】
【 例 】 设 有 正 规 文 法 G : Z → 0 A A → 0 A ∣ 0 B B → 1 A ∣ ϵ 试 给 出 该 文 法 生 成 语 言 的 正 规 式 【例】设有正规文法G:\\\\ Z→0A\\\\ A→0A|0B\\\\ B→1A|\epsilon \\\\试给出该文法生成语言的正规式 Z0AA0A0BB1Aϵ

首先给出相应的正规式方程组(+代替|)
Z = 0A ………(1)
A = 0A+0B ………(2)
B = 1A+$\epsilon $ ………(3)

将(3)代入(2)式中的 B 得
A = 0A+01A+0 ………(4)
对(4)利用分配率 A = (0+01)A+0 ………(5)
对(5)使用规则得 A = (0+01)*0 ………(6)
将(6)代入(1)得 Z = 0(0+01)*0
即正规文法G[Z]所生成语言的正规式是 0(0|01)*0

【例2】

设有正规文法G:
A→aB|bB
B→aC|a|b
C→aB
试给出该文法生成语言的正规式
同上述步骤
首先给出相应的正规式方程组(+代替|)
A = aB+bB ………(1)
B = aC+a+b ………(2)
C = aB ………(3)
将(3)代入(2)式中得
B = aaB+a+b ………(4)
对(4)使用规则得 B = (aa)*(a+b) ………(5)
将(5)代入(1)得 A = (a+b)(aa)*(a+b)
即正规文法G[Z]所生成语言的正规式是 (a|b)(aa)*(a|b)

【例3】

设有正规文法G:
	Z→U0|V1
	U→Z1|1
	V→Z0|0	试给出该文法生成语言的正规式
首先给出相应的正规式方程组(+代替|)
			Z = U0+V1			………(1)
			U = Z1+1 			………(2)
			V = Z0+0			………(3)
将(2)(3)代入(1)式得
			Z = Z10+10+Z01+01	………(4)
			Z = Z(10+01)+10+01	………(4)
对(4)使用规则得Z = (10+01)(10+01)*
即正规文法G[Z]所生成语言的正规式是 (10|01)(10|01)*

【例4】

已知描述“标识符”单词符号的正规文法
	<标识符> →l | <标识符>l | <标识符>d
首先给出相应的正规式方程组(+代替|)
	S = l+Sl+Sd
	S = l+S(l+d)
使用规则得
	S = l(l+d)*
该文法的正规式是 
	l(l|d)*

正规式到正规文法的转换

字母表∑上的正规式到正规文法 G=( V N V_N VN, V T V_T VT,P,S)的转换方法如下:

  1. 令 VT = ∑
  2. 对任意正规式 R 选择一个非终结符 Z,生成规则 Z→R,并令 S=Z;
  3. 若 a 和 b 都是正规式,对形如 A→ab 的规则转换成 A→aB 和 B→b两规则,其中 B 是新增的非终结符;
  4. 在已转换的文法中,将形如 A→a*b 的规则进一步转换成 A →aA | b;
  5. 不断利用规则(3)和(4)进行转换,直到每条规则最多含有一个终结符为止。

【例1】

将R = (a|b)(aa)*(a|b) 转换成相应的正规文法
令 A 是文法开始符号,根据规则(2)变换为
A → (a|b)(aa)*(a|b)
根据规则(3)变换为
A → (a|b)B
B → (aa)*(a|b)
根据规则(4)变换为(逆推,将*转变为|)
A → aB|bB
B → aaB|a|b(aaB中有两个a,要简化到只有一个终结符为止)
根据规则(3)变换为
A → aB|bB
B → aC|a|b
C → aB

【例2】

将描述标识符的正规式R=l(l|d)*转换成相应的正规文法
令 S 是文法开始符号,根据规则(2)变换为
S→l(l|d)*
根据规则(3)变换为
S→lA 
A→(l|d)*
根据规则(4)变换为
S→lA
A→(l|d)A |ε
进一步变换为
S→lA
A→lA|dA|ε(消除ε)
进一步变换为
S→l|lA
A→l|d|lA|dA

有穷自动机

有穷自动机是具有离散输入与输出系统的一种抽象数学模型。有穷自动机有“确定的”和“非确定的”两类。确定的有穷自动机 和 非确定的有穷自动机都能准确地识别正规集。

确定有穷自动机(DFA)

一个确定有穷自动机 DFA M 是一个五元式:M=( Q, ∑, f, S, Z) 其中:
Q:有限状态集,它的每个元素称为一个状态。
∑:有穷字母表,它的每个元素称为一个输入字符。
F:状态转换函数,是从 Q×∑ 至 Q 的单值映射。f(qi, a) = qj (qi,qj ∈ Q,a ∈ ∑)表示:当现行状态为 qi、输入字符为 a 时,自动机将转换到下一状态 qj 。称 qj 为 qi 的一个后继。
S∈Q:是唯一的初态。
Z $\subset $ Q:终态集(可空)。

【例】设DFA  M=({q0,q1,q2},{a,b},f,q0,{q2})
其中:
f(q0,a)= q1
f(q1,b)= q1
f(q0,b)= q2
f(q2,a)= q2
f(q1,a)= q1
f(q2,b)= q1

状态转换矩阵、状态转换图

一个 DFA 可用一个矩阵表示,该矩阵的行表示状态,列表示输入字符,矩阵元素表示 f(s, a) 的值。这个矩阵称为状态转换距阵,或称转换表。
一个 DFA 也可以用一张(确定的)状态转换图表示,假定 DFA M 含有 m 个状态 n 个输入字符,这个状态转换图则有 m 个结点,每个结点最多有 n 条箭弧射出和别的状态相连,同一结点射出的每条箭弧用 ∑ 中的一个不同的输入字符作标记,整张图含有唯一一个初态结点和若干个(可以是0个)终态结点。

image.png image.png

DFA M 识别的符号串:对于 ∑* 中的任何字 β,若存在一条从初态到某一终态结点的通路,且这条通路上的所有弧的标记符连接成的字等于 β ,则称β 可为 DFA M 所识别。若 M 的初态结同时又是终态结,则ε可为 M 所识别。

DFA M 所能识别的符号串的全体为其接受的语言,记为L(M)。

结论:V $ \subset $ ∑* 是正规的当且仅当存在 ∑ 上的自动机 M,使得 V=L(M)

模拟 DFA 的算法

输入:输入以文件结束符 eof 结尾的串 x;一个 DFA D,其开始状态为 s0,接受状态集合为 F。
输出:如果 D 接受 x,则回答 “yes”,否则回答“No”。
方法:把下列算法应用于输入字符串x。函数 move(s,c) 给出在状态 s 上遇到输入字符 c 时应该转换到的下一个状态。函数 getch() 返回输入串 x 的下一个字符。

s=s0;
while ((c=getch())!=eof) {
   
	s=move(s,c);
	if (s is in F) return “yes”;
}

非确定有限自动机(NFA)

一个非确定有限自动机 M 是一个五元式:M=(Q,∑,S,Z,F),其中:

Q:有限状态集
∑:有穷字母表
F:状态转换函数,是一个从 Q×∑* 至 S 的子集 S’ 的映射(多值映射)。即
f: Q×∑* →2Q 幂集
S $\subset $ Q:非空初态集
Z $\subset $ Q:终态集(可空)

一个 NFA 也可用一个矩阵表示,该矩阵的行表示状态,列表示输入字符,矩阵元素表示 f(s, a) 的值(状态集)。一个 NFA 也可以用一张状态转换图表示。

NFA 和DFA的区别

NFA可以有多个初态;
弧上的标记可以是∑*中的一个字(甚至可以是一个正规式),而不一定是单个字符;
同一个字可能出现在同状态射出的多条弧上;
DFA是NFA的特例。

image.png

NFA M 识别的符号串,对于 ∑* 中的任何字 β,若存在一条从初态到某一终态结点的通路,且这条通路上的所有弧的标记符连接成的字(忽略ε弧)等于 β,则称β 可为 NFA M 所识别。若 M 的某些状态既是初态又是终态则空字 ε 被 M 所接受。

NFA M 所能识别的符号串的全体为其接受的语言,记为L(M),如上例中NFA M’ 所识别的语言为L(M’) = b*(b|ab)(bb)*

由 NFA 的定义可知,同一个符号串 β 可由多条路来识别,DFA 是 NFA的特例,利用有穷自动机构造词法分析程序的方法是:

  1. 从语言单词的描述中构造出 NFA;
  2. 将 NFA 转化为 DFA;
  3. 化简为状态最少化的 DFA;
  4. 对 DFA 的每一个状态构造一个程序段将其转化为识别单词的词法分析程序。

NFA 确定化为 DFA 的方法

NFA 的确定化是指对任给的 NFA,都能相应地构造一DFA,使它们接受相同的语言。

对于一个 NFA,由于状态转换函数 f 是一个多值函数,因此总存在一些状态 q,对于它们有

f(q,a)={q1, q2,…,qn}

它是 NFA 状态集合的一个子集,为了将 NFA 转换为 DFA,把状态集合{q1, q2,…,qn}看做一个状态 A,也就是说,从 NFA 构造 DFA 的基本思想是 DFA 的每一个状态代表 NFA 状态集合的某个子集,这个 DFA 使用它的状态去记录在 NFA 读入输入符号之后可能到达的所有状态的集合,称此构造方法为子集法。

状态集合 I 的 ε-闭包

设 I 是 NFA N 的一个状态子集,ε-CLOSURE(I)定义如下:

若 s ∈ I,则 s ∈ ε-CLOSURE(I)
若 s ∈ I,那么从 s 出发经过任意条 ε 弧而能到达的任何状态 s’,都属于 ε-CLOSURE(I)

ε-CLOSURE(I)是一个从给定结点集合出发在转换图上搜索可达结点集合的过程。

将 I 中所有的状态压入栈 stack 中;
将 ε-CLOSURE(I)初始化为 I;
while 栈stack不空 do
begin
	将栈顶元素 t 弹出栈;
	for 每个这样的状态u:从t到u有一条标记为ε的边 do
		if u 不在 ε-CLOSURE(I) 中 do
		begin
		将 u 添加到 ε-CLOSURE(I);
		将 u 压入栈 stack 中
		end 
end
image.png

从 NFA N=(Q,∑,F,S,Z)构造等价的DFA M=(Q’,∑,F’,S’,Z’)的方法

首先将从初态 S 出发经过任意条 ε 弧所能到达的状态所组成的集合作为 M 的初态 S’,然后从 S’ 出发,经过对输入符号 a∈∑ 的状态转移所能到达的状态(包括读输入符号之前或之后所有可能的 ε 转移所能到达的状态)所组成的集合作为 M 的新状态,如此重复,直到不再有新的状态出现为止。

置 DFA M 中的状态集合 Q’和 Z’为 ∅ 集。
给出 M 的初态 S’= ε-CLOSURE(S),并把 S’ 置为未标记状态后加入到 Q’中。
初始时,ε-CLOSURE(S)是Q’中唯一的状态且未被标记;
while Q’中存在一个未标记的状态 T do
begin
标记 T;
  for 每个输入符号 a do
  begin 
  	U = ε-CLOSURE( f(T,a) );
    if  U 没有在 Q’中 then
    将 U 作为一个未标记的状态添加到 Q’中;
    f’(T,a) = U;
    end
end

【例】NFA确定化

image.png image.png image.png image.png image.png image.png image.png image.png

有穷自动机与文法的相互转化

右线性正规文法到有穷自动机的转换方法

image.png image.png

左线性正规文法到有穷自动机的转换方法

image.png image.png

有穷自动机到正规文法的转换方法

image.png

有穷自动机与正则表达式的相互转化

由正则表达式构造NFA

输入:字母表上的正规式 R

输出:识别语言 L® 的 NFA N

image.png
image.png
image.png

整个分裂过程中,所有新结点均采用不同的名字,保留 X,Y 为全图唯一初态结点和终态结点。

image.png

有穷自动机到正规式的转换

逆推过程,增加新初态 X,与所有原初态用ε相连,增加新终态 Y,与所有原终态用ε相连,从而构成一个新的NFA M’,它只有一个初态 X 和一个终态 Y。在X 与 Y 之间进行弧合并。

图片.png 图片.png 图片.png 图片.png

实际设计

词法分析的任务就是扫描源文件,按照(单词符号(token),单词符号属性值)这样的二元形式输出单词符号串

词法分析中要使用正则表达式来扫描整个文件以判别单词符号的种类,单词符号按照种类进行划分,例如

TOKEN: {
<VOID : "void">
| <CHAR : "char">
| <SHORT : "short">
| <INT : "int">
| <LONG : "long">
| <STRUCT : "struct">
| <UNION : "union">
| <ENUM : "enum">
| <STATIC : "static">
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值