Hvordan samles Python abstrakte syntaks træer (AST) i Mathematica?

Jeg vil gerne samle Python “ ast.Module “objekter inde i Mathematica og derefter sende dem til Python via f.eks ExternalEvaluate["exec(astObject)"].

Python ast , parse , eval , exec og kompilér funktioner kan alle fungere på “ ast.Module “objekter, enten udsender dem eller tager dem som input. Men jeg ved ikke, hvordan jeg skal samle dette astObject inden for Mathematica / WL og send det derefter til Python via ExternalEvaluate.

Jeg forsøger at programmere generere Python-kode i MMA (for en genetisk algoritme) og derefter sende den til Python til evaluering. Jeg kunne samle Python-kodestrenge, men så er jeg nødt til at håndtere hele indrykket, som virker som en smerte.

For eksempel er det i Python muligt at gø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 muligt at gøre:

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

for at give det samme resultat (dvs. 12).

Jeg vil dog generere mine bits af Python-kode ("X1=3", "X2=4", "X3=X1*X2") i Mathematica. Disse bits her er enkle nok, men jeg har til hensigt at generere komplette programmer, dvs. udsagn og udtryk, metaprogrammatisk (!). For at gøre det skal jeg derefter finde ud af, hvordan man kan analysere Pythons irriterende fordybninger, hvilket naturligvis er, hvordan det adskiller et sæt udtryk fra det næste, og hvad deres afhængighed er. Jeg er utilbøjelig til at gøre det og regnede med, at det måske var lettere at betjene strukturen ast .

Oprindeligt troede jeg, at jeg muligvis kunne bruge en mellemliggende strengform fra Python “s ast.dump() -funktion, der ser ud 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 da dette astString serierer i det væsentlige astObject Jeg kunne også generere dette i stedet. Dog kan jeg ikke finde nogen måde at få Python til at gøre noget med denne astString.

Er det muligt at oprette denne slags Python-objekt – som min astObject ovenfor – på Mathematica-siden?

B

PS: Her er en beskrivelse af “ ast.Module ” objekter: https://greentreesnakes.readthedocs.io/en/latest/tofrom.html

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

Kommentarer

  • ” For at gøre det skal jeg derefter finde ud af, hvordan jeg kan analysere Python ‘ s irriterende fordybninger […] ” – Jeg ved, du beder AST om at blive brugt, men måske er svar, der beskæftiger sig med Python ‘ s fordybninger også af interesse for dig.
  • Ja, Anton, du har ret, jeg ser også på rent strengbaserede løsninger. Og i den retning så jeg på din FunctionalParsers.m-pakke som en måde til automatisk at importere Python ‘ s EBNF-grammatik for måske at give mig mulighed for at generere pseudo-Python-med-eksplicit-bracketing , eller måske generere Hy-kode (som er Lisp skrevet i Python), som naturligvis har ordentlig parentes. Jeg kan bare ‘ ikke forstå, hvorfor nogen ville oprette et sprog baseret på indrykning, eller hvorfor andre ‘ derefter ville fortsætte med at bruge det …
  • ” Jeg kan bare ‘ ikke forstå, hvorfor nogen ville oprette et sprog baseret på indrykning, eller hvorfor andre ‘ s derefter ville fortsætte med at bruge det … ” – LOL! Mange mennesker bliver mystificeret af det!
  • Brug af ” FunctionalParsers.m ” er en interessant idé, men at forfølge den kan være langt fra en let opgave …
  • Det er måske bedre, du programmerer to softwaremonader, en i WL og en anden i Python, der har det samme arbejdsflowdesign. Derefter overfører du mellem dem med mindre kodeændringer; det er heller ikke nødvendigt med nogen speciel formatering til Python. Her er et eksempel med en monade kaldet LSAMon . (Rul ned til bunden.)

Svar

Jeg er ikke sikker på, hvad du lige leder efter. Jeg tror svaret på dit spørgsmål:

Jeg forsøger at programmere generere Python-kode i MMA […] og send det derefter til Python til evaluering. Jeg kunne samle Python-kodestrenge, men så er jeg nødt til at håndtere hele fordybningen, hvilket virker som en smerte.

Er det muligt at oprette denne slags Python-objekt på Mathematica-siden?

er ret ligetil ved hjælp af 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 brugte eval i stedet for exec, fordi eval returnerer en værdi.)

Kommentarer

  • Tak Anton. Ja, jeg kunne have været mere klar. Lad mig opdatere mit spørgsmål lidt. Grundlæggende vil jeg generere ” ‘ [i ** 2 for i inden for rækkevidde (10)] ‘ ” del i dit svar på MMA-siden, men ikke som en Python-streng, men som det ast.Module-objekt, der kan tilsluttes kompilering og eval og exec.

Svar

Jeg synes, at kompilering af Python-kode som en streng faktisk er enklere end hvad du foreslår. Men jeg ved også, at jeg bare sagde, at det ikke vil overbevise nogen, så her er et eksempel.

Vi definerer et par symbolske hoveder, der repræsenterer vores Python-program i Mathematica, og en funktion, der skal gengives udtryk med disse symbolske hoveder:

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 ] 

Hvad disse funktioner tillader os at gøre er at skrive Python-kode i Mathematica uden at tænke på indrykket, det er lidt ligesom opbygning af Python-kode med ast-modulet.

Dette er et eksempel på gengivelse af det symbolske udtryk 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] 

Ud:

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 let bygge videre på dette og gøre vores symbolske repræsentation af Python-programmet mere 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 hjælperedefinitioner kan vi nu skrive følgende program i en meget læselig 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] 

Ud:

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 endnu, skal du kigge op ” symbolsk C i Mathematica-dokumentationen. Det er dybest set en måde at opbygge en AST til et C-sprogprogram i Mathematica, som derefter kan konverteres til kørbar C-kode. Det er dybest set, hvor vi også er på vej med denne kode, selvom hvis jeg havde til hensigt at foretage en komplet implementering på den måde, ville det ikke se nøjagtigt sådan ud (ast-modulet er bestemt værd at studere, hvis man vil gå ned ad ruten).

Tilbage til det punkt: hvad jeg vil formidle med dette svar er, at du ikke behøver at bruge meget tid på at opbygge en lille ramme, der mere eller mindre løser det indrykningsproblem, du nævner i dit spørgsmål .

Kommentarer

  • Ja CE dette ser meget ud som hvad jeg forestillede mig ville være tortur at implementere, og du har ret, du har gjort det ret ligetil. Jeg bliver nødt til at fortsætte på denne måde med at udvide sproget nedad mod bladene til at dække atomer og operatører og lister og tupler og lignende, men denne ramme håndterer hele indrykket, og det bør være gennemførligt.
  • @berniethejet I dette eksempel definerede jeg f.eks. PyIf til noget, der evalueres til noget andet. Du kan i stedet overveje at tilføje definitionen til ToPythonString, dvs. ToPythonString[PyIf[...]] :=. På den måde kan du inspicere hele dit program i symbolsk form uden at få det evalueret, før du ringer til ToPythonString.
  • Tak, det er godt at vide. Din ramme er bestemt bedre end nogen anden, som jeg ville have brostet sammen (knurret hele vejen, som jeg uundgåeligt ville være, irriteret over Guido ‘ s indrykningstog).

Skriv et svar

Din e-mailadresse vil ikke blive publiceret. Krævede felter er markeret med *