Hvordan montere Python abstrakte syntaks trær (AST) i Mathematica?

Jeg vil gjerne montere Python « ast.Module «objekter inne i Mathematica og send dem deretter til Python via f.eks ExternalEvaluate["exec(astObject)"].

Python ast , analyse , eval , exec og kompilere funksjoner kan alle fungere på « ast.Module «objekter, enten å matche ut dem eller ta dem som innganger. Imidlertid vet jeg ikke hvordan jeg skal montere dette astObject innen Mathematica / WL og send den deretter til Python via ExternalEvaluate.

Jeg prøver å generere Python-kode programmatisk i MMA (for en genetisk algoritme) og deretter sende den til Python for evaluering. Jeg kunne sette sammen Python-kodestrenger, men da må jeg håndtere all fordypningen, noe som virker som en smerte.

For eksempel er det i Python mulig å gjøre følgende:

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

Og selvfølgelig fra MMA er det mulig å gjøre:

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

for å gi det samme resultat (dvs. 12).

Imidlertid vil jeg generere mine biter av Python-kode ("X1=3", "X2=4", "X3=X1*X2") i Mathematica. Disse bitene her er enkle nok, men jeg har tenkt å generere komplette programmer, dvs. utsagn og uttrykk, metaprogrammatisk (!). For å gjøre det må jeg da finne ut hvordan man kan analysere Pythons irriterende fordypninger, som selvfølgelig er hvordan det skiller ett sett med uttrykk fra det neste og hva deres avhengighet er. Jeg avskyr å gjøre det, og skjønte at det kan være lettere å operere på ast strukturen.

Opprinnelig hadde jeg tenkt at jeg kanskje kunne bruke en mellomliggende strengform fra Python «s ast.dump() -funksjon som ser ut som:

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

og siden denne astString Serierer i hovedsak astObject Jeg kan også generere dette i stedet. Jeg kan imidlertid ikke finne noen måte å få Python til å gjøre noe med denne astString.

Er det mulig å lage denne typen Python-objekt – som min astObject over – på Mathematica-siden?

B

PS: Her er en beskrivelse av « ast.Module » objekter: https://greentreesnakes.readthedocs.io/en/latest/tofrom.html

PPS: Jeg har krysspost dette på Wolfram Community: https://community.wolfram.com/groups/-/m/t/2070851

Kommentarer

  • » For å gjøre det må jeg finne ut hvordan jeg kan analysere Python ‘ s irriterende fordypninger […] » – Jeg vet at du ber om at AST skal brukes, men kanskje svar som handler om Python ‘ s fordypninger er også av interesse for deg.
  • Ja, Anton, du har rett, jeg ser også på rent strengbaserte løsninger. Og i den retning så jeg på FunctionalParsers.m-pakken din som en måte å automatisk importere Python ‘ EBNF-grammatikk for å kanskje tillate meg å generere pseudo-Python-med-eksplisitt-bracketing , eller for å kanskje generere Hy-kode (som er Lisp skrevet i Python) som selvfølgelig har riktig parentes. Jeg kan bare ‘ ikke forstå hvorfor noen vil lage et språk basert på innrykk, eller hvorfor andre ‘ s da fortsetter å bruke det …
  • » Jeg kan bare ‘ t forstå hvorfor noen ville lage et språk basert på innrykk, eller hvorfor andre ‘ s da vil fortsette å bruke den … » – LOL! Mange mennesker blir mystifisert av det!
  • Bruk av » FunctionalParsers.m » er en interessant ide, men å forfølge den kan være langt fra en enkel oppgave …
  • Det kan være bedre å programmere to programvaremonader, en i WL og en annen i Python, som har samme arbeidsflytdesign. Så overfører du mellom dem med mindre kodeendringer; det er heller ikke nødvendig med noen spesiell formatering for Python. Her er et eksempel med en monade kalt LSAMon . (Bla til bunnen.)

Svar

Jeg er ikke sikker på hva du leter etter. Jeg tror svaret på spørsmålet ditt:

Jeg prøver å programmere generere Python-kode i MMA […] og send den deretter til Python for evaluering. Jeg kunne sette sammen Python-kodestrenger, men da må jeg håndtere all fordypningen, noe som virker som en smerte.

Er det mulig å lage denne typen Python-objekt på Mathematica-siden?

er ganske grei å bruke ExternalEvaluate og Python «s » ast » bibliotek.

Her er et eksempel:

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

(jeg brukte eval i stedet for exec, fordi eval returnerer en verdi.)

Kommentarer

  • Takk Anton. Ja, jeg kunne ha vært tydeligere. La meg oppdatere spørsmålet mitt litt. I utgangspunktet vil jeg generere » ‘ [i ** 2 for i innen rekkevidde (10)] ‘ » del i svaret ditt på MMA-siden, men ikke som en Python-streng, men som det ast.Module-objektet som kan plugges i kompilering og eval og exec.

Svar

Jeg tror at å kompilere Python-kode som en streng faktisk er enklere enn det du foreslår. Men jeg vet også at jeg bare sa at det ikke vil overbevise noen, så her er et eksempel.

Vi definerer et par symbolske hoder som vil representere vårt Python-program i Mathematica, og en funksjon som skal gjengis uttrykk med de symbolske hodene:

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 ] 

Hva disse funksjonene tillater oss å gjøre er å skrive Python-kode i Mathematica uten å tenke på fordypningen, den er litt som bygge Python-kode med ast-modulen.

Dette er et eksempel på å gjengi det symbolske uttrykket som en streng:

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] 

Ut:

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

Vi kan enkelt bygge videre på dette og gjøre vår symbolske fremstilling av Python-programmet mer beskrivende.

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

Med disse hjelperdefinisjonene kan vi nå skrive følgende program i en veldig lesbar stil.

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] 

Ut:

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) 

Hvis du ikke har det ennå, kan du slå opp » symbolsk C i Mathematica-dokumentasjonen. Det er i utgangspunktet en måte å bygge en AST for et C-språkprogram i Mathematica, som deretter kan konverteres til kjørbar C-kode. Det er i utgangspunktet dit vi også er på vei med denne koden, men hvis jeg hadde tenkt å gjøre en fullstendig implementering som den, ville den ikke se ut akkurat slik (ast-modulen er absolutt verdt å studere hvis man vil gå nedover ruten). / p>

Tilbake til poenget: det jeg ønsker å formidle med dette svaret er at du ikke trenger å bruke mye tid på å bygge et lite rammeverk som mer eller mindre løser fordypningsproblemet du nevner i spørsmålet ditt .

Kommentarer

  • Ja CE dette ser veldig ut som det jeg forestilte meg ville være tortur å implementere, og du har rett, du har klart det ganske grei. Jeg blir nødt til å fortsette på denne måten og utvide språket nedover mot bladene for å dekke atomer og operatører og lister og tupler og slikt, men dette rammeverket håndterer all fordypningen, og det bør være mulig.
  • @berniethejet I dette eksemplet definerte jeg for eksempel PyIf til noe som evalueres til noe annet. Du kan vurdere å i stedet legge til definisjonen til ToPythonString, dvs. ToPythonString[PyIf[...]] :=. På den måten kan du inspisere hele programmet ditt i symbolsk form, uten at det blir evaluert før du ringer til ToPythonString.
  • Takk, det er godt å vite. Ditt rammeverk er absolutt bedre enn noe annet jeg ville ha brokket sammen (knurrende hele veien, som jeg uunngåelig ville være, irritert over Guido ‘ s innrykkstog).

Legg igjen en kommentar

Din e-postadresse vil ikke bli publisert. Obligatoriske felt er merket med *