Comment assembler des arbres de syntaxe abstraite Python (AST) dans Mathematica?

Je voudrais assembler Python «  ast.Module « objets à lintérieur de Mathematica, puis soumettez-les à Python via par exemple ExternalEvaluate["exec(astObject)"].

Le Python ast , analyser , eval , exec et les fonctions compile peuvent toutes fonctionner sur «  ast.Module « objets, soit en les sortant, soit en les prenant comme entrées. Cependant, je ne sais pas comment assembler ce astObject dans Mathematica / WL puis envoyez-le à Python via ExternalEvaluate.

Jessaie de générer par programme du code Python en MMA (pour un algorithme génétique) puis de le soumettre à Python pour lévaluation. Je pourrais assembler des chaînes de code Python, mais ensuite je dois gérer toute lindentation, ce qui semble être une douleur.

Par exemple, en Python, il est possible de faire ce qui suit:

import ast pythonString="X3=X1*X2" astObject=ast.parse(pythonString) X1=3 X2=4 exec(compile(astObject,"","exec")) print(X3) -> 12 

Et bien sûr à partir de MMA, il est possible de faire:

session=StartExternalSession["Python"] ExternalEvaluate[session, {"import ast","X1=3","X2=4", "exec(compile(ast.parse(\"X3=X1*X2\"),\"\",\"exec\"))"} ] 

pour obtenir le même résultat (ie 12).

Cependant je souhaite générer mes bits de code Python ("X1=3", "X2=4", "X3=X1*X2") dans Mathematica. Ces bits ici sont assez simples, mais jai lintention de générer des programmes complets, cest-à-dire des déclarations et des expressions, de manière métaprogrammatique (!). Pour ce faire, je dois alors comprendre comment analyser les indentations ennuyeuses de Python, qui est bien sûr comment il distingue un ensemble dexpressions du suivant et quelles sont leurs dépendances. Je répugne à le faire, et jai pensé quil serait peut-être plus facile dopérer sur la structure ast .

Au départ, javais pensé que je pourrais utiliser une forme de chaîne intermédiaire de Python « s ast.dump() fonction qui ressemble à:

astString = ast.dump(pythonString) -> "Module(Body=[Assign(targets=[Name(id="X3",ctx=Store())],value=BinOp(left=Name(id="X1", ctx=Load()),op=Mult(),right=Name(id="X2",ctx=Load())))])" 

et depuis ce astString sérialise essentiellement le astObject Je pourrais également générer ceci à la place. Cependant, je ne trouve aucun moyen pour que Python fasse quoi que ce soit avec ce astString.

Est-il possible de créer ce type dobjet Python – comme mon astObject ci-dessus – du côté Mathematica?

B

PS: Voici une description des objets «  ast.Module « : https://greentreesnakes.readthedocs.io/en/latest/tofrom.html

PPS: Jai posté ceci sur la communauté Wolfram: https://community.wolfram.com/groups/-/m/t/2070851

Commentaires

  •  » Pour ce faire, je dois ensuite comprendre comment analyser les indentations ennuyeuses de Python ‘ […]  » – Je sais que vous demandez à AST dêtre utilisé, mais peut-être que les réponses qui traitent des indentations de Python ‘ vous intéressent également.
  • Oui, Anton, vous avez raison, je cherche aussi des solutions purement basées sur des chaînes. Et dans ce sens, je regardais votre package FunctionalParsers.m comme un moyen dimporter automatiquement la grammaire EBNF de Python ‘ pour peut-être me permettre de générer un pseudo-Python-avec-explicite-bracketing , ou pour peut-être générer du code Hy (qui est Lisp écrit en Python) qui a bien sûr un bracketing approprié. Je ne peux simplement ‘ comprendre pourquoi quelquun créerait un langage basé sur lindentation, ou pourquoi dautres ‘ continueraient à lutiliser …
  •  » Je ne peux ‘ pas comprendre pourquoi quelquun créerait un langage basé sur lindentation, ou pourquoi dautres ‘ continueraient à lutiliser …  » – LOL! Beaucoup de gens sont mystifiés par cela!
  • Utiliser  » FunctionalParsers.m  » est une idée intéressante, mais la poursuivre pourrait être loin dêtre une entreprise facile …
  • Vous feriez peut-être mieux de programmer deux monades logicielles, une en WL et une autre en Python, qui ont la même conception de flux de travail. Ensuite, vous transférez entre eux avec des changements de code mineurs; aucun formatage spécial pour Python nest également nécessaire. Voici un exemple avec une monade appelée LSAMon . (Faites défiler vers le bas.)

Réponse

Je ne sais pas exactement ce que vous recherchez. Je pense que la réponse à votre question:

Jessaie de générer par programme du code Python en MMA […] puis soumettez-le à Python pour évaluation. Je pourrais assembler des chaînes de code Python, mais ensuite je dois gérer toute lindentation, ce qui semble compliqué.

Est-il possible de créer ce type dobjet Python du côté Mathematica?

est assez simple en utilisant ExternalEvaluate et Python « s  » ast  » bibliothèque.

Voici un exemple:

code = ""[i**2 for i in range(10)]""; astTemplate = StringTemplate["import ast; eval(compile(ast.parse(`1`, mode="eval"), "", "eval"))"]; astTemplate[code] (* "import ast; eval(compile(ast.parse("[i**2 for i in range(10)]", mode="eval"), "", "eval"))" *) ExternalEvaluate["Python", astTemplate[code]] (* {0, 1, 4, 9, 16, 25, 36, 49, 64, 81} *) 

(jai utilisé eval au lieu de exec, car eval renvoie une valeur.)

Commentaires

  • Merci Anton. Oui, jaurais pu être plus clair. Permettez-moi de mettre à jour ma question un peu. En gros, je veux générer le  » ‘ [i ** 2 pour i dans lintervalle (10)] ‘  » partie de votre réponse côté MMA, mais pas comme une chaîne Python, mais comme un objet ast.Module qui peut se brancher sur compile et eval et exec.

Réponse

Je pense que compiler du code Python sous forme de chaîne est en fait plus simple que ce que vous proposez. Mais je sais aussi que le simple fait de dire que cela ne convaincra personne, alors voici un exemple.

Nous définissons quelques têtes symboliques qui représenteront notre programme Python dans Mathematica, et une fonction pour rendre expressions avec ces têtes symboliques:

ToPythonString[statements_List] := StringRiffle[ToPythonString /@ statements, "\n"] ToPythonString[PyBlock[statement_, children_, indent_ : 0]] := StringJoin[ StringRepeat[" ", indent], statement, ":\n", ToPythonString[PyIndent /@ children] ] ToPythonString[PyStatement[statement_, indent_ : 0]] := StringJoin[ StringRepeat[" ", indent], statement ] PyIndent[PyBlock[statement_, children_, indent_ : 0]] := PyBlock[ statement, PyIndent /@ children, indent + 1 ] PyIndent[PyStatement[statement_, indent_ : 0]] := PyStatement[ statement, indent + 1 ] 

Ce que ces fonctions nous permettent de faire est décrire du code Python dans Mathematica sans penser à lindentation, cest un peu comme construction de code Python avec le module ast.

Voici un exemple de rendu de lexpression symbolique sous forme de chaîne:

prog = { PyStatement["a = 1"], PyStatement["b = 2"], PyBlock["If a > b", { PyStatement["Print("a is larger than b")"] }], PyBlock["def f(x)", { PyStatement["Print("executing f")"], PyBlock["if x > 0", { PyStatement["Print("x is larger than 0")"] }] }] }; ToPythonString[prog] 

Out:

a = 1 b = 2 If a > b: Print("a is larger than b") def f(x): Print("executing f") if x > 0: Print("x is larger than 0") 

Nous pouvons facilement construire là-dessus et rendre notre représentation symbolique du programme Python plus descriptive.

PyAssign[lhs_, rhs_] := PyStatement[lhs <> " = " <> rhs] PyPrint[text_] := PyStatement["Print(" <> text <> ")"] PyFunction[name_, args_, statements_] := PyBlock[ "def " <> name <> "(" <> StringRiffle[args, ", "] <> ")", statements ] PyIf[cond_, statements_] := PyBlock[ "If " <> cond, statements ] PyIf[cond_, statements_, elseStatements_] := { PyBlock[ "If " <> cond, statements ], PyBlock[ "else", elseStatements ] } 

Avec ces définitions daide, nous pouvons maintenant écrire le programme suivant dans un style très lisible.

prog = { PyAssign["a", "1"], PyAssign["b", "2"], PyIf[ "a > b", { PyPrint["a is larger than b"] }], PyFunction["f", {"x"}, PyIf[ "x > 0", {PyPrint["x is larger than 0"]}, {PyPrint["x is not larger than 0"]} ] ] }; ToPythonString[prog] 

Out:

a = 1 b = 2 If a > b: Print(a is larger than b) def f(x): If x > 0: Print(x is larger than 0) else: Print(x is not larger than 0) 

Si vous ne lavez pas encore fait, recherchez  » C symbolique dans la documentation Mathematica. Cest essentiellement un moyen de construire un AST pour un programme en langage C dans Mathematica qui peut ensuite être converti en code C exécutable. Cest essentiellement là que nous nous dirigeons avec ce code, bien que si javais lintention de faire une implémentation complète comme celle-ci, cela ne ressemblerait pas exactement à ceci (le module ast vaut certainement la peine dêtre étudié si lon veut suivre la voie). / p>

Revenons au point: ce que je veux transmettre avec cette réponse, cest que vous navez pas à passer beaucoup de temps pour construire un petit framework qui résout plus ou moins le problème dindentation que vous évoquez dans votre question .

Commentaires

  • Oui CE cela ressemble beaucoup à ce que jimaginais serait une torture à mettre en œuvre, et vous avez raison, vous lavez fait assez simple. Je vais devoir continuer de cette manière en étendant le langage vers le bas vers les feuilles pour couvrir les atomes et les opérateurs, les listes et les tuples et autres, mais ce cadre gère toute lindentation, et cela devrait être faisable.
  • @berniethejet Dans cet exemple, jai défini par exemple PyIf quelque chose qui sévalue en autre chose. Vous pouvez envisager dajouter à la place la définition à ToPythonString, cest-à-dire ToPythonString[PyIf[...]] :=. De cette façon, vous pouvez inspecter lensemble de votre programme sous forme symbolique, sans lavoir évalué jusquà ce que vous appeliez ToPythonString.
  • Merci, cest bon à savoir. Votre framework est certainement meilleur que tout ce que jaurais bricolé (grognant tout le long, comme je le serais inévitablement, ennuyé par la croisade dindentation de Guido ‘).

Laisser un commentaire

Votre adresse e-mail ne sera pas publiée. Les champs obligatoires sont indiqués avec *