ここでは、最初にプログラムのヘルプ情報を示し、次に簡単な四則演算のテストをいくつか行いますが、問題ないようです(プログラムにバグがないことは保証できません)。 !)。
この形式の JSON メッセージは長すぎます。役に立ちません。直接見るために。これをレンダリングして、最終的に生成されたツリー図を確認します (方法については、前の 2 つのブログを参照してください)。次の JSON をファイルに保存します。ここでは、demo.json と呼びます。その後、次のコマンドを実行します。 pytm-cli -d LR -i Demon.json -o Demon.html
を実行して、ブラウザの HTML ファイルに生成されたファイル。
my_eval.pyを実行する場合は、コピーして貼り付けて、デモの手順に従います。
電卓の字句解析メソッドは、単語の分割に使用されます。当初は、正則化を使用する予定でした。私の以前のブログを読んでいるなら、正規表現を使用して単語を分割したことがわかりましたか (Python の公式ドキュメントの正規表現に簡単な単語分割プログラムがあるため)。しかし、他の人が分詞を手書きしているのを見たので、私も同じようにしましたが、あまり良い気分ではなく、非常に面倒で間違いが発生しやすかったです。
parse メソッドは解析を行うためのもので、主に式の構造を分析し、四則演算の文法に準拠しているかどうかを判断し、最終的に式ツリー (その 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: """ 简单的抽象语法树节点,定义一些需要使用到的具有层次结构的节点 """ 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())
my_eval.py という名前なのかについて話したかったのですが、これを支持している人があまりいないような気がするので、このように言います。それはここです。複雑な式を書いた場合、それが正しいかどうかをどのように確認しますか?ここでは、最も完璧なインタープリターである Python を使用するだけです (笑)。ここでは Python の eval 関数を使用していますが、もちろんこの関数を呼び出す必要はなく、計算式を直接コピーするだけです。 eval 関数を使用するのは、プログラムが
my_eval と呼ばれる理由を表現するためだけです。
以上がPython を使用して単純な四則演算インタープリターを実装するの詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。