Python을 사용하여 간단한 4개의 산술 인터프리터 구현

王林
풀어 주다: 2023-04-21 11:46:09
앞으로
1659명이 탐색했습니다.

계산 기능 시연

여기서 먼저 프로그램의 도움말 정보를 보여준 다음 몇 가지 간단한 4가지 산술 연산 테스트를 수행하면 문제가 없는 것 같습니다(프로그램에 버그가 없다고 보장할 수는 없습니다!).

Python을 사용하여 간단한 4개의 산술 인터프리터 구현

출력 토큰

Python을 사용하여 간단한 4개의 산술 인터프리터 구현

출력 AST

이 형식의 JSON 정보가 너무 길어서 직접 보는 데 도움이 되지 않습니다. 최종 생성된 트리 다이어그램을 보기 위해 렌더링합니다(방법은 이전 두 블로그 참조). 다음 JSON을 파일에 저장합니다. 여기서는 데모.json이라고 부릅니다. 그런 다음 pytm-cli -d LR -i 데모.json -o 데모.html 명령을 실행하고 엽니다. 브라우저에서 생성된 html 파일에 있습니다. pytm-cli -d LR -i demo.json -o demo.html,然后再浏览器打开生成的 html 文件。

Python을 사용하여 간단한 4개의 산술 인터프리터 구현

Python을 사용하여 간단한 4개의 산술 인터프리터 구현

代码

所有的代码都在这里了,只需要一个文件 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 &#39;0&#39; <= ch <= &#39;9&#39;:
                num_str = ch
                index += 1
                while index < len(self.expr):
                    n = self.expr[index]
                    if &#39;0&#39; <= n <= &#39;9&#39;:
                        if ch == &#39;0&#39;:
                            raise Exception("Invalid number!")
                        num_str = n
                        index += 1
                        continue
                    break
                self.toks.append({
                    "kind": "INT",
                    "value": int(num_str)
                })
            elif ch in [&#39;+&#39;, &#39;-&#39;, &#39;*&#39;, &#39;/&#39;, &#39;(&#39;, &#39;)&#39;]:
                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&#39;
        E&#39; -> + T E&#39; | - T E&#39; | ɛ
        """
        # E -> E E&#39;
        left = self.parse_term()
        # E&#39; -> + T E&#39; | - T E&#39; | ɛ
        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&#39;
        T&#39; -> * F T&#39; | / F T&#39; | ɛ
        """
        # T -> F T&#39;
        left = self.parse_factor()
        # T&#39; -> * F T&#39; | / F T&#39; | ɛ
        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 -> &#39;(&#39; E &#39;)&#39; | num | name
        """
        tok = self.get_token()
        kind = tok["kind"]
        value = tok["value"]
        if kind == &#39;(&#39;:
            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 [&#39;+&#39;, &#39;-&#39;, &#39;*&#39;, &#39;/&#39;, &#39;(&#39;, &#39;)&#39;, &#39;num&#39;]")
    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[&#39;kind&#39;]:3s} ==> {t[&#39;value&#39;]}")
    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을 사용하여 미니멀리스트 4개 산술 인터프리터를 만드는 방법

Python을 사용하여 간단한 4개의 산술 인터프리터 구현 Python을 사용하여 미니멀리스트 4개 산술 인터프리터를 만드는 방법

코드

모든 코드는 여기에 있습니다. my_eval.py 파일 하나만 있으면 됩니다. 복사하여 붙여넣은 다음 데모 단계를 따르세요.

🎜Node, BinOp, Constan은 노드를 나타내는 데 사용되는 클래스입니다.
원래는 정규화를 사용하려고 했으나 Calculator의 lexizer 메서드를 사용했습니다. 나는 정규식을 사용하여 단어를 분할합니다(파이썬 공식 문서의 정규식에 간단한 단어 분할 프로그램이 있기 때문입니다). 그런데 다른 사람들이 손으로 분사를 쓰는 걸 보고 나도 똑같이 했는데 기분이 별로 좋지 않았고, 매우 지루하고 오류가 발생하기 쉬웠습니다.
parse 메소드는 주로 표현식의 구조를 분석하고, 4가지 산술 연산의 문법에 맞는지 판단하고, 최종적으로 표현식 트리(AST)를 생성합니다. 🎜rrreee

요약

🎜원래 왜 my_eval.py라고 하는지 얘기하고 싶었는데 뒤에 사람이 별로 없는 것 같아서 여기서 말하겠습니다. . 복잡한 표현식을 작성하는 경우 그것이 올바른지 어떻게 확인합니까? 여기서는 가장 완벽한 인터프리터인 Python을 사용하면 됩니다. 하하. 여기서는 Python의 eval 함수가 사용됩니다. 물론 이 함수를 호출할 필요는 없으며 계산된 표현식을 직접 복사하면 됩니다. 나는 단지 내 프로그램이 my_eval이라고 불리는 이유를 표현하기 위해 eval 함수를 사용합니다. 🎜🎜🎜🎜🎜이렇게 구현하면 간단한 4연산 해석기라고 볼 수 있습니다. 하지만 다시 해보면 아마 저처럼 전체적인 과정이 너무 번거롭다고 느끼실 겁니다. 단어 분할 및 문법 분석을 위한 기성 도구가 있고 오류가 발생하지 않기 때문에 작업량을 크게 줄일 수 있습니다. 그러나 도구를 사용하기 전에 최소한 도구의 기능을 이해해야 합니다. 🎜

위 내용은 Python을 사용하여 간단한 4개의 산술 인터프리터 구현의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!

관련 라벨:
원천:yisu.com
본 웹사이트의 성명
본 글의 내용은 네티즌들의 자발적인 기여로 작성되었으며, 저작권은 원저작자에게 있습니다. 본 사이트는 이에 상응하는 법적 책임을 지지 않습니다. 표절이나 침해가 의심되는 콘텐츠를 발견한 경우 admin@php.cn으로 문의하세요.
인기 튜토리얼
더>
최신 다운로드
더>
웹 효과
웹사이트 소스 코드
웹사이트 자료
프론트엔드 템플릿