1.5 编译程序的生成
第七章 作用域
第八章
一个状态(字符集合)在接收到同样的字符串时,会转变到不同的状态(集合),导致了它的非确定性。
所谓的状态闭包是指,从当前节点,经过 ε 符(无条件转移)能到达的所有节点的集合,包括当前节点自己。
A -> 1 -> B 指 B 从 A 接收 1
self 0 指该节点有一个指向自己的、以接收到的字符为 0 为条件的无限循环
1/0 指该节点有两条指向下一节点的、分别以 0 和 1 为条件的状态转移
ε 不代表什么都接收,而是无条件转移的意思
A -> 1(0|1)*(1|0) -> B
A -> 1 -> C -> E(self 0/1) -> D -> 1/0 -> B
A -> 1*(0|1)0* -> B
A -> C(self 1) -> C -> 0/1 -> D -> E(self 0) -> B
第一行 I 是起始点的状态闭包,在求第一行 Ia 的时候,除了要列出可以从 I 中某个元素接收 a 的节点,还要在全部列出后补充 Ia 这些节点的状态闭包,即可以从 Ia 某个节点通过 ε 前往的节点。
然后,新的集合添加到下一行,进行重复的步骤。
最后,为每一行从头到尾添加节点编号,进行化简。
将终止态和非终止态分开
用非终止态开始一个一个字符开始测试
测试完得到一些划分
两个字符到同一个状态集合就算等价,不一定只能是到一个状态
例如,{3,4,5,6} 中的 3,4,5 接收 a 不到达同一个状态,那就也不是同一个功能,需要分开。
编译原理学习笔记(七)~LR (0) 分析_海轰 Pro 的博客 - CSDN 博客
在 LR (0) 的基础后面加 #,{c,d} 等
词法分析包括:标识符、常数、字符串、关键字、界符
中间代码、优化器 会出简答题
出错处理:词法分析中关键词与标识符冲突、界符写错(没有大括号),打印 / 显示出来
表格管理:不断地在每个步骤生成结果,保存到表格中
一个典型的编译器通常由以下几个模块组成,每个模块都有不同的功能和任务:
编译器的工作流程通常包括以下几个步骤:
扫描越多遍越不好
ε 空串的意思,起到一个语义复写的作用,把属性复写成综合属性往上传
空集和空字符串不是一个东西
右箭头 产生式符号
右箭头加星号 推导
句型:从开始符号经过 N 步推导,之间生成的任何字符串都是句型
句子:相比句型,只有有终结符的,才是句子
0 型文法最简单、表达能力最强,1 型文法增加了约束条件,2、3 型增加了更多。约束条件越多,表达能力越弱。
就是一开始学的图
确定化
最少化
1(0|1)*101
E->Ex|Ey|z
把所有包含左递归的候选式提取出来 E -> E (x|y)|z
X=x|y Y=z
E->EX|Y
用通用格式改写
E->YE’
E’->XE’|e
再代入 X 和 Y
E->zE’
E’->xE’|yE’|e
直接左递归是指产生式中右边的第一个符号就是左边的符号,间接左递归则不是。
把间接左递归通过代入的方式,改写成直接左递归,再使用直接左递归通用的改写方式
第一个 L:从左往右输入,每次读入一个非终结符进行推导
第二个 L:产生式使用最左推导
1:每次只需要看右边的第一个数字
LL 代表左向右扫描输入,左推导,1 代表在任何时候,仅仅需要查看输入的下一个符号。
不能存在左递归和二义性
可以通过 FIRST 集和 FOLLOW 集构造出一个 LL (1) 分析表
LL (1) 语法符合哪三个条件:不允许有左递归(改写),求 FIRST 集(从下往上,每个产生式的候选式相交为空、左边的符号能否推出 epsilon,如果一个产生式的候选式 L 推导出来之后能推出 epsilon,要拿它的 FOLLOW 集与每一个 FIRST 集相交)
如果一个文法的候选式可以推导出 ε,则 FIRST 和 FOLLOW 集都为空
求 FOLLOW 集以右边为主
FOLLOW 集里是不可能有 epsilon 的,最多只有#
操作符在中间,左右两边的子结点是两个数
在属性文法阶段生成抽象语法树
逆波兰式:使用后序遍历抽象语法树的结果
考试可能会考给一个逆波兰式,把抽象语法树写出来
AST 对编程是很重要的工具,因为操作符就是 “操作码”、子结点就是 “操作数”
DAG 图是抽象语法树的简化表达形式
难度比较大,后面还有四元式、三元式、间接三元式,要注意区别。作用域及其之后的内容不考察。
三地址代码是代码的逻辑表达形式,两个操作数加上一个运算符就是三地址了(如 a+b)
三地址语句是中间代码的一种抽象形式,四元式(操作码、两个操作数、操作结果)是三地址代码在内存中的一种存放形式。因此四元式表格中会有很多操作数是子树的操作结果(帮助自底向上运算)。
三元式相比四元式,省去了操作结果,把需要中间结果参与运算的地方替换成了运算这个中间结果的指令的编号(内存地址)。缺点:如果指令编号发生变化,则绑定操作结果的指令都出错了。为了解决这个缺点,有了间接三元式。
间接三元式的表格中的编号(1、2 等)指的并不是表格第一列的指令编号(内存地址),而是指向了间接码表中的间接代码,间接代码再去指向指令编号。需要调整运算顺序时,只需重新安排间接码表,无需改动三元式。
上下文无关文法(Context-Free Grammar, CFG)是一种形式文法,其产生式规则只能够在左侧非终结符号周围添加或删除终结符号,而不考虑符号周围的上下文信息。在上下文无关文法中,每个产生式规则都由一个非终结符号和一个由终结符号和非终结符号构成的字符串组成,用符号 “->” 分隔。
最左推导:每次都从产生式最多边第一个符号开始推导
语法的二义性:二义性文法是指存在两种或多种不同的解析方式,即存在多个推导树的上下文无关文法。这种情况会导致语法分析器无法准确地确定语法结构,进而产生歧义。
为了避免二义性,可以采取以下几种方法:
E -> E * E | num
和 E -> E + E | num
,从而明确乘法优先于加法。E -> E + E
转化为 E -> E + T
和 T -> E
,从而消除左递归,减少二义性。词法分析的目标
结果(五类词):标识符,关键字,阶符,,分隔符,常数
正则表达式(正规式):正则闭包和正法闭包的区别
正规式改写为 NFA DFA DFA 化简 必考
NFA 和 DFA 的三个区别:
总之,DFA 具有确定性、状态转移表简单、只有一个接受状态等特点,适合处理结构相对简单的输入;而 NFA 具有非确定性、状态转移图复杂、可有多个接受状态等特点,适合处理结构相对复杂的输入。
从上往下推导是一种自顶向下的分析方法,它从语法的起始符号开始,通过不断地将它展开为更小的非终结符号,直到最终生成语法的终结符号序列。这个过程可以看作是一种自上而下的递归过程,常用的方法有递归下降分析(Recursive Descent Parsing)和预测分析(Predictive Parsing)。从上往下推导的一个特点是它需要提前知道语法的结构,也就是需要一个预测的语法分析表或递归下降分析程序。
从下往上推导是一种自底向上的分析方法,它从输入符号开始,逐步合并符号直到生成起始符号。这个过程可以看作是一种自底向上的规约过程,常用的方法有移进 - 归约分析(Shift-Reduce Parsing)和 LR 分析(LR Parsing)。从下往上推导的一个特点是它不需要提前知道语法的结构,而是通过观察输入符号和栈中的符号来进行规约操作,直到最终生成起始符号。
从上往下推导和从下往上推导在语法分析过程中的顺序和方式上存在一些区别。从上往下推导是自顶向下的展开过程,它从起始符号开始逐步展开直到终结符号;而从下往上推导是自底向上的合并过程,它从终结符号开始逐步合并直到起始符号。此外,从上往下推导需要预先知道语法的结构,而从下往上推导则是根据输入符号和栈中符号进行动态的规约操作。
LR (0):使用状态机来模拟堆栈 (LR 分析法,它的模型时什么?根据 LR 的分析表来判断四个动作,ACCEPT、空白出错等。规约(必会):栈顶形成句柄时要将句柄弹出来,将栈顶的字符规约成一个符号,此时需要查 GOTO 表,这决定了新的字符进入新的栈顶之后,新的字符是什么字段) - r 规约 s 移进 accept:1, 空白:出错
移进和规约冲突等,LR (0) 就不行了
规约与…
推导项目集(作业里有,考试时没有那么难)
LR (1) 会生成大量的项目集,爆炸式增长
S 属性、L 属性、为什么要定义 L 属性
L 属性的引入是为了处理依赖于上下文信息的语法结构和语义规则。它允许从产生式的右部符号向左部符号传递属性值,以满足上下文相关的计算需求。在某些语法结构中,仅使用 S 属性无法满足对上下文信息的处理,而 L 属性可以提供更灵活的属性传递机制,支持对上下文信息的分析和计算。
四个表达式(逆波兰式等)