Wie werden abstrakte Python-Syntaxbäume (AST) in Mathematica zusammengestellt?
Ich möchte Python ' ast.Module' -Objekte in Mathematica zusammenstellen und sie dann über z ExternalEvaluate["exec(astObject)"].
Die Funktionen Python ast , parse , eval , exec und compile können alle auf ' ast.Module' -Objekten ausgeführt werden, indem sie entweder ausgegeben oder als Eingaben verwendet werden. Ich weiß jedoch nicht, wie ich dies astObjectin Mathematica / WL zusammenstellen und dann über an Python senden soll ExternalEvaluate.
Ich versuche, Python-Code programmgesteuert in MMA (für einen genetischen Algorithmus) zu generieren und ihn dann zur Auswertung 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 beispielsweise Folgendes möglich:
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 das gleiche Ergebnis zu erzielen (dh 12).
Allerdings möchte ich meine Bits von Python - Code (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. Um das zu tun, muss ich dann herausfinden, wie man Pythons nervige Einrückungen analysiert, was natürlich ist, wie es einen Satz von Ausdrücken vom nächsten unterscheidet und welche Abhängigkeiten sie haben. Ich bin abgeneigt, dies zu tun, und dachte mir, dass es einfacher sein könnte, an der Ast- Struktur zu arbeiten.
Ursprünglich hatte ich gedacht, ich könnte möglicherweise eine Zwischenzeichenfolge aus Pythons ast.dump()Funktion verwenden, 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 da dies im astStringwesentlichen serialisiert, astObjectkönnte ich dies stattdessen auch generieren. Ich kann jedoch keine Möglichkeit finden, Python dazu zu bringen, etwas damit zu tun astString.
Ist es möglich, diese Art von Python-Objekt - wie astObjectoben - auf der Mathematica-Seite zu erstellen ?
B.
PS: Hier ist eine Beschreibung der ' ast.Module' -Objekte: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
Antworten
Ich bin mir nicht sicher, wonach Sie genau suchen. Ich denke die Antwort auf Ihre Frage:
Ich versuche, Python-Code programmgesteuert in MMA [...] zu generieren und ihn dann zur Auswertung 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.
Ist es möglich, diese Art von Python-Objekt auf der Mathematica-Seite zu erstellen?
ist ziemlich einfach zu benutzen ExternalEvaluateund 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 evalstatt verwendet exec, weil evalein Wert zurückgegeben wird.)
Ich denke, dass das Kompilieren von Python-Code als Zeichenfolge einfacher ist als das, was Sie vorschlagen. Aber ich weiß auch, dass ich nur sage, dass es niemanden überzeugen wird, also hier ein Beispiel.
Wir definieren einige symbolische Köpfe, die unser Python-Programm in Mathematica darstellen, und eine Funktion zum Rendern von Ausdrücken 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. Es ist ein bisschen so, als würde man Python-Code mit dem ast-Modul erstellen.
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]
Aus:
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 nun 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]
Aus:
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, schlagen Sie in der Mathematica-Dokumentation nach "symbolisches C". 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 sicherlich einen Besuch wert, wenn man den Weg gehen möchte).
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.