|
""" |
|
This module implements the functionality to take any Python expression as a |
|
string and fix all numbers and other things before evaluating it, |
|
thus |
|
|
|
1/2 |
|
|
|
returns |
|
|
|
Integer(1)/Integer(2) |
|
|
|
We use the ast module for this. It is well documented at docs.python.org. |
|
|
|
Some tips to understand how this works: use dump() to get a nice |
|
representation of any node. Then write a string of what you want to get, |
|
e.g. "Integer(1)", parse it, dump it and you'll see that you need to do |
|
"Call(Name('Integer', Load()), [node], [], None, None)". You do not need |
|
to bother with lineno and col_offset, just call fix_missing_locations() |
|
before returning the node. |
|
""" |
|
|
|
from sympy.core.basic import Basic |
|
from sympy.core.sympify import SympifyError |
|
|
|
from ast import parse, NodeTransformer, Call, Name, Load, \ |
|
fix_missing_locations, Constant, Tuple |
|
|
|
class Transform(NodeTransformer): |
|
|
|
def __init__(self, local_dict, global_dict): |
|
NodeTransformer.__init__(self) |
|
self.local_dict = local_dict |
|
self.global_dict = global_dict |
|
|
|
def visit_Constant(self, node): |
|
if isinstance(node.value, int): |
|
return fix_missing_locations(Call(func=Name('Integer', Load()), |
|
args=[node], keywords=[])) |
|
elif isinstance(node.value, float): |
|
return fix_missing_locations(Call(func=Name('Float', Load()), |
|
args=[node], keywords=[])) |
|
return node |
|
|
|
def visit_Name(self, node): |
|
if node.id in self.local_dict: |
|
return node |
|
elif node.id in self.global_dict: |
|
name_obj = self.global_dict[node.id] |
|
|
|
if isinstance(name_obj, (Basic, type)) or callable(name_obj): |
|
return node |
|
elif node.id in ['True', 'False']: |
|
return node |
|
return fix_missing_locations(Call(func=Name('Symbol', Load()), |
|
args=[Constant(node.id)], keywords=[])) |
|
|
|
def visit_Lambda(self, node): |
|
args = [self.visit(arg) for arg in node.args.args] |
|
body = self.visit(node.body) |
|
n = Call(func=Name('Lambda', Load()), |
|
args=[Tuple(args, Load()), body], keywords=[]) |
|
return fix_missing_locations(n) |
|
|
|
def parse_expr(s, local_dict): |
|
""" |
|
Converts the string "s" to a SymPy expression, in local_dict. |
|
|
|
It converts all numbers to Integers before feeding it to Python and |
|
automatically creates Symbols. |
|
""" |
|
global_dict = {} |
|
exec('from sympy import *', global_dict) |
|
try: |
|
a = parse(s.strip(), mode="eval") |
|
except SyntaxError: |
|
raise SympifyError("Cannot parse %s." % repr(s)) |
|
a = Transform(local_dict, global_dict).visit(a) |
|
e = compile(a, "<string>", "eval") |
|
return eval(e, global_dict, local_dict) |
|
|