Reflection (by the example of Python)¶
Accessing the AST¶
It is also possible to access code that is written in Python. This works using the ast
module, and works as follows:
In [8]:
SRC = """
def f(x, y):
return 2*x + y**2 + 5
"""
import ast
tree = ast.parse(SRC)
print(ast.dump(tree))
Module(body=[FunctionDef(name='f', args=arguments(args=[arg(arg='x', annotation=None), arg(arg='y', annotation=None)], vararg=None, kwonlyargs=[], kw_defaults=[], kwarg=None, defaults=[]), body=[Return(value=BinOp(left=BinOp(left=BinOp(left=Num(n=2), op=Mult(), right=Name(id='x', ctx=Load())), op=Add(), right=BinOp(left=Name(id='y', ctx=Load()), op=Pow(), right=Num(n=2))), op=Add(), right=Num(n=5)))], decorator_list=[], returns=None)])
It is possible to transcribe the expressions here into the form discussed earlier.
In [5]:
print(ast.dump(tree.body[0].body[0].value))
BinOp(left=BinOp(left=BinOp(left=Num(n=2), op=Mult(), right=Name(id='x', ctx=Load())), op=Add(), right=BinOp(left=Name(id='y', ctx=Load()), op=Pow(), right=Num(n=2))), op=Add(), right=Num(n=5))
In [6]:
from pymbolic.interop.ast import ASTToPymbolic
expr = ASTToPymbolic()(tree.body[0].body[0].value)
print(expr)
2*x + y**2 + 5
But beware when defining languages this way. Python has very well-defined semantics, and the user will expect that your way of executing their code is a good match for their mental model of what the code should do. As such, it may be better to start with a "blank slate" in terms of language design, so as to not run afoul of already formed expectations.
Accessing the Bytecode¶
In [7]:
def f(x, y):
return 2*x + y**2 + 5
import dis
dis.dis(f)
2 0 LOAD_CONST 1 (2) 2 LOAD_FAST 0 (x) 4 BINARY_MULTIPLY 6 LOAD_FAST 1 (y) 8 LOAD_CONST 1 (2) 10 BINARY_POWER 12 BINARY_ADD 14 LOAD_CONST 2 (5) 16 BINARY_ADD 18 RETURN_VALUE