forked from sympy/sympy
/
ast_parser.py
112 lines (85 loc) · 3.6 KB
/
ast_parser.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
import compiler
import parser
from compiler.transformer import Transformer
from compiler.visitor import ASTVisitor
from compiler.ast import CallFunc, Name, Const, Tuple
from compiler.pycodegen import ExpressionCodeGenerator
import re
from basic import Basic
from function import FunctionClass
from symbol import Symbol
###################################################3
# HELPERS
###################################################3
_is_integer = re.compile(r'\A\d+(l|L)?\Z').match
int_types = (int, long)
float_types = (float)
complex_types = (complex)
string_types = (str) # XXX: unicode?
classes = Basic
###################################################3
# CODE
###################################################3
class SymPyTransformer(Transformer):
def __init__(self, local_dict, global_dict):
Transformer.__init__(self)
self.symbol_class = 'Symbol'
self.local_dict = local_dict
self.global_dict = global_dict
def atom_number(self, nodelist):
n = Transformer.atom_number(self, nodelist)
number, lineno = nodelist[0][1:]
if _is_integer(number):
n = Const(long(number), lineno)
return CallFunc(Name('Integer'), [n])
if number.endswith('j'):
n = Const(complex(number), lineno)
return CallFunc(Name('sympify'), [n])
n = Const(number, lineno)
return CallFunc(Name('Real'), [n])
def atom_name(self, nodelist):
name, lineno = nodelist[0][1:]
if self.local_dict.has_key(name):
name_obj = self.local_dict[name]
return Const(name_obj, lineno=lineno)
elif self.global_dict.has_key(name):
name_obj = self.global_dict[name]
if isinstance(name_obj, (Basic, type)) or callable(name_obj):
return Const(name_obj, lineno=lineno)
elif name in ['True', 'False']:
return Const(eval(name), lineno=lineno)
symbol_obj = Symbol(name)
self.local_dict[name] = symbol_obj
return Const(symbol_obj, lineno=lineno)
def lambdef(self, nodelist):
#this is python stdlib symbol, not SymPy symbol:
from sympy import stdlib_symbol
if nodelist[2][0] == stdlib_symbol.varargslist:
names, defaults, flags = self.com_arglist(nodelist[2][1:])
else:
names = defaults = ()
flags = 0
lineno = nodelist[1][2]
code = self.com_node(nodelist[-1])
assert not defaults,`defaults` # sympy.Lambda does not support optional arguments
assert len(names) <= 1
if len(names) > 0:
argument = CallFunc( Name('Symbol'), [Const(names[0], lineno=lineno)])
else:
argument = CallFunc( Name('Symbol'), [Const('x')])
return CallFunc(Name('Lambda'),[argument, code])
class SymPyParser:
def __init__(self, local_dict={}): #Contents of local_dict change, but it has proper effect only in global scope
global_dict = {}
exec 'from sympy import *' in global_dict
self.r_transformer = SymPyTransformer(local_dict, global_dict)
self.local_dict = local_dict
self.global_dict = global_dict
def parse_expr(self, ws_expression):
expression = ws_expression.strip() #in case of " x"
ast_tree = parser.expr(expression)
ast_tree = self.r_transformer.transform(ast_tree)
compiler.misc.set_filename('<sympify>', ast_tree)
code = ExpressionCodeGenerator(ast_tree).getCode()
parsed_expr = eval(code, self.local_dict, self.global_dict) #Changed order to prefer sympy objects to user defined
return parsed_expr