Come assemblare alberi di sintassi astratta Python (AST) in Mathematica?
Vorrei assemblare oggetti Python ' ast.Module ' all'interno di Mathematica e quindi inviarli a Python tramite es ExternalEvaluate["exec(astObject)"].
Le funzioni Python ast , parse , eval , exec e compile possono tutte operare sugli oggetti ' ast.Module ', sia emettendoli che prendendoli come input. Tuttavia non so come assemblarlo astObjectall'interno di Mathematica / WL e quindi inviarlo a Python tramite ExternalEvaluate.
Sto cercando di generare a livello di codice codice Python in MMA (per un algoritmo genetico) e quindi inviarlo a Python per la valutazione. Potrei assemblare stringhe di codice Python, ma poi devo gestire tutto il rientro, il che sembra una seccatura.
Ad esempio, in Python è possibile fare quanto segue:
import ast
pythonString="X3=X1*X2"
astObject=ast.parse(pythonString)
X1=3
X2=4
exec(compile(astObject,"","exec"))
print(X3)
-> 12
E ovviamente da MMA è possibile fare:
session=StartExternalSession["Python"]
ExternalEvaluate[session, {"import ast","X1=3","X2=4",
"exec(compile(ast.parse(\"X3=X1*X2\"),\"\",\"exec\"))"} ]
per ottenere lo stesso risultato (cioè 12).
Tuttavia voglio generare i miei pezzi di codice Python ( "X1=3", "X2=4", "X3=X1*X2") in Mathematica. Questi bit qui sono abbastanza semplici, ma intendo generare programmi completi, cioè istruzioni ed espressioni, metaprogrammaticamente (!). Per farlo devo quindi capire come analizzare le fastidiose rientranze di Python, che è ovviamente il modo in cui distingue un insieme di espressioni dal successivo e quali sono le loro dipendenze. Sono restio a farlo, e pensato che potrebbe essere più facile operare sul ast struttura.
Inizialmente avevo pensato che potevo essere in grado di utilizzare una forma stringa intermedia dalla ast.dump()funzione di Python che assomiglia a:
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())))])"
e poiché questo astStringessenzialmente serializza il astObjectpotrei anche generarlo invece. Tuttavia non riesco a trovare alcun modo per convincere Python a fare qualcosa con questo astString.
È possibile creare questo tipo di oggetto Python - come il mio astObjectsopra - dal lato Mathematica?
B
PS: ecco una descrizione degli oggetti ' ast.Module ':https://greentreesnakes.readthedocs.io/en/latest/tofrom.html
PPS: Ho postato in modo incrociato questo sulla comunità Wolfram: https://community.wolfram.com/groups/-/m/t/2070851
Risposte
Non sono sicuro di cosa stai cercando esattamente. Penso che la risposta alla tua domanda:
Sto cercando di generare in modo programmatico codice Python in MMA [...] e quindi di inviarlo a Python per la valutazione. Potrei assemblare stringhe di codice Python, ma poi devo gestire tutto il rientro, il che sembra una seccatura.
È possibile creare questo tipo di oggetto Python dal lato Mathematica?
è abbastanza semplice usare ExternalEvaluatee la libreria "ast" di Python.
Ecco un esempio:
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} *)
(Ho usato evalinvece di exec, perché evalrestituisce un valore.)
Penso che la compilazione del codice Python come stringa sia in realtà più semplice di quello che proponi. Ma so anche che solo dicendo che non convincerà nessuno, ecco un esempio.
Definiamo un paio di teste simboliche che rappresenteranno il nostro programma Python in Mathematica e una funzione per rendere le espressioni con quelle teste simboliche:
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
]
Ciò che queste funzioni ci consentono di fare è scrivere codice Python in Mathematica senza pensare al rientro, è un po 'come costruire codice Python con il modulo ast.
Questo è un esempio di rendering dell'espressione simbolica come stringa:
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]
Su:
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')
Possiamo facilmente costruire su questo e rendere la nostra rappresentazione simbolica del programma Python più descrittiva.
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
]
}
Con queste definizioni di supporto, ora possiamo scrivere il seguente programma in uno stile molto leggibile.
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]
Su:
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)
Se non l'hai ancora fatto, cerca la "C simbolica" nella documentazione di Mathematica. È fondamentalmente un modo per creare un AST per un programma in linguaggio C in Mathematica che può quindi essere convertito in codice C eseguibile. Fondamentalmente è qui che ci stiamo dirigendo anche con questo codice, sebbene se intendessi realizzare un'implementazione completa del genere non sarebbe esattamente così (il modulo ast merita sicuramente di essere studiato se si vuole seguire la strada).
Tornando al punto: quello che voglio trasmettere con questa risposta è che non devi spendere molto tempo per costruire un piccolo framework che risolva più o meno il problema di indentazione che hai menzionato nella tua domanda.