Hoe Python abstracte syntaxisbomen (AST) in Mathematica samen te stellen?

Ik wil Python “ ast.Module “objecten in Mathematica en verzend ze vervolgens naar Python via bijv ExternalEvaluate["exec(astObject)"].

De Python ast , ontleden , eval , exec en compileren functies kunnen allemaal werken op “ ast.Module “-objecten, door ze uit te voeren of ze als invoer te nemen. Ik weet echter niet hoe ik dit astObject in Mathematica / WL moet samenstellen en stuur het vervolgens naar Python via ExternalEvaluate.

Ik probeer Python-code programmatisch te genereren in MMA (voor een genetisch algoritme) en verzend het vervolgens naar Python voor evaluatie. Ik zou Python code strings kunnen samenstellen, maar dan moet ik alle inspringingen afhandelen, wat lastig lijkt.

In Python is het bijvoorbeeld mogelijk om het volgende te doen:

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

En natuurlijk is het vanuit MMA mogelijk om:

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

hetzelfde te doen resultaat (dwz 12).

Ik wil echter mijn stukjes Python-code genereren ("X1=3", "X2=4", "X3=X1*X2") in Mathematica. Deze stukjes zijn hier eenvoudig genoeg, maar ik ben van plan om metaprogrammatisch (!) volledige programmas te genereren, dwz statements en uitdrukkingen. Om dat te doen moet ik zoek uit hoe je de vervelende inspringingen van Python kunt ontleden, en dat is natuurlijk hoe het de ene set uitdrukkingen van de volgende onderscheidt en wat hun afhankelijkheden zijn. Ik heb een hekel aan dat te doen, en ik dacht dat het misschien gemakkelijker zou zijn om te werken met de ast -structuur.

Oorspronkelijk had ik gedacht dat ik misschien een tussenliggende string-vorm zou kunnen gebruiken van Python “s ast.dump() functie die eruitziet als:

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

en aangezien dit astString serialiseert in wezen de astObject Ik zou dit ook kunnen genereren. Ik kan echter geen manier vinden om Python iets te laten doen met dit astString.

Is het mogelijk om dit soort Python-objecten te maken – zoals mijn astObject hierboven – aan de Mathematica-kant?

B

PS: Hier is een beschrijving van de “ ast.Module ” objecten: https://greentreesnakes.readthedocs.io/en/latest/tofrom.html

PPS: ik heb dit gepost op Wolfram Community: https://community.wolfram.com/groups/-/m/t/2070851

Reacties

  • ” Om dat te doen, moet ik uitzoeken hoe ik Python ‘ s vervelende inkepingen kan parseren […] ” – Ik weet dat je AST vraagt, maar misschien zijn antwoorden die te maken hebben met de inspringingen van Python ‘ s ook interessant voor jou.
  • Ja, Anton, je hebt gelijk, ik kijk ook naar puur string-gebaseerde oplossingen. En in die zin keek ik naar je FunctionalParsers.m-pakket als een manier om automatisch de EBNF-grammatica van Python ‘ te importeren, zodat ik misschien pseudo-Python-met-expliciete-bracketing kan genereren , of misschien om Hy-code te genereren (dit is Lisp geschreven in Python) die natuurlijk de juiste bracketing heeft. Ik kan ‘ niet begrijpen waarom iemand een taal zou maken op basis van inspringing, of waarom andere ‘ s het dan zouden gebruiken …
  • ” Ik kan ‘ gewoon niet begrijpen waarom iemand een taal zou maken op basis van inspringing, of waarom andere ‘ s het dan zouden gebruiken … ” – LOL! Veel mensen zijn daar verbaasd over!
  • Het gebruik van ” FunctionalParsers.m ” is een interessant idee, maar het nastreven ervan is misschien verre van een gemakkelijke onderneming …
  • Misschien is het beter om twee softwaremonaden te programmeren, één in WL en één in Python, die hetzelfde workflow-ontwerp hebben. Vervolgens zet u tussen hen over met kleine codewijzigingen; ook is er geen speciale opmaak voor Python nodig. Hier is een voorbeeld met een monade genaamd LSAMon . (Scroll naar beneden.)

Antwoord

Ik weet niet zeker wat u precies zoekt. Ik denk dat het antwoord op je vraag:

Ik probeer Python-code programmatisch te genereren in MMA […] en dien het vervolgens ter evaluatie in bij Python. Ik zou Python-codetekenreeksen kunnen samenstellen, maar dan moet ik alle inspringingen afhandelen, wat lastig lijkt.

Is het mogelijk om dit soort Python-objecten aan de Mathematica-kant te maken?

is vrij eenvoudig met ExternalEvaluate en Python “s ” ast ” bibliotheek.

Hier is een voorbeeld:

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

(ik gebruikte eval in plaats van exec, omdat eval een waarde retourneert.)

Opmerkingen

  • Bedankt Anton. Ja, ik had duidelijker kunnen zijn. Laat me mijn vraag een beetje bijwerken. In principe wil ik de ” ‘ [i ** 2 voor i in bereik (10)] ‘ ” deel aan je antwoord aan de MMA-kant, maar niet als een Python-string, maar als het ast.Module-object dat kan worden aangesloten op compileren en evalueren en uitvoeren.

Antwoord

Ik denk dat het compileren van Python-code als een string eigenlijk eenvoudiger is dan wat je voorstelt. Maar ik weet ook dat ik gewoon zeg dat het niemand zal overtuigen, dus hier is een voorbeeld.

We definiëren een aantal symbolische hoofden die ons Python-programma in Mathematica vertegenwoordigen, en een functie om uitdrukkingen met die symbolische koppen:

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 ] 

Wat deze functies ons toestaan is om Python-code in Mathematica te schrijven zonder na te denken over de inspringing, het is een beetje zoals Python-code bouwen met de ast-module.

Dit is een voorbeeld van het weergeven van de symbolische uitdrukking als een string:

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

We kunnen hier gemakkelijk op voortbouwen en onze symbolische representatie van het Python-programma beschrijvend maken.

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

Met deze hulpdefinities kunnen we nu het volgende programma in een zeer leesbare stijl schrijven.

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) 

Als je dit nog niet hebt gedaan, zoek dan ” symbolisch C in de Mathematica-documentatie. Het is eigenlijk een manier om een AST te bouwen voor een C-taalprogramma in Mathematica dat vervolgens kan worden omgezet in uitvoerbare C-code. Dat is eigenlijk waar we ook met deze code naartoe gaan, maar als ik van plan was om zon complete implementatie te maken, zou het er niet precies zo uitzien (de ast-module is zeker de moeite waard om te bestuderen als je de route wilt inslaan). / p>

Terug naar het punt: wat ik met dit antwoord wil overbrengen, is dat je niet veel tijd hoeft te besteden aan het bouwen van een klein raamwerk dat min of meer het indentatieprobleem oplost dat je in je vraag noemt .

Reacties

  • Ja CE dit lijkt heel erg op wat ik me voorstelde dat marteling zou zijn om te implementeren, en je hebt gelijk, je hebt het gehaald vrij eenvoudig. Ik zal op deze manier door moeten gaan met het uitbreiden van de taal naar beneden richting de bladeren om atomen en operatoren en lijsten en tupels en dergelijke te omvatten, maar dit raamwerk behandelt alle inspringingen, en het zou goed moeten zijn.
  • @berniethejet In dit voorbeeld heb ik bijvoorbeeld PyIf gedefinieerd als iets dat in iets anders evalueert. U kunt overwegen om in plaats daarvan de definitie toe te voegen aan ToPythonString, d.w.z. ToPythonString[PyIf[...]] :=. Op die manier kun je je hele programma in symbolische vorm bekijken, zonder dat het geëvalueerd wordt totdat je ToPythonString aanroept.
  • Bedankt, dat is goed om te weten. Jouw raamwerk is zeker beter dan alle andere die ik in elkaar zou hebben geplaveid (de hele weg mopperen, zoals ik onvermijdelijk zou zijn, geïrriteerd door Guido ‘ s indentation crusade).

Geef een reactie

Het e-mailadres wordt niet gepubliceerd. Vereiste velden zijn gemarkeerd met *