表达式求值的递归实现,顺便复习编译原理

Posted OJBK

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了表达式求值的递归实现,顺便复习编译原理相关的知识,希望对你有一定的参考价值。

  本次试验的内容是四则运算——或者说表达式求值,我对此并不陌生,也曾用不同语言分别实现过,但都是利用“栈”实现的相关功能,对于这一问题的递归实现我还是第一次尝试。两种实现方式各有优劣,这里不再展开。

  程序总体结构图如下:

  词法分析的作用是将字符序列转换为单词(Token),本次实验中体现在读取整数功能,其余部分基本只有检测非法字符的作用。这部分没有难度不再详述。

  

  语法分析的任务是在词法分析的基础上将单词序列组合成各类语法短语,生成抽象语法树。以表达式(4+3*2)为例,抽象语法树形式如下:

  

  这一部分的算法结构图如下:

  

  不难看出来这三种结构是循环定义的,这也是为什么我将之称为递归方法。

  最后,本实验实现的是词法分析和语法分析两个部分。也就是说我都讲完了,谢谢大家,请大家自己动手实现。

  

  讲完了原理就是代码实现了,我最近正好在学Python,就当练习了。完整代码如下:

  

INTEGER, PLUS, MINUS, MUL, DIV, LPAREN, RPAREN, EOF =(
    \'INTEGER\', \'PLUS\', \'MINUS\', \'MUL\', \'DIV\', \'LPAREN\', \'RPAREN\', \'EOF\')

class Token:
    def __init__(self,type,value):
        self.type = type
        self.value = value
        
    def __str__(self):
        return \'Token(%s, %s)\' % (self.type,self.value)
    
    __repr__ = __str__

class Lexer:
    def __init__(self, text):
        self.text = text.strip()
        self.pos = 0
        self.current_char = self.text[0]

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

    def step_forward(self):
        self.pos += 1
        while self.pos < len(self.text):
            if self.text[self.pos].isspace():
                self.pos += 1
                continue
            else:
                self.current_char = self.text[self.pos]
                return
        self.current_char = None

    def get_next_token(self):
        if self.current_char == None:
            return Token(EOF,None)
        
        if self.current_char.isdigit():
            value = \'\'
            while True:
                value += self.current_char
                self.step_forward()
                if (self.current_char == None) or (not self.current_char.isdigit()):
                    return Token(INTEGER,value)

        if self.current_char == \'+\':
            self.step_forward()
            return Token(PLUS,\'+\')
        
        if self.current_char == \'-\':
            self.step_forward()
            return Token(MINUS,\'-\')
        
        if self.current_char == \'*\':
            self.step_forward()
            return Token(MUL,\'*\')
        
        if self.current_char == \'/\':
            self.step_forward()
            return Token(DIV,\'/\')
        
        if self.current_char == \'(\':
            self.step_forward()
            return Token(LPAREN,\'(\')
        
        if self.current_char == \')\':
            self.step_forward()
            return Token(RPAREN,\')\')

        self.error()

class Interpreter:
    def __init__(self,lexer):
        self.lexer = lexer
        self.current_token = self.lexer.get_next_token()

    def error(self):
        raise Exception(\'Invalid syntax\')

    def check_token(self, token_type):
        if self.current_token.type == token_type:
            self.current_token = self.lexer.get_next_token()
        else:
            self.error()
 
    def factor(self):
        token = self.current_token
        
        if token.type == INTEGER:
            self.check_token(INTEGER)
            return float(token.value)
        elif token.type == LPAREN:
            self.check_token(LPAREN)
            result = self.expr()
            self.check_token(RPAREN)
            return float(result)

        self.error()
 
    def term(self):
        result = self.factor()
        while self.current_token.type in (MUL,DIV):
            if self.current_token.type == MUL:
                self.check_token(MUL)
                result *= self.factor()
            elif self.current_token.type == DIV:
                self.check_token(DIV)
                result /= self.factor()
                
        return float(result)
 
    def expr(self):
        result = self.term()
        while self.current_token.type in (PLUS,MINUS):
            if self.current_token.type == PLUS:
                self.check_token(PLUS)
                result += self.term()
            elif self.current_token.type == MINUS:
                self.check_token(MINUS)
                result -= self.term()
        return float(result)     

if __name__ == \'__main__\':
    while True:
        try:
            text = input(\'>>>calc \')
            if text:
                interpreter = Interpreter(Lexer(text))
                result = interpreter.expr()
                if interpreter.current_token.type == EOF:
                    print(result)
                else:
                    raise Exception(\'Invalide syntax\')
        except Exception as e:
            print(e)
            continue
 

 

  

  可以看到,基本上就是老师上课讲的内容,部分实现可能略有不同,逻辑功能基本是一样的。有几个部分做了改进,下面详细说:

 

 

 

  

  上图体现了课上所讲程序的几个漏洞,可以处理操作数和操作符之间的空格,但是数字中间的空格无法处理,也没有返回异常;由合法字符组成无意义序列无法处理,也没有返回异常。

 

  我对此做的改进在上面代码中都有体现,你自己翻回去看吧。这里带大家再回顾一遍。

  第一种情况:

 

    def step_forward(self):
        self.pos += 1
        while self.pos < len(self.text):
            if self.text[self.pos].isspace():
                self.pos += 1
                continue
            else:
                self.current_char = self.text[self.pos]
                return
        self.current_char = None

  

  第二种情况:

 

interpreter = Interpreter(Lexer(text))
result = interpreter.expr()
if interpreter.current_token.type == EOF:
    print(result)
else:
    raise Exception(\'Invalide syntax\')

 

 

  改进后的代码运行效果:

 

  

  这下真的讲完了,可能有些地方讲的不够清楚,请你自己善用搜索引擎,谢谢。请大家见谅。

以上是关于表达式求值的递归实现,顺便复习编译原理的主要内容,如果未能解决你的问题,请参考以下文章

IntelliJ:求值lambda表达式在调试时引发编译错误

中缀表达式求值的思路分析与代码实现

中缀表达式求值的思路分析与代码实现

六千字快速复习七种常用排序

六千字快速复习七种常用排序

使用 Horner 方法进行多项式求值的 C++ constexpr