#!/usr/bin/python # -*- encoding: utf-8 -*- """Evaluate simple infix exprs with +, -, implicit multiply, parens, and vars. Not useful. Doesn’t: - provide useful error messages (in some cases it will give a spurious answer for a malformed expression); - handle bignums; - handle unary operators; - handle division or powers. This demonstrates that a few lines of higher-order functions can go a long way to building an embedded DSL (but can be hard to read), that stacks are totally hot shit for expression evaluation, and that Python’s built-in dicts, variable-size mutable lists and string handling go a long way. Originally this only did infix in 14 lines of code, but now with a leading `=` it does either infix or postfix in 17 lines. """ seq = lambda f, g: lambda *x: (lambda _: g(*x))(f(*x)) # Scheme macro defn push = lambda k: lambda s: s.append(k) add = lambda s: s.append(s.pop() + s.pop()) mul = lambda s: s.append(s.pop() * s.pop()) lit = lambda e, t: lambda s: s.append(float(t) if num(t) else e[t]) num = lambda t: all(c in '0123456789.' for c in t) ops = { '(': seq(push(0), push(1)), '+': seq(add, push(1)), ')': seq(add, mul), '-': seq(add, push(-1)), 'lit': mul } pop = lambda s: s.pop() rpn = { '=': pop, '*': mul, '+': add, 'lit': lambda s: None, '-': seq(push(-1), seq(mul, add)) } def compute(tokens, env, ops=ops): stack = [1] for token in tokens: ops.get(token, seq(lit(env, token), ops['lit']))(stack) rv, = stack return rv assert compute('( ( 3 x + 4 ) x + 5 )'.split(), {'x': 7}) == 180 assert compute('= 3 x * 4 + x * 5 +'.split(), {'x': 7}, rpn) == 180 if __name__ == '__main__': import sys env = {k: float(v) for k, v in (arg.split('=') for arg in sys.argv[2:])} if sys.argv[1].startswith('='): print compute(sys.argv[1].split(), env, ops=rpn) else: print compute(('( ' + sys.argv[1] + ' )').split(), env)