여기서 먼저 프로그램의 도움말 정보를 보여준 다음 몇 가지 간단한 4가지 산술 연산 테스트를 수행하면 문제가 없는 것 같습니다(프로그램에 버그가 없다고 보장할 수는 없습니다!).
이 형식의 JSON 정보가 너무 길어서 직접 보는 데 도움이 되지 않습니다. 최종 생성된 트리 다이어그램을 보기 위해 렌더링합니다(방법은 이전 두 블로그 참조). 다음 JSON을 파일에 저장합니다. 여기서는 데모.json이라고 부릅니다. 그런 다음 pytm-cli -d LR -i 데모.json -o 데모.html
명령을 실행하고 엽니다. 브라우저에서 생성된 html 파일에 있습니다. pytm-cli -d LR -i demo.json -o demo.html
,然后再浏览器打开生成的 html 文件。
所有的代码都在这里了,只需要一个文件 my_eval.py
,想要运行的话,复制、粘贴,然后按照演示的步骤执行即可。
Node、BinOp、Constan 是用来表示节点的类.
Calculator 中 lexizer 方法是进行分词的,本来我是打算使用正则的,如果你看过我前面的博客的话,可以发现我是用的正则来分词的(因为 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
모든 코드는 여기에 있습니다. my_eval.py
파일 하나만 있으면 됩니다. 복사하여 붙여넣은 다음 데모 단계를 따르세요.
my_eval.py
라고 하는지 얘기하고 싶었는데 뒤에 사람이 별로 없는 것 같아서 여기서 말하겠습니다. . 복잡한 표현식을 작성하는 경우 그것이 올바른지 어떻게 확인합니까? 여기서는 가장 완벽한 인터프리터인 Python을 사용하면 됩니다. 하하. 여기서는 Python의 eval 함수가 사용됩니다. 물론 이 함수를 호출할 필요는 없으며 계산된 표현식을 직접 복사하면 됩니다. 나는 단지 내 프로그램이 my_eval
이라고 불리는 이유를 표현하기 위해 eval 함수를 사용합니다. 🎜🎜🎜🎜🎜이렇게 구현하면 간단한 4연산 해석기라고 볼 수 있습니다. 하지만 다시 해보면 아마 저처럼 전체적인 과정이 너무 번거롭다고 느끼실 겁니다. 단어 분할 및 문법 분석을 위한 기성 도구가 있고 오류가 발생하지 않기 때문에 작업량을 크게 줄일 수 있습니다. 그러나 도구를 사용하기 전에 최소한 도구의 기능을 이해해야 합니다. 🎜위 내용은 Python을 사용하여 간단한 4개의 산술 인터프리터 구현의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!