1.介绍

本系列文章参照该教程学习总结,欢迎查看原文。我个人的学习代码已经放到我的github
词法分析、语法分析、语义分析等定义请参考编译器解释器中的词法分析、语法分析、语义分析

英文原作者所有源代码查看

2.概念补充巩固

2.1 语法图

语法图:语法图是指表示一种程序设计语言语法规则的示意图。本质上,一个语法图直观地显示了在你的程序设计语言中,允许使用哪些语句和不允许使用哪些语句。
语法图的两个主要目的:

它们以图表的形式表示一种程序设计语言的规范(语法)。
它们可以帮助你编写解析器,你可以通过遵循简单的规则将一个图表映射成代码。

上图中term指的是输入表达式当中的token,以上的语法图改写成代码为:

def term(self):         #匹配期望类型
    self.eat(INTEGER)   # 注意eat过程后current_token指向下一个token

def expr(self):             # 词法分析器的代码段(parsing)

# 当前的token设置成输入的第一个token
    self.current_token = self.get_next_token()

    self.term() #token被term方法判断是否符号支持的表达式
    while self.current_token.type in (PLUS, MINUS):     # 在第一个token经过term处理后开始处理符号
        token = self.current_token
        if token.type == PLUS:
            self.eat(PLUS)      # 匹配操作符
            self.term()         # 匹配右值
        elif token.type == MINUS:
            self.eat(MINUS)       # 匹配操作符
            self.term()          # 匹配右值

以上展现的词法分析器在分析后还没有解释运行,所以修改代码增加一部分代码去调用词法分析器完成结果的计算

def term(self):

"""Return an INTEGER token value"""
    token = self.current_token
    self.eat(INTEGER)
    return token.value

def expr(self):

"""Parser / Interpreter 词法分析器的代码,包括了解释运行 """

# set current token to the first token taken from the input
    self.current_token = self.get_next_token()

    result = self.term()
    while self.current_token.type in (PLUS, MINUS):
        token = self.current_token
        if token.type == PLUS:
            self.eat(PLUS)
            result = result + self.term()
        elif token.type == MINUS:
            self.eat(MINUS)
            result = result - self.term()

    return result

新版解释器的完整代码如下,通过该代码了解lexer、parsing、tokens等概念.

calc5.py代码如下

'''第三篇中作者给出的代码,用于完成整数的计算,支持长式子'''

INTEGER, PLUS, MINUS, EOF = 'INTEGER', 'PLUS', 'MINUS', 'EOF'


class Token(object):

    def __init__(self, type, value):
        self.type = type
        self.value = value

    def __str__(self):
        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):
        self.text = text
        self.pos = 0
        self.current_token = None
        self.current_char = self.text[self.pos]

##############################################
# 词法分析器部分代码,完成token识别           #
# ############################################

    def error(self):
        raise Exception('Invalid syntax')

    def advance(self):
        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):
        while self.current_char is not None and self.current_char.isspace():
            self.advance()

    def integer(self):
        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):
        while self.current_char is not None:
            if self.current_char.isspace():
                self.skip_whitespace()
                continue

            if self.current_char.isdigit():
                return Token(INTEGER, self.integer())

            if self.current_char == '+':
                self.advance()
                return Token(PLUS, '+')

            if self.current_char == '-':
                self.advance()
                return Token(MINUS, '-')

            self.error()

        return Token(EOF, None)

############################################
#   语法分析(parsing)和解释运行的代码    #
############################################

    def eat(self, token_type):
        if self.current_token.type == token_type:
            self.current_token = self.get_next_token()
        else:
            self.error()

    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()
        while self.current_token.type in (PLUS, MINUS):
            token = self.current_token
            if token.type == PLUS:
                self.eat(PLUS)
                result += self.term()
            elif token.type == MINUS:
                self.eat(MINUS)
                result -= self.term()

        return result


def main():
    while True:
        try:
            text = input('calc>')
        except EOFError:
            break
        if not text:
            continue
        interpreter = Interpreter(text)
        result = interpreter.expr()
        print(result)

if __name__ == '__main__':
    main()

3.练习

关于实现乘除和加减法的解释器我们已经在calc.4.py中实现过了,这里不再赘述。语法图把本文中那张图中的加减号换成乘除号就好了。