程序猿阵线联盟-汇总各类技术干货

自己动手写个简单的解释器(二)

2017-12-19  本文已影响0人  大白杏仁

我只是个项目的搬运工,来自国外一个非常有趣的博客

英文原博: Ruslan Spivak
中文翻译可参考:博乐在线

之前第一版只是实现了非常简单的加减法,原博最终完成版就是带括号的加减乘除。

其实实现这个计算器,说难也不难说简单也不简单,最重要的是理解词法分析器和解析器,从而简单地了解解释器的处理过程。在这一版的完善过程中,作者同时讲解了文法语法图的概念,其实这俩东西很强大,在处理逻辑的过程中可以很简单地将文法对应到代码,当项目越复杂,文法越显作用。


"""
1. 任意数量加减乘除
2. 可以有括号
3. 去除运算符与数字间空格

"""

# token类型:integer, '+', '-', '*', '/', '(', ')', end-of-file
INTEGER, PLUS, MINUS, MUL, DIV, LPAREN, RPAREN, EOF = 'INTEGER', 'PLUS', 'MINUS', 'MUL', 'DIV', '(', ')', '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 = self.value
        )

    def __repr__(self):
        return self.__str__()


class Lexer(object):
    """词法分析器"""
    def __init__(self, text):
        self.text = text  # 输入的内容
        self.pos = 0  # 当前内容的索引
        self.current_char = self.text[self.pos]  # 当前字符

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

    def advance(self):
        """后移pos设置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):
        """跳过空格"""
        while self.current_char and self.current_char.isspace():
            self.advance()

    def integer(self):
        """返回多位数字"""
        result = ''
        while self.current_char and self.current_char.isdigit():
            result += self.current_char
            self.advance()
        return int(result)

    def get_next_token(self):
        """获取当前记号"""
        while self.current_char:

            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, '-')

            if self.current_char == '*':
                self.advance()
                return Token(MUL, '*')

            if self.current_char == '/':
                self.advance()
                return Token(DIV, '/')

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

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

            self.error()

        return Token(EOF, None)


class Interpreter(object):
    """解释器类"""
    def __init__(self, lexer):
        self.lexer = lexer
        self.current_token = self.lexer.get_next_token()  # 当前记号

    def error(self):
        # 解析错误时抛出异常
        raise Exception('Invalid syntax')

    def eat(self, token_type):
        """消除与 token_type 类型符合的当前记号,后面的记号前移"""
        if self.current_token.type == token_type:
            self.current_token = self.lexer.get_next_token()
        else:
            self.error()

    def factor(self):
        """
        返回数值并设置当前token
        factor: INTEGER | LPAREN expr RPAREN
        """
        token  = self.current_token
        if token.type == INTEGER:
            self.eat(INTEGER)
            return token.value
        elif token.type == LPAREN:
            self.eat(LPAREN)
            result = self.expr()
            self.eat(RPAREN)
            return result

    def term(self):
        """
        返回优先级高的运算结果
        term: factor((MUL|DIV)factor)*
        """
        result = self.factor()
        while self.current_token.type in (MUL, DIV):
            token = self.current_token
            if token.type == MUL:
                self.eat(MUL)
                result *= self.factor()
            elif token.type == DIV:
                self.eat(DIV)
                result /= self.factor()
        return result


    def expr(self):
        """
        文法:

        expr: term((PLUS|MINUS)term)*
        term: factor((MUL|DIV)factor)*
        factor: INTEGER | LPAREN expr RPAREN
        """
        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
        lexer = Lexer(text)
        interpreter = Interpreter(lexer)
        result = interpreter.expr()
        print(result)
            

if __name__ == '__main__':
    main()

另外,这天真冷~

上一篇 下一篇

猜你喜欢

热点阅读