Wie werden abstrakte Python-Syntaxbäume (AST) in Mathematica zusammengestellt?

Ich möchte Python zusammenstellen „ ast.Module „Objekte in Mathematica und senden Sie sie dann über z ExternalEvaluate["exec(astObject)"].

Der Python ast , parse , eval , exec und kompilieren Funktionen können alle mit „ ast.Module „Objekte, die entweder ausgegeben oder als Eingaben verwendet werden. Ich weiß jedoch nicht, wie ich diese astObject in Mathematica / WL zusammenstellen soll und senden Sie es dann über ExternalEvaluate an Python.

Ich versuche, Python-Code in MMA (für einen genetischen Algorithmus) programmgesteuert zu generieren und dann an Python zu senden Ich könnte Python-Code-Strings zusammenstellen, aber dann muss ich die gesamte Einrückung verarbeiten, was wie ein Schmerz erscheint.

In Python ist es beispielsweise möglich, Folgendes zu tun:

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

Und natürlich ist es mit MMA möglich:

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

, um dasselbe zu erhalten Ergebnis (dh 12).

Ich möchte jedoch meine Python-Code-Bits generieren ("X1=3", "X2=4", "X3=X1*X2") in Mathematica. Diese Bits hier sind einfach genug, aber ich beabsichtige, vollständige Programme, dh Anweisungen und Ausdrücke, metaprogrammatisch (!) zu generieren. Dazu muss ich das tun Finden Sie heraus, wie Sie die nervigen Einrückungen von Python analysieren können. Dies unterscheidet natürlich eine Reihe von Ausdrücken von der nächsten und deren Abhängigkeiten. Ich bin abgeneigt, dies zu tun, und dachte mir, dass es möglicherweise einfacher ist, mit der ast -Struktur zu arbeiten.

Ursprünglich hatte ich gedacht, ich könnte möglicherweise eine Zwischenzeichenfolgenform verwenden aus Pythons ast.dump() -Funktion, die wie folgt aussieht:

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())))])" 

und seitdem astString serialisiert im Wesentlichen die astObject Ich könnte dies stattdessen auch generieren. Ich kann jedoch keine Möglichkeit finden, Python dazu zu bringen, etwas mit dieser astString.

Ist es möglich, diese Art von Python-Objekt – wie meine astObject oben – auf der Mathematica-Seite zu erstellen?

B

PS: Hier ist eine Beschreibung der Objekte „ ast.Module „: https://greentreesnakes.readthedocs.io/en/latest/tofrom.html

PPS: Ich habe dies in der Wolfram Community veröffentlicht: https://community.wolfram.com/groups/-/m/t/2070851

Kommentare

  • “ Dazu muss ich herausfinden, wie die nervigen Einrückungen von Python ‚ analysiert werden […] “ – Ich weiß, dass Sie AST zur Verwendung auffordern, aber möglicherweise sind Antworten, die sich mit den Einrückungen von Python ‚ befassen, auch für Sie von Interesse.
  • Ja, Anton, Sie haben Recht, ich suche auch nach rein stringbasierten Lösungen. In diesem Sinne habe ich mir Ihr FunctionalParsers.m-Paket angesehen, um die EBNF-Grammatik von Python ‚ automatisch zu importieren, damit ich möglicherweise Pseudo-Python mit expliziter Klammerung generieren kann oder um vielleicht Hy-Code (der in Python geschriebener Lisp ist) zu generieren, der natürlich die richtige Klammer hat. Ich kann nur ‚ nicht verstehen, warum jemand eine Sprache basierend auf Einrückungen erstellen würde oder warum andere ‚ sie dann weiter verwenden würden …
  • “ Ich kann nur ‚ nicht verstehen, warum jemand eine Sprache basierend auf Einrückungen erstellen würde, oder warum andere ‚ s es dann weiter verwenden würden … “ – LOL! Viele Menschen sind davon verwirrt!
  • Die Verwendung von “ FunctionalParsers.m “ ist eine interessante Idee, aber sie zu verfolgen Möglicherweise ist dies kein einfaches Unterfangen …
  • Es ist möglicherweise besser, zwei Softwaremonaden zu programmieren, eine in WL und eine in Python, die dasselbe Workflow-Design haben. Dann übertragen Sie zwischen ihnen mit geringfügigen Codeänderungen; Außerdem ist keine spezielle Formatierung für Python erforderlich. Hier ist ein Beispiel mit einer Monade namens LSAMon . (Scrollen Sie nach unten.)

Antwort

Ich bin nicht sicher, wonach Sie genau suchen. Ich denke die Antwort auf Ihre Frage:

Ich versuche, Python-Code programmgesteuert in MMA zu generieren […] und senden Sie es dann zur Auswertung an Python. Ich könnte Python-Code-Strings zusammenstellen, aber dann muss ich die gesamte Einrückung verarbeiten, was wie ein Schmerz erscheint.

Ist es möglich, diese Art von Python-Objekt auf der Mathematica-Seite zu erstellen?

ist mit ExternalEvaluate und Pythons “ ast “ Bibliothek.

Hier ist ein Beispiel:

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} *) 

(Ich habe eval anstelle von exec, da eval einen Wert zurückgibt.)

Kommentare

  • Danke Anton. Ja, ich hätte klarer sein können. Lassen Sie mich meine Frage ein wenig aktualisieren. Grundsätzlich möchte ich die “ ‚ [i ** 2 für i im Bereich (10)] ‚ “ Teil Ihrer Antwort auf der MMA-Seite, aber nicht als Python-String, sondern als ast.Module-Objekt, das sich in compile und eval and exec einbinden lässt.

Antwort

Ich denke, das Kompilieren von Python-Code als Zeichenfolge ist tatsächlich einfacher als das, was Sie vorschlagen. Aber ich weiß auch, dass ich nur sage, dass es niemanden überzeugen wird, also hier ein Beispiel.

Wir definieren ein paar symbolische Köpfe, die unser Python-Programm in Mathematica darstellen, und eine Funktion zum Rendern Ausdrücke mit diesen symbolischen Köpfen:

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 ] 

Mit diesen Funktionen können wir Python-Code in Mathematica schreiben, ohne über die Einrückung nachzudenken Erstellen von Python-Code mit dem ast-Modul.

Dies ist ein Beispiel für das Rendern des symbolischen Ausdrucks als Zeichenfolge:

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") 

Darauf können wir leicht aufbauen und unsere symbolische Darstellung des Python-Programms aussagekräftiger gestalten.

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 ] } 

Mit diesen Hilfsdefinitionen können wir jetzt das folgende Programm in einem sehr lesbaren Stil schreiben.

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) 

Wenn Sie dies noch nicht getan haben, suchen Sie bitte nach “ symbolischem C in der Mathematica-Dokumentation. Es ist im Grunde eine Möglichkeit, einen AST für ein C-Sprachprogramm in Mathematica zu erstellen, das dann in ausführbaren C-Code konvertiert werden kann. Das ist im Grunde genommen auch der Punkt, an dem wir uns mit diesem Code befassen, obwohl es, wenn ich beabsichtige, eine vollständige Implementierung wie diese vorzunehmen, nicht genau so aussehen würde (das Ast-Modul ist es sicherlich wert, studiert zu werden, wenn man den Weg gehen möchte) / p>

Zurück zum Punkt: Was ich mit dieser Antwort vermitteln möchte, ist, dass Sie nicht viel Zeit aufwenden müssen, um ein kleines Framework zu erstellen, das das in Ihrer Frage erwähnte Einrückungsproblem mehr oder weniger löst .

Kommentare

  • Ja CE, das sieht sehr nach dem aus, was ich mir vorgestellt habe, wäre Folter, und Sie haben Recht, Sie haben es geschafft Recht einfach. Ich werde auf diese Weise fortfahren müssen, um die Sprache nach unten in Richtung der Blätter zu erweitern, um Atome und Operatoren sowie Listen und Tupel und dergleichen abzudecken, aber dieser Rahmen behandelt die gesamte Einrückung und sollte machbar sein.
  • @berniethejet In diesem Beispiel habe ich zum Beispiel PyIf für etwas definiert, das zu etwas anderem ausgewertet wird. Sie können stattdessen die Definition zu ToPythonString hinzufügen, d. H. ToPythonString[PyIf[...]] :=. Auf diese Weise können Sie Ihr gesamtes Programm in symbolischer Form überprüfen, ohne dass es ausgewertet wird, bis Sie ToPythonString aufrufen.
  • Danke, das ist gut zu wissen. Ihr Rahmen ist sicherlich besser als jeder andere, den ich zusammengeschustert hätte (ich murrte den ganzen Weg, wie ich es unweigerlich sein würde, verärgert über Guidos Kreuzzug mit Einrückungen).

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert.