词法分析器的作用
- 读入字符流,组成词素,输出词法单元
- 过滤空白、换行、制表符、注释
- 将词素添加到符号表
- 逻辑上独立于语法分析,但通常和语法分析处于同一趟
- 独立词法分析
- 简化设计
- 提高编译器效率
- 增强编译器的可移植性
- 概念
- 词法单元 Token:
<单元名,属性> - 模式 Pattern:描述了一类 Token 可能具有的形式
- 词素 Lexeme:源程序中的字符序列
- 词法单元 Token:
词法单元的规约(正则表达式)
- 概念
- 字符表:一个有穷符号的集合
- 串:符号的有穷序列
- 语言:给定字母表上串的可数集合
- 前缀,后缀,子串
- 连接:
- 指数运算:
- 语言运算
- 正则表达式
- 基本部分
- 归纳部分
- 运算优先级 连接
- 扩展
- =
- =
- =
- 基本部分
- 正则集合:可以用正则表达式定义的语言
- 正则定义:
- 不能重复定义,不能与字母表重复
词法分析的识别(状态转换图)
- Transition diagram
- State: 识别词素时可能出现的情况
- 状态是已处理部分的总结
- 开始状态
- 终止状态(带星号表示回退一个符号)
- Edge:一个符号或多个符号
- State: 识别词素时可能出现的情况
- 保留字和标识符的处理
- 法一:在符号表中先填保留字
- 法二:建立高优先级的保留字
- 构造词法分析器
- State 记录当前状态
- switch 跳转状态
- if 对比边
- 失败 fail()
- 接受状态返回词法单元(*标记回退 forward 指针)
词法分析器生成工具及设计
- Lex/Flex 编译器:lex.l (Lex compiler)-> lex.yy.c (C compiler)-> a.out
- 声明部分
- 常量
- 正则定义
- 转换规则
- 模式 { 动作 }
- 辅助函数
- 各个动作的函数
- 各部分用
%%隔开 %{%}之间直接拷贝
%{
//comment
}%
ws { \t\n}
%%
{ws} {/*no action*/}
if {return(IF)}
{id} {yylval = (int) installID(); return (ID);}
%%
int installID() {/* install lexeme */}
int installNum() {}- 冲突解决
- 多个前缀匹配:选择最长的前缀
- 多个模式匹配:选择前面的模式
有穷自动机
- 有穷自动机:只回答 Yes/No
- 状态有穷
- 不确定有穷自动机 (NFA):边上符号限制,一个符号可出现在离开同一个状态的多条边上, 可以做符号
- 确定有穷自动机 (DFA)
- 每个可以用正则表达式描述的语言,均可以用 NFA 和 DFA 来识别,反之亦然
- NFA 定义
- 有穷状态集合
- 输入符号集合
- 转换函数:
- 开始状态:
- 接受状态:
- 转换表表示法
- 行头:条件
- 列头:状态
正则表达式到 DFA
- NFA -> DFA:子集构造法
- 构造得到的 DFA 每个状态和 NFA 的状态子集对应
- 最坏情况:
- 基本操作
- -closure():从状态 开始,只能通过 转换到的状态
- -closure()
- move()
- 正则表达式 -> NFA
- 基本规则部分
- 表达式
- 表达式
- 归纳部分
- NFA 合并:引入新的开始状态
- 转化为 DFA 中一个状态包含 NFA 中不同的结束状态:冲突
- 关键字是其它模式的一个特例(优先选择特例)
- 基本规则部分
- DFA 状态数量最小化
- 状态的可区分:如果存在串 使从 和 一个到达接受另一个到达非接受状态,则 区分 和
- 最小化算法
- 基本步骤:接受和非接受状态
- 归纳步骤: 和 可区分,且 到 , 到 有标号 的边, 和 可区分
- 最终没有区分开的态等价
- 死状态:未定义时到达的状态