Utiliser Python pour implémenter un simple interpréteur arithmétique à quatre

王林
Libérer: 2023-04-21 11:46:09
avant
1651 Les gens l'ont consulté

Démonstration de la fonction de calcul

Ici, nous montrons d'abord les informations d'aide du programme, puis quelques tests simples de quatre opérations arithmétiques. Il semble qu'il n'y ait pas de problème (je ne peux pas garantir que le programme n'a pas de bugs !).

Utiliser Python pour implémenter un simple interpréteur arithmétique à quatre

Jetons de sortie

Utiliser Python pour implémenter un simple interpréteur arithmétique à quatre

Sortie AST

Ces informations JSON formatées sont trop longues et ne sont pas propices à une visualisation directe. Nous le rendons pour voir l'arborescence générée finale (voir les deux blogs précédents pour les méthodes). Enregistrez le JSON suivant dans un fichier, ici je l'appelle demo.json, puis exécutez la commande suivante : pytm-cli -d LR -i demo.json -o demo.html, puis ouvrez dans le navigateur Fichier HTML généré. pytm-cli -d LR -i demo.json -o demo.html,然后再浏览器打开生成的 html 文件。

Utiliser Python pour implémenter un simple interpréteur arithmétique à quatre

Utiliser Python pour implémenter un simple interpréteur arithmétique à quatre

代码

所有的代码都在这里了,只需要一个文件 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())
Copier après la connexion

总结

本来想在前面说一下为什么叫 my_eval.py,但是感觉看到后面的人不多,那就在这里说好了。如果写了一个复杂的表达式,那么怎么验证是否正确的。这里我们直接利用 Python 这个最完美的解释器就好了,哈哈。这里用 Python 的 eval 函数,你当然是不需要调用这个函数,直接复制计算的表达式即可。我用 eval 函数,只是想表达为什么我的程序会叫 my_eval

Comment utiliser Python pour créer un interpréteur arithmétique minimaliste à quatre

Utiliser Python pour implémenter un simple interpréteur arithmétique à quatre Comment utiliser Python pour créer un interpréteur arithmétique minimaliste à quatre

Code

Tout le code est ici. Vous n'avez besoin que d'un seul fichier my_eval.py. Si vous souhaitez l'exécuter, copiez-le et collez-le, puis suivez les étapes de la démonstration.

🎜Node, BinOp et Constan sont des classes utilisées pour représenter des nœuds.
La méthode lexizer dans Calculator est utilisée pour la segmentation des mots. À l'origine, j'avais prévu d'utiliser la régularisation. Si vous avez lu mon blog précédent, vous pouvez le constater. Je suis Les expressions régulières sont utilisées pour segmenter les mots (car il existe un simple programme de segmentation de mots dans les expressions régulières de la documentation officielle de Python). Mais j’ai vu que d’autres personnes écrivaient les participes à la main, alors j’ai fait la même chose, mais ça ne me semblait pas très agréable, c’était très fastidieux et sujet aux erreurs.
La méthode parse effectue une analyse. Elle analyse principalement la structure de l'expression, détermine si elle est conforme à la grammaire des quatre opérations arithmétiques et génère enfin l'arbre d'expression (son AST). 🎜rrreee

Résumé

🎜Au départ, je voulais expliquer pourquoi il s'appelle my_eval.py, mais j'ai l'impression qu'il n'y a pas beaucoup de monde derrière, alors je vais le dire ici . Si vous écrivez une expression complexe, comment vérifier si elle est correcte ? Ici, nous pouvons simplement utiliser Python, l'interpréteur le plus parfait, haha. La fonction eval de Python est utilisée ici. Bien sûr, vous n'avez pas besoin d'appeler cette fonction, copiez simplement l'expression calculée directement. J'utilise la fonction eval juste pour exprimer pourquoi mon programme s'appelle my_eval. 🎜🎜🎜🎜🎜Après avoir été implémenté de cette manière, il peut être considéré comme un simple interpréteur arithmétique à quatre. Cependant, si vous recommencez, vous aurez probablement l'impression que l'ensemble du processus est très fastidieux, comme moi. Puisqu’il existe des outils prêts à l’emploi pour la segmentation des mots et l’analyse grammaticale, et qu’ils ne sont pas sujets aux erreurs, la charge de travail peut être considérablement réduite. Cependant, il est nécessaire de le faire vous-même. Avant d’utiliser l’outil, vous devez au moins comprendre la fonction de l’outil. 🎜

Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!

Étiquettes associées:
source:yisu.com
Déclaration de ce site Web
Le contenu de cet article est volontairement contribué par les internautes et les droits d'auteur appartiennent à l'auteur original. Ce site n'assume aucune responsabilité légale correspondante. Si vous trouvez un contenu suspecté de plagiat ou de contrefaçon, veuillez contacter admin@php.cn
Tutoriels populaires
Plus>
Derniers téléchargements
Plus>
effets Web
Code source du site Web
Matériel du site Web
Modèle frontal