整本书的核心:语言的设计与实现
程序正文中出现的文本结构:显式的
相对而言而实现是隐式的:编译或运行时的行为。
了解设计的目的,可以用不同的语言模拟实现该设计。
行文的逻辑,对于一种特定设计:
1.设计的好处
从程序员(使用者)角度,解释使用设计的好处。
设计存在的价值:简化代码,清晰概念。
2.实现的细节
包括编译与运行中的推断与检查等。
也有可能是个语法糖,简单的实现换来优美的设计。
3.历史演变
设计的演变:更清晰的语义
实现的演变:效率的提升
从历史的角度说明设计的目的与被接受的必然
4.不同的语言的实现方案
5.反例:设计缺失,带来的不便与混乱
6.设计的实现可能来自于自举,或对其他语言而言,模拟该实现
引言
1.机器语言通过指令集控制处理器,机器语言的形式为进制数
2.汇编语言是指令集的助记形式,用的是英文的缩写
汇编语言与机器语言保持一一对应,所以本质上还是用指令集思考问题,是机器相关的。
由汇编器完成两种语言的简单翻译
机器相关的汇编语言+不同机器不同指令集=同一想法在不同机器上复现很困难
最后,高级语言登场,实现机器无关性
高级语言的单条语句与指令集中的单条语句不再对应
由编译器了解程序正文的语义,翻译成机器语言(或汇编语言)
硬件的复杂化(不容易提升手写汇编语言的效率)+编译技术进步(生成越来越高效的汇编语言)=高级语言被人接受
这一历史体现了“边际成本”的有趣现象,一种设计的优势与劣势是相对的
人们追求优雅一致的设计,但又不想放弃高效的实现
直至原有设计的效率提升达到瓶颈
效率的继续提升带来了难以控制的复杂性
1.2程序设计语言的分类
说明式
函数式:
数据流:
命令式
冯 诺伊曼:C
脚本:python
面向对象:Java
函数式:函数递归,目标函数递归为子问题
数据流:数据在功能节点间流动 tensorflow?
逻辑式:满足逻辑的值,不断筛选和加工 SQL
冯 诺伊曼:计算值(表达式,函数返回值),在变量上存储值,修改值。
脚本:为特定目标快速建立原型
面向对象:对象维护状态(域),修改状态(方法)
1.3:设计一种语言的目的
表达能力:简单高效地编写程序
实现效率:了解实现的细节对效率的影响
在语言中模拟缺乏的特性
1.4编译与解释
编译器 控制 编译过程,然后交由
目标程序 控制 执行过程
解释器在执行过程中控制程序的执行。
编译器与解释器的差别:运行前/运行中的检查与推断
这两个策略都是有价值的,两者借鉴对方。
编译器尝试在运行前理解源程序,并维护程序的部分信息,供执行时使用,从而提升效率。
并且生成代码进行运行时检查。
静态与动态
静态是指由程序正文提供的,在编译时可以推断的信息
动态指的是运行时的,编译时难以预测的信息(无法预测,或预测的代价太大)
程序经过编译后,依然存在不确定性
对程序输入的依赖
对子程序参数的依赖
函数调用等复杂控制结构导致执行流的不确定性
编译器的特性:彻底的分析与非平凡的变换
其中彻底的分析表明了两个意图:
1.在运行前尽可能的提取运行时需要的信息,早做安排
2.尽可能的预测错误的发生
数据库系统的查询语言也是编译器,SQL语言翻译成文本操作。
1.5程序设计环境
语言相关的开发环境
IDE:汇编器,调试器,预处理器,连接器,编译器,解释器,编辑器
检查词法语法,维护语法树
1.6编译概览
一个编译遍
代码形态 编译的子步骤
字符流
扫描器(词法分析)
单词流
语法分析器
语法分析树
语义分析
抽象语法树
编译优化(机器无关)
中间形式
目标代码生成
目标语言(汇编)
编译优化(机器相关)
目标语言
扫描器(词法分析):检查单个单词(标识符)的正确性
语法分析树:结构依赖于语法,上下文无关文法
语义分析器:标识符与表达式的类型,相容性
构造符号表,维护各个标识符的类型,内部结构,作用域
检查标识符的定义,上下文,子程序参数签名与调用,返回值
语义动作例程
依然不可检查的:动态语义
变量使用前初始化
指针指向有效对象
下标越界
算术溢出
具体语法树:序列的合法性
抽象语法树:删去不必要节点,增加符号表
代码生成器:保存符号表,包含在目标代码非执行部分
代码改进:在中间代码生成后,在目标代码生成后
rest:
语言的设计目标:
目的:清晰,
最小:紧凑,
可控:易于维护,
代码少:表达能力强,
有常用功能:方便优雅,
快:实现效率
编译后的链接步骤,可以看作是通过库例程扩充硬件指令集,可也以认为编译器就像虚拟机那样生成代码,而虚拟机中包含了硬件和库的功能。
预处理器:执行简单的文本模式匹配。没有了解程序的正确性(语义的分析)。
自举:简单的实现到复杂的实现,逐步扩充语言