Kuinka koota Pythonin abstraktin syntaksin puut (AST) Mathematicaan?

Haluan koota Python ” ast.Module ”-kohteet Mathematican sisällä ja lähetä ne sitten Pythonille esim ExternalEvaluate["exec(astObject)"].

Python ast , jäsentää , eval , exec ja compile toiminnot voivat kaikki toimia laitteella ” ast.Module ”esineitä joko tulostamalla ne tai ottamalla ne syötteiksi. En kuitenkaan tiedä, miten koota tämä astObject Mathematica / WL-verkkoon ja lähetä se sitten Pythonille ExternalEvaluate kautta.

Yritän luoda Python-koodia ohjelmallisesti MMA: ssa (geneettiselle algoritmille) ja lähettää sen sitten Pythonille voisin koota Python-koodimerkkijonoja, mutta minun on sitten käsiteltävä kaikkia sisennyksiä, mikä tuntuu tuskalta.

Esimerkiksi Pythonissa on mahdollista tehdä seuraava: >

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

Ja tietysti MMA: sta on mahdollista:

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

tuottaa sama tulos (eli 12).

Haluan kuitenkin luoda bittini Python-koodia ("X1=3", "X2=4", "X3=X1*X2") Mathematicassa. Nämä bitit ovat tässä riittävän yksinkertaisia, mutta aion luoda metaprogrammaattisesti kokonaisia ohjelmia, ts. lauseita ja lausekkeita (!). selvittää, miten jäsentää Pythonin ärsyttävät sisennykset, mikä tietysti erottaa yhden lausekesarjan seuraavasta ja mitkä ovat niiden riippuvuudet. Minua ei haluta tehdä niin, ja ajattelin, että ast -rakenteen käyttö saattaa olla helpompaa.

Alun perin ajattelin, että voisin pystyä käyttämään välimerkkijonoa Pythonista ”s ast.dump() -funktio, joka näyttää tältä:

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

ja koska tämä astString sarjoittaa olennaisesti astObject, voisin myös luoda tämän. En kuitenkaan löydä mitään tapaa saada Python tekemään mitään tällä astString.

Voiko tällaisen Python-objektin – kuten yllä olevan astObject – luoda Mathematica-puolelle?

B

PS: Tässä on kuvaus ” ast.Module ” -objekteista: https://greentreesnakes.readthedocs.io/en/latest/tofrom.html

PPS: Olen lähettänyt tämän Wolfram-yhteisöön: https://community.wolfram.com/groups/-/m/t/2070851

kommentit

  • ” Tätä varten minun on sitten selvitettävä, miten jäsennetään Python ’: n ärsyttävät sisennykset […] ” – tiedän, että pyydät AST: n käyttöä, mutta ehkä myös Sinua kiinnostavat vastaukset, jotka käsittelevät Pythonin ’ sisennyksiä.
  • Kyllä, Anton, olet oikeassa, katson myös puhtaasti merkkijonoihin perustuvia ratkaisuja. Näiden linjojen perusteella katselin FunctionalParsers.m-pakettiasi keinona tuoda automaattisesti Python ’ -sopimus EBNF-kieliopille, jotta voisin ehkä luoda pseudo-Python-explicit-bracketing , tai ehkä luoda Hy-koodi (joka on Lisp kirjoitettu Pythonissa), jolla on tietysti oikea haarukointi. En voi ’ ymmärtää, miksi joku loisi sisennykseen perustuvan kielen tai miksi muut ’ s jatkaisivat sen käyttöä …
  • ” En vain osaa ’ ymmärtää, miksi joku loisi sisennykseen perustuvan kielen tai miksi muut ’ s käyttävät sitä sitten … ” – LOL! Se hämmentää monia ihmisiä!
  • ” FunctionalParsers.m ” on mielenkiintoinen idea, mutta sen toteuttaminen saattaa olla kaukana helpposta …
  • Saatat olla parempi ohjelmoida kaksi ohjelmistomonadia, yksi WL: ssä ja toinen Pythonissa, joilla on sama työnkulun suunnittelu. Sitten siirrät niiden välillä pienillä koodimuutoksilla; myöskään erityistä muotoilua Pythonille ei tarvita. Tässä on esimerkki monadista nimeltä LSAMon . (Vieritä alareunaan.)

Vastaa

En ole varma mitä etsit. Luulen, että vastauksesi kysymykseesi:

Yritän luoda Python-koodia ohjelmallisesti MMA: ssa […] ja lähetä se sitten Pythonille arvioitavaksi. Voisin koota Python-koodimerkkijonoja, mutta sitten minun on hoidettava kaikki sisennykset, mikä tuntuu tuskalta.

Voiko tällaisen Python-objektin luoda Mathematica-puolelle?

on melko suoraviivainen käyttämällä ExternalEvaluate ja Python ”s ” ast ” kirjasto.

Tässä on esimerkki:

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

(Käytin eval exec: n sijaan, koska eval palauttaa arvon.)

Kommentit

  • Kiitos Anton. Kyllä, olisin voinut olla selvempi. Haluan päivittää kysymykseni hieman. Haluan periaatteessa luoda ” ’ [i ** 2 i: lle alueella (10)] ’ ” osa vastauksestasi MMA-puolella, mutta ei Python-merkkijonona, vaan ast.Module-objektina, joka voidaan liittää kääntämiseen ja evaliin ja execiin.

vastaus

Mielestäni Python-koodin kokoaminen merkkijonona on itse asiassa yksinkertaisempaa kuin ehdotat. Mutta tiedän myös, että minä vain sanoin, että se ei vakuuttaa ketään, joten tässä on esimerkki.

Määritämme pari symbolista päätä, jotka edustavat Python-ohjelmaa Mathematicassa, ja toiminto, joka renderoidaan lausekkeet näiden symbolisten päiden kanssa:

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 ] 

Näiden toimintojen avulla voimme kirjoittaa Python-koodin Mathematicaan ajattelematta sisennystä, se on vähän kuin Python-koodin rakentaminen ast-moduulilla.

Tämä on esimerkki symbolisen lausekkeen renderoinnista merkkijonona:

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

Voimme helposti rakentaa tämän ja tehdä Python-ohjelman symbolisesta esityksestä kuvailevamman.

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

Näillä auttajamääritelmillä voimme nyt kirjoittaa seuraavan ohjelman hyvin luettavalla tyylillä.

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) 

Jos et vielä ole, etsi ” symbolinen C Mathematica-dokumentaatiossa. Se on pohjimmiltaan tapa rakentaa AST C-kieliohjelmalle Mathematicassa, joka voidaan sitten muuntaa ajettavaksi C-koodiksi. Pohjimmiltaan olemme menossa myös tämän koodin kanssa, vaikka jos aikaisin toteuttaa täydellisen toteutuksen, se ei näytä tarkalleen tältä (ast-moduuli on varmasti opiskelemisen arvoinen, jos haluaa mennä reittiä pitkin). / p>

Takaisin kohtaan: Haluan välittää tällä vastauksella, että sinun ei tarvitse viettää paljon aikaa sellaisen pienen kehyksen rakentamiseen, joka enemmän tai vähemmän ratkaisee kysymyksessäsi mainitsemasi sisennysongelman .

Kommentit

  • Kyllä CE näyttää siltä, että mitä kuvittelin olevan kidutus toteutettavaksi, ja olet oikeassa, olet tehnyt sen melko suoraviivainen. Minun on jatkettava tällä tavoin kielen laajentamista alaspäin kohti lehtiä kattamaan atomit ja operaattorit sekä luettelot ja joukot ja vastaavat, mutta tämä kehys käsittelee kaikki sisennykset, ja sen pitäisi olla toteutettavissa.
  • @berniethejet Tässä esimerkissä määritin esimerkiksi PyIf jollekin, joka arvioi jotain muuta. Voit sen sijaan lisätä määritelmän kohtaan ToPythonString, ts. ToPythonString[PyIf[...]] :=. Näin voit tarkastaa koko ohjelmasi symbolisessa muodossa ilman, että sitä arvioidaan, ennen kuin soitat numeroon ToPythonString.
  • Kiitos, se on hyvä tietää. Kehyksesi on varmasti parempi kuin mikään, jonka olisin mukannut mukanani (nurisi koko matkan, kuten väistämättä olisin, ärsyttänyt Guidon ’ ristisotaa).

Vastaa

Sähköpostiosoitettasi ei julkaista. Pakolliset kentät on merkitty *