本次試驗的內容是四則運算——或者說表達式求值,我對此並不陌生,也曾用不同語言分別實現過,但都是利用“棧”實現的相關功能,對於這一問題的遞歸實現我還是第一次嘗試。兩種實現方式各有優劣,這里不再展開。
程序總體結構圖如下:
詞法分析的作用是將字符序列轉換為單詞(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')
改進后的代碼運行效果:
這下真的講完了,可能有些地方講的不夠清楚,請你自己善用搜索引擎,謝謝。請大家見諒。