Jeremy Sanders wrote:
Colin J. Williams wrote:
Could you not have functions a and b each of which returns a NumArray
instance?
Your expression would then be something like a(..)+2*b(..).
The user enters the expression (yes - I'm aware of the possible security
issues), as it is a scientific application. I don't think they'd like to
put () after each variable name.
I could always munge the expression after the user enters it, of course.
Jeremy
Alternatively, you could build your own expression calculator, and initialize
the objects if necessary as they are evaluated. If you are happy with Python
syntax for your expressiones then the stdlib compiler package is helpful. The
example below is not tested beyond what you see. It's a bit verbose, but most
of the code is boilerplate.
a = 3
b = 4
calc('a * b')
using a
using b
12 calc('a * b ** (b - a) * "a"')
using a
using b
using b
using a
'aaaaaaaaaaaa' calc("0 and a or b")
using b
4 calc("1 and a or b")
using a
3 calc("1 and a or c")
using a
3 calc("0 and a or c")
Undefined symbol: c
HTH, Michael
-----------------
import compiler
class CalcError(Exception):
def __init__(self,error,descr = None,node = None):
self.error = error
self.descr = descr
self.node = node
def __repr__(self):
return "%s: %s" % (self.error, self.descr)
__str__ = __repr__
class LazyCalc(object):
def __init__(self, namespace):
self._cache = {} # dispatch table
self.context = namespace
def visit(self, node,**kw):
cls = node.__class__
meth = self._cache.setdefault(cls,
getattr(self,'visit'+cls.__name__,self.default))
return meth(node, **kw)
def visitExpression(self, node, **kw):
return self.visit(node.node)
# Binary Ops
def visitAdd(self,node,**kw):
return self.visit(node.left) + self.visit(node.right)
def visitDiv(self,node,**kw):
return self.visit(node.left) / self.visit(node.right)
def visitFloorDiv(self,node,**kw):
return self.visit(node.left) // self.visit(node.right)
def visitLeftShift(self,node,**kw):
return self.visit(node.left) << self.visit(node.right)
def visitMod(self,node,**kw):
return self.visit(node.left) % self.visit(node.right)
def visitMul(self,node,**kw):
return self.visit(node.left) * self.visit(node.right)
def visitPower(self,node,**kw):
return self.visit(node.left) ** self.visit(node.right)
def visitRightShift(self,node,**kw):
return self.visit(node.left) >> self.visit(node.right)
def visitSub(self,node,**kw):
return self.visit(node.left) - self.visit(node.right)
# Unary ops
def visitNot(self,node,*kw):
return not self.visit(node.expr)
def visitUnarySub(self,node,*kw):
return -self.visit(node.expr)
def visitInvert(self,node,*kw):
return ~self.visit(node.expr)
def visitUnaryAdd(self,node,*kw):
return +self.visit(node.expr)
# Flow Control
def visitAnd(self,node,**kw):
for arg in node.nodes:
val = self.visit(arg)
if not val:
return val
return val
def visitOr(self,node,**kw):
for arg in node.nodes:
val = self.visit(arg)
if val:
return val
return val
# Logical Ops
def visitBitand(self,node,**kw):
return reduce(lambda a,b: a & b,[self.visit(arg) for arg in node.nodes])
def visitBitor(self,node,**kw):
return reduce(lambda a,b: a | b,[self.visit(arg) for arg in node.nodes])
def visitBitxor(self,node,**kw):
return reduce(lambda a,b: a ^ b,[self.visit(arg) for arg in node.nodes])
def visitCompare(self,node,**kw):
comparisons = {
"<": operator.lt, # strictly less than
"<=": operator.le,# less than or equal
">": operator.gt, # strictly greater than
">=": operator.ge, # greater than or equal
"==": operator.eq, # equal
"!=": operator.ne, # not equal
"<>": operator.ne, # not equal
"is": operator.is_, # object identity
"is not": operator.is_not # negated object identity
}
obj = self.visit(node.expr)
for op, compnode in node.ops:
compobj = self.visit(compnode)
if not comparisons[op](obj, compobj):
return False
obj = compobj
return True
# Values
def visitCallFunc(self,node,**kw):
raise CalcError("Functions not supported", node.node)
def visitName(self, node, **kw):
"""LazyEvaluation"""
name = node.name
try:
val = eval(name, self.context)
except NameError:
raise CalcError("Undefined symbol",name)
except:
raise
print "using %s" % name # init if necessary here
return val
def visitConst(self, node, **kw):
return node.value
# Other
def default(self, node, **kw):
"""Anything not expressly allowed is forbidden"""
raise CalcError("Not Allowed",
node.__class__.__name__,node)
def calc(source, context = None):
walker = LazyCalc(context or globals())
try:
ast = compiler.parse(source,"eval")
except SyntaxError, err:
raise
try:
return walker.visit(ast)
except CalcError, err:
return err