12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879 |
- """
- 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, Str, 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_Num(self, node):
- if isinstance(node.n, int):
- return fix_missing_locations(Call(func=Name('Integer', Load()),
- args=[node], keywords=[]))
- elif isinstance(node.n, 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=[Str(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)
|