新書推薦:
《
实用对联大全
》
售價:NT$
225.0
《
想象欧洲丛书(7册)欧洲史
》
售價:NT$
1880.0
《
没有伞的孩子必须努力奔跑
》
售價:NT$
149.0
《
做最好的班主任(李镇西教育文集版)
》
售價:NT$
230.0
《
数据驱动法律
》
售價:NT$
390.0
《
对话世界一流大学图书馆馆长
》
售價:NT$
995.0
《
揭秘立体翻翻书--我们的国宝
》
售價:NT$
490.0
《
清代贡院史
》
售價:NT$
800.0
編輯推薦:
对不同类型计算机语言的需求正在迅速增长,开发人员更喜欢创建领域特定语言来解决特定的应用程序领域问题。虽然构建自己的编程语言可以解决软件不断增长的规模和复杂性问题,但这并不容易。
本书融合作者构建Unicon编程语言的经验,全面系统地阐述了编程语言的设计与实现。书中既涵盖语法树的一系列遍历、字节码虚拟机的代码生成,又介绍了如何通过内置于语言中的操作符和函数而不是库函数来很好地表示领域特定语言的特性,而且展示了如何实现垃圾收集,包括引用计数和标记-清理垃圾收集。在整本书中,作者提供了基于Unicon和Java的相关示例,以帮助读者更好地理解概念的上下文并掌握相关技术和方法。
学完本书,读者将能够构建和部署自己的领域特定语言,并编译和运行程序。
阅读完本书,读者将能够:
·对新语言进行需求分析,并设计语言语法和语义;
·为常用表达式和控制结构编写词法和上下文无关的文法规则;
·开发一个读取源代码的扫描器,并生成一个检查语法的解析器;
·在编译器中构建关键数据结构,并使用编译器构建语法着色代码编辑器;
·实现一个字节码解释器,并运行由编译器生成的字节码;
·编写将信息插入语法
內容簡介:
本书主要研究如何构建一种新的编程语言。书中将介绍编程语言设计方面的主题,并重点介绍编程语言实现。本书的新颖之处在于将传统的编译器-编译器工具(Flex和BYACC)与两种更高级的实现语言融合。一种非常高级的语言(Unicon)可以像黄油一样穿透编译器的数据结构和算法,而另一种主流的现代语言(Java)则展示了如何在更典型的生产环境中实现相同的代码。
本书主要面向对发明编程语言或开发领域特定语言感兴趣的软件开发人员。学习编译器构建课程的计算机科学相关专业学生也会发现这本书非常适合作为语言实现的实用指南,可以为理论教材提供有益补充。
關於作者:
克林顿 · L. 杰弗瑞
(Clinton L. Jeffery)Clinton L. Jeffery是New Mexico Institute of Mining and Technology计算机科学与工程系的教授和系主任。他在华盛顿大学获得计算机科学学士学位,在亚利桑那大学获得计算机科学硕士和博士学位。他主要从事编程语言、程序监控、调试、图形、虚拟环境和可视化方面的研究,并撰写了许多相关书籍和论文。他与同事一起发明了Unicon编程语言,该语言在Unicon网站上托管。
目錄 :
Contents 目 录
前言
第一部分 编程语言导论
第1章 为什么要构建另一种编程
语言2
1.1 编写自己的编程语言的动机2
1.1.1 编程语言实现的类型3
1.1.2 组织字节码语言实现4
1.1.3 示例中使用的语言4
1.2 编程语言与库的差别5
1.3 适用于其他软件工程任务6
1.4 建立语言需求6
1.5 案例研究:Unicon语言的创建需求8
1.5.1 Unicon需求#1—保留人们
对Icon的喜爱8
1.5.2 Unicon需求#2—支持大型
大数据项目9
1.5.3 Unicon需求#3—现代应用
程序的高级输入/输出9
1.5.4 Unicon需求#4—提供可实
现的通用系统接口9
1.6 本章小结10
1.7 思考题10
第2章 编程语言设计11
2.1 确定要编程语言提供的单词和
标点符号的类型11
2.2 指定控制流13
2.3 决定支持哪种数据14
2.3.1 原子类型14
2.3.2 复合类型15
2.3.3 领域特定类型16
2.4 整体程序结构16
2.5 完成Jzero语言的定义17
2.6 案例研究:设计Unicon中的图形
功能18
2.6.1 2D图形语言支持18
2.6.2 添加3D图形支持20
2.7 本章小结21
2.8 思考题21
第3章 扫描源代码22
3.1 技术需求22
3.2 词素、词类和标记23
3.3 正则表达式24
3.3.1 正则表达式规则24
3.3.2 正则表达式示例25
3.4 使用UFlex和JFlex26
3.4.1 头部分26
3.4.2 正则表达式部分27
3.4.3 编写一个简单的源代码扫
描器27
3.4.4 运行扫描器30
3.4.5 标记和词法属性31
3.4.6 扩展示例以构造标记31
3.5 为Jzero编写扫描器34
3.5.1 Jzero Flex规范34
3.5.2 Unicon Jzero代码36
3.5.3 Java Jzero代码39
3.5.4 运行Jzero扫描器42
3.6 正则表达式并不总是足够的44
3.7 本章小结46
3.8 思考题47
第4章 解析48
4.1 技术需求48
4.2 语法分析49
4.3 理解上下文无关文法49
4.3.1 编写上下文无关文法规则50
4.3.2 编写编程构造规则51
4.4 使用iyacc和BYACC/J53
4.4.1 声明头部分中的符号53
4.4.2 组合yacc上下文无关文法
部分54
4.4.3 理解yacc解析器55
4.4.4 修复yacc解析器中的冲突56
4.4.5 语法错误修复57
4.4.6 组合简单示例57
4.5 为Jzero编写解析器62
4.5.1 Jzero lex规范62
4.5.2 Jzero yacc规范62
4.5.3 Unicon Jzero代码66
4.5.4 Java Jzero解析器代码68
4.5.5 运行Jzero解析器69
4.6 改进语法错误消息70
4.6.1 向Unicon语法错误消息添加
详细信息71
4.6.2 向Java语法错误消息添加详
细信息71
4.6.3 使用Merr生成更好的语法错
误消息72
4.7 本章小结72
4.8 思考题73
第5章 语法树74
5.1 技术需求74
5.2 GNU make的使用75
5.3 树77
5.3.1 定义语法树类型78
5.3.2 解析树与语法树79
5.4 从终结符创建叶子81
5.4.1 用叶子包装标记81
5.4.2 使用YACC的值栈82
5.4.3 为解析器的值栈包装叶子83
5.4.4 确定需要哪些叶子84
5.5 从产生式规则构建内部节点85
5.5.1 访问值栈上的树节点85
5.5.2 使用树节点工厂方法86
5.6 为Jzero语言形成语法树88
5.7 调试并测试语法树93
5.7.1 避免常见的语法树错误94
5.7.2 以文本格式输出语法树95
5.7.3 使用dot输出语法树96
5.8 本章小结101
5.9 思考题101
第二部分 语法树遍历
第6章 符号表104
6.1 技术需求104
6.2 建立符号表基础105
6.2.1 声明和作用域105
6.2.2 赋值和取消引用变量106
6.2.3 选择正确的树遍历106
6.3 为每个作用域创建和填充符号表107
6.3.1 向语法树添加语义属性108
6.3.2 定义符号表和符号表条目的类109
6.3.3 创建符号表110
6.3.4 填充符号表112
6.3.5 综合isConst属性114
6.4 检查未声明的变量115
6.4.1 识别方法体115
6.4.2 发现方法体中变量的使用116
6.5 查找重新声明的变量118
6.5.1 将符号插入符号表118
6.5.2 报告语义错误119
6.6 在Unicon中处理包和类作用域119
6.6.1 名称修饰120
6.6.2 为成员变量引用插入self120
6.6.3 在方法调用中插入self作为
第一个参数121
6.7 测试和调试符号表122
6.8 本章小结123
6.9 思考题124
第7章 基本类型检查125
7.1 技术需求125
7.2 编译器中的类型表示125
7.2.1 定义表示类型的基类126
7.2.2 子类化复杂类型的基类127
7.3 将类型信息分配给声明的变量129
7.3.1 从保留字合成类型130
7.3.2 将类型继承到变量列表中131
7.4 确定每个语法树节点的类型132
7.4.1 确定叶子的类型133
7.4.2 计算和检查内部节点的类型135
7.5 Unicon中的运行时类型检查和
类型推断139
7.6 本章小结140
7.7 思考题140
第8章 检查数组、方法调用和结
构访问的类型142
8.1 技术需求142
8.2 检查数组类型的操作142
8.2.1 处理数组变量声明143
8.2.2 在数组创建期间检查类型143
8.2.3 在数组访问期间检查类型145
8.3 检查方法调用146
8.3.1 计算参数和返回类型信息147
8.3.2 检查每个方法调用站点的类型149
8.3.3 检查返回语句中的类型152
8.4 检查结构化类型访问153
8.4.1 处理实例变量声明154
8.4.2 在创建实例时检查类型154
8.4.3 在实例访问时检查类型157
8.5 本章小结159
8.6 思考题160
第9章 中间代码生成161
9.1 技术需求161
9.2 准备生成代码161
9.2.1 为什么要生成中间代码162
9.2.2 了解生成程序的存储区域162
9.2.3 为中间代码引入数据类型163
9.2.4 将中间代码属性添加到树中165
9.2.5 生成标签和临时变量165
9.3 中间代码指令集168
9.3.1 指令168
9.3.2 声明168
9.4 用标签为控制流注释语法树 169
9.5 为表达式生成代码171
9.6 为控制流生成代码173
9.6.1 为条件表达式生成标签目标174
9.6.2 生成循环代码177
9.6.3 为方法调用生成中间代码178
9.6.4 检查生成的中间代码179
9.7 本章小结180
第10章 IDE中的语法着色182
10.1 下载本章中使用的示例IDE183
10.2 将编译器集成到程序员的编辑
器中184
10.2.1 从IDE中分析源代码185
10.2.2 将编译器输出发送到IDE185
10.3 避免在每次更改时重新解析
整个文件186
10.4 使用词法信息为标记着色189
10.4.1 扩展EditableTextList组件以
支持颜色189
10.4.2 在绘制单个标记时对其
进行着色190
10.5 使用解析结果突出显示错误191
10.6 添加Java支持192
10.7 本章小结194
第三部分 代码生成与运行时系统
第11章 字节码解释器196
11.1 技术需求196
11.2 什么是字节码196
11.3 比较字节码和中间码198
11.4 为Jzero构建字节码指令集200
11.4.1 定义Jzero字节码文件格式200
11.4.2 了解栈机操作的基础知识202
11.5 实现字节码解释器203
11.5.1 将字节码加载到内存中203
11.5.2 初始化解释器状态205
11.5.3 获取指令并推进指令指针206
11.5.4 指令解码207
11.5.5 执行指令208
11.5.6 启动Jzero解释器211
11.6 编写Jzero运行时系统212
11.7 运行Jzero程序213
11.8 检查Unicon字节码解释器iconx213
11.8.1 了解目标导向的字节码214
11.8.2 在运行时保留类型信息214
11.8.3 获取、解码和执行指令214
11.8.4 制作运行时系统的其余部分215
11.9 本章小结215
11.10 思考题215
第12章 生成字节码217
12.1 技术需求217
12.2 转换中间代码为Jzero字节码217
12.2.1 为字节码指令添加类218
12.2.2 将中间代码地址映射到
字节码地址219
12.2.3 实现字节码生成器方法220
12.2.4 为简单表达式生成字节码221
12.2.5 生成指针操作的代码223
12.2.6 为分支和条件分支生成
字节码224
12.2.7 为方法调用和返回生成代码225
12.2.8 处理中间代码中的标签和
其他伪指令226
12.3 比较字节码汇编程序与二进制
格式227
12.3.1 以汇编格式输出字节码227
12.3.2 以二进制格式输出字节码229
12.4 链接、加载并包括运行时系统230
12.5 Unicon示例:icont中的字节码
生成230
12.6 本章小结232
12.7 思考题232
第13章 生成本机代码233
13.1 技术需求233
13.2 决定是否生成本机代码233
13.3 x64指令集234
13.3.1 为x64指令添加类234
13.3.2 将内存区域映射到基于x64
寄存器的地址模式235
13.4 使用寄存器235
13.4.1 从空策略开始236
13.4.2 分配寄存器以加速本地
区域237
13.5 将中间代码转换为x64代码239
13.5.1 将中间代码地址映射到x64
内存地址240
13.5.2 实现x64代码生成器方法243
13.5.3 生成简单表达式的x64代码244
13.5.4 生成指针操作的代码245
13.5.5 为分支和条件分支生成
本机代码246
13.5.6 为方法调用和返回生成代码247
13.5.7 处理标签和伪指令249
13.6 生成x64输出250
13.6.1 以汇编语言格式编写x64
代码251
13.6.2 从本机汇编程序到目标文件251
13.6.3 链接、加载并包括运行时
系统252
13.7 本章小结253
13.8 思考题253
第14章 运算符和内置函数的实现254
14.1 实现运算符254
14.1.1 运算符是否需要硬件支持255
14.1.2 在中间代码生成中添加字符
串连接255
14.1.3 为字节码解释器添加字符
串连接257
14.1.4 将字符串连接添加到本机
运行时系统259
14.2 编写内置函数260
14.2.1 向字节码解释器添加内置
函数260
14.2.2 编写用于本机代码实现的
内置函数261
14.3 集成内置组件与控制结构262
14.4 为Unicon开发运算符和函数262
14.4.1 在Unicon中编写运算符263
14.4.2 开发Unicon的内置函数265
14.5 本章小结266
14.6 思考题266
第15章 域控制结构267
15.1 了解何时需要新的控制结构267
15.1.1 定义控制结构268
15.1.2 减少过多的冗余参数268
15.2 Icon和Unicon中的字符串扫描269
15.2.1 扫描环境及其基本操作270
15.2.2 通过控制结构消除过多参数271
15.3 Unicon中的渲染区域271
15.3.1 从显示列表渲染3D图形272
15.3.2 使用内置函数指定渲染区域272
15.3.3 使用嵌套渲染区域更改
图形细节层次273
15.3.4 创建渲染区域控制结构274
15.4 本章小结278
15.5 思考题278
第16章 垃圾收集279
16.1 认识垃圾收集的重要性279
16.2 对象的引用计数281
16.2.1 将引用计数添加到Jzero281
16.2.2 生成堆分配代码281
16.2.3 为赋值运算符修改生成的
代码283
16.2.4 引用计数的缺点和局限性283
16.3 标记实时数据并清理剩余数据284
16.3.1 组织堆内存区域285
16.3.2 遍历基本变量以标记实时
数据286
16.3.3 回收实时内存并将其放入
连续内存块290
16.4 本章小结293
16.5 思考题293
第17章 结语294
17.1 反思从编写这本书中学到的
东西294
17.2 决定何去何从295
17.2.1 学习编程语言设计295
17.2.2 学习如何实现解释器和字
节码机器296
17.2.3 获取代码优化方面的专业
知识297
17.2.4 监视和调试程序执行297
17.2.5 设计和实现IDE和GUI
构建器298
17.3 延伸阅读的参考资料298
17.4 本章小结301
第四部分 附录
附录A Unicon基础304
附录B 部分章节要点324
內容試閱 :
Preface 前 言
虽然高级语言的开发已经有60年之久,但编程仍然非常困难。随着硬件不断进步,软件规模不断增长,软件复杂性也越来越高,而编程语言的进步则要慢得多。为特定用途创建新的编程语言是解决这种软件危机的一剂良药。
本书主要研究如何构建一种新的编程语言。书中将介绍编程语言设计方面的主题,并重点介绍编程语言实现。本书的新颖之处在于将传统的编译器-编译器工具(Flex和BYACC)与两种更高级的实现语言融合。一种非常高级的语言(Unicon)可以像黄油一样穿透编译器的数据结构和算法,而另一种主流的现代语言(Java)则展示了如何在更典型的生产环境中实现相同的代码。
有一个问题直到我上完大学编译器课程都没有真正理解:编译器只是编程语言实现的一部分。更高级的语言,包括大多数较新的语言,都有一个运行时系统令其编译器相形见绌。为此,本书的后半部分花了大量篇幅介绍语言运行时系统的各个方面,从字节码解释器到垃圾收集。
本书读者对象
本书面向对发明编程语言或开发领域特定语言(Domain-Specific Languages,DSL)感兴趣的软件开发人员。学习编译器构建课程的计算机科学系学生也会发现这本书非常适合作为语言实现的实用指南,可以为理论教材提供有益补充。为了更好地学习本书知识,读者需要具有中等高级语言(如Java或C )知识水平和使用经验。
本书主要内容
第1章讨论何时构建编程语言,以及何时设计函数库或类库。本书的读者大都想构建自己的编程语言,或者想设计一个库。
第2章介绍如何精确定义编程语言,这在试图构建编程语言之前了解是非常重要的。这包括语言的词法和语法特征以及语义的设计,好的语言设计通常尽可能多地使用熟悉语法。
第3章介绍词法分析,包括正则表达式表示法及UFlex和JFlex这两个工具。最后,我们将打开源代码文件,逐个字符地读取源代码,并将其内容作为标记(token)流报告。标记流由源文件中的单个单词、运算符和标点符号组成。
第4章介绍语法分析,包括上下文无关文法及iyacc和BYACC/J这两个工具。我们将学习如何对文法中阻挠解析的问题进行调试,并在出现语法错误时报告错误。
第5章介绍语法树。解析过程的主要副产品是构造表示源代码逻辑结构的树数据结构,树节点的构造发生在对每个文法规则执行的语义动作中。
第6章展示如何构造符号表,将符号插入其中,并使用符号表来识别两种语义错误:未声明的变量和非法重新声明的变量。为了理解可执行代码中的变量引用,必须跟踪每个变量的范围和生存期。这是通过辅助语法树的表数据结构实现的。
第7章对类型检查进行介绍,这是大多数编程语言所面临的主要任务。类型检查可以在编译时或运行时执行。本章将介绍对基本类型(也称为原子或标量类型)进行静态编译时类型检查的常见情况。
第8章介绍如何对Java Jzero子集中的方法调用的数组、参数和返回类型执行类型检查。当涉及多个或复合类型时,类型检查更困难。当必须检查具有多个参数类型的函数,或者必须检查数组、哈希表、类实例或其他复合类型时,就是这种情况。
第9章通过剖析由Jzero语言编写的示例,介绍如何生成中间代码。在生成要执行的代码之前,大多数编译器将语法树转换为与机器无关的中间代码指令列表。此时将处理控制流的关键方面,例如标签和goto指令的生成。
第10章介绍将语法分析的信息合并到IDE中,以便解决提供语法着色和有关语法错误的视觉反馈时所遇到的一些挑战。编程语言不仅需要编译器或解释器,还需要开发人员的工具生态系统。这个生态系统包括调试器、联机帮助或集成开发环境。本章是一个来自Unicon IDE的Unicon示例。
第11章介绍设计指令集和执行字节码的解释器。一种新的领域特定语言可能包括主流CPU不直接支持的高级域编程特性。为许多语言生成代码的最实用方法是为抽象机器生成字节码,其指令集直接支持域,然后通过解释该指令集来执行程序。
第12章继续介绍代码生成,从第9章中提取中间代码,并从中生成字节码。从中间代码到字节码的转换需要遍历一个巨大的链表,将每个中间代码指令转换为一个或多个字节码指令。通常,这是一个遍历链表的循环,每个中间代码指令都有不同的代码块。
第13章介绍如何为x64生成本机代码。一些编程语言需要本机代码来实现其性能要求。本机代码生成类似于字节码生成,但更复杂,涉及寄存器分配和内存寻址模式。
第14章介绍如何通过添加内置在语言中的运算符和函数来支持领域特定语言的高级特性。领域特定语言的高级特性通常最好由内置在语言中的运算符和函数来表示,而不是库函数。添加内置函数可能会简化编程语言,提高其性能,或者在语言语义中产生副作用,否则这些副作用是很难或不可能产生的。本章中的示例来自Unicon,因为它是比Java更高级的语言,并且在其内置函数中实现了更复杂的语义。
第15章介绍何时需要新的控制结构,并提供使用字符串扫描处理文本和渲染图形区域的控制结构示例。前几章中的通用代码涵盖了基本的条件和循环控制结构,但领域特定语言通常具有独特或自定义的语义,它们为此引入了新的控制结构。添加新的控制结构比添加新的函数或运算符要困难