国防科技大学 编译原理 第4讲 词法分析1
课程主页:
https://www.icourse163.org/course/NUDT-1003101005?tid=1460976445
这次回顾第4讲:词法分析1。
第4讲 词法分析1
词法分析概述
词法分析的任务
- 词法分析的任务
- 从左至右逐个字符地对源程序进行扫描,产生一个个单词符号
- 词法分析器(Lexical Analyzer)
- 扫描器(Scanner)
- 执行词法分析的程序
词法分析器的功能
- 功能
- 输入源程序、输出单词符号
- 单词符号的种类
- 基本字:如begin,repeat,for,…
- 标识符:用来表示各种名字,如变量名、数组名和过程名
- 常数:各种类型的常数
- 运算符:+,-,*,/,…
- 界符:逗号、分号、括号和空白
词法分析器的输出
- 输出的单词符号的表示形式
- (单词种别,单词自身的值)
- 单词种别通常用整数编码表示
- 若一个种别只有一个单词符号,则种别编码就代表该单词符号。假定基本字、运算符和界符都是一符一种。
- 若一个种别有多个单词符号,则对于每个单词符号,给出种别编码和自身的值。
- 标识符单列一种;标识符自身的值表示成按机器字节划分的内部码
- 常数按类型分种;常数的值则表示成标准的二进制形式
来看两个例子:
词法分析器作为一个独立子程序
- 词法分析作为一个独立的阶段
- 结构简洁、清晰和条理化,有利于集中考虑词法分析一些枝节问题
- 但不一定不作为单独的一遍
- 将其处理为一个子程序
词法分析器在编译器中地位
词法分析器的结构
整体结构如下:
扫描缓冲区
超前搜索
单词符号的识别:超前搜索
基本字识别
- 例如
DO99K=1,10(等价于DO 99 K = 1,10)
DO99K=1.10
IF(5.EQ.M)GOTO55(等价于IF (5.EQ.M) GOTO 55)
IF(5)=55 - 需要超前搜索才能确定哪些是基本字
- 例如
标识符识别
- 字母开头的字母数字串,后跟界符或算符
常数识别
- 识别出算术常数并将其转变为二进制内码表示
5.EQ.M
5.E08
- 识别出算术常数并将其转变为二进制内码表示
算符和界符的识别
把多字符组成的算符和界符拼合成一个单词符号
:=, **, .EQ. , ++,--,>=
几点限制——不必使用超前搜索
- 所有基本字都是保留字;用户不能用它们作自己的标识符
- 基本字作为特殊的标识符来处理,使用保留字表
- 如果基本字、标识符和常数(或标号)之间没有确定的运算符或界符作间隔,则必须使用一个空白符作间隔
- DO99K=1,10
- 要写成DO 99 K=1,10
状态转换图
- 状态转换图是一张有限方向图
- 结点代表状态,用圆圈表示
- 双圆圈表示接受状态,双圆圈上方加$\star$表示最后读入的字符不属于刚才的单词
- 状态之间用箭弧连结,箭弧上的标记(字符)代表射出结状态下可能出现的输入字符或字符类
- 一张转换图只包含有限个状态,其中有一个为初态,至少要有一个终态
- 状态转换图可用于识别(或接受)一定的字符串
- 若存在一条从初态到某一终态的道路,且这条路上所有弧上的标记符连接成的字等于$α$,则称$α$被该状态转换图所识别(接受)
词法分析器的设计示例
- 一个简单的程序设计语言
- 单词表
- 设计状态转换图
- 单词表
状态转换图的程序实现
还是以之前的状态转换图为例:
每个状态结点对应一小段程序:
- 不含回路的分叉结点
- 可用一个CASE语句或一组IF-THEN-ELSE语句实现
- 含回路的状态结点
- 对应一段由WHILE结构和IF语句构成的程序
- 终态结点
- 表示识别出某种单词符号,对应返回语句
完整实现
首先定义如下符号:
- 全局变量与过程
- 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( ); /* 错误处理*/
将状态图的代码一般化
变量$\text{curState}$用于保存现有的状态
用二维数组表示状态图:$\text{stateTrans[state][ch]}$
通用框架如下:
curState = 初态 GetChar(); while( stateTrans[curState][ch]有定义){ //存在后继状态,读入、拼接 Concat(); //转换入下一状态,读入下一字符 curState= stateTrans[curState][ch]; if curState是终态 then 返回strToken中的单词 GetChar( ); }
本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来自 Doraemonzzz!
评论
ValineLivere