表達式求值的遞歸實現,順便複習編譯原理

  本次試驗的內容是四則運算——或者說表達式求值,我對此並不陌生,也曾用不一樣語言分別實現過,但都是利用「棧」實現的相關功能,對於這一問題的遞歸實現我仍是第一次嘗試。兩種實現方式各有優劣,這裏再也不展開。git

  程序整體結構圖以下:算法

  詞法分析的做用是將字符序列轉換爲單詞(Token),本次實驗中體如今讀取整數功能,其他部分基本只有檢測非法字符的做用。這部分沒有難度再也不詳述。ide

  

  語法分析的任務是在詞法分析的基礎上將單詞序列組合成各種語法短語,生成抽象語法樹。以表達式(4+3*2)爲例,抽象語法樹形式以下:搜索引擎

  

  這一部分的算法結構圖以下:spa

  

  不難看出來這三種結構是循環定義的,這也是爲何我將之稱爲遞歸方法。code

  最後,本實驗實現的是詞法分析和語法分析兩個部分。也就是說我都講完了,謝謝你們,請你們本身動手實現。blog

  

  講完了原理就是代碼實現了,我最近正好在學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
 

 

  

  能夠看到,基本上就是老師上課講的內容,部分實現可能略有不一樣,邏輯功能基本是同樣的。有幾個部分作了改進,下面詳細說:索引

 

 

 

  

  上圖體現了課上所講程序的幾個漏洞,能夠處理操做數和操做符之間的空格,可是數字中間的空格沒法處理,也沒有返回異常;由合法字符組成無心義序列沒法處理,也沒有返回異常。token

 

  我對此作的改進在上面代碼中都有體現,你本身翻回去看吧。這裏帶你們再回顧一遍。

  第一種狀況:

 

    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')

 

 

  改進後的代碼運行效果:

 

  

  這下真的講完了,可能有些地方講的不夠清楚,請你本身善用搜索引擎,謝謝。請你們見諒。

相關文章
相關標籤/搜索