本文小編為大家詳細(xì)介紹“怎么使用Python實(shí)現(xiàn)一個(gè)簡(jiǎn)單的四則運(yùn)算解釋器”,內(nèi)容詳細(xì),步驟清晰,細(xì)節(jié)處理妥當(dāng),希望這篇“怎么使用Python實(shí)現(xiàn)一個(gè)簡(jiǎn)單的四則運(yùn)算解釋器”文章能幫助大家解決疑惑,下面跟著小編的思路慢慢深入,一起來(lái)學(xué)習(xí)新知識(shí)吧。
創(chuàng)新互聯(lián)-專業(yè)網(wǎng)站定制、快速模板網(wǎng)站建設(shè)、高性價(jià)比平邑網(wǎng)站開發(fā)、企業(yè)建站全套包干低至880元,成熟完善的模板庫(kù),直接使用。一站式平邑網(wǎng)站制作公司更省心,省錢,快速模板網(wǎng)站建設(shè)找我們,業(yè)務(wù)覆蓋平邑地區(qū)。費(fèi)用合理售后完善,十年實(shí)體公司更值得信賴。
這里先展示了程序的幫助信息,然后是幾個(gè)簡(jiǎn)單的四則運(yùn)算測(cè)試,看起來(lái)是沒問(wèn)題了(我可不敢保證,程序沒有bug?。?。
這個(gè)格式化的 JSON 信息太長(zhǎng)了,不利于直接看到。我們將它渲染出來(lái)看最后生成的樹形圖。保存下面這個(gè) JSON 在一個(gè)文件中,這里我叫做 demo.json,然后執(zhí)行如下命令:pytm-cli -d LR -i demo.json -o demo.html
,然后再瀏覽器打開生成的 html 文件。
所有的代碼都在這里了,只需要一個(gè)文件 my_eval.py
,想要運(yùn)行的話,復(fù)制、粘貼,然后按照演示的步驟執(zhí)行即可。
Node、BinOp、Constan 是用來(lái)表示節(jié)點(diǎn)的類.
Calculator 中 lexizer 方法是進(jìn)行分詞的,本來(lái)我是打算使用正則的,如果你看過(guò)我前面的博客的話,可以發(fā)現(xiàn)我是用的正則來(lái)分詞的(因?yàn)?Python 的官方文檔正則表達(dá)式中有一個(gè)簡(jiǎn)易的分詞程序)。不過(guò)我看其他人都是手寫的分詞,所以我也這樣做了,不過(guò)感覺并不是很好,很繁瑣,而且容易出錯(cuò)。
parse 方法是進(jìn)行解析的,主要是解析表達(dá)式的結(jié)構(gòu),判斷是否符合四則運(yùn)算的文法,最終生成表達(dá)式樹(它的 AST)。
""" Grammar G -> E E -> T E' E' -> '+' T E' | '-' T E' | ? T -> F T' T' -> '*' F T' | '/' F T' | ? F -> '(' E ')' | num | name """ import json import argparse class Node: """ 簡(jiǎn)單的抽象語(yǔ)法樹節(jié)點(diǎn),定義一些需要使用到的具有層次結(jié)構(gòu)的節(jié)點(diǎn) """ def eval(self) -> float: ... # 節(jié)點(diǎn)的計(jì)算方法 def visit(self): ... # 節(jié)點(diǎn)的訪問(wèn)方法 class BinOp(Node): """ BinOp Node """ def __init__(self, left, op, right) -> None: self.left = left self.op = op self.right = right def eval(self) -> float: if self.op == "+": return self.left.eval() + self.right.eval() if self.op == "-": return self.left.eval() - self.right.eval() if self.op == "*": return self.left.eval() * self.right.eval() if self.op == "/": return self.left.eval() / self.right.eval() return 0 def visit(self): """ 遍歷樹的各個(gè)節(jié)點(diǎn),并生成 JSON 表示 """ return { "name": "BinOp", "children": [ self.left.visit(), { "name": "OP", "children": [ { "name": self.op } ] }, self.right.visit() ] } class Constant(Node): """ Constant Node """ def __init__(self, value) -> None: self.value = value def eval(self) -> float: return self.value def visit(self): return { "name": "NUMBER", "children": [ { "name": str(self.value) # 轉(zhuǎn)成字符是因?yàn)殇秩境蓤D像時(shí),需要該字段為 str } ] } class Calculator: """ Simple Expression Parser """ def __init__(self, expr) -> None: self.expr = expr # 輸入的表達(dá)式 self.parse_end = False # 解析是否結(jié)束,默認(rèn)未結(jié)束 self.toks = [] # 解析的 tokens self.index = 0 # 解析的下標(biāo) def lexizer(self): """ 分詞 """ index = 0 while index < len(self.expr): ch = self.expr[index] if ch in [" ", "\r", "\n"]: index += 1 continue if '0' <= ch <= '9': num_str = ch index += 1 while index < len(self.expr): n = self.expr[index] if '0' <= n <= '9': if ch == '0': raise Exception("Invalid number!") num_str = n index += 1 continue break self.toks.append({ "kind": "INT", "value": int(num_str) }) elif ch in ['+', '-', '*', '/', '(', ')']: self.toks.append({ "kind": ch, "value": ch }) index += 1 else: raise Exception("Unkonwn character!") def get_token(self): """ 獲取當(dāng)前位置的 token """ if 0 <= self.index < len(self.toks): tok = self.toks[self.index] return tok if self.index == len(self.toks): # token解析結(jié)束 return { "kind": "EOF", "value": "EOF" } raise Exception("Encounter Error, invalid index = ", self.index) def move_token(self): """ 下標(biāo)向后移動(dòng)一位 """ self.index += 1 def parse(self) -> Node: """ G -> E """ # 分詞 self.lexizer() # 解析 expr_tree = self.parse_expr() if self.parse_end: return expr_tree else: raise Exception("Invalid expression!") def parse_expr(self): """ E -> T E' E' -> + T E' | - T E' | ? """ # E -> E E' left = self.parse_term() # E' -> + T E' | - T E' | ? while True: tok = self.get_token() kind = tok["kind"] value = tok["value"] if tok["kind"] == "EOF": # 解析結(jié)束的標(biāo)志 self.parse_end = True break if kind in ["+", "-"]: self.move_token() left = BinOp(left, value, self.parse_term()) else: break return left def parse_term(self): """ T -> F T' T' -> * F T' | / F T' | ? """ # T -> F T' left = self.parse_factor() # T' -> * F T' | / F T' | ? while True: tok = self.get_token() kind = tok["kind"] value = tok["value"] if kind in ["*", "/"]: self.move_token() right = self.parse_factor() left = BinOp(left, value, right) else: break return left def parse_factor(self): """ F -> '(' E ')' | num | name """ tok = self.get_token() kind = tok["kind"] value = tok["value"] if kind == '(': self.move_token() expr_node = self.parse_expr() if self.get_token()["kind"] != ")": raise Exception("Encounter Error, expected )!") self.move_token() return expr_node if kind == "INT": self.move_token() return Constant(value=value) raise Exception("Encounter Error, unknown factor: ", kind) if __name__ == "__main__": # 添加命令行參數(shù)解析器 cmd_parser = argparse.ArgumentParser( description="Simple Expression Interpreter!") group = cmd_parser.add_mutually_exclusive_group() group.add_argument("--tokens", help="print tokens", action="store_true") group.add_argument("--ast", help="print ast in JSON", action="store_true") cmd_parser.add_argument( "expr", help="expression, contains ['+', '-', '*', '/', '(', ')', 'num']") args = cmd_parser.parse_args() calculator = Calculator(expr=args.expr) tree = calculator.parse() if args.tokens: # 輸出 tokens for t in calculator.toks: print(f"{t['kind']:3s} ==> {t['value']}") elif args.ast: # 輸出 JSON 表示的 AST print(json.dumps(tree.visit(), indent=4)) else: # 計(jì)算結(jié)果 print(tree.eval())
讀到這里,這篇“怎么使用Python實(shí)現(xiàn)一個(gè)簡(jiǎn)單的四則運(yùn)算解釋器”文章已經(jīng)介紹完畢,想要掌握這篇文章的知識(shí)點(diǎn)還需要大家自己動(dòng)手實(shí)踐使用過(guò)才能領(lǐng)會(huì),如果想了解更多相關(guān)內(nèi)容的文章,歡迎關(guān)注創(chuàng)新互聯(lián)行業(yè)資訊頻道。