On 2013-01-04, Chris Angelico <ros...@gmail.com> wrote: > On Sat, Jan 5, 2013 at 4:14 AM, Grant Edwards <invalid@invalid.invalid> wrote: >> On 2013-01-04, Chris Angelico <ros...@gmail.com> wrote: >>> On Sat, Jan 5, 2013 at 3:38 AM, Grant Edwards <invalid@invalid.invalid> >>> wrote: >> >>>> I've added equals, backslash, commas, square/curly brackets, colons >>>> and semicolons to the prohibited character list. I also reduced the >>>> maximum length to 60 characters. It's unfortunate that parentheses >>>> are overloaded for both expression grouping and for function >>>> calling... >>> >>> I have to say that an expression evaluator that can't handle parens >>> for grouping is badly flawed. >> >> Indeed. That's why I didn't disallow parens. >> >> What I was implying was that since you have to allow parens for >> grouping, there's no simple way to disallow function calls. > > Yeah, and a safe evaluator that allows function calls is highly vulnerable. > >>> Can you demand that open parenthesis be preceded by an operator (or >>> beginning of line)? >> >> Yes, but once you've parsed the expression to the point where you can >> enforce rules like that, you're probably most of the way to doing the >> "right" thing and evaluating the expression using ast or pyparsing or >> similar. >> >> Some might argue that repeated tweaking of and adding limitiations to >> a "safe eval" is just heading down that same road in a different car. >> They'd probably be right: in the end, it will probably have been less >> work to just do it with ast. But it's still interesting to try. :) > > Yep, have fun with it. As mentioned earlier, though, security isn't > all that critical; so in this case, chances are you can just leave > parens permitted and let function calls potentially happen.
An ast-based evaluator wasn't as complicated as I first thought: the examples I'd been looking at implemented far more features than I needed. This morning I found a simpler example at http://stackoverflow.com/questions/2371436/evaluating-a-mathematical-expression-in-a-string The error messages are still pretty cryptic, so improving that will add a few more lines. One nice thing about the ast code is that it's simple to add code to allow C-like character constants such that ('A' === 0x41). Here's the first pass at ast-based code: import ast,operator operators = \ { ast.Add: operator.iadd, ast.Sub: operator.isub, ast.Mult: operator.imul, ast.Div: operator.idiv, ast.BitXor: operator.ixor, ast.BitAnd: operator.iand, ast.BitOr: operator.ior, ast.LShift: operator.lshift, ast.RShift: operator.rshift, ast.Invert: operator.invert, ast.USub: operator.neg, ast.UAdd: operator.pos, } def _eval_expr(node): global symbolTable if isinstance(node, ast.Name): if node.id not in symbolTable: raise ParseError("name '%s' undefined" % node.id) return symbolTable[node.id] elif isinstance(node, ast.Num): return node.n elif isinstance(node, ast.operator) or isinstance(node, ast.unaryop): return operators[type(node)] elif isinstance(node, ast.BinOp): return _eval_expr(node.op)(_eval_expr(node.left), _eval_expr(node.right)) elif isinstance(node, ast.UnaryOp): return _eval_expr(node.op)(_eval_expr(node.operand)) else: raise ParseError("error parsing expression at node %s" % node) def eval_expr(expr): return _eval_expr(ast.parse(expr).body[0].value) -- Grant Edwards grant.b.edwards Yow! A can of ASPARAGUS, at 73 pigeons, some LIVE ammo, gmail.com and a FROZEN DAQUIRI!! -- http://mail.python.org/mailman/listinfo/python-list