本系列文章参考国外编程高手鲁斯兰的博客文章《Let’s Build A Simple Interpreter》。
在之前的文章中,我们一起学习了如何实现对两个数字加减法表达式的解释。
这一篇文章,我们一起来学习对多个数字加减法表达式的解释。
例如:12 + 8 – 6 – 10
在编写代码之前,我们先来看一张图。
这张图是语法图。
语法图是表示一种程序设计语言语法规则的示意图。
实际上,语法图体现了语法分析器(Parser)的运行规则。
一张语法图能够直观地体现在我们的程序设计语言中,允许使用哪些语句和不允许使用哪些语句。
那么,怎么阅读这张语法图呢?
我们只需跟随箭头所指的路径去了解程序的运行。
在语法图中,有些路径表示选择,例如:加法表达式和减法表达式需要选择不同的路径。
另外,有的路径表示循环,例如:多次进行加减运算的过程。
为了把这张图看的更明白,我将不同的表达式对应语法图中的路径,做了几张示意图。
其中橙色路径就是当前表达式运行的路径。
例如:
>>>3
当我们只输入一个数字,此时程序只找到了一个操作数,那就只需要把这个操作数传递出去。
例如:
>>>3+6
当我们输入了一个完整的两位数加法表达式,此时程序会先找到一个操作数,然后选择加法操作符,再找到另外一个操作数,最终识别出一个完整的短语传递出去。
例如:
>>>3+5-4-2+6
当我们输入了一个多个数字的加减法表达式,此时程序会先找到一个操作数,然后选择加法操作符,再找到另外一个操作数;此时,程序发现还有新的记号,就开始循环的过程;再次选择操作符,找到操作数,直到找不到新的记号,将完整的短语传递出去。
相信到这里,大家都应该理解了这张语法图的含义。
那么,这里我们看到了一个新的英文单词“Term”,中文含义是“术语”,但是这很难理解。
不要搞那么复杂,在这篇文章中,Term就是一个整数。
根据上面的语法图,以下表达式都是合法的:
- 3
- 5+16
- 33-15+7
实际上,因为在不同的程序设计语言中,算术表达式的语法规则十分相似。
所以,在Python的Shell中,我们运行这些表达式看到的结果也没有什么区别。
然而,对于我们的解释器,表达式“3 + ”并不是一个有效的表达式。
因为,根据语法图,加号后面必须跟着一个Term(整数),否则将会是一个语法错误。
在上一篇文章中,我们知道一连串Token(Token流)中识别出一个短语的过程叫做“Parsing”(语法分析)。一个解释器或者编译器用于语法分析的部分叫“Parser”(语法分析器)。
其实,“Parsing”也叫做“Syntax Analysis”,而“parser ”也叫做“Syntax Analyzer”。
而且,我们也知道“Parser”和“Interpreter”位于“expr()”方法中。
“Parser”只负责识别结构并确保结构符合规范,当“Parser”成功识别(解析)出结构,Interpreter 会对表达式进行求值计算。
根据上面的语法图,我们重写“Parser”部分的代码。
这里我们添加一个新的方法“term()”,用于解析一个整数。
示例代码:
def term(self): self.eat(INTEGER) # 验证整数 def expr(self): self.current_token = self.get_next_token() # 获取记号 self.term() # 验证第一个整数 while self.current_token.value_type in (PLUS, MINUS): # 循环 if self.current_token.value_type == PLUS: # 选择加法运算符 self.eat(PLUS) # 验证运算符 self.term() # 验证下一个整数 if self.current_token.value_type == MINUS: # 选择减法运算符 self.eat(MINUS) # 验证运算符 self.term() # 验证下一个整数
上方代码就是根据语法图重写的代码。
不过很显然,“Parser”不会完成全部解释工作,它正确的识别表达式之后没有任何结果,只有在表达式识别失败的时候会抛出异常。
提示:大家可以尝试运行程序,输入“1+2”和“1+”进行验证。
因为,Interpreter需要表达式的值,所以,我们可以修改“term()”方法,让它返回一个整数值。
并且,我们还要修改“expr()”方法,让它在适当的位置执行加法和减法的计算,并且返回解释的结果。
示例代码:
def term(self): token = self.current_token # 获取记号 self.eat(INTEGER) return token.value # 返回记号中的整数值 def expr(self): self.current_token = self.get_next_token() result = self.term() # 获取第一个整数(Term) while self.current_token.value_type in (PLUS, MINUS): if self.current_token.value_type == PLUS: self.eat(PLUS) result += self.term() # 已有结果加上新获取的整数(Term) if self.current_token.value_type == MINUS: self.eat(MINUS) # 已有结果减去新获取的整数(Term) result -= self.term() return result
到这里,我们的解释器就可以完成多个数字的加减法表达式运算了。
在这篇内容的基础上,大家可以尝试进行以下功能的扩展:
- 画一个只包含乘法和除法的算术表达式语法图,例如“7 * 4 / 2 * 3”;(不要认为很简单就不去做)
- 重写Interpreter类的所有源代码,不要再参考写过的代码和文章,只靠自己的思考,让它可以解释只包含乘法和除法的算术表达式。
而且,当学习完这篇文章,请大家自我检查一下,是否了解了以下内容:
- 什么是语法图?
- 什么是语法分析?
- 什么是语法分析器?
项目源代码下载:【点此下载】
转载请注明:魔力Python » 一起来写个简单的解释器(3)