元数据
用Go语言自制解释器
- 书名: 用Go语言自制解释器
- 作者: 索斯藤·鲍尔
- 简介: 在程序员与计算机的“交流”过程中,解释器无疑扮演着优秀的翻译角色。它为只懂0和1的计算机翻译源代码,为看似随机的字符赋予含义。这是如何实现的呢?充满好奇心的你,是否曾经思考过这个问题?跟随本书,你将揭开解释器的神秘面纱,通晓它的工作原理,并编写出自己的解释器。本书采用Go语言来为自创的编程语言Monkey实现解释器。你将为Monkey语言实现类C语法、变量绑定、基本数据类型、算术运算、内置函数、闭包等特性,并了解什么是词法分析器、语法分析器和抽象语法树。
- 出版时间 2022-06-01 00:00:00
- ISBN: 9787115588289
- 分类: 计算机-编程设计
- 出版社: 人民邮电出版社有限公司
高亮划线
1.3 词法分析器
- 📌 这里之所以用两个“指针”来指向所输入的字符串,是因为词法分析器除了查看当前字符,还需要进一步“查看”字符串,即查看字符串中的下一个字符。 ^3300018889-10-2726-2794
- ⏱ 2023-07-13 10:24:14
1.4 扩展词法单元和词法分析器
-
📌 只是窥视一下输入中的下一个字符,不会移动位于输入中的指针位置 ^3300018889-11-5327-5357
- ⏱ 2023-07-13 14:42:01
-
📌 大多数词法分析器和语法分析器具有这样的“窥视”函数,且大部分情况是用来向前看一个字符的。 ^3300018889-11-5387-5431
- ⏱ 2023-07-13 14:42:14
第2章 语法分析
-
📌 “抽象语法树”(Abstract Syntax Tree,AST)。“抽象”是指AST中省略了源代码中可见的某些细节。比如分号、换行符、空格、注释、花括号、方括号和括号等信息不会出现在AST中,它们只是用来指导语法分析器如何构造AST。 ^3300018889-13-2441-2559
- ⏱ 2023-07-13 15:15:03
-
📌 语法分析器将文本或词法单元形式的源代码作为输入,产生一个表示该源代码的数据结构。 ^3300018889-13-3854-3894
- ⏱ 2023-07-13 15:19:04
2.4 语法分析器的第一步:解析let语句
- 📌 根据当前词法单元来决定调用哪些函数构造AST节点 ^3300018889-16-7695-7719
- ⏱ 2023-07-13 17:08:12
2.6 解析表达式
-
📌 普拉特解析法 ^3300018889-18-3661-3667
- ⏱ 2023-07-13 18:21:28
-
📌 这个想法的关键是,每种词法单元类型都可以具有两个与之相关联的解析函数,具体取决于词法单元的位置,比如是中缀还是前缀。 ^3300018889-18-3785-3843
- ⏱ 2023-07-13 18:21:35
-
📌 本书构建的Monkey解释器没有后缀运算符。这不是因为技术限制,只是纯粹为了限制本书的范围。 ^3300018889-18-4695-4741
- ⏱ 2023-07-13 18:28:08
-
📌 普拉特语法分析器的主要思想是将解析函数(普拉特称为语义代码)与词法单元类型相关联。每当遇到某个词法单元类型时,都会调用相关联的解析函数来解析对应的表达式,最后返回生成的AST节点。 ^3300018889-18-9780-9870
- ⏱ 2023-07-13 18:42:21
-
📌 infixParseFn接受另一个ast.Expression作为参数。该参数是所解析的中缀运算符左侧的内容 ^3300018889-18-10212-10292
- ⏱ 2023-07-13 18:48:24
-
📌 所有解析函数,如prefixParseFn或infixParseFn都会遵循这个方式:函数在开始解析表达式时,当前curToken必须是所关联的词法单元类型,返回分析的表达式结果时,curToken是当前表达式类型中的最后一个词法单元。 ^3300018889-18-16093-16211
- ⏱ 2023-07-13 19:11:40
-
📌 表格驱动的测试方法 ^3300018889-18-22241-22250
- ⏱ 2023-07-13 19:39:24
2.7 普拉特解析的工作方式
-
📌 对peekPrecedence的调用。该调用返回的值代表下一个运算符p.peekToken的左约束能力。 ^3300018889-19-8140-8192
- ⏱ 2023-07-13 22:56:38
-
📌 左约束能力“融合”了之前已解析的内容,将其用作正在构建的AST节点的“左半边”。 ^3300018889-19-9567-9607
- ⏱ 2023-07-13 22:57:12
2.8 扩展语法分析器
-
📌 在调用表达式中并没有新的词法单元类型 ^3300018889-20-29073-29110
- ⏱ 2023-07-14 10:37:22
-
📌 此时左括号位于标识符和参数列表之间,相当于在中缀位置 ^3300018889-20-29254-29280
- ⏱ 2023-07-14 10:42:43
3.2 求值策略
-
📌 字节码是AST的另一种中间表示,信息密度比AST高。 ^3300018889-23-1027-1053
- ⏱ 2023-07-14 11:39:23
-
📌 某些编程语言的实现会解析源代码,构建AST并将其转换为字节码。在执行之前,虚拟机会即时将字节码编译为机器代码,而不是直接在虚拟机中执行字节码指定的操作。这就是所谓的JIT(Just in Time)解释器/编译器。 ^3300018889-23-1470-1603
- ⏱ 2023-07-14 11:50:39
3.10 函数和函数调用
-
📌 扩展已有的环境意味着需要创建一个新的object.Environment实例,以及一个指向待扩展环境的指针。 ^3300018889-31-7895-7949
- ⏱ 2023-07-14 22:01:46
-
📌 如果在当前环境中找不到值,那么就调用包裹自己的上一层环境。以此类推,直到遇到没有外层包裹的环境为止 ^3300018889-31-8073-8122
- ⏱ 2023-07-14 22:00:55
-
📌 如果其最后的求值结果是*object.ReturnValue,则必须进行解包。这很有必要,否则return语句会向上冒泡多个函数并停止对所有这些函数求值。但是实际上我们只想停止对最后调用的函数体求值,因此需要解包返回值 ^3300018889-31-11169-11278
- ⏱ 2023-07-14 22:16:43
4.4 数组
- 📌 索引运算符在所有运算符中必须具有最高的优先级 ^3300018889-36-9206-9228
- ⏱ 2023-07-15 10:56:17
5.1 宏系统
-
📌 宏可以分为两大类:文本替换宏系统和语法宏系统。在我看来,它们分别相当于搜索替换和代码即数据两个类别。 ^3300018889-40-520-570
- ⏱ 2023-07-15 13:38:43
-
📌 文本替换宏系统 ^3300018889-40-599-606
- ⏱ 2023-07-15 13:39:11
-
📌 其工作原理是在C编译器编译和生成代码之前,先单独解析和求值这种语言。 ^3300018889-40-663-697
- ⏱ 2023-07-15 13:39:16
-
📌 语法宏系统不是将代码作为文本,而是将代码视为数据。 ^3300018889-40-1295-1346
- ⏱ 2023-07-15 13:41:49
-
📌 Elixir的quote函数。它可以停止对代码求值,然后高效地将代码转化为数据 ^3300018889-40-2215-2254
- ⏱ 2023-07-15 16:38:08
-
📌 在Elixir以及其他许多具有语法宏系统的语言中,最重要的一点是,作为参数传递给宏的所有内容都相当于在quote中。也就是说,宏的参数不进行求值,可以像任何其他数据一样访问。 ^3300018889-40-4003-4090
- ⏱ 2023-07-15 16:45:37
5.2 Monkey的宏系统
- 📌 添加quote函数和unquote函数,用来准确控制Monkey代码的求值时机。 ^3300018889-41-725-765
- ⏱ 2023-07-15 16:50:28
5.3 quote
-
📌 quote只在宏中使用,其目的很简单:调用时不对参数求值,而是返回表示参数的AST节点。 ^3300018889-42-497-541
- ⏱ 2023-07-15 16:59:04
-
📌 为了让quote最后能返回ast.Node,需要进行简单的封装,也就是传递一个包含ast.Node的object.Object ^3300018889-42-743-806
- ⏱ 2023-07-15 17:02:58
5.4 unquote
- 📌 quote会让Eval“跳过这部分不进行求值”,而unquote相当于“对(在quote中的)这些内容求值”。 ^3300018889-43-608-663
- ⏱ 2023-07-15 17:41:49
5.5 宏扩展
-
📌 词法分析阶段将字符串转换为词法单元;语法分析阶段将词法单元转换为AST;宏扩展阶段获取AST并对其进行修改;最后求值修改后的AST。 ^3300018889-44-1096-1162
- ⏱ 2023-07-15 20:18:42
-
📌 怎么实现宏扩展呢? ^3300018889-44-1207-1216
- ⏱ 2023-07-15 20:21:02
-
📌 第一步是遍历AST并找到所有的宏定义。 ^3300018889-44-1261-1280
- ⏱ 2023-07-15 20:25:36
-
📌 一旦找到了这样的宏定义,就必须将其提取出来。这意味着要从AST中删除这个宏,然后保存到其他地方,以便稍后访问。遇到的宏必须从AST中删除,否则在稍后的求值阶段会遇到问题。第二步是找到对这些宏的调用并对其求值。这与在Eval中处理函数调用非常类似。两者的重要区别在于,对宏调用求值这个阶段,在对宏主体求值之前不会对宏调用的参数求值。宏调用的参数在宏主体中会以未求值的ast.Node形式访问。这就是宏与普通函数的不同之处,即宏要与未求值的AST打交道。求值完成后,必须将宏调用的结果重新插回AST ^3300018889-44-1422-1753
- ⏱ 2023-07-15 20:27:24
-
📌 为宏系统定义的规则:必须从宏返回*object.Quote ^3300018889-44-16098-16153
- ⏱ 2023-07-15 22:36:14
-
📌 这里值得注意的是其参数不会被求值 ^3300018889-44-16538-16554
- ⏱ 2023-07-15 22:08:58
