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

在这里插入图片描述

功能大升级!
这个自己实现的程序解释器已经支持

  • 变量运算
  • 分号间隔多条语句
  • 变量未定义报错

这是从一个普通计算器到高级计算器的重大飞跃!可以参考上一篇文章比较这个版本的代码变化

程序解释器 Version 13

"""
compound_statement: (statement SEMI)*
statement: assign_statement | empty
empty : 
assign_statement: variable ASSIGN expr
expr: term((PLUS|MINUS)term)*
term: factor(MUL|DIV)factor)*
factor: NUMBER| LPAR expr RPAR | (PLUS|MINUS) factor | variable
variable: ID
"""
import json
import copy

NUMBER, PLUS, MINUS, MUL, DIV, LPAR, RPAR, EOF = 'NUMBER', 'PLUS', 'MINUS', 'MUL', 'DIV', '(', ')', 'EOF'
SEMI = 'SEMI'
ASSIGN = 'ASSIGN'
ID = 'ID'

class AST:
    pass

class CompoundStatement(AST):
    def __init__(self, node_type, statement_list):
        self.type = node_type
        self.children = statement_list
        
    def __repr__(self):
        return f'{{"type":"{self.type}", "children":{self.children}}}'
    
    __str__ = __repr__
    
        

class BinOp(AST):
    def __init__(self, node_type, left_node, right_node):
        self.type = node_type
        self.left_node = left_node
        self.right_node = right_node
        
    def __repr__(self):
        return f'{{"type":"{self.type}", "left":{self.left_node}, "right":{self.right_node}}}'
    
    __str__ = __repr__

class UniOp(AST):
    def __init__(self, node_type, child_node):
        self.type = node_type
        self.child_node = child_node
        
    def __repr__(self):
        return f'{{"type":"{self.type}", "child":{self.child_node}}}'
    
    __str__ = __repr__

class Leaf(AST):
    def __init__(self, node_type, value):
        self.type = node_type
        self.value = value
        
    def __repr__(self):
        return f'{{"type":"{self.type}", "value":{self.value}}}'
    
    __str__ = __repr__

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 copy(self,):
        l = Lexer(self.text)
        l.pos = self.pos
        
    def error(self,):
        raise Exception('Lexer Error!')
        
    def advance(self):
        self.pos += 1
        if self.pos >= len(self.text):
            self.current_char = None
        else:
            self.current_char = self.text[self.pos]
            
    def skip_space(self):
        if self.current_char == ' ':
            while self.current_char == ' ':
                self.advance()
                
    def get_next_token(self, ):
        
        self.skip_space()
        
        if self.current_char is None:
            return Token(EOF, None)
        
        if self.current_char.isdigit():
            number = ''
            while self.current_char is not None and self.current_char.isdigit():
                number += self.current_char
                self.advance()
            return Token(NUMBER, int(number))
        
        if self.current_char.isalpha():
            _id = ''
            while self.current_char is not None and self.current_char.isalnum():
                _id += self.current_char
                self.advance()
            return Token(ID, _id)
        
        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(LPAR, '(')
        
        if self.current_char == ')':
            self.advance()
            return Token(RPAR, ')')
        
        if self.current_char == ';':
            self.advance()
            return Token(SEMI, ';')
        
        if self.current_char == '=':
            self.advance()
            return Token(ASSIGN, '=')
        
        self.error()

class Parser:
    """
    statement_list: (statement SEMI)*
    statement: assign_statement | empty
    empty: 
    assign_statement: variable ASSIGN expr
    expr: term((PLUS|MINUS)term)*
    term: factor(MUL|DIV)factor)*
    factor: NUMBER| LPAR expr RPAR | (PLUS|MINUS) factor | variable
    variable: ID
    """
    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 peek(self,):
        if self.current_token is None:
            return None
        return copy.copy(self.lexer).get_next_token()
        
        
    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 variable(self,):
        f = self.current_token
        self.eat(ID)
        return Leaf(ID, f.value)
            
    def factor(self,):
        f = self.current_token
        
        if f.type == NUMBER:
            self.eat(NUMBER)
            return Leaf(NUMBER, f.value)
        
        elif f.type == LPAR:
            self.eat(LPAR)
            expr = self.expr()
            self.eat(RPAR)
            return expr
        
        elif f.type in [PLUS, MINUS]:
            self.eat(f.type)
            return UniOp(f.type, self.factor())
        
        else:
            return self.variable()
        
        self.error()
    
    def term(self,):
        """
        term: factor(MUL|DIV)factor)*
        """
        node = self.factor()
        while(self.current_token.type in [MUL, DIV]):
            op = self.current_token
            self.eat([MUL, DIV])
            f = self.factor()
            _node = BinOp(op.type, node, f)
            node = _node
                
        return node
          
    def expr(self,):
        
        node = self.term()

        while(self.current_token.type in [PLUS, MINUS]):
            op = self.current_token
            self.eat([PLUS, MINUS])
            t = self.term()
            _node = BinOp(op.type, node, t)
            node = _node
            
        return node
    
    def assign_statement(self,):
        """
        assign_statement: VAR ASSIGN expr
        """
        var = self.variable()
        op = self.current_token
        self.eat(ASSIGN)
        expr_node = self.expr()
        return BinOp(ASSIGN, var, expr_node)
    
    def empty(self):
        return Leaf('EMPTY', None)
    
    def statement(self,):
        """
        statement: assign_statement | empty
        """
            
        if self.current_token.type == ID and self.peek().type == ASSIGN:
            return self.assign_statement()
        else:
            return self.empty()

    
    def compound_statement(self):
        node = self.statement()
        node_list = [node]
        while self.current_token.type != EOF:
            self.eat(SEMI)
            node_list.append(self.statement())
        return CompoundStatement('CompoundStatement', node_list)
    
    def ast(self):
        return self.compound_statement()
        

class Interpreter:
    def __init__(self, parser):
        self.parser = parser
        self.GLOBAL_SCOPE = {}
        
    def error(self, msg=''):
        raise Exception(f'Interpeter Error! {msg}')

    def compute(self):
        ast = self.parser.ast()
        self.visit_node(ast)
        return self.GLOBAL_SCOPE

    
    def visit_BinOp(self, node):
        if node.type == PLUS:
            return self.visit_node(node.left_node) + self.visit_node(node.right_node)       
        if node.type == MINUS:
            return self.visit_node(node.left_node) - self.visit_node(node.right_node)
        if node.type == MUL:
            return self.visit_node(node.left_node) * self.visit_node(node.right_node)
        if node.type == DIV:
            return self.visit_node(node.left_node) / self.visit_node(node.right_node)

    def visit_UniOp(self, node):
        if node.type == PLUS:
            return self.visit_node(node.child_node)
        if node.type == MINUS:
            return - self.visit_node(node.child_node)
    
    def visit_statement(self, node):
        if node.type == ASSIGN:
            var = node.left_node
            expr_result = self.visit_node(node.right_node)
            self.GLOBAL_SCOPE[var.value] = expr_result
    
    def visit_CompoundStatement(self, node):
        for statement in node.children:
            self.visit_statement(statement)
            
    def visit_Leaf(self, node):
        if node.type == NUMBER:
            return node.value
        elif node.type == ID:
            if node.value in self.GLOBAL_SCOPE:
                return self.GLOBAL_SCOPE[node.value]
            else:
                self.error(f"variable '{node.value}' not defined!")

    def visit_node(self, node): 
        if isinstance(node, Leaf):
            return self.visit_Leaf(node)
        
        elif isinstance(node, BinOp):
            return self.visit_BinOp(node)
        
        elif isinstance(node, UniOp):
            return self.visit_UniOp(node)
        
        elif isinstance(node, CompoundStatement):
            self.visit_CompoundStatement(node)

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

main()

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值