Rumah > Artikel > pembangunan bahagian belakang > Menggunakan Python untuk melaksanakan empat penterjemah aritmetik mudah
Berikut ialah maklumat bantuan program, dan kemudian beberapa ujian operasi empat aritmetik yang mudah Nampaknya tiada masalah (saya tidak dapat menjamin bahawa program ini tidak mempunyai pepijat! ).
Mesej JSON yang diformatkan ini terlalu panjang untuk tontonan langsung. Kami memberikannya untuk melihat gambar rajah pokok yang dijana akhir (lihat dua blog sebelumnya untuk kaedah). Simpan JSON berikut dalam fail, di sini saya memanggilnya demo.json, dan kemudian laksanakan arahan berikut: pytm-cli -d LR -i demo.json -o demo.html
, dan kemudian buka fail html yang dijana dalam penyemak imbas.
Semua kod ada di sini, anda hanya perlukan satu fail my_eval.py
untuk dijalankan Jika ya, hanya salin, tampal, dan ikuti langkah-langkah dalam demonstrasi.
Node, BinOp dan Constan ialah kelas yang digunakan untuk mewakili nod.
Kaedah lexizer dalam Kalkulator digunakan untuk pembahagian perkataan Pada asalnya, saya bercadang untuk menggunakan regularization Jika anda telah membaca blog saya sebelum ini bolehkah saya mendapati bahawa saya menggunakan ungkapan biasa untuk membahagikan perkataan (kerana terdapat program pembahagian perkataan yang mudah dalam ungkapan biasa dokumentasi rasmi Python). Tetapi saya melihat bahawa orang lain menulis participles dengan tangan, jadi saya melakukan perkara yang sama, tetapi ia tidak terasa sangat baik, ia sangat membosankan dan terdedah kepada kesilapan.
Kaedah penghuraian adalah untuk menghurai, terutamanya untuk menganalisis struktur ungkapan, menentukan sama ada ia mematuhi tatabahasa empat operasi aritmetik, dan akhirnya menjana pepohon ungkapan (ASTnya).
""" 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: """ 简单的抽象语法树节点,定义一些需要使用到的具有层次结构的节点 """ def eval(self) -> float: ... # 节点的计算方法 def visit(self): ... # 节点的访问方法 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): """ 遍历树的各个节点,并生成 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) # 转成字符是因为渲染成图像时,需要该字段为 str } ] } class Calculator: """ Simple Expression Parser """ def __init__(self, expr) -> None: self.expr = expr # 输入的表达式 self.parse_end = False # 解析是否结束,默认未结束 self.toks = [] # 解析的 tokens self.index = 0 # 解析的下标 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): """ 获取当前位置的 token """ if 0 <= self.index < len(self.toks): tok = self.toks[self.index] return tok if self.index == len(self.toks): # token解析结束 return { "kind": "EOF", "value": "EOF" } raise Exception("Encounter Error, invalid index = ", self.index) def move_token(self): """ 下标向后移动一位 """ 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": # 解析结束的标志 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__": # 添加命令行参数解析器 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: # 计算结果 print(tree.eval())
Saya pada asalnya ingin bercakap tentang mengapa ia dipanggil my_eval.py
di hadapan, tetapi saya merasakan tidak ramai orang di belakangnya, jadi saya akan mengatakannya di sini. Jika anda menulis ungkapan yang kompleks, bagaimana anda mengesahkan sama ada ia betul? Di sini kita hanya boleh menggunakan Python, jurubahasa yang paling sempurna, haha. Fungsi eval Python digunakan di sini Sudah tentu, anda tidak perlu memanggil fungsi ini, hanya menyalin ungkapan yang dikira secara langsung. Saya menggunakan fungsi eval hanya untuk menyatakan mengapa program saya dipanggil my_eval
.
Selepas dilaksanakan dengan cara ini, ia boleh dianggap sebagai melengkapkan penterjemah empat aritmetik mudah. Walau bagaimanapun, jika anda melakukannya sekali lagi, anda mungkin akan merasakan bahawa keseluruhan proses itu sangat menyusahkan seperti saya. Oleh kerana terdapat alat sedia untuk pembahagian perkataan dan analisis tatabahasa, dan ia tidak terdedah kepada kesilapan, beban kerja boleh dikurangkan dengan banyak. Walau bagaimanapun, perlu melakukannya sendiri Sebelum menggunakan alat, anda mesti memahami fungsi alat tersebut.
Atas ialah kandungan terperinci Menggunakan Python untuk melaksanakan empat penterjemah aritmetik mudah. Untuk maklumat lanjut, sila ikut artikel berkaitan lain di laman web China PHP!