栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Python

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

Python 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

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

本系列的原始英文教程地址:https://ruslanspivak.com/lsbasi-part9/

本文的代码和原作的可能略有出入,因为是博主一行一行敲出来的,原作者也鼓励大家亲自动手实现一遍,才能真正理解编译器与解释器的真谛!因此建议大家来理解了思路之后,从最基本的计算器开始写起:支持10以内加法 => 支持任意整数加法 => 任意整数加减法 => 任意整数加减乘除 => 支持括号 => 支持一元运算 => 支持变量 => 支持关键字 => … 一路下来真的收获满满!

本文开始正式进行程序语言解释器的开发,目标语言为 pascal!实现对如下代码的解析与执行:

BEGIN
    BEGIN
        number := 2;
        a := number;
        b := 10 * a + 10 * number / 4;
        c := a - - b
    END;
    x := 11;
END.

有了前面一系列文章的铺垫,在上一篇的支持变量的多行程序解释器的基础之上,实现起来真的很简单,语法规则中无非是增加了关键字 (BEGIN,END,DOT)

program : compound_statement DOT

compound_statement : BEGIN statement_list END

statement_list : statement
               | statement SEMI statement_list

statement : compound_statement
          | assignment_statement
          | empty

assignment_statement : variable ASSIGN expr

empty :

expr: term ((PLUS | MINUS) term)*

term: factor ((MUL | DIV) factor)*

factor : PLUS factor
       | MINUS factor
       | INTEGER
       | LPAREN expr RPAREN
       | variable

variable: ID

实现效果如下:

srcCode>  
.......> BEGIN
.......>     BEGIN
.......>         number := 2;
.......>         a := number;
.......>         b := 10 * a + 10 * number / 4;
.......>         c := a - - b
.......>     END;
.......>     x := 11;
.......> END.
.......> 
{'number': 2, 'a': 2, 'b': 25.0, 'c': 27.0, 'x': 11}

程序解释器 Version 14
import json
import copy

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

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:
    reserved_words = [BEGIN, END]
    
    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 peek(self):
        if 1 + self.pos >= len(self.text):
            return None
        return self.text[self.pos + 1]
    
    def error(self, msg=''):
        raise Exception(f'Lexer Error! {msg}')
        
    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):
        blank_chars = [' ', 'n', 't']
        if self.current_char in blank_chars:
            while self.current_char in blank_chars:
                self.advance()
                
    def _id(self,):
        var_id = ''
        while self.current_char is not None and self.current_char.isalnum():
            var_id += self.current_char
            self.advance()
        
        if var_id.upper() in self.reserved_words:
            return Token(var_id.upper(),var_id.upper())
            
        return Token(ID, var_id)
                
    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():
            return self._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 == ':' and self.peek() == '=':
            self.advance()
            self.advance()
            return Token(ASSIGN, '=')
        
        if self.current_char == '.':
            self.advance()
            return Token(DOT, '.')
        
        self.error(f'current_char: {self.current_char}')

class Parser:
    """
    statement_list: (statement SEMI)*
    statement: assign_statement | expr | empty
    empty: 
    assign_statement: VAR 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 == BEGIN:
            return self.compound_statement()
        elif self.current_token.type == ID and self.peek().type == ASSIGN:
            return self.assign_statement()
        else:
            return self.empty()

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

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
            
        elif isinstance(node, CompoundStatement):
            self.visit_CompoundStatement(node)
    
    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)

测试用例

test_cases = []
test_cases.append("""
BEGIN
    BEGIN
        number := 2;
        a := number;
        b := 10 * a + 10 * number / 4;
        c := a - - b
    END;
    x := 11;
END.
""")
test_cases.append("""
BEGIN
    BEGIN
        number := 2;
        a := number;
        b := 10 * a + 10 * number / 4;
        c := a - - b
    END;
    BEGIN
        number2 := c;
        a := number2;
        b := 10 * a + 10 * number2 / 4;
        c := a - - b
    END;
    x := number + number2
END.
""")

for text in test_cases:
    try:
        print('srcCode> ', text.replace('n', 'n.......> '))
        if text:
            lexer = Lexer(text)
            parser = Parser(lexer)
            interpreter = Interpreter(parser)
            print(interpreter.compute())
            print()

    except Exception as e:
        print(e)
        print()
转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/1022259.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号