Wow, thanks so much guys!
"Michael Spencer" <[EMAIL PROTECTED]> wrote in message news:[EMAIL PROTECTED] > Brian Kazian wrote: >> Thanks for the help, I didn't even think of that. >> >> I'm guessing there's no easy way to handle exponents or logarithmic >> functions? I will be running into these two types as well. >> "Artie Gold" <[EMAIL PROTECTED]> wrote in message >> news:[EMAIL PROTECTED] >> > > eval will handle exponents just fine: try eval("2**16") > in fact, it will evaluate any legal python expression* > > logarithmic functions live in the math module, so you will either need to > import the functions/symbols you want from math, or give that namespace to > eval: > > >>> import math > >>> eval("log(e)", vars(math)) > 1.0 > >>> > > * this means that, eval("sys.exit()") will likely stop your interpreter, > and there are various other inputs with possibly harmful consequences. > > Concerns like these may send you back to your original idea of doing your > own expression parsing. The good news is that the compiler package will > parse any legal Python expression, and return an Abstract Syntax Tree. > It's straightforward to walk the tree and achieve fine-grain control over > evaluation. > > Here's an example of a math calculator that doesn't use eval. It > evaluates any Python scalar numeric expression (i.e., excludes container > types), and only those symbols and functions that are explicity specified. > This code is barely tested and probably not bullet-proof. But with care > and testing it should be possible to achieve a good balance of > functionality and security. > > > import compiler > import types > import math > > # create a namespace of useful funcs > mathfuncs = {"abs":abs, "min": min, "max": max} > mathfuncs.update((funcname, getattr(math,funcname)) for funcname in > vars(math) > if not funcname.startswith("_")) > > mathsymbols = {"pi":math.pi, "e":math.e} > > # define acceptable types - others will raise an exception if > # entered as literals > mathtypes = (int, float, long, complex) > > class CalcError(Exception): > def __init__(self,error,descr = None,node = None): > self.error = error > self.descr = descr > self.node = node > #self.lineno = getattr(node,"lineno",None) > > def __repr__(self): > return "%s: %s" % (self.error, self.descr) > __str__ = __repr__ > > > class EvalCalc(object): > > def __init__(self): > self._cache = {} # dispatch table > > 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) > > def visitConst(self, node, **kw): > value = node.value > if isinstance(value, mathtypes): > return node.value > else: > raise CalcError("Not a numeric type", value) > > # 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) > > # Logical Ops > def visitAnd(self,node,**kw): > return reduce(lambda a,b: a and b,[self.visit(arg) for arg in > node.nodes]) > 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 > def visitOr(self,node,**kw): > return reduce(lambda a,b: a or b,[self.visit(arg) for arg in > node.nodes]) > > def visitCallFunc(self,node,**kw): > > func = self.visit(node.node, context = "Callable") > # Handle only positional args > posargs = [self.visit(arg) for arg in node.args] > > return func(*posargs) > > def visitName(self, node, context = None, **kw): > name = node.name > if context == "Callable": > # Lookup the function only in mathfuncs > try: > return mathfuncs[name] > except KeyError: > raise CalcError("Undefined function", name) > else: > try: > return mathsymbols[name] > except KeyError: > raise CalcError("Undefined symbol",name) > > def default(self, node, **kw): > """Anything not expressly allowed is forbidden""" > raise CalcError("Syntax Error", > node.__class__.__name__,node) > > > def calc(source): > walker = EvalCalc() > try: > ast = compiler.parse(source,"eval") > except SyntaxError, err: > raise > try: > return walker.visit(ast) > except CalcError, err: > return err > > Examples: > > >>> calc("2+3*(4+5)*(7-3)**2") > 434 > >>> eval("2+3*(4+5)*(7-3)**2") # Check > 434 > >>> calc("sin(pi/2)") > 1.0 > >>> calc("sys.exit()") > Syntax Error: Getattr > >>> calc("0x1000 | 0x0100") > 4352 > >>> > > > Michael > > > > -- http://mail.python.org/mailman/listinfo/python-list