mirror of
https://github.com/titanscouting/tra-analysis.git
synced 2024-11-10 15:04:45 +00:00
521 lines
15 KiB
Python
521 lines
15 KiB
Python
from .Hybrid_Utils import Core, ExpressionFunction, ExpressionVariable, ExpressionValue
|
|
import sys
|
|
|
|
if sys.version_info >= (3,):
|
|
xrange = range
|
|
basestring = str
|
|
|
|
class HybridExpressionParser(object):
|
|
|
|
def __init__(self,core,expression,argorder=[],*args,**kwargs):
|
|
super(HybridExpressionParser,self).__init__(*args,**kwargs)
|
|
if isinstance(expression,type(self)): # clone the object
|
|
self.core = core
|
|
self.__args = list(expression.__args)
|
|
self.__vars = dict(expression.__vars) # intenral array of preset variables
|
|
self.__argsused = set(expression.__argsused)
|
|
self.__expr = list(expression.__expr)
|
|
self.variables = {} # call variables
|
|
else:
|
|
self.__expression = expression
|
|
self.__args = argorder;
|
|
self.__vars = {} # intenral array of preset variables
|
|
self.__argsused = set()
|
|
self.__expr = [] # compiled equation tokens
|
|
self.variables = {} # call variables
|
|
self.__compile()
|
|
del self.__expression
|
|
|
|
def __getitem__(self, name):
|
|
if name in self.__argsused:
|
|
if name in self.__vars:
|
|
return self.__vars[name]
|
|
else:
|
|
return None
|
|
else:
|
|
raise KeyError(name)
|
|
|
|
def __setitem__(self,name,value):
|
|
|
|
if name in self.__argsused:
|
|
self.__vars[name] = value
|
|
else:
|
|
raise KeyError(name)
|
|
|
|
def __delitem__(self,name):
|
|
|
|
if name in self.__argsused:
|
|
if name in self.__vars:
|
|
del self.__vars[name]
|
|
else:
|
|
raise KeyError(name)
|
|
|
|
def __contains__(self, name):
|
|
|
|
return name in self.__argsused
|
|
|
|
def __call__(self,*args,**kwargs):
|
|
|
|
if len(self.__expr) == 0:
|
|
return None
|
|
self.variables = {}
|
|
self.variables.update(self.core.constants)
|
|
self.variables.update(self.__vars)
|
|
if len(args) > len(self.__args):
|
|
raise TypeError("<{0:s}.{1:s}({2:s}) object at {3:0=#10x}>() takes at most {4:d} arguments ({5:d} given)".format(
|
|
type(self).__module__,type(self).__name__,repr(self),id(self),len(self.__args),len(args)))
|
|
for i in xrange(len(args)):
|
|
if i < len(self.__args):
|
|
if self.__args[i] in kwargs:
|
|
raise TypeError("<{0:s}.{1:s}({2:s}) object at {3:0=#10x}>() got multiple values for keyword argument '{4:s}'".format(
|
|
type(self).__module__,type(self).__name__,repr(self),id(self),self.__args[i]))
|
|
self.variables[self.__args[i]] = args[i]
|
|
self.variables.update(kwargs)
|
|
for arg in self.__argsused:
|
|
if arg not in self.variables:
|
|
min_args = len(self.__argsused - (set(self.__vars.keys()) | set(self.core.constants.keys())))
|
|
raise TypeError("<{0:s}.{1:s}({2:s}) object at {3:0=#10x}>() takes at least {4:d} arguments ({5:d} given) '{6:s}' not defined".format(
|
|
type(self).__module__,type(self).__name__,repr(self),id(self),min_args,len(args)+len(kwargs),arg))
|
|
expr = self.__expr[::-1]
|
|
args = []
|
|
while len(expr) > 0:
|
|
t = expr.pop()
|
|
r = t(args,self)
|
|
args.append(r)
|
|
if len(args) > 1:
|
|
return args
|
|
else:
|
|
return args[0]
|
|
|
|
def __next(self,__expect_op):
|
|
if __expect_op:
|
|
m = self.core.gematch.match(self.__expression)
|
|
if m != None:
|
|
self.__expression = self.__expression[m.end():]
|
|
g = m.groups()
|
|
return g[0],'CLOSE'
|
|
m = self.core.smatch.match(self.__expression)
|
|
if m != None:
|
|
self.__expression = self.__expression[m.end():]
|
|
return ",",'SEP'
|
|
m = self.core.omatch.match(self.__expression)
|
|
if m != None:
|
|
self.__expression = self.__expression[m.end():]
|
|
g = m.groups()
|
|
return g[0],'OP'
|
|
else:
|
|
m = self.core.gsmatch.match(self.__expression)
|
|
if m != None:
|
|
self.__expression = self.__expression[m.end():]
|
|
g = m.groups()
|
|
return g[0],'OPEN'
|
|
m = self.core.vmatch.match(self.__expression)
|
|
if m != None:
|
|
self.__expression = self.__expression[m.end():]
|
|
g = m.groupdict(0)
|
|
if g['dec']:
|
|
if g["ivalue"]:
|
|
return complex(int(g["rsign"]+"1")*float(g["rvalue"])*10**int(g["rexpoent"]),int(g["isign"]+"1")*float(g["ivalue"])*10**int(g["iexpoent"])),'VALUE'
|
|
elif g["rexpoent"] or g["rvalue"].find('.')>=0:
|
|
return int(g["rsign"]+"1")*float(g["rvalue"])*10**int(g["rexpoent"]),'VALUE'
|
|
else:
|
|
return int(g["rsign"]+"1")*int(g["rvalue"]),'VALUE'
|
|
elif g["hex"]:
|
|
return int(g["hexsign"]+"1")*int(g["hexvalue"],16),'VALUE'
|
|
elif g["oct"]:
|
|
return int(g["octsign"]+"1")*int(g["octvalue"],8),'VALUE'
|
|
elif g["bin"]:
|
|
return int(g["binsign"]+"1")*int(g["binvalue"],2),'VALUE'
|
|
else:
|
|
raise NotImplemented("'{0:s}' Values Not Implemented Yet".format(m.string))
|
|
m = self.core.nmatch.match(self.__expression)
|
|
if m != None:
|
|
self.__expression = self.__expression[m.end():]
|
|
g = m.groups()
|
|
return g[0],'NAME'
|
|
m = self.core.fmatch.match(self.__expression)
|
|
if m != None:
|
|
self.__expression = self.__expression[m.end():]
|
|
g = m.groups()
|
|
return g[0],'FUNC'
|
|
m = self.core.umatch.match(self.__expression)
|
|
if m != None:
|
|
self.__expression = self.__expression[m.end():]
|
|
g = m.groups()
|
|
return g[0],'UNARY'
|
|
return None
|
|
|
|
def show(self):
|
|
"""Show RPN tokens
|
|
|
|
This will print out the internal token list (RPN) of the expression
|
|
one token perline.
|
|
"""
|
|
for expr in self.__expr:
|
|
print(expr)
|
|
|
|
def __str__(self):
|
|
"""str(fn)
|
|
|
|
Generates a Printable version of the Expression
|
|
|
|
Returns
|
|
-------
|
|
str
|
|
Latex String respresation of the Expression, suitable for rendering the equation
|
|
"""
|
|
expr = self.__expr[::-1]
|
|
if len(expr) == 0:
|
|
return ""
|
|
args = [];
|
|
while len(expr) > 0:
|
|
t = expr.pop()
|
|
r = t.toStr(args,self)
|
|
args.append(r)
|
|
if len(args) > 1:
|
|
return args
|
|
else:
|
|
return args[0]
|
|
|
|
def __repr__(self):
|
|
"""repr(fn)
|
|
|
|
Generates a String that correctrly respresents the equation
|
|
|
|
Returns
|
|
-------
|
|
str
|
|
Convert the Expression to a String that passed to the constructor, will constuct
|
|
an identical equation object (in terms of sequence of tokens, and token type/value)
|
|
"""
|
|
expr = self.__expr[::-1]
|
|
if len(expr) == 0:
|
|
return ""
|
|
args = [];
|
|
while len(expr) > 0:
|
|
t = expr.pop()
|
|
r = t.toRepr(args,self)
|
|
args.append(r)
|
|
if len(args) > 1:
|
|
return args
|
|
else:
|
|
return args[0]
|
|
|
|
def __iter__(self):
|
|
return iter(self.__argsused)
|
|
|
|
def __lt__(self, other):
|
|
if isinstance(other, Expression):
|
|
return repr(self) < repr(other)
|
|
else:
|
|
raise TypeError("{0:s} is not an {1:s} Object, and can't be compared to an Expression Object".format(repr(other), type(other)))
|
|
|
|
def __eq__(self, other):
|
|
if isinstance(other, Expression):
|
|
return repr(self) == repr(other)
|
|
else:
|
|
raise TypeError("{0:s} is not an {1:s} Object, and can't be compared to an Expression Object".format(repr(other), type(other)))
|
|
|
|
def __combine(self,other,op):
|
|
if op not in self.core.ops or not isinstance(other,(int,float,complex,type(self),basestring)):
|
|
return NotImplemented
|
|
else:
|
|
obj = type(self)(self)
|
|
if isinstance(other,(int,float,complex)):
|
|
obj.__expr.append(ExpressionValue(other))
|
|
else:
|
|
if isinstance(other,basestring):
|
|
try:
|
|
other = type(self)(other)
|
|
except:
|
|
raise SyntaxError("Can't Convert string, \"{0:s}\" to an Expression Object".format(other))
|
|
obj.__expr += other.__expr
|
|
obj.__argsused |= other.__argsused
|
|
for v in other.__args:
|
|
if v not in obj.__args:
|
|
obj.__args.append(v)
|
|
for k,v in other.__vars.items():
|
|
if k not in obj.__vars:
|
|
obj.__vars[k] = v
|
|
elif v != obj.__vars[k]:
|
|
raise RuntimeError("Predifined Variable Conflict in '{0:s}' two differing values defined".format(k))
|
|
fn = self.core.ops[op]
|
|
obj.__expr.append(ExpressionFunction(fn['func'],fn['args'],fn['str'],fn['latex'],op,False))
|
|
return obj
|
|
|
|
def __rcombine(self,other,op):
|
|
if op not in self.core.ops or not isinstance(other,(int,float,complex,type(self),basestring)):
|
|
return NotImplemented
|
|
else:
|
|
obj = type(self)(self)
|
|
if isinstance(other,(int,float,complex)):
|
|
obj.__expr.insert(0,ExpressionValue(other))
|
|
else:
|
|
if isinstance(other,basestring):
|
|
try:
|
|
other = type(self)(other)
|
|
except:
|
|
raise SyntaxError("Can't Convert string, \"{0:s}\" to an Expression Object".format(other))
|
|
obj.__expr = other.__expr + self.__expr
|
|
obj.__argsused = other.__argsused | self.__expr
|
|
__args = other.__args
|
|
for v in obj.__args:
|
|
if v not in __args:
|
|
__args.append(v)
|
|
obj.__args = __args
|
|
for k,v in other.__vars.items():
|
|
if k not in obj.__vars:
|
|
obj.__vars[k] = v
|
|
elif v != obj.__vars[k]:
|
|
raise RuntimeError("Predifined Variable Conflict in '{0:s}' two differing values defined".format(k))
|
|
fn = self.core.ops[op]
|
|
obj.__expr.append(ExpressionFunction(fn['func'],fn['args'],fn['str'],fn['latex'],op,False))
|
|
return obj
|
|
|
|
def __icombine(self,other,op):
|
|
if op not in self.core.ops or not isinstance(other,(int,float,complex,type(self),basestring)):
|
|
return NotImplemented
|
|
else:
|
|
obj = self
|
|
if isinstance(other,(int,float,complex)):
|
|
obj.__expr.append(ExpressionValue(other))
|
|
else:
|
|
if isinstance(other,basestring):
|
|
try:
|
|
other = type(self)(other)
|
|
except:
|
|
raise SyntaxError("Can't Convert string, \"{0:s}\" to an Expression Object".format(other))
|
|
obj.__expr += other.__expr
|
|
obj.__argsused |= other.__argsused
|
|
for v in other.__args:
|
|
if v not in obj.__args:
|
|
obj.__args.append(v)
|
|
for k,v in other.__vars.items():
|
|
if k not in obj.__vars:
|
|
obj.__vars[k] = v
|
|
elif v != obj.__vars[k]:
|
|
raise RuntimeError("Predifined Variable Conflict in '{0:s}' two differing values defined".format(k))
|
|
fn = self.core.ops[op]
|
|
obj.__expr.append(ExpressionFunction(fn['func'],fn['args'],fn['str'],fn['latex'],op,False))
|
|
return obj
|
|
|
|
def __apply(self,op):
|
|
fn = self.core.unary_ops[op]
|
|
obj = type(self)(self)
|
|
obj.__expr.append(ExpressionFunction(fn['func'],1,fn['str'],fn['latex'],op,False))
|
|
return obj
|
|
|
|
def __applycall(self,op):
|
|
fn = self.core.functions[op]
|
|
if 1 not in fn['args'] or '*' not in fn['args']:
|
|
raise RuntimeError("Can't Apply {0:s} function, dosen't accept only 1 argument".format(op))
|
|
obj = type(self)(self)
|
|
obj.__expr.append(ExpressionFunction(fn['func'],1,fn['str'],fn['latex'],op,False))
|
|
return obj
|
|
|
|
def __add__(self,other):
|
|
return self.__combine(other,'+')
|
|
|
|
def __sub__(self,other):
|
|
return self.__combine(other,'-')
|
|
|
|
def __mul__(self,other):
|
|
return self.__combine(other,'*')
|
|
|
|
def __div__(self,other):
|
|
return self.__combine(other,'/')
|
|
|
|
def __truediv__(self,other):
|
|
return self.__combine(other,'/')
|
|
|
|
def __pow__(self,other):
|
|
return self.__combine(other,'^')
|
|
|
|
def __mod__(self,other):
|
|
return self.__combine(other,'%')
|
|
|
|
def __and__(self,other):
|
|
return self.__combine(other,'&')
|
|
|
|
def __or__(self,other):
|
|
return self.__combine(other,'|')
|
|
|
|
def __xor__(self,other):
|
|
return self.__combine(other,'</>')
|
|
|
|
def __radd__(self,other):
|
|
return self.__rcombine(other,'+')
|
|
|
|
def __rsub__(self,other):
|
|
return self.__rcombine(other,'-')
|
|
|
|
def __rmul__(self,other):
|
|
return self.__rcombine(other,'*')
|
|
|
|
def __rdiv__(self,other):
|
|
return self.__rcombine(other,'/')
|
|
|
|
def __rtruediv__(self,other):
|
|
return self.__rcombine(other,'/')
|
|
|
|
def __rpow__(self,other):
|
|
return self.__rcombine(other,'^')
|
|
|
|
def __rmod__(self,other):
|
|
return self.__rcombine(other,'%')
|
|
|
|
def __rand__(self,other):
|
|
return self.__rcombine(other,'&')
|
|
|
|
def __ror__(self,other):
|
|
return self.__rcombine(other,'|')
|
|
|
|
def __rxor__(self,other):
|
|
return self.__rcombine(other,'</>')
|
|
|
|
def __iadd__(self,other):
|
|
return self.__icombine(other,'+')
|
|
|
|
def __isub__(self,other):
|
|
return self.__icombine(other,'-')
|
|
|
|
def __imul__(self,other):
|
|
return self.__icombine(other,'*')
|
|
|
|
def __idiv__(self,other):
|
|
return self.__icombine(other,'/')
|
|
|
|
def __itruediv__(self,other):
|
|
return self.__icombine(other,'/')
|
|
|
|
def __ipow__(self,other):
|
|
return self.__icombine(other,'^')
|
|
|
|
def __imod__(self,other):
|
|
return self.__icombine(other,'%')
|
|
|
|
def __iand__(self,other):
|
|
return self.__icombine(other,'&')
|
|
|
|
def __ior__(self,other):
|
|
return self.__icombine(other,'|')
|
|
|
|
def __ixor__(self,other):
|
|
return self.__icombine(other,'</>')
|
|
|
|
def __neg__(self):
|
|
return self.__apply('-')
|
|
|
|
def __invert__(self):
|
|
return self.__apply('!')
|
|
|
|
def __abs__(self):
|
|
return self.__applycall('abs')
|
|
|
|
def __getfunction(self,op):
|
|
if op[1] == 'FUNC':
|
|
fn = self.core.functions[op[0]]
|
|
fn['type'] = 'FUNC'
|
|
elif op[1] == 'UNARY':
|
|
fn = self.core.unary_ops[op[0]]
|
|
fn['type'] = 'UNARY'
|
|
fn['args'] = 1
|
|
elif op[1] == 'OP':
|
|
fn = self.core.ops[op[0]]
|
|
fn['type'] = 'OP'
|
|
return fn
|
|
|
|
def __compile(self):
|
|
self.__expr = []
|
|
stack = []
|
|
argc = []
|
|
__expect_op = False
|
|
v = self.__next(__expect_op)
|
|
while v != None:
|
|
if not __expect_op and v[1] == "OPEN":
|
|
stack.append(v)
|
|
__expect_op = False
|
|
elif __expect_op and v[1] == "CLOSE":
|
|
op = stack.pop()
|
|
while op[1] != "OPEN":
|
|
fs = self.__getfunction(op)
|
|
self.__expr.append(ExpressionFunction(fs['func'],fs['args'],fs['str'],fs['latex'],op[0],False))
|
|
op = stack.pop()
|
|
if len(stack) > 0 and stack[-1][0] in self.core.functions:
|
|
op = stack.pop()
|
|
fs = self.core.functions[op[0]]
|
|
args = argc.pop()
|
|
if fs['args'] != '+' and (args != fs['args'] and args not in fs['args']):
|
|
raise SyntaxError("Invalid number of arguments for {0:s} function".format(op[0]))
|
|
self.__expr.append(ExpressionFunction(fs['func'],args,fs['str'],fs['latex'],op[0],True))
|
|
__expect_op = True
|
|
elif __expect_op and v[0] == ",":
|
|
argc[-1] += 1
|
|
op = stack.pop()
|
|
while op[1] != "OPEN":
|
|
fs = self.__getfunction(op)
|
|
self.__expr.append(ExpressionFunction(fs['func'],fs['args'],fs['str'],fs['latex'],op[0],False))
|
|
op = stack.pop()
|
|
stack.append(op)
|
|
__expect_op = False
|
|
elif __expect_op and v[0] in self.core.ops:
|
|
fn = self.core.ops[v[0]]
|
|
if len(stack) == 0:
|
|
stack.append(v)
|
|
__expect_op = False
|
|
v = self.__next(__expect_op)
|
|
continue
|
|
op = stack.pop()
|
|
if op[0] == "(":
|
|
stack.append(op)
|
|
stack.append(v)
|
|
__expect_op = False
|
|
v = self.__next(__expect_op)
|
|
continue
|
|
fs = self.__getfunction(op)
|
|
while True:
|
|
if (fn['prec'] >= fs['prec']):
|
|
self.__expr.append(ExpressionFunction(fs['func'],fs['args'],fs['str'],fs['latex'],op[0],False))
|
|
if len(stack) == 0:
|
|
stack.append(v)
|
|
break
|
|
op = stack.pop()
|
|
if op[0] == "(":
|
|
stack.append(op)
|
|
stack.append(v)
|
|
break
|
|
fs = self.__getfunction(op)
|
|
else:
|
|
stack.append(op)
|
|
stack.append(v)
|
|
break
|
|
__expect_op = False
|
|
elif not __expect_op and v[0] in self.core.unary_ops:
|
|
fn = self.core.unary_ops[v[0]]
|
|
stack.append(v)
|
|
__expect_op = False
|
|
elif not __expect_op and v[0] in self.core.functions:
|
|
stack.append(v)
|
|
argc.append(1)
|
|
__expect_op = False
|
|
elif not __expect_op and v[1] == 'NAME':
|
|
self.__argsused.add(v[0])
|
|
if v[0] not in self.__args:
|
|
self.__args.append(v[0])
|
|
self.__expr.append(ExpressionVariable(v[0]))
|
|
__expect_op = True
|
|
elif not __expect_op and v[1] == 'VALUE':
|
|
self.__expr.append(ExpressionValue(v[0]))
|
|
__expect_op = True
|
|
else:
|
|
raise SyntaxError("Invalid Token \"{0:s}\" in Expression, Expected {1:s}".format(v,"Op" if __expect_op else "Value"))
|
|
v = self.__next(__expect_op)
|
|
if len(stack) > 0:
|
|
op = stack.pop()
|
|
while op != "(":
|
|
fs = self.__getfunction(op)
|
|
self.__expr.append(ExpressionFunction(fs['func'],fs['args'],fs['str'],fs['latex'],op[0],False))
|
|
if len(stack) > 0:
|
|
op = stack.pop()
|
|
else:
|
|
break |