『我不生产代码,我只是代码的搬运工。』当然了,这是一个玩笑。说到代码,我们要学习各种编程语言,学习如何让编译器能懂我们编写的代码。但是,编译器是如何做到能听懂我们的话的呢?按照我们既定的语句一行行的去执行,最终达到我们的目的。这篇文章,我会讲一个很简单的四则运算解释器,通过使用 Python 实现它来一步步了解一个解释器是如何工作的,它们又是怎么得到我们想要的结果的。
语法
计算机语言同样也是一种语言,是一种计算机能够理解的语言,我们想要和计算机对话,就要去学习这种语言。和我们人类的语言一样,计算机语言也是有语法的。以汉语为例,我说『我是中国人』,因为汉语的语法,你听到这句话之后会知道『我』是主语,『是』是谓语,『中国人』是宾语,同时你又很清楚每一个成分的含义,最终理解了整句话的意思。
同样,对于计算机语言也是一样,有着程序的语法,一个解释器知道哪个词是操作数,哪个是操作符,哪个是关键字,它们都有着怎样的含义和功能,通过解释器的解释,计算机明白了某行语句的意义,然后进行运算,得到最后的执行结果。
语法图
语法图就是用来表示一种编程语言语法规则设计的示意图,它很直观的显示出了在一种编程语言中,允许使用的语句和不支持的语句。语法图十分易于阅读:只需跟随箭头指示的路径。一些路径表示选择。另一些路径表示循环。
一个简单的语法图
这里我们举一个语法图的例子:
这个语法图就是一个描述了简单的加减运算的语法图,term 在其中的意思就是一个操作数,一开始输入一个操作数,有三条路径可以选择『+』,『-』和退出,如果进入了『+』、『-』路径,则需要再输入一个操作数,之后的路径包括『+』、『-』和退出,如此循环,就能解释一个简单的加减法解释器。
根据上边的语法图,下面的表达式都是合法的:
- 4
- 1 + 1
- 1 + 4 - 3
下面的表达式不合法:
- -
- + -
- 2 +
- + 3 - 3
语法图可以帮助我们:
- 用图的方式表示出一种计算机语言的设计规范
- 可以帮助理解解释器,将图表表示成代码
代码实现(Python)
学习一样东西最简单的就是阅读代码(Read the Fucking Code!),因此我们先来看完整代码然后再来分析一下,完整代码在:https://github.com/luoyhang003/Pascal-Interpreter/blob/master/calc3.py
# Token Types:
# EOF: End-Of-File
INTEGER, PLUS, MINUS, EOF = 'INTEGER', 'PLUS', 'MINUS', 'EOF'
class Token(object):
def __init__(self, type, value):
# Token types: INTEGER, PLUS, MINUS, EOF
self.type = type
# Token value: 0,1,2,3,4,5,6,7,8,9,+,None
self.value = value
def __str__(self):
"""
The format of the print infomation
For examle:
Token(INTEGER, 3)
Token(PLUS, '+')
Token(MINUS, '-')
"""
return 'Token({type},{value})'.format(
type = self.type,
value = repr(self.value)
)
def __repr__(self):
return self.__str__()
class Interpreter(object):
def __init__(self, text):
# Process the whole input
# e.g. 3+5
self.text = text
self.pos = 0
self.current_token = None
self.current_char = self.text[self.pos]
def error(self):
raise Exception('Error Parsing Error!')
def advance(self):
# Advance the pos and set current_char
self.pos += 1
if self.pos > len(self.text)-1:
self.current_char = None
else:
self.current_char = self.text[self.pos]
def skip_whitespace(self):
# Skip space
while self.current_char is not None and self.current_char.isspace():
self.advance()
def integer(self):
# Support mutidigit integer
result = ''
while self.current_char is not None and self.current_char.isdigit():
result += self.current_char
self.advance()
return int(result)
def get_next_token(self):
"""
Lexical Analyzer:
Parsing the input into tokens.
Strategy:
1. is pos past the end of the text?
2. if so, return EOF
3. get a character at pos, and decide its type depends on the single char
4. if it is a space, advance the pos
5. if it is a digit, then convert it to integer and return INTEGER token
6. if it is a '+', then return PLUS token
7. if it is a '-', then return MINUS