Cum să asamblați arbori de sintaxă abstractă Python (AST) în Mathematica?

Aș dori să asamblu Python „ ast.Module „obiecte din interiorul Mathematica și apoi trimiteți-le către Python prin ex ExternalEvaluate["exec(astObject)"].

Python ast , analiza , eval , exec și compila funcțiile toate pot funcționa pe „ ast.Module „obiecte, fie ieșindu-le, fie luându-le ca intrări. Cu toate acestea, nu știu cum să asamblu acest astObject în Mathematica / WL și apoi trimiteți-l către Python prin ExternalEvaluate.

Încerc să generez cod Python programat în MMA (pentru un algoritm genetic) și apoi să îl trimit către Python pentru evaluare. Aș putea asambla șiruri de cod Python, dar apoi trebuie să mă ocup de toată indentarea, ceea ce pare a fi o durere.

De exemplu, în Python este posibil să faceți următoarele:

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

Și, bineînțeles, din MMA este posibil să faceți:

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

pentru a obține același lucru result (adică 12).

Cu toate acestea, vreau să-mi generez biții de cod Python ("X1=3", "X2=4", "X3=X1*X2") în Mathematica. Acești biți sunt destul de simpli, dar intenționez să generez programe complete, adică instrucțiuni și expresii, metaprogramatic (!). Pentru a face acest lucru, atunci trebuie să aflați cum să analizați indentările enervante ale lui Python, care este, desigur, modul în care distinge un set de expresii de următorul și care sunt dependențele lor. Nu mă interesează să fac acest lucru și m-am gândit că ar putea fi mai ușor să funcționez pe structura ast .

Inițial m-am gândit că aș putea folosi o formă de șir intermediar din funcția Python „s ast.dump() care arată ca:

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

și din moment ce acest astString serializează în esență astObject aș putea genera și asta în schimb. Totuși nu găsesc nicio modalitate de a-l determina pe Python să facă ceva cu acest astString.

Este posibil să creați acest tip de obiect Python – ca astObject de mai sus – pe partea Mathematica?

B

PS: Iată o descriere a obiectelor „ ast.Module „: https://greentreesnakes.readthedocs.io/en/latest/tofrom.html

PPS: Am postat încrucișat acest lucru în Comunitatea Wolfram: https://community.wolfram.com/groups/-/m/t/2070851

Comentarii

  • ” Pentru a face acest lucru, trebuie să îmi dau seama cum să analizez indentările enervante ale Python ‘ […] ” – Știu că solicitați AST să fie utilizat, dar poate că răspunsurile care tratează indentările Python ‘ vă interesează, de asemenea.
  • Da, Anton, ai dreptate, mă uit și la soluții pur bazate pe șiruri. Și de-a lungul acelor linii, m-am uitat la pachetul dvs. FunctionalParsers.m ca o modalitate de a importa automat gramatica EBNF a Python ‘ pentru a-mi permite să generez pseudo-Python-cu-explicit-bracketing , sau, poate, pentru a genera cod Hy (care este Lisp scris în Python), care, desigur, are paranteză adecvată. Nu pot ‘ să înțeleg de ce cineva ar crea un limbaj bazat pe indentare sau de ce alte ‘ ar continua apoi să îl folosească …
  • ” Pur și simplu nu pot ‘ să înțeleg de ce cineva ar crea un limbaj bazat pe indentare sau de ce alte ‘ ar continua apoi să o folosească … ” – LOL! Mulți oameni sunt surprinși de asta!
  • Folosirea ” FunctionalParsers.m ” este o idee interesantă, dar urmărirea ei s-ar putea să fie departe de a fi o sarcină ușoară …
  • S-ar putea să fiți mai bine să programați două monade software, una în WL și alta în Python, care au același design al fluxului de lucru. Apoi transferați între ele cu modificări minore de cod; de asemenea, nu este necesară o formatare specială pentru Python. Iată un exemplu cu o monadă numită LSAMon . (Derulați în partea de jos.)

Răspundeți

Nu sunt sigur ce căutați exact. Cred că răspunsul la întrebarea ta:

Încerc să generez cod Python programat în MMA […] și apoi trimiteți-l la Python pentru evaluare. Aș putea asambla șiruri de cod Python, dar apoi trebuie să mă ocup de toată indentarea, ceea ce pare a fi o durere.

Este posibil să creăm acest tip de obiect Python pe partea Mathematica?

este destul de simplu folosind ExternalEvaluate și Python „s ” ast ” biblioteca.

Iată un exemplu:

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

(am folosit eval în loc de exec, deoarece eval returnează o valoare.)

Comentarii

  • Mulțumesc Anton. Da, aș fi putut fi mai clar. Permiteți-mi să-mi actualizez puțin întrebarea. Practic vreau să generez ” ‘ [i ** 2 pentru i în intervalul (10)] ‘ ” faceți parte din răspunsul dvs. pe partea MMA, dar nu ca un șir Python, ci ca obiect ast.Module care se poate conecta la compilare, eval și exec.

Răspuns

Cred că compilarea codului Python ca șir este de fapt mai simplă decât ceea ce propuneți. Dar știu și că spun doar că nu va convinge pe nimeni, așa că iată un exemplu.

Definim câteva capete simbolice care vor reprezenta programul nostru Python în Mathematica și o funcție de redat expresii cu aceste capete simbolice:

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 ] 

Ceea ce ne permit aceste funcții este să scriem cod Python în Mathematica fără să ne gândim la indentare, este un pic ca construirea codului Python cu modulul ast.

Acesta este un exemplu de redare a expresiei simbolice ca un șir:

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

Ne putem baza cu ușurință pe acest lucru și putem face reprezentarea noastră simbolică a programului Python mai descriptivă.

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

Cu aceste definiții de ajutor, putem scrie acum următorul program într-un stil foarte lizibil.

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) 

Dacă nu aveți încă, vă rugăm să căutați ” simbolic C în documentația Mathematica. Este practic o modalitate de a construi un AST pentru un program de limbaj C în Mathematica care poate fi apoi convertit în cod C rulabil. De fapt, acolo ne îndreptăm și cu acest cod, deși, dacă aș intenționa să realizez o implementare completă de genul acesta, nu ar arăta exact așa (modulul ast merită cu siguranță să fie studiat dacă se dorește să mergem pe traseu).

Înapoi la subiect: ceea ce vreau să transmit cu acest răspuns este că nu trebuie să petreceți mult timp pentru a construi un cadru mic care rezolvă mai mult sau mai puțin problema de indentare pe care o menționați în întrebarea dvs. .

Comentarii

  • Da CE, acest lucru seamănă foarte mult cu ceea ce îmi închipuiam că ar fi tortura de implementat și ai dreptate, ai reușit destul de direct. Va trebui să continui astfel, extinzând limbajul în jos spre frunze pentru a acoperi atomii și operatorii și listele și tuplele și altele, dar acest cadru gestionează toată indentarea și ar trebui să fie realizabil.
  • @berniethejet În acest exemplu, am definit de exemplu PyIf la ceva care se evaluează în altceva. Vă recomandăm să adăugați definiția la ToPythonString, adică ToPythonString[PyIf[...]] :=. În acest fel, vă puteți inspecta întregul program într-o formă simbolică, fără ca acesta să fie evaluat până când nu apelați ToPythonString.
  • Vă mulțumim, este bine să știți. Cadrul dvs. este cu siguranță mai bun decât oricare altul pe care l-aș fi coborât împreună (mormăind tot drumul, așa cum aș fi inevitabil, supărat de cruciada indentării lui Guido ‘).

Lasă un răspuns

Adresa ta de email nu va fi publicată. Câmpurile obligatorii sunt marcate cu *