python 编写简单的程序解释器(四)

本文档介绍了一个逐步演进的过程,从支持两个整数的加减运算,扩展到任意多个整数的加减运算。通过调整解释器类的解析规则,实现了对`NUMBER((PLUS|MINUS)NUMBER)*`模式的支持。优化后的代码中,`eat`函数整合了获取下一个 token 的逻辑,使得代码更加简洁。最后,文章预告了将要添加乘除运算的功能。
摘要由CSDN通过智能技术生成

上文中实现了一个可以支持

  • 两个整数间的加减法
  • 允许空格

的计算器,但这显然太逊了,咱们一步一步来,先把它拓展到支持多个整数相加减

整数计算器 Version 7

先思考一下,如果要支持任意多个整数相加减,这样的输入的元素序列是怎样的?

NUMBER((PLUS|MINUS)NUMBER)*

可以用上述模式表示,其中 | 表示*表示任意个(0个或多个),可正则表达式类似

搞懂了这一点,就可以修改咱们的解释器了,由于token类型不变,所以只需要修改 Interpreter 类

"""
expr: NUMBER((PLUS|MINUS)NUMBER)*
"""
NUMBER, PLUS, MINUS, EOF = 'NUMBER', 'PLUS', 'MINUS', 'EOF'

class Token:
    def __init__(self, token_type, value):
        self.type = token_type
        self.value = value
        
    def __repr__(self,):
        return f'Token({self.type}, {self.value})'
    
class Lexer:
    def __init__(self, text):
        self.text = text
        self.pos = 0
        self.current_char = self.text[self.pos]
        
    def error(self,):
        raise Exception('Lexer Error!')
    
    def get_next_token(self, ):
        
        if self.pos >= len(self.text):
            return Token(EOF, None)
        
        self.current_char = self.text[self.pos]
        
        if self.current_char == ' ':
            while self.current_char == ' ':
                self.pos += 1
                if self.pos >= len(self.text):
                    return Token(EOF, None)
                self.current_char = self.text[self.pos]
        
        if self.current_char.isdigit():
            number = ''
            while self.current_char.isdigit():
                number += self.current_char
                self.pos += 1
                if self.pos >= len(self.text):
                    break
                self.current_char = self.text[self.pos]
            return Token(NUMBER, int(number))
        
        if self.current_char == '+':
            self.pos += 1
            return Token(PLUS, '+')
        
        if self.current_char == '-':
            self.pos += 1
            return Token(MINUS, '-')
        
        self.error()


class Interpreter:
    def __init__(self, lexer):
        self.lexer = lexer
        self.current_token = None
        
    def get_next_token(self,):
        self.current_token = self.lexer.get_next_token()
        return self.current_token
        
    def error(self, msg = ''):
        raise Exception(f'Parse Error! {msg}')
        
    def eat(self, token_type):
        if type(token_type) == list and self.current_token.type not in token_type:
            self.error(f'current token not in list, expect {token_type}, got {self.current_token}.')
        elif type(token_type) == str and self.current_token.type != token_type:
            self.error(f'current token not match, expect {token_type}, got {self.current_token}.')
            
    def expr(self,):
        
        result = 0
        
        term = self.get_next_token()
        self.eat(NUMBER)
        result += term.value

        while(self.get_next_token().type != EOF):
            op = self.current_token
            self.eat([PLUS, MINUS])
            term = self.get_next_token()
            self.eat(NUMBER)
            if op.type == PLUS:
                result += term.value
            else:
                result -= term.value
         
        return result

def main():
    while True:
        try:
            text = input('cal> ')
            lexer = Lexer(text)
            interpreter = Interpreter(lexer)
            print(interpreter.expr())
        except Exception as e:
            print(e)
            break

main()

在这里插入图片描述
咱们支持多个整数相加减的计算器就实现了,

代码实现也很简答,while循环很容易就可以实现任意个整数

整数计算器 Version 8

写代码的时候发现 get_next_token, current_token 交替使用有些别扭,于是再把这一块优化一下
在这里插入图片描述

"""
expr: NUMBER((PLUS|MINUS)NUMBER)*
"""
class Interpreter:
    def __init__(self, lexer):
        self.lexer = lexer
        self.current_token = self.lexer.get_next_token()
        
    def error(self, msg = ''):
        raise Exception(f'Parse Error! {msg}')
        
    def eat(self, token_type):
        if type(token_type) == list and self.current_token.type not in token_type:
            self.error(f'current token not in list, expect {token_type}, got {self.current_token}.')
        elif type(token_type) == str and self.current_token.type != token_type:
            self.error(f'current token not match, expect {token_type}, got {self.current_token}.')
        else:
            self.current_token = self.lexer.get_next_token()
            
    def expr(self,):
        
        result = 0
        
        term = self.current_token
        self.eat(NUMBER)
        result += term.value

        while(self.current_token.type != EOF):
            op = self.current_token
            self.eat([PLUS, MINUS])
            term = self.current_token
            self.eat(NUMBER)
            if op.type == PLUS:
                result += term.value
            else:
                result -= term.value
         
        return result

def main():
    while True:
        try:
            text = input('cal> ')
            lexer = Lexer(text)
            interpreter = Interpreter(lexer)
            print(interpreter.expr())
        except Exception as e:
            print(e)
            break

main()

将 get_next_token 合并到 eat 函数里,这样每次只要调用 current_token + eat 就可以了,代码上整齐一点!

到目前为止,咱们已经实现了能够支持任意个整数相加减的计算器了

接下来给它增加乘除运算,且看下回分解!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值