美文网首页程序猿阵线联盟-汇总各类技术干货
自己动手写个简单的解释器(二)

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

作者: 大白杏仁 | 来源:发表于2017-12-19 23:08 被阅读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()
    
    

    另外,这天真冷~

    相关文章

      网友评论

        本文标题:自己动手写个简单的解释器(二)

        本文链接:https://www.haomeiwen.com/subject/byfbwxtx.html