mirror of
https://github.com/LCTT/TranslateProject.git
synced 2025-01-13 22:30:37 +08:00
Merge pull request #8388 from wxy/20150812-Let-s-Build-A-Simple-Interpreter.-Part-3-
PRF&PUB:20150812 Let s Build A Simple Interpreter. Part 3
This commit is contained in:
commit
b99c8da039
@ -1,11 +1,11 @@
|
||||
让我们做个简单的解释器(3)
|
||||
让我们做个简单的解释器(三)
|
||||
======
|
||||
|
||||
早上醒来的时候,我就在想:“为什么我们学习一个新技能这么难?”
|
||||
|
||||
我不认为那是因为它很难。我认为原因可能在于我们花了太多的时间,而这件难事需要有丰富的阅历和足够的知识,然而我们要把这样的知识转换成技能所用的练习时间又不够。
|
||||
拿游泳来说,你可以花上几天时间来阅读很多有关游泳的书籍,花几个小时和资深的游泳者和教练交流,观看所有可以获得的训练视频,但你第一次跳进水池的时候,仍然会像一个石头那样沉入水中,
|
||||
|
||||
拿游泳来说,你可以花上几天时间来阅读很多有关游泳的书籍,花几个小时和资深的游泳者和教练交流,观看所有可以获得的训练视频,但你第一次跳进水池的时候,仍然会像一个石头那样沉入水中,
|
||||
|
||||
要点在于:你认为自己有多了解那件事都无关紧要 —— 你得通过练习把知识变成技能。为了帮你练习,我把训练放在了这个系列的 [第一部分][1] 和 [第二部分][2] 了。当然,你会在今后的文章中看到更多练习,我保证 :)
|
||||
|
||||
@ -17,7 +17,7 @@
|
||||
|
||||
![][3]
|
||||
|
||||
什么是语法图? **语法图** 是对一门编程语言中的语法规则进行图像化的表示。基本上,一个语法图就能告诉你哪些语句可以在程序中出现,哪些不能出现。
|
||||
什么是<ruby>语法图<rt>syntax diagram</rt></ruby>? **语法图** 是对一门编程语言中的语法规则进行图像化的表示。基本上,一个语法图就能告诉你哪些语句可以在程序中出现,哪些不能出现。
|
||||
|
||||
语法图很容易读懂:按照箭头指向的路径。某些路径表示的是判断,有些表示的是循环。
|
||||
|
||||
@ -28,9 +28,7 @@
|
||||
* 它们用图形的方式表示一个编程语言的特性(语法)。
|
||||
* 它们可以用来帮你写出解析器 —— 你可以根据下列简单规则把图片转换成代码。
|
||||
|
||||
|
||||
|
||||
你已经知道,识别出记号流中的词组的过程就叫做 **解析**。解释器或者编译器执行这个任务的部分叫做 **解析器**。解析也称为 **语法分析**,并且解析器这个名字很合适,你猜的对,就是 **语法分析**。
|
||||
你已经知道,识别出记号流中的词组的过程就叫做 **解析**。解释器或者编译器执行这个任务的部分叫做 **解析器**。解析也称为 **语法分析**,并且解析器这个名字很合适,你猜的对,就是 **语法分析器**。
|
||||
|
||||
根据上面的语法图,下面这些表达式都是合法的:
|
||||
|
||||
@ -38,9 +36,8 @@
|
||||
* 3 + 4
|
||||
* 7 - 3 + 2 - 1
|
||||
|
||||
|
||||
|
||||
因为算术表达式的语法规则在不同的编程语言里面是很相近的,我们可以用 Python shell 来“测试”语法图。打开 Python shell,运行下面的代码:
|
||||
|
||||
```
|
||||
>>> 3
|
||||
3
|
||||
@ -53,6 +50,7 @@
|
||||
意料之中。
|
||||
|
||||
表达式 “3 + ” 不是一个有效的数学表达式,根据语法图,加号后面必须要有个 term (整数),否则就是语法错误。然后,自己在 Python shell 里面运行:
|
||||
|
||||
```
|
||||
>>> 3 +
|
||||
File "<stdin>", line 1
|
||||
@ -63,9 +61,10 @@ SyntaxError: invalid syntax
|
||||
|
||||
能用 Python shell 来做这样的测试非常棒,让我们把上面的语法图转换成代码,用我们自己的解释器来测试,怎么样?
|
||||
|
||||
从之前的文章里([第一部分][1] 和 [第二部分][2])你知道 expr 方法包含了我们的解析器和解释器。再说一遍,解析器仅仅识别出结构,确保它与某些特性对应,而解释器实际上是在解析器成功识别(解析)特性之后,就立即对表达式进行评估。
|
||||
从之前的文章里([第一部分][1] 和 [第二部分][2])你知道 `expr` 方法包含了我们的解析器和解释器。再说一遍,解析器仅仅识别出结构,确保它与某些特性对应,而解释器实际上是在解析器成功识别(解析)特性之后,就立即对表达式进行评估。
|
||||
|
||||
以下代码片段显示了对应于图表的解析器代码。语法图里面的矩形方框(term)变成了 term 方法,用于解析整数,expr 方法和语法图的流程一致:
|
||||
|
||||
```
|
||||
def term(self):
|
||||
self.eat(INTEGER)
|
||||
@ -85,9 +84,10 @@ def expr(self):
|
||||
self.term()
|
||||
```
|
||||
|
||||
你能看到 expr 首先调用了 term 方法。然后 expr 方法里面的 while 循环可以执行 0 或多次。在循环里面解析器基于标记做出判断(是加号还是减号)。花一些时间,你就知道,上述代码确实是遵循着语法图的算术表达式流程。
|
||||
你能看到 `expr` 首先调用了 `term` 方法。然后 `expr` 方法里面的 `while` 循环可以执行 0 或多次。在循环里面解析器基于标记做出判断(是加号还是减号)。花一些时间,你就知道,上述代码确实是遵循着语法图的算术表达式流程。
|
||||
|
||||
解析器并不解释任何东西:如果它识别出了一个表达式,它就静默着,如果没有识别出来,就会抛出一个语法错误。改一下 `expr` 方法,加入解释器的代码:
|
||||
|
||||
解析器并不解释任何东西:如果它识别出了一个表达式,它就静默着,如果没有识别出来,就会抛出一个语法错误。改一下 expr 方法,加入解释器的代码:
|
||||
```
|
||||
def term(self):
|
||||
"""Return an INTEGER token value"""
|
||||
@ -113,14 +113,16 @@ def expr(self):
|
||||
return result
|
||||
```
|
||||
|
||||
因为解释器需要评估一个表达式, term 方法被改成返回一个整型值,expr 方法被改成在合适的地方执行加法或减法操作,并返回解释的结果。尽管代码很直白,我建议花点时间去理解它。
|
||||
因为解释器需要评估一个表达式, `term` 方法被改成返回一个整型值,`expr` 方法被改成在合适的地方执行加法或减法操作,并返回解释的结果。尽管代码很直白,我建议花点时间去理解它。
|
||||
|
||||
进行下一步,看看完整的解释器代码,好不?
|
||||
|
||||
这时新版计算器的源代码,它可以处理包含有任意多个加法和减法运算的有效的数学表达式。
|
||||
这是新版计算器的源代码,它可以处理包含有任意多个加法和减法运算的有效的数学表达式。
|
||||
|
||||
```
|
||||
# 标记类型
|
||||
#
|
||||
# EOF (end-of-file 文件末尾) 标记是用来表示所有输入都解析完成
|
||||
# EOF (end-of-file 文件末尾)标记是用来表示所有输入都解析完成
|
||||
INTEGER, PLUS, MINUS, EOF = 'INTEGER', 'PLUS', 'MINUS', 'EOF'
|
||||
|
||||
|
||||
@ -265,9 +267,10 @@ if __name__ == '__main__':
|
||||
main()
|
||||
```
|
||||
|
||||
把上面的代码保存到 calc3.py 文件中,或者直接从 [GitHub][4] 上下载。试着运行它。看看它能不能处理我之前给你看过的语法图里面派生出的数学表达式。
|
||||
把上面的代码保存到 `calc3.py` 文件中,或者直接从 [GitHub][4] 上下载。试着运行它。看看它能不能处理我之前给你看过的语法图里面派生出的数学表达式。
|
||||
|
||||
这是我在自己的笔记本上运行的示例:
|
||||
|
||||
```
|
||||
$ python calc3.py
|
||||
calc> 3
|
||||
@ -297,15 +300,13 @@ Traceback (most recent call last):
|
||||
Exception: Invalid syntax
|
||||
```
|
||||
|
||||
|
||||
记得我在文章开始时提过的练习吗:他们在这儿,我保证过的:)
|
||||
记得我在文章开始时提过的练习吗:它们在这儿,我保证过的:)
|
||||
|
||||
![][5]
|
||||
|
||||
* 画出只包含乘法和除法的数学表达式的语法图,比如 “7 * 4 / 2 * 3”。认真点,拿只钢笔或铅笔,试着画一个。
|
||||
修改计算器的源代码,解释只包含乘法和除法的数学表达式。比如 “7 * 4 / 2 * 3”。
|
||||
* 从头写一个可以处理像 “7 - 3 + 2 - 1” 这样的数学表达式的解释器。用你熟悉的编程语言,不看示例代码自己思考着写出代码。做的时候要想一想这里面包含的组件:一个 lexer,读取输入并转换成标记流,一个解析器,从 lexer 提供的记号流中取食,并且尝试识别流中的结构,一个解释器,在解析器成功解析(识别)有效的数学表达式后产生结果。把这些要点串起来。花一点时间把你获得的知识变成一个可以运行的数学表达式的解释器。
|
||||
|
||||
* 从头写一个可以处理像 “7 - 3 + 2 - 1” 这样的数学表达式的解释器。用你熟悉的编程语言,不看示例代码自己思考着写出代码。做的时候要想一想这里面包含的组件:一个词法分析器,读取输入并转换成标记流,一个解析器,从词法分析器提供的记号流中获取,并且尝试识别流中的结构,一个解释器,在解析器成功解析(识别)有效的数学表达式后产生结果。把这些要点串起来。花一点时间把你获得的知识变成一个可以运行的数学表达式的解释器。
|
||||
|
||||
**检验你的理解。**
|
||||
|
||||
@ -314,8 +315,6 @@ Exception: Invalid syntax
|
||||
3. 什么是语法分析器?
|
||||
|
||||
|
||||
|
||||
|
||||
嘿,看!你看完了所有内容。感谢你们坚持到今天,而且没有忘记练习。:) 下次我会带着新的文章回来,尽请期待。
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
@ -323,14 +322,14 @@ Exception: Invalid syntax
|
||||
via: https://ruslanspivak.com/lsbasi-part3/
|
||||
|
||||
作者:[Ruslan Spivak][a]
|
||||
译者:[译者ID](https://github.com/译者ID)
|
||||
校对:[校对者ID](https://github.com/校对者ID)
|
||||
译者:[BriFuture](https://github.com/BriFuture)
|
||||
校对:[wxy](https://github.com/wxy)
|
||||
|
||||
本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
|
||||
|
||||
[a]:https://ruslanspivak.com
|
||||
[1]:http://ruslanspivak.com/lsbasi-part1/ (Part 1)
|
||||
[2]:http://ruslanspivak.com/lsbasi-part2/ (Part 2)
|
||||
[1]:https://linux.cn/article-9399-1.html
|
||||
[2]:https://linux.cn/article-9520-1.html
|
||||
[3]:https://ruslanspivak.com/lsbasi-part3/lsbasi_part3_syntax_diagram.png
|
||||
[4]:https://github.com/rspivak/lsbasi/blob/master/part3/calc3.py
|
||||
[5]:https://ruslanspivak.com/lsbasi-part3/lsbasi_part3_exercises.png
|
Loading…
Reference in New Issue
Block a user