Jak sestavit Python abstraktní syntaxe stromy (AST) v Mathematica?

Chtěl bych sestavit Python „ ast.Module „objekty uvnitř Mathematica a poté je odeslat do Pythonu prostřednictvím např ExternalEvaluate["exec(astObject)"].

Python ast , analyzovat , eval , exec a kompilace funkce mohou všechny fungovat na „ ast.Module „objekty, a to buď na výstup, nebo jako vstupy. Nicméně nevím, jak sestavit astObject v rámci Mathematica / WL a poté jej odeslat do Pythonu pomocí ExternalEvaluate.

Snažím se programově generovat kód Pythonu v MMA (pro genetický algoritmus) a poté jej odeslat do Pythonu pro vyhodnocení. Mohl bych sestavit řetězce kódu Pythonu, ale pak musím zvládnout všechny odsazení, které vypadají jako bolest.

Například v Pythonu je možné udělat následující:

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

A samozřejmě z MMA je možné udělat:

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

získat stejné výsledek (tj. 12).

Chci však vygenerovat své kousky kódu Pythonu ("X1=3", "X2=4", "X3=X1*X2") v Mathematice. Tyto bity jsou zde dostatečně jednoduché, ale mám v úmyslu generovat úplné programy, tj. příkazy a výrazy, metaprogramově (!). K tomu musím přijít na to, jak analyzovat otravné odsazení Pythonu, což samozřejmě odlišuje jednu sadu výrazů od další a jaké jsou jejich závislosti. Nerad to dělám a přišel jsem na to, že by mohlo být jednodušší pracovat se strukturou ast .

Původně jsem si myslel, že bych mohl použít přechodnou formu řetězce z funkce Pythonu „s ast.dump(), která vypadá takto:

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

a od této astString v zásadě serializuje astObject místo toho bych to mohl také vygenerovat. Nemohu však najít způsob, jak přimět Python, aby s tím astString.

Je možné vytvořit tento druh objektu Pythonu – jako můj astObject výše – na straně Mathematica?

B

PS: Zde je popis objektů „ ast.Module „: https://greentreesnakes.readthedocs.io/en/latest/tofrom.html

PPS: Zveřejnil jsem to křížově na komunitě Wolfram: https://community.wolfram.com/groups/-/m/t/2070851

Komentáře

  • “ K tomu musím zjistit, jak analyzovat nepříjemné odsazení Pythonu ‚ […] “ – Vím, že žádáte o použití AST, ale možná vás zajímají i odpovědi, které se zabývají odsazením Pythonu ‚.
  • Ano, Anton, máš pravdu, dívám se také na čistě řetězcová řešení. A v těchto liniích jsem se díval na váš balíček FunctionalParsers.m jako na způsob automatického importu Python ‚ s EBNF gramatiky, aby mi snad umožnil generovat pseudo-Python-with-explicit-bracketing , nebo snad vygenerovat Hy kód (což je Lisp napsaný v Pythonu), který má samozřejmě správný bracketing. Prostě nemohu ‚ pochopit, proč by někdo vytvořil jazyk na základě odsazení, nebo proč by jej ostatní ‚ mohli používat …
  • “ Jen ‚ nerozumím, proč by někdo vytvořil jazyk na základě odsazení, nebo proč by jej potom používali ostatní ‚ s … “ – LOL! Mnoho lidí je tím zmateno!
  • Používání “ FunctionalParsers.m “ je zajímavý nápad, ale věnovat se mu může být daleko od snadného podnikání …
  • Možná vám bude lépe programovat dva softwarové monády, jeden ve WL a druhý v Pythonu, které mají stejný design pracovního postupu. Poté mezi nimi přenášíte drobné změny kódu; také není nutné žádné speciální formátování pro Python. Zde je příklad monády s názvem LSAMon . (Přejděte dolů.)

Odpovědět

Nejsem si jistý, co přesně hledáte. Myslím, že odpověď na vaši otázku:

Snažím se programově generovat kód Pythonu v MMA […] a poté jej odešlete do Pythonu k vyhodnocení. Mohl bych sestavit řetězce kódu Pythonu, ale pak musím zvládnout všechny odsazení, které vypadají jako bolest.

Je možné vytvořit tento druh objektu Pythonu na straně Mathematica?

je docela jednoduché používat a Python „s “ ast “ knihovna.

Zde je příklad:

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

(použil jsem eval místo exec, protože eval vrací hodnotu.)

Komentáře

  • Díky Anton. Ano, mohl jsem být jasnější. Dovolte mi trochu aktualizovat svoji otázku. V zásadě chci vygenerovat “ ‚ [i ** 2 pro i v rozsahu (10)] ‚ “ část vaší odpovědi na straně MMA, ale ne jako řetězec v Pythonu, ale jako objekt ast.Module, který lze připojit do kompilace a eval a exec.

Odpověď

Myslím si, že kompilace kódu Pythonu jako řetězce je ve skutečnosti jednodušší než to, co navrhujete. Ale také vím, že jen říkám, že nikoho nepřesvědčím, takže tady je příklad.

Definujeme několik symbolických hlav, které budou představovat náš program Python v Mathematice, a funkci, která se má vykreslit výrazy s těmito symbolickými hlavami:

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 ] 

To, co nám tyto funkce umožňují, je psát kód Pythonu v Mathematice bez přemýšlení o odsazení, je to trochu jako vytváření kódu Pythonu pomocí modulu ast.

Toto je příklad vykreslení symbolického výrazu jako řetězce:

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

Na tom můžeme snadno stavět a učinit svou symbolickou reprezentaci programu Python více popisnou.

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

S těmito definicemi pomocníků můžeme nyní psát následující program ve velmi čitelném stylu.

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) 

Pokud ještě nemáte, vyhledejte “ symbolické C v dokumentaci Mathematica. Je to v podstatě způsob, jak vytvořit program pro jazyk C v programu Mathematica, který lze poté převést na spustitelný kód C. To je v podstatě to, kam míříme také s tímto kódem, i když kdybych měl v úmyslu provést takovou úplnou implementaci, nevypadalo by to přesně takto (modul ast určitě stojí za to studovat, pokud chce člověk jít cestou dolů).

Zpět k věci: touto odpovědí vám chci sdělit, že nemusíte trávit spoustu času vytvářením malého rámce, který víceméně vyřeší problém s odsazením, který jste zmínili ve své otázce .

Komentáře

  • Ano CE, vypadá to velmi podobně jako to, co jsem si představoval, že bude mučením implementovat, a máte pravdu, zvládli jste to docela jednoduché. Budu muset pokračovat tímto způsobem a rozšiřovat jazyk směrem dolů k listům, aby pokryl atomy a operátory a seznamy a n-tice atd., Ale tento rámec zpracovává všechny odsazení a mělo by to být proveditelné.
  • @berniethejet V tomto příkladu jsem definoval například PyIf něco, co se vyhodnotí v něco jiného. Můžete zvážit přidání definice do ToPythonString, tj. ToPythonString[PyIf[...]] :=. Tímto způsobem můžete zkontrolovat celý svůj program v symbolické podobě, aniž byste jej museli vyhodnocovat, dokud nezavoláte ToPythonString.
  • Díky, to je dobré vědět. Váš rámec je rozhodně lepší než jakýkoli jiný, který bych dláždil dohromady (reptající celou cestu, jak bych nevyhnutelně byl, naštvaný na Guido ‚ s odsazovací křížovou výpravou).

Napsat komentář

Vaše e-mailová adresa nebude zveřejněna. Vyžadované informace jsou označeny *