读书笔记(II) 编译原理及实践



点击进入书籍详情页

编译原理及实践

作者:Kenneth C.Louden

机械工业出版社

出版年:2000-3-1

综合评分:7.0

_________________________

果然如网上流传的那样,翻译的真是和google有的一拼。难得这么一本很重实践的编译原理书,就这样被翻译打了折扣。反过来说,如果真的用心看,翻译的结果无非是英式文法的中文,要看也是可以看得了。这是苦了我们反复推敲语义,这种阅读实在不爽。问题终归问题,对于我这个自学者来说,这是难得的好教材,结合c代码一步一步的讲解,让读者把握哪些是真正的重点,哪些是实现的关键。我觉得这样对于自学者速成确实是很有帮助的。反之,看那本“龙书”,对于初学者抑或自学者确实有点难度。

 
近重新阅读《编译原理》,当初的认识仍然很浅薄,书评也写得幼稚,如今又有些新的体会在此列出:
1. 语法分析的目标是构建语法树,因为计算机常用的运算是二元运算:加减乘除等等,因此构建的结构是一个二叉树。同时,我们根据二叉树的遍历等等一些知识,可以认为语法分析的任务就是根据树的遍历结果(程序代码或表达式)重新构建树;另外,我们知道我们给计算机的表达式的方式是带有二义性的,必须定义算符的优先级以及结合规律方可以回避二义性。考虑a+b*c,可以翻译为(a+b)*c或者a+(b*c),因此我们的常规表达方法为回避这些问题,必须要带有"(",")",当然我们所说的常规表达式就是对语法树的中缀遍历结果。那么,如果考虑计算机的计算模型是基于栈和寄存器的方式,显然适合人类读写的中缀表达并不适合计算机理解运算,因此我们意外的发现,波兰式以及逆波兰式(对应对语法树的前序遍历以及后序遍历)十分适合现代计算机的实现,而且他们的最大优点就是不需要括号以及优先级,特别适合栈和寄存器计算模型去实现。综上所述,语法分析的任务(显然我们将代码生成也并入了,不过为了说明主旨)就是根据适合人类读写的表达式(中序遍历)重构完整的语法树,并转化成适合计算机模型计算的波兰式或者逆波兰式(对应于前序遍历以及后续遍历);当然在编译器实现中,我们并不需要真的必须构造一个语法树实体。
 
2. 理解语法分析的难点往往集中在对 上下文无关语言、栈、树等知识的理解上。形式语言无需再讲,对于下推自动机等等已经在算法笔记上有所涉及,这里主要讲一讲,为什么出现LL,LR这两种不同的语法分析方法。首先从语法分析的本质结构:二叉树出发,我们知道对于树的处理很多是基于其独有的递归特征进行展开的,这一点体现在对树的遍历、旋转等等操作上,一般而言,我们可以通过 自顶向下 以及 自底向上 两种方式去访问和处理树结构,例如在实现计算典型的树形结构问题Fibonacci数列计算问题的时候,既可以设计递归计算程序,也可以反过来设计递推程序一样,这就对应了实现方式的不同,递归对应自顶向下,而递推对应自底向上方式。同样道理,LL以及LR就分别对应自顶向下以及自顶向上方式去访问树结构。
 
3. 这里有人要问,一般树都会讲两种自顶向下语法分析方法:递归下降以及LL;他们又是什么关系呢?这一点就要从递归的实现说起,我们知道递归必将显式或者隐式的实用栈结构,这是我们理解的基本出发点。当然,我们仍是采用自顶向下的方式去求解问题,这一点要和递推(自底向上)区分。对于递归程序,系统已经帮助我们将函数的调用完成,其本质也就是一个压栈出栈的过程,只是这些是我们看不到的。反之,如果我们构造栈结构,模拟这个过程,那么就可以将递归去除,这也是为什么对于很多问题(如八皇后问题)往往即有递归算法又有非递归算法的原因。那么,我们现在清楚了,递归下降算法就是一种隐式的栈实现,而LL就是显式的栈实现。
 
4. 补充说明1:自顶向下代表的是对树结构的前序遍历,而自底向上代表后序遍历(注:这种说法并不严谨,但是辅助理解还是有益的)。
_________________________
2010.11.6
最近的学习和阅读中,特别是理解动态规划的两种实现:递归以及递推上。我有发现了top-down以及bottom-up设计方法[wiki]和编译原理的语法分析以及程序设计的哲学的联系,其本质都是针对树结构的特点。自顶向下的分析方法[ref],掌控全局,对全局的功能实现做一个推断,继而细化至每一个子模块,重复这个过程直至足够细或者不能划分为止(即递归的base case或者数学归纳法的基本情况),这种方式显然要求设计师拥有足够的信息和知识、全局的洞察力和分析能力;自底向上方法[unix]鼓励设计师设计基本元素,继而将这些基本元素组合起来,也要依赖于设计师丰富的经验,并且如何设计基本元素显然是一个精益求精提炼的探索过程,当然优点就是一旦提炼出这些基本的组件和元素,重用非常的方便。当然,在实际中这两种方案往往是混合使用的。对于自顶向下来说,将系统划分成独立的模块显然在实际中并不总是有效(因为很难将各个模块设计到完全独立,往往各个模块需要调用相同的东西:类似于动态规划的子问题重复性),特别是软件工程上强调的可重用性:回顾软件设计的发展,从早期的自顶向下典型:面向过程式设计方法要求划分程序的模块,实现各个过程,面临着不可重用的困境,直到面向对象出现,强调设计出元组件(这些元组件可以方便的移植到不同的模块),在拼装成复杂的模型,大大提高了软件的可重用性以及开发效率。当然,两种设计方法在实践中都是非常重要的,各有优缺,单纯的一种开发模式是不可取的。
_________________________
2010.12.05
 
上面的部分主要讲解的都是词法分析和语法分析的内容,在编译领域,我们统称这两种法则为文法。有了文法就有了语言,但是我们仍然有重要的一步:语义分析。任何一门编译原理的书中,语义分析都是重点,通常来说,读完了艰深的词法分析和语法分析后,面对的语法分析同样是一个挑战。语义分析的内容很多,同时它也是后续产生中间代码生成的必须过程。简单的来说,我们可以将语义分析理解成如何根据上述两个过程得到的语法树进行标注。这个标注我们称作属性,其实这个属性包含的东西很多,可以说变量以及过程的各种信息:名称,值,类型等等,我们都可以称之为属性。看来若我们把语法树的信息都合理有效的标出来了,那么似乎我们已经把语句信息合理的分析出来了,剩下的工作也就可以顺利的开展了。
这里就需要注意一个理解的要点:属性可以分为综合属性以及继承属性这两种。在语法树上理解的话,我们可以理解为综合属性是需要了解子节点的属性才可以得到父节点的属性,这相当于一个自底向上的过程;而对于继承属性,则意味着我们必须先知道父节点的属性或者其兄弟节点的属性才可以判断其属性,这相当于一种自顶向下的思路。当然这种规定仍然对我们的编译器实现仍很复杂,于是我们规定了两种属性法则:S属性文法与L属性文法;对于前者,我们了解其只包含综合属性;而后者并不是只包含继承属性,而是包含一种特殊的继承属性和若干的综合属性构成的属性文法。这种特殊的继承属性是一种节点只会继承其父代或者最左面的节点属性的规定,其实这个我们的语句书写有关,因为我们总是规定先访问左边的节点后才会访问右边的若干节点。
属性这个词往往比较抽象,这是为了教授研究而进行的泛化;其实在编译器中我们最关心两个重要的属性:类型和值。对于类型这个属性来说,其可以是继承的,也可以是综合的。而且类型关系到我们的语义的正确性:例如我们总不能让一个浮点数做数组的下表吧,应当在语义分析部分对类型进行检查。
 
______________________________
2011.3.8
 


点击进入书籍详情页

编译原理及实践

作者:Kenneth C.Louden

机械工业出版社

出版年:2000-3-1

综合评分:7.0

_________________________

果然如网上流传的那样,翻译的真是和google有的一拼。难得这么一本很重实践的编译原理书,就这样被翻译打了折扣。反过来说,如果真的用心看,翻译的结果无非是英式文法的中文,要看也是可以看得了。这是苦了我们反复推敲语义,这种阅读实在不爽。问题终归问题,对于我这个自学者来说,这是难得的好教材,结合c代码一步一步的讲解,让读者把握哪些是真正的重点,哪些是实现的关键。我觉得这样对于自学者速成确实是很有帮助的。反之,看那本“龙书”,对于初学者抑或自学者确实有点难度。

 
近重新阅读《编译原理》,当初的认识仍然很浅薄,书评也写得幼稚,如今又有些新的体会在此列出:
1. 语法分析的目标是构建语法树,因为计算机常用的运算是二元运算:加减乘除等等,因此构建的结构是一个二叉树。同时,我们根据二叉树的遍历等等一些知识,可以认为语法分析的任务就是根据树的遍历结果(程序代码或表达式)重新构建树;另外,我们知道我们给计算机的表达式的方式是带有二义性的,必须定义算符的优先级以及结合规律方可以回避二义性。考虑a+b*c,可以翻译为(a+b)*c或者a+(b*c),因此我们的常规表达方法为回避这些问题,必须要带有"(",")",当然我们所说的常规表达式就是对语法树的中缀遍历结果。那么,如果考虑计算机的计算模型是基于栈和寄存器的方式,显然适合人类读写的中缀表达并不适合计算机理解运算,因此我们意外的发现,波兰式以及逆波兰式(对应对语法树的前序遍历以及后序遍历)十分适合现代计算机的实现,而且他们的最大优点就是不需要括号以及优先级,特别适合栈和寄存器计算模型去实现。综上所述,语法分析的任务(显然我们将代码生成也并入了,不过为了说明主旨)就是根据适合人类读写的表达式(中序遍历)重构完整的语法树,并转化成适合计算机模型计算的波兰式或者逆波兰式(对应于前序遍历以及后续遍历);当然在编译器实现中,我们并不需要真的必须构造一个语法树实体。
 
2. 理解语法分析的难点往往集中在对 上下文无关语言、栈、树等知识的理解上。形式语言无需再讲,对于下推自动机等等已经在算法笔记上有所涉及,这里主要讲一讲,为什么出现LL,LR这两种不同的语法分析方法。首先从语法分析的本质结构:二叉树出发,我们知道对于树的处理很多是基于其独有的递归特征进行展开的,这一点体现在对树的遍历、旋转等等操作上,一般而言,我们可以通过 自顶向下 以及 自底向上 两种方式去访问和处理树结构,例如在实现计算典型的树形结构问题Fibonacci数列计算问题的时候,既可以设计递归计算程序,也可以反过来设计递推程序一样,这就对应了实现方式的不同,递归对应自顶向下,而递推对应自底向上方式。同样道理,LL以及LR就分别对应自顶向下以及自顶向上方式去访问树结构。
 
3. 这里有人要问,一般树都会讲两种自顶向下语法分析方法:递归下降以及LL;他们又是什么关系呢?这一点就要从递归的实现说起,我们知道递归必将显式或者隐式的实用栈结构,这是我们理解的基本出发点。当然,我们仍是采用自顶向下的方式去求解问题,这一点要和递推(自底向上)区分。对于递归程序,系统已经帮助我们将函数的调用完成,其本质也就是一个压栈出栈的过程,只是这些是我们看不到的。反之,如果我们构造栈结构,模拟这个过程,那么就可以将递归去除,这也是为什么对于很多问题(如八皇后问题)往往即有递归算法又有非递归算法的原因。那么,我们现在清楚了,递归下降算法就是一种隐式的栈实现,而LL就是显式的栈实现。
 
4. 补充说明1:自顶向下代表的是对树结构的前序遍历,而自底向上代表后序遍历(注:这种说法并不严谨,但是辅助理解还是有益的)。
_________________________
2010.11.6
最近的学习和阅读中,特别是理解动态规划的两种实现:递归以及递推上。我有发现了top-down以及bottom-up设计方法[wiki]和编译原理的语法分析以及程序设计的哲学的联系,其本质都是针对树结构的特点。自顶向下的分析方法[ref],掌控全局,对全局的功能实现做一个推断,继而细化至每一个子模块,重复这个过程直至足够细或者不能划分为止(即递归的base case或者数学归纳法的基本情况),这种方式显然要求设计师拥有足够的信息和知识、全局的洞察力和分析能力;自底向上方法[unix]鼓励设计师设计基本元素,继而将这些基本元素组合起来,也要依赖于设计师丰富的经验,并且如何设计基本元素显然是一个精益求精提炼的探索过程,当然优点就是一旦提炼出这些基本的组件和元素,重用非常的方便。当然,在实际中这两种方案往往是混合使用的。对于自顶向下来说,将系统划分成独立的模块显然在实际中并不总是有效(因为很难将各个模块设计到完全独立,往往各个模块需要调用相同的东西:类似于动态规划的子问题重复性),特别是软件工程上强调的可重用性:回顾软件设计的发展,从早期的自顶向下典型:面向过程式设计方法要求划分程序的模块,实现各个过程,面临着不可重用的困境,直到面向对象出现,强调设计出元组件(这些元组件可以方便的移植到不同的模块),在拼装成复杂的模型,大大提高了软件的可重用性以及开发效率。当然,两种设计方法在实践中都是非常重要的,各有优缺,单纯的一种开发模式是不可取的。
_________________________
2010.12.05
 
上面的部分主要讲解的都是词法分析和语法分析的内容,在编译领域,我们统称这两种法则为文法。有了文法就有了语言,但是我们仍然有重要的一步:语义分析。任何一门编译原理的书中,语义分析都是重点,通常来说,读完了艰深的词法分析和语法分析后,面对的语法分析同样是一个挑战。语义分析的内容很多,同时它也是后续产生中间代码生成的必须过程。简单的来说,我们可以将语义分析理解成如何根据上述两个过程得到的语法树进行标注。这个标注我们称作属性,其实这个属性包含的东西很多,可以说变量以及过程的各种信息:名称,值,类型等等,我们都可以称之为属性。看来若我们把语法树的信息都合理有效的标出来了,那么似乎我们已经把语句信息合理的分析出来了,剩下的工作也就可以顺利的开展了。
这里就需要注意一个理解的要点:属性可以分为综合属性以及继承属性这两种。在语法树上理解的话,我们可以理解为综合属性是需要了解子节点的属性才可以得到父节点的属性,这相当于一个自底向上的过程;而对于继承属性,则意味着我们必须先知道父节点的属性或者其兄弟节点的属性才可以判断其属性,这相当于一种自顶向下的思路。当然这种规定仍然对我们的编译器实现仍很复杂,于是我们规定了两种属性法则:S属性文法与L属性文法;对于前者,我们了解其只包含综合属性;而后者并不是只包含继承属性,而是包含一种特殊的继承属性和若干的综合属性构成的属性文法。这种特殊的继承属性是一种节点只会继承其父代或者最左面的节点属性的规定,其实这个我们的语句书写有关,因为我们总是规定先访问左边的节点后才会访问右边的若干节点。
属性这个词往往比较抽象,这是为了教授研究而进行的泛化;其实在编译器中我们最关心两个重要的属性:类型和值。对于类型这个属性来说,其可以是继承的,也可以是综合的。而且类型关系到我们的语义的正确性:例如我们总不能让一个浮点数做数组的下表吧,应当在语义分析部分对类型进行检查。
 
______________________________
2011.3.8
 


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
目 录 译者序 前言 第1章 概论 1 1.1 为什么要用编译器 2 1.2 与编译器相关的程序 3 1.3 翻译步骤 5 1.4 编译器中的主要数据结构 8 1.5 编译器结构中的其他问题 10 1.6 自举与移植 12 1.7 TINY样本语言与编译器 14 1.7.1 TINY语言 15 1.7.2 TINY编译器 15 1.7.3 TM机 17 1.8 C-Minus:编译器项目的一种语言 18 练习 19 注意与参考 20 第2章 词法分析 21 2.1 扫描处理 21 2.2 正则表达式 23 2.2.1 正则表达式的定义 23 2.2.2 正则表达式的扩展 27 2.2.3 程序设计语言记号的正则表达式 29 2.3 有穷自动机 32 2.3.1 确定性有穷自动机的定义 32 2.3.2 先行、回溯和非确定性自动机 36 2.3.3 用代码实现有穷自动机 41 2.4 从正则表达式到DFA 45 2.4.1 从正则表达式到NFA 45 2.4.2 从NFA到DFA 48 2.4.3 利用子集构造模拟NFA 50 2.4.4 将DFA中的状态数最小化 51 2.5 TINY扫描程序的实现 52 2.5.1 为样本语言TINY实现一个扫描 程序 53 2.5.2 保留字与标识符 56 2.5.3 为标识符分配空间 57 2.6 利用Lex 自动生成扫描程序 57 2.6.1 正则表达式的Lex 约定 58 2.6.2 Lex输入文件的格式 59 2.6.3 使用Lex的TINY扫描程序 64 练习 65 编程练习 67 注意与参考 67 第3章 上下文无关文法及分析 69 3.1 分析过程 69 3.2 上下文无关文法 70 3.2.1 与正则表达式比较 70 3.2.2 上下文无关文法规则的说明 71 3.2.3 推导及由文法定义的语言 72 3.3 分析树与抽象语法树 77 3.3.1 分析树 77 3.3.2 抽象语法树 79 3.4 二义性 83 3.4.1 二义性文法 83 3.4.2 优先权和结合性 85 3.4.3 悬挂else问题 87 3.4.4 无关紧要的二义性 89 3.5 扩展的表示法:EBNF和语法图 89 3.5.1 EBNF表示法 89 3.5.2 语法图 91 3.6 上下文无关语言的形式特性 93 3.6.1 上下文无关语言的形式定义 93 3.6.2 文法规则和等式 94 3.6.3 乔姆斯基层次和作为上下文无关 规则的语法局限 95 3.7 TINY语言的语法 97 3.7.1 TINY的上下文无关文法 97 3.7.2 TINY编译器的语法树结构 98 练习 101 注意与参考 104 第4章 自顶向下的分析 105 4.1 使用递归下降分析算法进行自顶向下 的分析 105 4.1.1 递归下降分析的基本方法 105 4.1.2 重复和选择:使用EBNF 107 4.1.3 其他决定问题 112 4.2 LL(1)分析 113 4.2.1 LL(1)分析的基本方法 113 4.2.2 LL(1)分析与算法 114 4.2.3 消除左递归和提取左因子 117 4.2.4 在LL(1)分析中构造语法树 124 4.3 First集合和Follow集合 125 4.3.1 First 集合 125 4.3.2 Follow 集合 130 4.3.3 构造LL(1)分析表 134 4.3.4 再向前:LL(k)分析程序 135 4.4 TINY语言的递归下降分析程序 136 4.5 自顶向下分析程序中的错误校正 137 4.5.1 在递归下降分析程序中的错误 校正 138 4.5.2 在LL(1)分析程序中的错误校正 140 4.5.3 在TINY分析程序中的错误校正 141 练习 143 编程练习 146 注意与参考 148 第5章 自底向上的分析 150 5.1 自底向上分析概览 151 5.2 LR(0)项的有穷自动机与LR(0)分析 153 5.2.1 LR(0)项 153 5.2.2 项目的有穷自动机 154 5.2.3 LR(0)分析算法 157 5.3 SLR(1)分析 160 5.3.1 SLR(1)分析算法 160 5.3.2 用于分析冲突的消除二义性 规则 163 5.3.3 SLR(1)分析能力的局限性
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值